From 7653842ae21002cf5231cfb72ca51842a823b2ff Mon Sep 17 00:00:00 2001 From: Alec Ritson Date: Wed, 15 Jan 2025 08:03:39 +0000 Subject: [PATCH 1/6] wip --- app/Livewire/CollectionPage.php | 18 ++- app/Livewire/SearchPage.php | 30 ++--- app/Livewire/Traits/WithSearch.php | 113 ++++++++++++++++ app/Search/ProductIndexer.php | 126 ++++++++++++++++++ composer.json | 7 + config/scout.php | 11 ++ package.json | 6 +- postcss.config.js | 6 + .../views/components/header/search.blade.php | 2 +- .../views/components/input/select.blade.php | 10 ++ .../views/components/product-card.blade.php | 35 ++--- resources/views/layouts/storefront.blade.php | 5 +- .../views/livewire/collection-page.blade.php | 27 +++- .../views/livewire/search-page.blade.php | 48 +++++-- .../partials/search/search-filters.blade.php | 28 ++++ .../views/partials/search/sorting.blade.php | 13 ++ tailwind.config.js | 10 +- 17 files changed, 429 insertions(+), 66 deletions(-) create mode 100644 app/Livewire/Traits/WithSearch.php create mode 100644 app/Search/ProductIndexer.php create mode 100644 postcss.config.js create mode 100644 resources/views/components/input/select.blade.php create mode 100644 resources/views/partials/search/search-filters.blade.php create mode 100644 resources/views/partials/search/sorting.blade.php diff --git a/app/Livewire/CollectionPage.php b/app/Livewire/CollectionPage.php index 92111777..610b2873 100644 --- a/app/Livewire/CollectionPage.php +++ b/app/Livewire/CollectionPage.php @@ -2,15 +2,21 @@ namespace App\Livewire; +use App\Livewire\Traits\WithSearch; use App\Traits\FetchesUrls; use Illuminate\Support\Collection; use Illuminate\View\View; +use Livewire\Attributes\Computed; use Livewire\Component; +use Livewire\WithPagination; use Lunar\Models\Collection as CollectionModel; +use Lunar\Search\Contracts\SearchManagerContract; class CollectionPage extends Component { use FetchesUrls; + use WithPagination; + use WithSearch; public function mount(string $slug): void { @@ -24,15 +30,19 @@ public function mount(string $slug): void ] ); + + if (! $this->url) { abort(404); } + + $this->filters = [ + 'collection_ids' => [$this->collection->id] + ]; } - /** - * Computed property to return the collection. - */ - public function getCollectionProperty(): mixed + #[Computed] + public function collection(): mixed { return $this->url->element; } diff --git a/app/Livewire/SearchPage.php b/app/Livewire/SearchPage.php index 4ffc903d..c06daa34 100644 --- a/app/Livewire/SearchPage.php +++ b/app/Livewire/SearchPage.php @@ -2,34 +2,30 @@ namespace App\Livewire; +use App\Livewire\Traits\SearchesProducts; +use App\Livewire\Traits\WithSearch; +use Illuminate\Http\Request; use Illuminate\Pagination\LengthAwarePaginator; +use Illuminate\Support\Collection; use Illuminate\View\View; +use Livewire\Attributes\Computed; +use Livewire\Attributes\Modelable; +use Livewire\Attributes\Url; use Livewire\Component; use Livewire\WithPagination; use Lunar\Models\Product; +use Lunar\Search\Contracts\SearchManagerContract; +use Lunar\Search\Data\SearchResults; +use Lunar\Search\Facades\Search; class SearchPage extends Component { use WithPagination; + use WithSearch; - /** - * {@inheritDoc} - */ - protected $queryString = [ - 'term', - ]; - - /** - * The search term. - */ - public ?string $term = null; - - /** - * Return the search results. - */ - public function getResultsProperty(): LengthAwarePaginator + public function mount(Request $request) { - return Product::search($this->term)->paginate(50); + $this->query = (string) $request->get('query', ''); } public function render(): View diff --git a/app/Livewire/Traits/WithSearch.php b/app/Livewire/Traits/WithSearch.php new file mode 100644 index 00000000..0fb5c731 --- /dev/null +++ b/app/Livewire/Traits/WithSearch.php @@ -0,0 +1,113 @@ +sort = (string) $request->get('sort', ''); + } + + #[Computed] + public function searchInstance(): SearchManagerContract + { + $search = Search::model(\Lunar\Models\Product::class); + + if ($this->facets && count($this->facets)) { + $facets = []; + + foreach ($this->facets as $facet) { + [$field, $facetValue] = explode(':', urldecode($facet)); + + if (empty($facets[$field])) { + $facets[$field] = []; + } + + $facets[$field][] = $facetValue; + } + + $search->setFacets($facets); + } + + $search->filter($this->filters); + + return $search; + } + + + + #[Computed] + public function results(): SearchResults + { + + $sorting = $this->sort ?: ''; + + return $this->searchInstance->perPage($this->perPage)->sort($sorting)->query($this->query)->get(); + } + + #[Computed] + public function displayFacets(): Collection + { + return collect($this->results->facets)->reject( + fn ($facet) => !count($facet->values) + )->values(); + } + + public function clearFacets(): void + { + $this->facets = []; + } + + public function updatedQuery(): void + { + $this->resetPage(); + } + + public function updatedFacets(): void + { + $this->resetPage(); + } + + public function updatedPerPage(): void + { + $this->resetPage(); + $this->instance->perPage($this->perPage); + } + + public function removeFacet(string $facet): void + { + $facets = $this->facets; + unset($facets[ + array_search($facet, $facets) + ]); + $this->facets = collect($facets)->values()->toArray(); + } +} diff --git a/app/Search/ProductIndexer.php b/app/Search/ProductIndexer.php new file mode 100644 index 00000000..460c4ba3 --- /dev/null +++ b/app/Search/ProductIndexer.php @@ -0,0 +1,126 @@ +with([ + 'thumbnail', + 'variants', + 'productType', + 'brand', + 'prices', + ]); + } + + public function toSearchableArray(Model $model): array + { +// $productOptions = $product->get(); +// dd($product->variants->pluck('values')); +// +// dd($model->variants->pluck('values')->flatten()); + + $priceModels = $model->prices; + + $basePrice = $priceModels->first(function ($price) { + return $price->min_quantity == 1; + }); + + $minPrice = $priceModels->sortBy('price')->first(); + + $options = $model->productOptions()->with([ + 'values' => function ($query) use ($model) { + $query->whereHas('variants', function ($relation) use ($model) { + $relation->where('product_id', $model->id); + }); + }, + ])->get()->mapWithKeys(function ($option) { + return [ + $option->handle => $option->values->map(function ($value) { + return $value->translate('name'); + }) + ]; + })->toArray(); + + return [ + 'id' => (string) $model->id, + 'name' => $model->attr('name'), + 'description' => $model->attr('description'), + 'brand' => $model->brand->name, + 'thumbnail' => $model->thumbnail?->getUrl('small'), + 'slug' => $model->defaultUrl?->slug, + 'price' => [ + 'inc_tax' => [ + 'value' => (int) $basePrice->priceIncTax()->value, + 'formatted' => $basePrice->priceIncTax()->formatted, + ], + 'ex_tax' => [ + 'value' => (int) $basePrice->priceExTax()->value, + 'formatted' => $basePrice->priceExTax()->formatted, + ], + ], + 'min_price' => $minPrice?->priceIncTax()?->value ?: 0, + ...$options, + ...$this->getCollections($model) + ]; + } + + private function getCollections(Model $product): array + { + $trail = []; + + $categories = []; + $categoryIds = []; + + foreach ($product->collections as $i => $collection) { + $levels = [$collection->translateAttribute('name')]; + $levelIds = [$collection->id]; + $node = $collection; + + while ($node->parent) { + array_unshift($levels, $node->parent->translateAttribute('name')); + array_unshift($levelIds, $node->parent->id); + $node = $node->parent; + } + + foreach ($levels as $level => $name) { + if (! isset($trail['lvl'.$level])) { + $trail['lvl'.$level] = []; + } + + $key = $level - 1; + + $breadcrumb = [$name]; + + while (isset($levels[$key])) { + array_unshift($breadcrumb, $levels[$key]); + $key--; + } + + $trail['lvl'.$level][] = implode(' > ', $breadcrumb); + } + + $categories = array_merge($levels, $categories); + $categoryIds = array_merge($levelIds, $categoryIds); + } + + foreach ($trail as $key => $values) { + $trail[$key] = collect($values) + ->unique() + ->values() + ->toArray(); + } + + return [ + 'hierarchical_collections' => $trail, + 'collection_ids' => collect($categoryIds)->unique()->values()->toArray(), + 'collections' => collect($categories)->unique()->values()->toArray(), + ]; + } +} diff --git a/composer.json b/composer.json index 37aac3ad..31e2ff8f 100644 --- a/composer.json +++ b/composer.json @@ -10,6 +10,12 @@ "laravel" ], "license": "MIT", + "repositories": [ + { + "type": "path", + "url": "../../lunar/search" + } + ], "require": { "php": "^8.2", "guzzlehttp/guzzle": "^7.2", @@ -20,6 +26,7 @@ "league/flysystem-aws-s3-v3": "^3.0", "lunarphp/lunar": "^1.0@beta", "lunarphp/stripe": "^1.0@beta", + "lunarphp/search": "*", "lunarphp/table-rate-shipping": "^1.0@beta", "meilisearch/meilisearch-php": "^1.6", "predis/predis": "^2.2" diff --git a/config/scout.php b/config/scout.php index 955f878d..2fa2f0e6 100644 --- a/config/scout.php +++ b/config/scout.php @@ -116,4 +116,15 @@ 'secret' => env('ALGOLIA_SECRET', ''), ], + 'meilisearch' => [ + 'host' => env('MEILISEARCH_HOST', 'http://127.0.0.1:7700'), + 'key' => env('MEILISEARCH_KEY'), + 'index-settings' => [ + \App\Models\Product::class => [ + 'filterableAttributes' => ['brand', 'size', 'shoe-size', 'colour', 'collection_ids'], + 'sortableAttributes' => ['name', 'sku', 'min_price'] + ], + ], + ], + ]; diff --git a/package.json b/package.json index d6bc2c46..e5bee683 100644 --- a/package.json +++ b/package.json @@ -8,11 +8,11 @@ "devDependencies": { "@ryangjchandler/alpine-clipboard": "^2.3.0", "@tailwindcss/forms": "^0.5.7", - "autoprefixer": "^10.4.17", + "autoprefixer": "^10.4.20", "axios": "^1.6.1", "laravel-vite-plugin": "^0.8.0", - "postcss": "^8.4.35", - "tailwindcss": "^3.4.1", + "postcss": "^8.5.1", + "tailwindcss": "^3.4.17", "vite": "^4.0.0" } } diff --git a/postcss.config.js b/postcss.config.js new file mode 100644 index 00000000..49c0612d --- /dev/null +++ b/postcss.config.js @@ -0,0 +1,6 @@ +export default { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +}; diff --git a/resources/views/components/header/search.blade.php b/resources/views/components/header/search.blade.php index eeb8479a..893753ae 100644 --- a/resources/views/components/header/search.blade.php +++ b/resources/views/components/header/search.blade.php @@ -1,6 +1,6 @@
merge(['class' => 'w-full relative']) }} action="{{ route('search.view') }}"> - + +
+ +
+ diff --git a/resources/views/components/product-card.blade.php b/resources/views/components/product-card.blade.php index 7afe73cb..5592be94 100644 --- a/resources/views/components/product-card.blade.php +++ b/resources/views/components/product-card.blade.php @@ -1,26 +1,31 @@ -@props(['product']) +@props([ + 'name', + 'slug', + 'thumbnail' => null, + 'price' +]) - +
- - {{ $product->translateAttribute('name') }} - + + {{ $name }} +

