From 90e32e337d7ad6e272326e01c69881cd4a9341b5 Mon Sep 17 00:00:00 2001 From: elvira Date: Wed, 26 Aug 2026 18:28:16 +0300 Subject: [PATCH] localization from boboko core and product service too --- .env.example | 6 + app/Http/Controllers/CategoryController.php | 46 +++- app/Http/Middleware/SetLocale.php | 49 ---- bootstrap/app.php | 4 +- composer.lock | 144 ++++++------ config/app.php | 13 -- config/lunar/search.php | 2 +- config/scout.php | 210 ++++++++++++++++++ docker-compose.dev.yml | 12 + docker-compose.yml | 15 ++ docker/entrypoint.sh | 8 +- .../__preview_newsletter_split.blade.php | 7 - resources/views/components/header.blade.php | 8 +- resources/views/layouts/app.blade.php | 6 +- routes/web.php | 13 +- 15 files changed, 374 insertions(+), 169 deletions(-) delete mode 100644 app/Http/Middleware/SetLocale.php create mode 100644 config/scout.php delete mode 100644 resources/views/__preview_newsletter_split.blade.php diff --git a/.env.example b/.env.example index 7a1538a..1e642cb 100644 --- a/.env.example +++ b/.env.example @@ -79,6 +79,11 @@ STOIC_SECRET=dev-secret-change-me STOIC_SSO_SECRET=dev-sso-secret-change-me STOIC_HOST=http://localhost:2727 +SCOUT_DRIVER=meilisearch +SCOUT_QUEUE=true +MEILISEARCH_KEY=dev-meilisearch-key-change-me +MEILISEARCH_HOST=http://meilisearch:7700 + # Host-side ports — change these if another project on this machine already uses the defaults APP_PORT=8090 STOIC_PORT=2727 @@ -86,3 +91,4 @@ VALKEY_PORT=6339 POSTGRES_HOST_PORT=5436 MAILPIT_PORT=8025 VITE_PORT=5173 +MEILISEARCH_PORT=7700 diff --git a/app/Http/Controllers/CategoryController.php b/app/Http/Controllers/CategoryController.php index 7c2f4bb..406e6bf 100644 --- a/app/Http/Controllers/CategoryController.php +++ b/app/Http/Controllers/CategoryController.php @@ -2,24 +2,46 @@ namespace App\Http\Controllers; +use Illuminate\Pagination\LengthAwarePaginator; use Lunar\Models\Collection; -use Lunar\Models\Product; +use Modules\Core\Catalog\ProductFilters; +use Modules\Core\Catalog\ProductService; class CategoryController extends Controller { + public function __construct( + private readonly ProductService $products, + ) {} + public function show(string $locale, Collection $collection) { - $products = $collection - ->products() - ->with(['variants.prices.currency', 'media']) - ->paginate(12) - ->withQueryString() - ->through(fn (Product $product) => [ - 'name' => $product->translateAttribute('name'), - 'price' => $product->variants->first()?->prices->first()?->price->decimal, - 'image' => $product->media->first()?->getUrl(), - 'href' => route('product.show', ['product' => $product]), - ]); + $perPage = 12; + $page = (int) request('page', 1); + + // Listing/filtering reads from the Meilisearch index via ProductService, + // not Eloquent — see Modules\Core\Catalog\ProductService. It returns plain + // arrays (already localized/flattened), not Product models. + $result = $this->products->list( + filters: new ProductFilters(collectionId: $collection->id), + perPage: $perPage, + page: $page, + ); + + $products = new LengthAwarePaginator( + items: collect($result['data'])->map(fn (array $product) => [ + 'name' => $product['name'], + 'price' => $product['price'], + 'image' => $product['media'][0]['url'] ?? null, + 'href' => route('product.show', ['product' => $product['id']]), + ]), + total: $result['meta']['total'], + perPage: $result['meta']['per_page'], + currentPage: $result['meta']['current_page'], + options: [ + 'path' => request()->url(), + 'query' => request()->query(), + ], + ); return view('category.show', [ 'collection' => $collection, diff --git a/app/Http/Middleware/SetLocale.php b/app/Http/Middleware/SetLocale.php deleted file mode 100644 index b24d142..0000000 --- a/app/Http/Middleware/SetLocale.php +++ /dev/null @@ -1,49 +0,0 @@ -route('locale'); - $available = config('app.available_locales'); - - if (! in_array($locale, $available, true)) { - abort(404); - } - - App::setLocale($locale); - - // Lets route() calls omit {locale} anywhere in the request lifecycle - // (controllers, views) — without this, every route() call inside the - // {locale} group would need locale passed explicitly every time. - URL::defaults(['locale' => $locale]); - - $altLocale = collect($available)->first(fn ($l) => $l !== $locale); - $routeName = $request->route()->getName(); - - View::share('currentLocale', $locale); - View::share('altLocale', $altLocale); - View::share( - 'altLocaleUrl', - $routeName - ? route($routeName, array_merge($request->route()->parameters(), ['locale' => $altLocale])) - : url('/'.$altLocale), - ); - - return $next($request); - } -} diff --git a/bootstrap/app.php b/bootstrap/app.php index 3acb151..c183276 100644 --- a/bootstrap/app.php +++ b/bootstrap/app.php @@ -11,9 +11,7 @@ health: '/up', ) ->withMiddleware(function (Middleware $middleware): void { - $middleware->alias([ - 'setlocale' => \App\Http\Middleware\SetLocale::class, - ]); + // }) ->withExceptions(function (Exceptions $exceptions): void { // diff --git a/composer.lock b/composer.lock index a494efc..3b75e00 100644 --- a/composer.lock +++ b/composer.lock @@ -565,11 +565,11 @@ }, { "name": "boboko/core", - "version": "0.5.0", + "version": "0.5.3", "source": { "type": "git", "url": "https://code.radical-elements.com/boboko/core.git", - "reference": "e31de1b4e3894c70a82a8d939b094bf305588eb5" + "reference": "09844ec2b5e26792722031b9dc2faa49205f6778" }, "require": { "laravel/framework": "^12.0", @@ -610,7 +610,7 @@ } }, "description": "Core module — authentication and shared panel behaviour", - "time": "2026-08-24T11:47:59+00:00" + "time": "2026-08-26T11:29:29+00:00" }, { "name": "brick/math", @@ -2438,23 +2438,23 @@ }, { "name": "google/protobuf", - "version": "v4.33.6", + "version": "v5.36.0", "source": { "type": "git", "url": "https://github.com/protocolbuffers/protobuf-php.git", - "reference": "84b008c23915ed94536737eae46f41ba3bccfe67" + "reference": "9c105104b54709ecd902494ab340ed2122789b2d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/protocolbuffers/protobuf-php/zipball/84b008c23915ed94536737eae46f41ba3bccfe67", - "reference": "84b008c23915ed94536737eae46f41ba3bccfe67", + "url": "https://api.github.com/repos/protocolbuffers/protobuf-php/zipball/9c105104b54709ecd902494ab340ed2122789b2d", + "reference": "9c105104b54709ecd902494ab340ed2122789b2d", "shasum": "" }, "require": { - "php": ">=8.1.0" + "php": ">=8.2.0" }, "require-dev": { - "phpunit/phpunit": ">=10.5.62 <11.0.0" + "phpunit/phpunit": ">=11.5.50 <12.0.0" }, "suggest": { "ext-bcmath": "Need to support JSON deserialization" @@ -2476,9 +2476,9 @@ "proto" ], "support": { - "source": "https://github.com/protocolbuffers/protobuf-php/tree/v4.33.6" + "source": "https://github.com/protocolbuffers/protobuf-php/tree/v5.36.0" }, - "time": "2026-03-18T17:32:05+00:00" + "time": "2026-08-20T13:06:50+00:00" }, { "name": "graham-campbell/result-type", @@ -3019,16 +3019,16 @@ }, { "name": "jacobjoergensen/laravel-paper", - "version": "1.15.0", + "version": "1.16.0", "source": { "type": "git", "url": "https://github.com/JacobJoergensen/laravel-paper.git", - "reference": "024ced2ee5469ab511f957f9a505f5db5745d32b" + "reference": "3bcdc1d929ace91e138b268d2394830d559e3695" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/JacobJoergensen/laravel-paper/zipball/024ced2ee5469ab511f957f9a505f5db5745d32b", - "reference": "024ced2ee5469ab511f957f9a505f5db5745d32b", + "url": "https://api.github.com/repos/JacobJoergensen/laravel-paper/zipball/3bcdc1d929ace91e138b268d2394830d559e3695", + "reference": "3bcdc1d929ace91e138b268d2394830d559e3695", "shasum": "" }, "require": { @@ -3081,9 +3081,9 @@ ], "support": { "issues": "https://github.com/JacobJoergensen/laravel-paper/issues", - "source": "https://github.com/JacobJoergensen/laravel-paper/tree/1.15.0" + "source": "https://github.com/JacobJoergensen/laravel-paper/tree/1.16.0" }, - "time": "2026-07-30T21:15:25+00:00" + "time": "2026-08-25T18:56:34+00:00" }, { "name": "kalnoy/nestedset", @@ -5855,16 +5855,16 @@ }, { "name": "open-telemetry/api", - "version": "1.9.0", + "version": "1.10.0", "source": { "type": "git", "url": "https://github.com/opentelemetry-php/api.git", - "reference": "6f8d237ce2c304ca85f31970f788e7f074d147be" + "reference": "7c029c4a6fd457094a20569bf98f93d95e9a7559" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/opentelemetry-php/api/zipball/6f8d237ce2c304ca85f31970f788e7f074d147be", - "reference": "6f8d237ce2c304ca85f31970f788e7f074d147be", + "url": "https://api.github.com/repos/opentelemetry-php/api/zipball/7c029c4a6fd457094a20569bf98f93d95e9a7559", + "reference": "7c029c4a6fd457094a20569bf98f93d95e9a7559", "shasum": "" }, "require": { @@ -5921,7 +5921,7 @@ "issues": "https://github.com/open-telemetry/opentelemetry-php/issues", "source": "https://github.com/open-telemetry/opentelemetry-php" }, - "time": "2026-02-25T13:24:05+00:00" + "time": "2026-07-06T12:28:04+00:00" }, { "name": "open-telemetry/context", @@ -6048,20 +6048,20 @@ }, { "name": "open-telemetry/gen-otlp-protobuf", - "version": "1.9.0", + "version": "1.10.0", "source": { "type": "git", "url": "https://github.com/opentelemetry-php/gen-otlp-protobuf.git", - "reference": "a229cf161d42001d64c8f21e8f678581fe1c66b9" + "reference": "66f04d0e448ad333033bfc7baae1aa56330be088" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/opentelemetry-php/gen-otlp-protobuf/zipball/a229cf161d42001d64c8f21e8f678581fe1c66b9", - "reference": "a229cf161d42001d64c8f21e8f678581fe1c66b9", + "url": "https://api.github.com/repos/opentelemetry-php/gen-otlp-protobuf/zipball/66f04d0e448ad333033bfc7baae1aa56330be088", + "reference": "66f04d0e448ad333033bfc7baae1aa56330be088", "shasum": "" }, "require": { - "google/protobuf": "^3.22 || ^4.0", + "google/protobuf": "^3.22 || ^4.0 || ^5.0", "php": "^8.0" }, "suggest": { @@ -6107,20 +6107,20 @@ "issues": "https://github.com/open-telemetry/opentelemetry-php/issues", "source": "https://github.com/open-telemetry/opentelemetry-php" }, - "time": "2025-10-19T06:44:33+00:00" + "time": "2026-06-17T12:06:32+00:00" }, { "name": "open-telemetry/opentelemetry-auto-laravel", - "version": "1.7.0", + "version": "1.8.0", "source": { "type": "git", "url": "https://github.com/opentelemetry-php/contrib-auto-laravel.git", - "reference": "4dbf215ac9caee21a7b4c2f1d819a3bf4bbba6bb" + "reference": "f0a750eeb0a69012d81c84010a4c7a7b61926e00" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/opentelemetry-php/contrib-auto-laravel/zipball/4dbf215ac9caee21a7b4c2f1d819a3bf4bbba6bb", - "reference": "4dbf215ac9caee21a7b4c2f1d819a3bf4bbba6bb", + "url": "https://api.github.com/repos/opentelemetry-php/contrib-auto-laravel/zipball/f0a750eeb0a69012d81c84010a4c7a7b61926e00", + "reference": "f0a750eeb0a69012d81c84010a4c7a7b61926e00", "shasum": "" }, "require": { @@ -6141,7 +6141,7 @@ "php-http/mock-client": "*", "phpstan/phpstan": "^1.1", "phpstan/phpstan-phpunit": "^1.0", - "phpunit/phpunit": "^9.5", + "phpunit/phpunit": "^9.5 || ^10 || ^11 || ^12 || ^13", "psalm/plugin-phpunit": "^0.19.2", "spatie/laravel-ignition": "*", "vimeo/psalm": "6.4.0" @@ -6174,22 +6174,22 @@ "tracing" ], "support": { - "source": "https://github.com/opentelemetry-php/contrib-auto-laravel/tree/1.7.0" + "source": "https://github.com/opentelemetry-php/contrib-auto-laravel/tree/1.8.0" }, - "time": "2026-04-29T13:32:51+00:00" + "time": "2026-07-09T20:34:43+00:00" }, { "name": "open-telemetry/sdk", - "version": "1.14.0", + "version": "1.15.0", "source": { "type": "git", "url": "https://github.com/opentelemetry-php/sdk.git", - "reference": "6e3d0ce93e76555dd5e2f1d19443ff45b990e410" + "reference": "77e1aa73850154abb86937d52a70883edc3b4547" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/opentelemetry-php/sdk/zipball/6e3d0ce93e76555dd5e2f1d19443ff45b990e410", - "reference": "6e3d0ce93e76555dd5e2f1d19443ff45b990e410", + "url": "https://api.github.com/repos/opentelemetry-php/sdk/zipball/77e1aa73850154abb86937d52a70883edc3b4547", + "reference": "77e1aa73850154abb86937d52a70883edc3b4547", "shasum": "" }, "require": { @@ -6197,7 +6197,7 @@ "nyholm/psr7-server": "^1.1", "open-telemetry/api": "^1.8", "open-telemetry/context": "^1.4", - "open-telemetry/sem-conv": "^1.0", + "open-telemetry/sem-conv": "^1.38.0", "php": "^8.1", "php-http/discovery": "^1.14", "psr/http-client": "^1.0", @@ -6220,7 +6220,10 @@ "spi": { "OpenTelemetry\\API\\Configuration\\ConfigEnv\\EnvComponentLoader": [ "OpenTelemetry\\API\\Instrumentation\\Configuration\\General\\ConfigEnv\\EnvComponentLoaderHttpConfig", - "OpenTelemetry\\API\\Instrumentation\\Configuration\\General\\ConfigEnv\\EnvComponentLoaderPeerConfig" + "OpenTelemetry\\API\\Instrumentation\\Configuration\\General\\ConfigEnv\\EnvComponentLoaderPeerConfig", + "OpenTelemetry\\SDK\\ConfigEnv\\Trace\\SpanSuppressionStrategySemConv", + "OpenTelemetry\\SDK\\ConfigEnv\\Trace\\SpanSuppressionStrategySpanKind", + "OpenTelemetry\\SDK\\ConfigEnv\\Distribution\\DistributionConfigurationSdk" ], "OpenTelemetry\\SDK\\Common\\Configuration\\Resolver\\ResolverInterface": [ "OpenTelemetry\\SDK\\Common\\Configuration\\Resolver\\SdkConfigurationResolver" @@ -6230,7 +6233,7 @@ ] }, "branch-alias": { - "dev-main": "1.12.x-dev" + "dev-main": "1.14.x-dev" } }, "autoload": { @@ -6273,7 +6276,7 @@ "issues": "https://github.com/open-telemetry/opentelemetry-php/issues", "source": "https://github.com/open-telemetry/opentelemetry-php" }, - "time": "2026-03-21T11:50:01+00:00" + "time": "2026-07-14T13:09:54+00:00" }, { "name": "open-telemetry/sem-conv", @@ -8258,22 +8261,23 @@ }, { "name": "spatie/crawler", - "version": "9.3.2", + "version": "9.4.2", "source": { "type": "git", "url": "https://github.com/spatie/crawler.git", - "reference": "3b3a9a1a8c750b0240c2b7c9e2ef22e84092416e" + "reference": "937bcf5786e66bca20da50db004f6d1443e81418" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/crawler/zipball/3b3a9a1a8c750b0240c2b7c9e2ef22e84092416e", - "reference": "3b3a9a1a8c750b0240c2b7c9e2ef22e84092416e", + "url": "https://api.github.com/repos/spatie/crawler/zipball/937bcf5786e66bca20da50db004f6d1443e81418", + "reference": "937bcf5786e66bca20da50db004f6d1443e81418", "shasum": "" }, "require": { - "guzzlehttp/guzzle": "^7.3", - "guzzlehttp/psr7": "^2.0", + "guzzlehttp/guzzle": "^7.8.2 || ^8.0", + "guzzlehttp/psr7": "^2.6.3 || ^3.0", "php": "^8.4", + "psr/http-client": "^1.0", "spatie/robots-txt": "^2.0", "symfony/css-selector": "^7.0|^8.0", "symfony/dom-crawler": "^7.0|^8.0" @@ -8321,7 +8325,7 @@ ], "support": { "issues": "https://github.com/spatie/crawler/issues", - "source": "https://github.com/spatie/crawler/tree/9.3.2" + "source": "https://github.com/spatie/crawler/tree/9.4.2" }, "funding": [ { @@ -8333,7 +8337,7 @@ "type": "github" } ], - "time": "2026-06-12T15:45:15+00:00" + "time": "2026-08-07T07:19:51+00:00" }, { "name": "spatie/image", @@ -8887,16 +8891,16 @@ }, { "name": "spatie/laravel-package-tools", - "version": "1.93.1", + "version": "1.93.2", "source": { "type": "git", "url": "https://github.com/spatie/laravel-package-tools.git", - "reference": "d5552849801f2642aea710557463234b59ef65eb" + "reference": "e927d5b5b05e9fecae098e559e51918aa608ad02" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/laravel-package-tools/zipball/d5552849801f2642aea710557463234b59ef65eb", - "reference": "d5552849801f2642aea710557463234b59ef65eb", + "url": "https://api.github.com/repos/spatie/laravel-package-tools/zipball/e927d5b5b05e9fecae098e559e51918aa608ad02", + "reference": "e927d5b5b05e9fecae098e559e51918aa608ad02", "shasum": "" }, "require": { @@ -8936,7 +8940,7 @@ ], "support": { "issues": "https://github.com/spatie/laravel-package-tools/issues", - "source": "https://github.com/spatie/laravel-package-tools/tree/1.93.1" + "source": "https://github.com/spatie/laravel-package-tools/tree/1.93.2" }, "funding": [ { @@ -8944,7 +8948,7 @@ "type": "github" } ], - "time": "2026-05-19T14:06:37+00:00" + "time": "2026-08-26T09:13:25+00:00" }, { "name": "spatie/laravel-passkeys", @@ -9256,21 +9260,21 @@ }, { "name": "spatie/laravel-webhook-server", - "version": "3.10.0", + "version": "3.11.0", "source": { "type": "git", "url": "https://github.com/spatie/laravel-webhook-server.git", - "reference": "6106840254e22b667e77a885da69fff6035549ba" + "reference": "3e3b14669de03555d943b9fc008bc58712712565" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/laravel-webhook-server/zipball/6106840254e22b667e77a885da69fff6035549ba", - "reference": "6106840254e22b667e77a885da69fff6035549ba", + "url": "https://api.github.com/repos/spatie/laravel-webhook-server/zipball/3e3b14669de03555d943b9fc008bc58712712565", + "reference": "3e3b14669de03555d943b9fc008bc58712712565", "shasum": "" }, "require": { "ext-json": "*", - "guzzlehttp/guzzle": "^6.3|^7.3", + "guzzlehttp/guzzle": "^6.3|^7.3|^8.0", "illuminate/bus": "^8.50|^9.0|^10.0|^11.0|^12.0|^13.0", "illuminate/queue": "^8.50|^9.0|^10.0|^11.0|^12.0|^13.0", "illuminate/support": "^8.50|^9.0|^10.0|^11.0|^12.0|^13.0", @@ -9318,7 +9322,7 @@ "webhook" ], "support": { - "source": "https://github.com/spatie/laravel-webhook-server/tree/3.10.0" + "source": "https://github.com/spatie/laravel-webhook-server/tree/3.11.0" }, "funding": [ { @@ -9326,7 +9330,7 @@ "type": "custom" } ], - "time": "2026-02-21T15:14:00+00:00" + "time": "2026-08-23T23:33:55+00:00" }, { "name": "spatie/php-structure-discoverer", @@ -10087,16 +10091,16 @@ }, { "name": "symfony/dom-crawler", - "version": "v8.1.1", + "version": "v8.1.5", "source": { "type": "git", "url": "https://github.com/symfony/dom-crawler.git", - "reference": "1dfadd25537c8fcb6752cce5775f24647d976bdc" + "reference": "94f70e1b2e5b7b8f8618871fd9eaad3e39e9f03d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/1dfadd25537c8fcb6752cce5775f24647d976bdc", - "reference": "1dfadd25537c8fcb6752cce5775f24647d976bdc", + "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/94f70e1b2e5b7b8f8618871fd9eaad3e39e9f03d", + "reference": "94f70e1b2e5b7b8f8618871fd9eaad3e39e9f03d", "shasum": "" }, "require": { @@ -10133,7 +10137,7 @@ "description": "Eases DOM navigation for HTML and XML documents", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/dom-crawler/tree/v8.1.1" + "source": "https://github.com/symfony/dom-crawler/tree/v8.1.5" }, "funding": [ { @@ -10153,7 +10157,7 @@ "type": "tidelift" } ], - "time": "2026-06-05T06:23:12+00:00" + "time": "2026-08-21T17:47:34+00:00" }, { "name": "symfony/error-handler", diff --git a/config/app.php b/config/app.php index fffad52..a4a9368 100644 --- a/config/app.php +++ b/config/app.php @@ -84,19 +84,6 @@ 'faker_locale' => env('APP_FAKER_LOCALE', 'en_US'), - /* - |-------------------------------------------------------------------------- - | Available Locales - |-------------------------------------------------------------------------- - | - | The locales the site is served in, and the ones allowed as the - | {locale} route segment (see routes/web.php and SetLocale middleware). - | Greek is the primary/default market, English is secondary. - | - */ - - 'available_locales' => ['el', 'en'], - /* |-------------------------------------------------------------------------- | Encryption Key diff --git a/config/lunar/search.php b/config/lunar/search.php index d9741b0..a976277 100644 --- a/config/lunar/search.php +++ b/config/lunar/search.php @@ -49,7 +49,7 @@ Lunar\Models\Collection::class => Lunar\Search\CollectionIndexer::class, Lunar\Models\Customer::class => Lunar\Search\CustomerIndexer::class, Lunar\Models\Order::class => Lunar\Search\OrderIndexer::class, - Lunar\Models\Product::class => Lunar\Search\ProductIndexer::class, + Lunar\Models\Product::class => Modules\Core\Search\ProductIndexer::class, Lunar\Models\ProductOption::class => Lunar\Search\ProductOptionIndexer::class, ], diff --git a/config/scout.php b/config/scout.php new file mode 100644 index 0000000..57bc34b --- /dev/null +++ b/config/scout.php @@ -0,0 +1,210 @@ + env('SCOUT_DRIVER', 'collection'), + + /* + |-------------------------------------------------------------------------- + | Index Prefix + |-------------------------------------------------------------------------- + | + | Here you may specify a prefix that will be applied to all search index + | names used by Scout. This prefix may be useful if you have multiple + | "tenants" or applications sharing the same search infrastructure. + | + */ + + 'prefix' => env('SCOUT_PREFIX', ''), + + /* + |-------------------------------------------------------------------------- + | Queue Data Syncing + |-------------------------------------------------------------------------- + | + | This option allows you to control if the operations that sync your data + | with your search engines are queued. When this is set to "true" then + | all automatic data syncing will get queued for better performance. + | + */ + + 'queue' => env('SCOUT_QUEUE', false), + + /* + |-------------------------------------------------------------------------- + | Database Transactions + |-------------------------------------------------------------------------- + | + | This configuration option determines if your data will only be synced + | with your search indexes after every open database transaction has + | been committed, thus preventing any discarded data from syncing. + | + */ + + 'after_commit' => false, + + /* + |-------------------------------------------------------------------------- + | Chunk Sizes + |-------------------------------------------------------------------------- + | + | These options allow you to control the maximum chunk size when you are + | mass importing data into the search engine. This allows you to fine + | tune each of these chunk sizes based on the power of the servers. + | + */ + + 'chunk' => [ + 'searchable' => 500, + 'unsearchable' => 500, + ], + + /* + |-------------------------------------------------------------------------- + | Soft Deletes + |-------------------------------------------------------------------------- + | + | This option allows to control whether to keep soft deleted records in + | the search indexes. Maintaining soft deleted records can be useful + | if your application still needs to search for the records later. + | + */ + + 'soft_delete' => false, + + /* + |-------------------------------------------------------------------------- + | Identify User + |-------------------------------------------------------------------------- + | + | This option allows you to control whether to notify the search engine + | of the user performing the search. This is sometimes useful if the + | engine supports any analytics based on this application's users. + | + | Supported engines: "algolia" + | + */ + + 'identify' => env('SCOUT_IDENTIFY', false), + + /* + |-------------------------------------------------------------------------- + | Algolia Configuration + |-------------------------------------------------------------------------- + | + | Here you may configure your Algolia settings. Algolia is a cloud hosted + | search engine which works great with Scout out of the box. Just plug + | in your application ID and admin API key to get started searching. + | + */ + + 'algolia' => [ + 'id' => env('ALGOLIA_APP_ID', ''), + 'secret' => env('ALGOLIA_SECRET', ''), + 'index-settings' => [ + // 'users' => [ + // 'searchableAttributes' => ['id', 'name', 'email'], + // 'attributesForFaceting'=> ['filterOnly(email)'], + // ], + ], + ], + + /* + |-------------------------------------------------------------------------- + | Meilisearch Configuration + |-------------------------------------------------------------------------- + | + | Here you may configure your Meilisearch settings. Meilisearch is an open + | source search engine with minimal configuration. Below, you can state + | the host and key information for your own Meilisearch installation. + | + | See: https://www.meilisearch.com/docs/learn/configuration/instance_options#all-instance-options + | + */ + + 'meilisearch' => [ + 'host' => env('MEILISEARCH_HOST', 'http://localhost:7700'), + 'key' => env('MEILISEARCH_KEY'), + 'index-settings' => [ + // 'users' => [ + // 'filterableAttributes'=> ['id', 'name', 'email'], + // ], + ], + ], + + /* + |-------------------------------------------------------------------------- + | Typesense Configuration + |-------------------------------------------------------------------------- + | + | Here you may configure your Typesense settings. Typesense is an open + | source search engine using minimal configuration. Below, you will + | state the host, key, and schema configuration for the instance. + | + */ + + 'typesense' => [ + 'client-settings' => [ + 'api_key' => env('TYPESENSE_API_KEY', 'xyz'), + 'nodes' => [ + [ + 'host' => env('TYPESENSE_HOST', 'localhost'), + 'port' => env('TYPESENSE_PORT', '8108'), + 'path' => env('TYPESENSE_PATH', ''), + 'protocol' => env('TYPESENSE_PROTOCOL', 'http'), + ], + ], + 'nearest_node' => [ + 'host' => env('TYPESENSE_HOST', 'localhost'), + 'port' => env('TYPESENSE_PORT', '8108'), + 'path' => env('TYPESENSE_PATH', ''), + 'protocol' => env('TYPESENSE_PROTOCOL', 'http'), + ], + 'connection_timeout_seconds' => env('TYPESENSE_CONNECTION_TIMEOUT_SECONDS', 2), + 'healthcheck_interval_seconds' => env('TYPESENSE_HEALTHCHECK_INTERVAL_SECONDS', 30), + 'num_retries' => env('TYPESENSE_NUM_RETRIES', 3), + 'retry_interval_seconds' => env('TYPESENSE_RETRY_INTERVAL_SECONDS', 1), + ], + // 'max_total_results' => env('TYPESENSE_MAX_TOTAL_RESULTS', 1000), + 'model-settings' => [ + // User::class => [ + // 'collection-schema' => [ + // 'fields' => [ + // [ + // 'name' => 'id', + // 'type' => 'string', + // ], + // [ + // 'name' => 'name', + // 'type' => 'string', + // ], + // [ + // 'name' => 'created_at', + // 'type' => 'int64', + // ], + // ], + // 'default_sorting_field' => 'created_at', + // ], + // 'search-parameters' => [ + // 'query_by' => 'name' + // ], + // ], + ], + 'import_action' => env('TYPESENSE_IMPORT_ACTION', 'upsert'), + ], + +]; diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index 21146a2..e58c94e 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -113,6 +113,17 @@ services: ports: - "${VALKEY_PORT:-6339}:6379" + meilisearch: + image: getmeili/meilisearch:v1.10 + ports: + - "${MEILISEARCH_PORT:-7700}:7700" + environment: + MEILI_MASTER_KEY: ${MEILISEARCH_KEY:?MEILISEARCH_KEY is required} + MEILI_NO_ANALYTICS: "true" + MEILI_ENV: development + volumes: + - meilidata:/meili_data + stoic: image: ghcr.io/lexx27/stoic:latest user: root @@ -149,3 +160,4 @@ volumes: vendor: node_modules: pgdata: + meilidata: diff --git a/docker-compose.yml b/docker-compose.yml index 8fbcb87..7bec8d0 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -115,6 +115,20 @@ services: volumes: - valkeydata:/data + meilisearch: + image: getmeili/meilisearch:v1.10 + restart: unless-stopped + environment: + MEILI_MASTER_KEY: ${MEILISEARCH_KEY:?MEILISEARCH_KEY is required} + MEILI_NO_ANALYTICS: "true" + volumes: + - meilidata:/meili_data + healthcheck: + test: ["CMD", "wget", "--no-verbose", "--spider", "http://localhost:7700/health"] + interval: 5s + timeout: 5s + retries: 10 + otel-collector: image: otel/opentelemetry-collector-contrib:latest restart: unless-stopped @@ -145,3 +159,4 @@ volumes: logs: framework: valkeydata: + meilidata: diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh index 3b881ed..71f6c33 100644 --- a/docker/entrypoint.sh +++ b/docker/entrypoint.sh @@ -6,6 +6,7 @@ set -e if [ "$APP_ENV" != "production" ]; then echo "[entrypoint] Installing Composer dependencies..." git config --global --add safe.directory /var/www/html 2>/dev/null || true + git config --global --add safe.directory /var/www/boboko-core 2>/dev/null || true composer install --no-interaction --prefer-dist # Re-resolve boboko/* on every boot, whether it's a local path-repo checkout @@ -32,7 +33,7 @@ if [ "$APP_ENV" != "production" ]; then echo "[entrypoint] Running migrations..." rm -f storage/framework/migrated - php artisan migrate --force + # php artisan migrate --force echo "[entrypoint] Touching migrated file" touch storage/framework/migrated echo "[entrypoint] Touched migrated file" @@ -46,6 +47,11 @@ if [ "$APP_ENV" != "production" ]; then # run concurrently. echo "[entrypoint] Trying Lunar install" php artisan lunar:install --quiet || true + + # Upserts by primary key (no --refresh), so this stays cheap and idempotent on + # every boot rather than flushing and rebuilding the whole index each time. + echo "[entrypoint] Syncing search indexes..." + php artisan lunar:search:index --quiet || true fi exec "$@" diff --git a/resources/views/__preview_newsletter_split.blade.php b/resources/views/__preview_newsletter_split.blade.php deleted file mode 100644 index a16c87e..0000000 --- a/resources/views/__preview_newsletter_split.blade.php +++ /dev/null @@ -1,7 +0,0 @@ -@extends('layouts.app') - -@section('content') -
- -
-@endsection diff --git a/resources/views/components/header.blade.php b/resources/views/components/header.blade.php index 7b07dac..dcd64c9 100644 --- a/resources/views/components/header.blade.php +++ b/resources/views/components/header.blade.php @@ -35,11 +35,11 @@
{{-- Language switcher --}} - @isset($altLocale) - - {{ $altLocale }} + @foreach ($altLocales ?? [] as $altLocale) + + {{ $altLocale['code'] }} - @endisset + @endforeach {{-- Cart --}} diff --git a/resources/views/layouts/app.blade.php b/resources/views/layouts/app.blade.php index 99a1f70..17a901e 100644 --- a/resources/views/layouts/app.blade.php +++ b/resources/views/layouts/app.blade.php @@ -15,10 +15,12 @@ @yield('title', config('app.name')) - {{-- Reciprocal hreflang: self-reference + the alternate locale + x-default --}} + {{-- Reciprocal hreflang: self-reference + every alternate locale + x-default --}} @isset($currentLocale) - + @foreach ($altLocales ?? [] as $altLocale) + + @endforeach @endisset diff --git a/routes/web.php b/routes/web.php index c467258..caf81f0 100644 --- a/routes/web.php +++ b/routes/web.php @@ -6,13 +6,15 @@ use App\Http\Controllers\LegalPageController; use App\Http\Controllers\ProductController; use Illuminate\Support\Facades\Route; +use Modules\Core\Localization\LocaleMiddleware; -// Bare root carries no language signal, so it just redirects to the default locale. -Route::redirect('/', '/'.config('app.locale')); +// Bare root carries no language signal, so it just redirects to the store's +// default language (Lunar's Language record with default=true) — the same +// source of truth LocaleMiddleware itself uses, so this never drifts from it. +Route::redirect('/', '/'.(LocaleMiddleware::defaultLocale() ?? config('app.locale'))); Route::prefix('{locale}') - ->where(['locale' => implode('|', config('app.available_locales'))]) - ->middleware('setlocale') + ->middleware(LocaleMiddleware::class) ->group(function () { Route::get('/', [HomeController::class, 'index'])->name('home'); @@ -39,7 +41,4 @@ 'legal.cookies', ); - Route::get('/__preview/newsletter-split', function () { - return view('__preview_newsletter_split'); - }); });