Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Load Magento urls from website/default base_url if enabled #567

Merged
merged 14 commits into from
Dec 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"prefer-stable": true,
"require": {
"php": "^8.1|^8.2|^8.3",
"rapidez/laravel-multi-cache": "^1.0",
"blade-ui-kit/blade-heroicons": "^2.4",
"mailerlite/laravel-elasticsearch": "^11.1",
"illuminate/database": "^10.0|^11.0",
Expand Down
3 changes: 3 additions & 0 deletions config/rapidez/system.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
// Elasticsearch prefix.
'es_prefix' => env('ELASTICSEARCH_PREFIX', 'rapidez'),

// Get Magento url from Database
'magento_url_from_db' => env('GET_MAGENTO_URL_FROM_DATABASE', false),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pros/cons of having this true/false by default?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pro for false:

  • no breaking changes by default, only when you opt into this functionality will it behave differently
  • after upgrading you have the time to first get your database in order before enabling this feature.
    Pro for true:
  • You get a much better experience with url handling out of the box since you no longer need to define the urls in Rapidez itself with subfolders working

If your database has the correct urls by default, and you don't have them set using env variables or config.php.
But this is an assumption we'd have to make when defaulting it to true

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! Just one thing left; documenting this option here: https://docs.rapidez.io/3.x/configuration.html#base-url


// Media url.
'media_url' => env('MEDIA_URL', env('MAGENTO_URL') . '/media'),

Expand Down
9 changes: 3 additions & 6 deletions src/Models/Attribute.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,9 @@ protected function filter(): CastsAttribute

public static function getCachedWhere(callable $callback): array
{
if (! $attributes = config('cache.app.attributes.' . config('rapidez.store'))) {
$attributes = Cache::rememberForever('attributes.' . config('rapidez.store'), function () {
return self::all()->toArray();
});
config(['cache.app.attributes.' . config('rapidez.store') => $attributes]);
}
$attributes = Cache::store('rapidez:multi')->rememberForever('attributes.' . config('rapidez.store'), function () {
return self::all()->toArray();
});

return Arr::where($attributes, function ($attribute) use ($callback) {
return $callback($attribute);
Expand Down
109 changes: 93 additions & 16 deletions src/Models/Config.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,17 @@
namespace Rapidez\Core\Models;

use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Cache;
use Rapidez\Core\Exceptions\DecryptionException;
use Rapidez\Core\Facades\Rapidez;

enum ConfigScopes
{
case SCOPE_STORE;
case SCOPE_WEBSITE;
case SCOPE_DEFAULT;
}

class Config extends Model
{
Expand All @@ -16,31 +25,99 @@ protected static function booting()
{
static::addGlobalScope('scope-fallback', function (Builder $builder) {
$builder
->where(function ($query) {
$query->where(function ($query) {
$query->where('scope', 'stores')->where('scope_id', config('rapidez.store'));
})->orWhere(function ($query) {
$query->where('scope', 'websites')->where('scope_id', config('rapidez.website'));
})->orWhere(function ($query) {
$query->where('scope', 'default')->where('scope_id', 0);
});
})
->orderByRaw('FIELD(scope, "stores", "websites", "default") ASC')
->where(fn ($query) => $query
->where(fn ($query) => $query->whereStore(config('rapidez.store')))
->orWhere(fn ($query) => $query->whereWebsite(config('rapidez.website')))
->orWhere(fn ($query) => $query->whereDefault())
)
->limit(1);
});

static::addGlobalScope('scope-default-sorting', function (Builder $builder) {
$builder
->orderByRaw('FIELD(scope, "stores", "websites", "default") ASC');
});
}

public function scopeWhereStore(Builder $query, ?int $storeId): void
{
$query->where('scope', 'stores')->where('scope_id', $storeId);
}

public function scopeWhereWebsite(Builder $query, ?int $websiteId): void
{
$query->where('scope', 'websites')->where('scope_id', $websiteId);
}

public function scopeWhereDefault(Builder $query): void
{
$query->where('scope', 'default')->where('scope_id', 0);
}

/**
* @deprecated see: Config::getValue
*/
public static function getCachedByPath(string $path, $default = false, bool $sensitive = false): string|bool
{
$cacheKey = 'config.' . config('rapidez.store') . '.' . str_replace('/', '.', $path);
return static::getValue($path, options: ['cache' => true, 'decrypt' => $sensitive]) ?? $default;
}

$value = Cache::rememberForever($cacheKey, function () use ($path, $default) {
$value = ($config = self::where('path', $path)->first('value')) ? $config->value : $default;
/**
* Magento compatible Config getValue method.
*/
public static function getValue(
string $path,
ConfigScopes $scope = ConfigScopes::SCOPE_STORE,
?int $scopeId = null,
array $options = ['cache' => true, 'decrypt' => false]
): mixed {
$scopeId ??= match ($scope) {
ConfigScopes::SCOPE_WEBSITE => config('rapidez.website') ?? Rapidez::getStore(config('rapidez.store'))['website_id'],
ConfigScopes::SCOPE_STORE => config('rapidez.store'),
default => 0
};

return ! is_null($value) ? $value : false;
});
if ($options['cache'] ?? true) {
$configCache = Cache::driver('rapidez:multi')->get('magento.config', []);
$cacheKey = implode(
'.',
[
match ($scope) {
ConfigScopes::SCOPE_WEBSITE => 'website',
ConfigScopes::SCOPE_STORE => 'store',
default => 'global'
},
$scopeId,
$path,
]
);
// Catch the case it is intentionally set to null
if (Arr::has($configCache, $cacheKey)) {
$result = Arr::get($configCache, $cacheKey);

return (bool) $options['decrypt'] && is_string($result) ? static::decrypt($result) : $result;
}
}

$websiteId = $scope === ConfigScopes::SCOPE_STORE ? Rapidez::getStore($scopeId)['website_id'] : $scopeId;

$result = static::query()
->withoutGlobalScope('scope-fallback')
->where('path', $path)
->where(fn ($query) => $query
->when($scope === ConfigScopes::SCOPE_STORE, fn ($query) => $query->whereStore($scopeId))
->when($scope !== ConfigScopes::SCOPE_DEFAULT, fn ($query) => $query->orWhere(fn ($query) => $query->whereWebsite($websiteId)))
->orWhere(fn ($query) => $query->whereDefault())
)
->first('value')
?->value;

if ($options['cache'] ?? true) {
Arr::set($configCache, $cacheKey, $result);
Cache::driver('rapidez:multi')->set('magento.config', $configCache);
}

return $sensitive && $value ? self::decrypt($value) : $value;
return (bool) $options['decrypt'] && is_string($result) ? static::decrypt($result) : $result;
}

public static function decrypt(string $value): string
Expand Down
24 changes: 11 additions & 13 deletions src/Models/OptionValue.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,18 @@ class OptionValue extends Model

public static function getCachedByOptionId(int $optionId): string
{
$cacheKey = 'optionvalue.' . config('rapidez.store') . '.' . $optionId;

if (! $optionValue = config('cache.app.' . $cacheKey)) {
$optionValue = Cache::rememberForever($cacheKey, function () use ($optionId) {
return html_entity_decode(self::where('option_id', $optionId)
->whereIn('store_id', [config('rapidez.store'), 0])
->orderByDesc('store_id')
->first('value')
->value ?? false);
});

config(['cache.app.' . $cacheKey => $optionValue]);
$cacheKey = 'optionvalues.' . config('rapidez.store');
$cache = Cache::store('rapidez:multi')->get($cacheKey, []);

if (! isset($cache[$optionId])) {
$cache[$optionId] = html_entity_decode(self::where('option_id', $optionId)
->whereIn('store_id', [config('rapidez.store'), 0])
->orderByDesc('store_id')
->first('value')
->value ?? false);
Cache::store('rapidez:multi')->forever($cacheKey, $cache);
}

return $optionValue;
return $cache[$optionId];
}
}
25 changes: 11 additions & 14 deletions src/Models/Store.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,20 +27,17 @@ protected static function booting()

public static function getCached(): array
{
if (! $stores = config('cache.app.stores')) {
$stores = Cache::rememberForever('stores', function () {
return self::select([
'store_id',
'store.name',
'store.code',
'store.website_id',
'store.group_id',
'store_group.root_category_id',
'store_website.code AS website_code',
])->get()->keyBy('store_id')->toArray();
});
config(['cache.app.stores' => $stores]);
}
$stores = Cache::store('rapidez:multi')->rememberForever('stores', function () {
return self::select([
'store_id',
'store.name',
'store.code',
'store.website_id',
'store.group_id',
'store_group.root_category_id',
'store_website.code AS website_code',
])->get()->keyBy('store_id')->toArray();
});

return $stores;
}
Expand Down
41 changes: 40 additions & 1 deletion src/Rapidez.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\URL;
use Rapidez\Core\Models\Config;
use Rapidez\Core\Models\ConfigScopes;
use Rapidez\Core\Models\Store;
use ReflectionClass;

Expand Down Expand Up @@ -56,7 +59,7 @@ public function getAllFallbackRoutes()

public function config(string $path, $default = null, bool $sensitive = false): ?string
{
return config('rapidez.models.config')::getCachedByPath($path, $default, $sensitive);
return config('rapidez.models.config')::getValue($path, options: ['cache' => true, 'decrypt' => $sensitive]) ?? $default;
}

public function content($content)
Expand Down Expand Up @@ -126,6 +129,42 @@ public function setStore(Store|array|callable|int|string $store): void
config()->set('rapidez.root_category_id', $store['root_category_id']);
config()->set('frontend.base_url', url('/'));

if (config()->get('rapidez.magento_url_from_db', false)) {
$magentoUrl = trim(
Config::getValue('web/secure/base_url', ConfigScopes::SCOPE_WEBSITE) ?? config()->get('rapidez.magento_url'),
'/'
);

$storeUrl = trim(
Config::getValue('web/secure/base_url', ConfigScopes::SCOPE_STORE) ?? config()->get('frontend.base_url'),
'/'
);

// Make sure the store url is not the same as the magentoUrl
if ($magentoUrl !== $storeUrl) {
URL::forceRootUrl($storeUrl);
}

// Make sure the Magento url is not the Rapidez url before setting it
if ($magentoUrl !== url('/')) {
config()->set('rapidez.magento_url', $magentoUrl);
}

$mediaUrl = trim(
str_replace(
['{{secure_base_url}}', '{{unsecure_base_url}}'],
config()->get('rapidez.magento_url') . '/',
Config::getValue('web/secure/base_media_url', ConfigScopes::SCOPE_WEBSITE) ?? config()->get('rapidez.media_url')
),
'/'
);
// Make sure the Magento media url is not the same as Rapidez url before setting it
if ($mediaUrl !== url('/media')) {
config()->set('rapidez.media_url', $mediaUrl);
}
}

config()->set('frontend.base_url', url('/'));
App::setLocale(strtok(Rapidez::config('general/locale/code', 'en_US'), '_'));

Event::dispatch('rapidez:store-set', [$store]);
Expand Down
18 changes: 18 additions & 0 deletions src/RapidezServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Blade;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\View;
use Illuminate\Support\Facades\Vite;
use Illuminate\Support\ServiceProvider;
Expand Down Expand Up @@ -282,6 +283,23 @@ protected function registerConfigs(): self
$this->mergeConfigFrom(__DIR__ . '/../config/rapidez/' . $configFile . '.php', 'rapidez.' . $configFile);
}

if (! config('cache.stores.rapidez:multi', false)) {
$fallbackDriver = config('cache.default');
if ($fallbackDriver === 'rapidez:multi') {
$fallbackDriver = config('cache.multi-fallback', 'file');
Log::warning('Default cache driver is rapidez:multi, setting fallback driver to ' . $fallbackDriver);
}

config()->set('cache.stores.rapidez:multi', [
'driver' => 'multi',
'stores' => [
'array',
$fallbackDriver,
],
'sync_missed_stores' => true,
]);
}

return $this;
}

Expand Down
2 changes: 2 additions & 0 deletions tests/DuskTestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use Rapidez\BladeDirectives\BladeDirectivesServiceProvider;
use Rapidez\Core\Facades\Rapidez;
use Rapidez\Core\RapidezServiceProvider;
use Rapidez\LaravelMultiCache\MultiStoreServiceProvider;
use TorMorten\Eventy\EventServiceProvider;

abstract class DuskTestCase extends BaseTestCase
Expand All @@ -18,6 +19,7 @@ protected function getPackageProviders($app)
{
return [
EventServiceProvider::class,
MultiStoreServiceProvider::class,
RapidezServiceProvider::class,
BladeIconsServiceProvider::class,
BladeHeroiconsServiceProvider::class,
Expand Down
2 changes: 2 additions & 0 deletions tests/TestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use Orchestra\Testbench\TestCase as BaseTestCase;
use Rapidez\Core\Facades\Rapidez;
use Rapidez\Core\RapidezServiceProvider;
use Rapidez\LaravelMultiCache\MultiStoreServiceProvider;
use TorMorten\Eventy\EventServiceProvider;

class TestCase extends BaseTestCase
Expand All @@ -21,6 +22,7 @@ protected function getPackageProviders($app)
{
return [
EventServiceProvider::class,
MultiStoreServiceProvider::class,
RapidezServiceProvider::class,
];
}
Expand Down
Loading