Price - -

- +
+ {{ $price }} +
+
diff --git a/resources/views/layouts/storefront.blade.php b/resources/views/layouts/storefront.blade.php index ad81472f..3e6d4c9a 100644 --- a/resources/views/layouts/storefront.blade.php +++ b/resources/views/layouts/storefront.blade.php @@ -12,10 +12,7 @@ name="description" content="Example of an ecommerce storefront built with Lunar." > - + @vite(['resources/css/app.css', 'resources/js/app.js']) -
+

- {{ $this->collection->translateAttribute('name') }} + {{ $this->collection->attr('name') }}

-
- @forelse($this->collection->products as $product) - - @empty - @endforelse +
+
+ @include('partials.search.search-filters') +
+
+
+ @foreach ($this->results->hits as $result) +
+ +
+ @endforeach +
+
diff --git a/resources/views/livewire/search-page.blade.php b/resources/views/livewire/search-page.blade.php index f8e7c233..41e2dbbe 100644 --- a/resources/views/livewire/search-page.blade.php +++ b/resources/views/livewire/search-page.blade.php @@ -1,16 +1,42 @@
-
-

- Search Results - @if (isset($term)) - for {{ $term }} - @endif -

+
+
+

+ @if($this->query) + Search Results for "{{ $this->query }}" + @else + All Products + @endif +

+
-
- @foreach ($this->results as $result) - - @endforeach +
+
+ @include('partials.search.search-filters') +
+
+ +
+
+ {{ $this->results->links }} +
+
+ @include('partials.search.sorting') +
+
+
+ @foreach ($this->results->hits as $result) +
+ +
+ @endforeach +
+
diff --git a/resources/views/partials/search/search-filters.blade.php b/resources/views/partials/search/search-filters.blade.php new file mode 100644 index 00000000..7a2fc812 --- /dev/null +++ b/resources/views/partials/search/search-filters.blade.php @@ -0,0 +1,28 @@ +
+ @foreach($this->displayFacets as $displayFacet) +
+

+ {{ $displayFacet->label }} +

+
+ @foreach($displayFacet->values as $facetValue) + + @endforeach +
+
+ @endforeach +
diff --git a/resources/views/partials/search/sorting.blade.php b/resources/views/partials/search/sorting.blade.php new file mode 100644 index 00000000..fbe28df7 --- /dev/null +++ b/resources/views/partials/search/sorting.blade.php @@ -0,0 +1,13 @@ +
+
+ +
+ +
+
+ +
diff --git a/tailwind.config.js b/tailwind.config.js index 06dfc796..7feeb422 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -1,8 +1,10 @@ -module.exports = { +/** @type {import('tailwindcss').Config} */ + +export default { content: [ - './resources/**/*.blade.php', - './resources/**/*.js', - './vendor/lunarphp/stripe-payments/resources/views/**/*.blade.php', + "./resources/**/*.blade.php", + "./resources/**/*.js", + "./resources/**/*.vue", ], theme: { extend: {}, From 416048fbebb55997c0967015113d4cb6562ea1f9 Mon Sep 17 00:00:00 2001 From: Alec Ritson Date: Wed, 15 Jan 2025 08:39:49 +0000 Subject: [PATCH 2/6] Init search config --- config/lunar/search.php | 73 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 config/lunar/search.php diff --git a/config/lunar/search.php b/config/lunar/search.php new file mode 100644 index 00000000..fa5ccf03 --- /dev/null +++ b/config/lunar/search.php @@ -0,0 +1,73 @@ + [ + /* + * These models are required by the system, do not change them. + */ + Lunar\Models\Brand::class, + Lunar\Models\Collection::class, + Lunar\Models\Customer::class, + Lunar\Models\Order::class, + Lunar\Models\Product::class, + Lunar\Models\ProductOption::class, + + /* + * Below you can add your own models for indexing... + */ + // App\Models\Example::class, + ], + + /* + |-------------------------------------------------------------------------- + | Search engine mapping + |-------------------------------------------------------------------------- + | + | You can define what search driver each searchable model should use. + | If the model isn't defined here, it will use the SCOUT_DRIVER env variable. + | + */ + 'engine_map' => [ + // Lunar\Models\Product::class => 'algolia', + // Lunar\Models\Order::class => 'meilisearch', + // Lunar\Models\Collection::class => 'meilisearch', + ], + + 'indexers' => [ + Lunar\Models\Brand::class => Lunar\Search\BrandIndexer::class, + 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 => \App\Search\ProductIndexer::class, + Lunar\Models\ProductOption::class => Lunar\Search\ProductOptionIndexer::class, + ], + + 'facets' => [ + \Lunar\Models\Product::class => [ + 'brand' => [ + 'label' => 'Brand', + ], + 'colour' => [ + 'label' => 'Colour', + ], + 'size' => [ + 'label' => 'Size', + ], + 'shoe-size' => [ + 'label' => 'Shoe Size', + ] + ] + ], + +]; From 851e52cd8e12c9b3773dadfc1d013cd421382123 Mon Sep 17 00:00:00 2001 From: Alec Ritson Date: Wed, 15 Jan 2025 08:39:59 +0000 Subject: [PATCH 3/6] update ignore --- .gitignore | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitignore b/.gitignore index 54fb416b..778248bd 100644 --- a/.gitignore +++ b/.gitignore @@ -23,7 +23,6 @@ yarn-error.log composer.lock package-lock.json yarn.lock -config/lunar public/build meilisearch/ .phpunit.cache/test-results From aa33e1131f02e20a53b9eeb269737fe28d3c61b2 Mon Sep 17 00:00:00 2001 From: Alec Ritson Date: Wed, 15 Jan 2025 12:16:13 +0000 Subject: [PATCH 4/6] WIP --- app/Livewire/CollectionPage.php | 7 +-- app/Search/ProductIndexer.php | 7 +-- .../views/components/product-card.blade.php | 29 +++++++---- .../views/livewire/collection-page.blade.php | 27 ---------- resources/views/livewire/home.blade.php | 7 ++- .../views/livewire/search-page.blade.php | 49 ++++++++++++++----- .../partials/search/search-filters.blade.php | 48 +++++++++++++----- tailwind.config.js | 1 + 8 files changed, 105 insertions(+), 70 deletions(-) delete mode 100644 resources/views/livewire/collection-page.blade.php diff --git a/app/Livewire/CollectionPage.php b/app/Livewire/CollectionPage.php index 610b2873..b1faf594 100644 --- a/app/Livewire/CollectionPage.php +++ b/app/Livewire/CollectionPage.php @@ -5,7 +5,6 @@ use App\Livewire\Traits\WithSearch; use App\Traits\FetchesUrls; use Illuminate\Support\Collection; -use Illuminate\View\View; use Livewire\Attributes\Computed; use Livewire\Component; use Livewire\WithPagination; @@ -47,8 +46,10 @@ public function collection(): mixed return $this->url->element; } - public function render(): View + public function render(): \Illuminate\Contracts\View\View { - return view('livewire.collection-page'); + return view('livewire.search-page', [ + 'isCollection' => true, + ]); } } diff --git a/app/Search/ProductIndexer.php b/app/Search/ProductIndexer.php index 460c4ba3..6ca64fd3 100644 --- a/app/Search/ProductIndexer.php +++ b/app/Search/ProductIndexer.php @@ -21,11 +21,6 @@ public function makeAllSearchableUsing(Builder $query): Builder public function toSearchableArray(Model $model): array { -// $productOptions = $product->get(); -// dd($product->variants->pluck('values')); -// -// dd($model->variants->pluck('values')->flatten()); - $priceModels = $model->prices; $basePrice = $priceModels->first(function ($price) { @@ -52,7 +47,7 @@ public function toSearchableArray(Model $model): array 'id' => (string) $model->id, 'name' => $model->attr('name'), 'description' => $model->attr('description'), - 'brand' => $model->brand->name, + 'brand' => $model->brand?->name, 'thumbnail' => $model->thumbnail?->getUrl('small'), 'slug' => $model->defaultUrl?->slug, 'price' => [ diff --git a/resources/views/components/product-card.blade.php b/resources/views/components/product-card.blade.php index 5592be94..8514e9a6 100644 --- a/resources/views/components/product-card.blade.php +++ b/resources/views/components/product-card.blade.php @@ -1,24 +1,35 @@ @props([ 'name', - 'slug', + 'slug' => null, 'thumbnail' => null, 'price' ])
-
- @if ($thumbnail) - - {{ $name }} - - @endif + + @if($slug) {{ $name }} + @else + {{ $name }} + @endif

diff --git a/resources/views/livewire/collection-page.blade.php b/resources/views/livewire/collection-page.blade.php deleted file mode 100644 index 9af733d8..00000000 --- a/resources/views/livewire/collection-page.blade.php +++ /dev/null @@ -1,27 +0,0 @@ -

-
-

- {{ $this->collection->attr('name') }} -

- -
-
- @include('partials.search.search-filters') -
-
-
- @foreach ($this->results->hits as $result) -
- -
- @endforeach -
-
-
-
-
diff --git a/resources/views/livewire/home.blade.php b/resources/views/livewire/home.blade.php index c99b2a77..30e13d73 100644 --- a/resources/views/livewire/home.blade.php +++ b/resources/views/livewire/home.blade.php @@ -14,7 +14,12 @@
@foreach ($this->randomCollection->products as $product) - + @endforeach
diff --git a/resources/views/livewire/search-page.blade.php b/resources/views/livewire/search-page.blade.php index 41e2dbbe..b10e57c4 100644 --- a/resources/views/livewire/search-page.blade.php +++ b/resources/views/livewire/search-page.blade.php @@ -1,8 +1,10 @@
-
+

- @if($this->query) + @if(!empty($isCollection)) + {{ $this->collection->attr('name') }} + @elseif($this->query) Search Results for "{{ $this->query }}" @else All Products @@ -10,21 +12,46 @@

-
-
+ @if(!$this->results->count) +
+ Sorry, looks like we don't have any products available. +
+ @endif +
+
+
+ Search Filters + +
@include('partials.search.search-filters')
-
- -
-
+
+ @if($this->results->count) +
+
{{ $this->results->links }}
-
- @include('partials.search.sorting') +
+
+ +
+
+ @include('partials.search.sorting') +
-
+ @endif +
@foreach ($this->results->hits as $result)
{{ $displayFacet->label }}

-
+
@foreach($displayFacet->values as $facetValue) - +
+ !$facetValue->active, + 'bg-blue-500 text-white' => $facetValue->active, + ]) + > + @if($facetValue->active) + + @endif + + +
+ +
@endforeach
diff --git a/tailwind.config.js b/tailwind.config.js index 7feeb422..45f36f86 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -5,6 +5,7 @@ export default { "./resources/**/*.blade.php", "./resources/**/*.js", "./resources/**/*.vue", + './vendor/laravel/framework/src/Illuminate/Pagination/resources/views/**/*.blade.php' ], theme: { extend: {}, From 9c353cef0debee8e00abe687f32422f99244d802 Mon Sep 17 00:00:00 2001 From: Alec Ritson Date: Wed, 15 Jan 2025 12:20:08 +0000 Subject: [PATCH 5/6] Update readme --- README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/README.md b/README.md index aa2d1c2b..ee406088 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,15 @@ docker-compose up The demo store will be available to `http://localhost` in your browser. +### Searching + +Out the box, the starter kit is configured to use Meilisearch. You will need to ensure you have this set up, you should also check the following config files to familiarise yourself how search is configured to work. + +- `config/lunar/search.php` +- `config/scout.php` + +To customise what is indexed, you should look at the `Search/ProductIndexer` class. + #### Log into Lunar panel Once the project is prepared, the Lunar panel will start and available to `http://localhost/lunar`. From bee287c5999bdc735f2052ae293dbcc691901251 Mon Sep 17 00:00:00 2001 From: Alec Ritson Date: Wed, 15 Jan 2025 13:00:00 +0000 Subject: [PATCH 6/6] Fix search version --- composer.json | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/composer.json b/composer.json index 31e2ff8f..6cd78c63 100644 --- a/composer.json +++ b/composer.json @@ -10,12 +10,6 @@ "laravel" ], "license": "MIT", - "repositories": [ - { - "type": "path", - "url": "../../lunar/search" - } - ], "require": { "php": "^8.2", "guzzlehttp/guzzle": "^7.2", @@ -26,7 +20,7 @@ "league/flysystem-aws-s3-v3": "^3.0", "lunarphp/lunar": "^1.0@beta", "lunarphp/stripe": "^1.0@beta", - "lunarphp/search": "*", + "lunarphp/search": "^0.1@alpha", "lunarphp/table-rate-shipping": "^1.0@beta", "meilisearch/meilisearch-php": "^1.6", "predis/predis": "^2.2"