From a424c43c6e950175917ab5b20290078bef7a965b Mon Sep 17 00:00:00 2001 From: David Bomba Date: Sun, 22 Sep 2024 14:49:43 +1000 Subject: [PATCH] Prototype for global search --- app/Events/Invoice/InvoiceWasPaid.php | 18 +- app/Http/Controllers/SearchController.php | 119 +++- .../Chart/ShowCalculatedFieldRequest.php | 2 +- app/Models/Client.php | 60 +- app/Models/ClientContact.php | 20 + app/Models/Invoice.php | 44 +- app/Services/Chart/ChartCalculations.php | 8 +- app/Transformers/InvoiceTransformer.php | 4 + composer.json | 2 + composer.lock | 611 +++++++++++++++++- config/elastic.client.php | 24 + config/elastic.scout_driver.php | 5 + config/scout.php | 202 ++++++ 13 files changed, 1061 insertions(+), 58 deletions(-) create mode 100644 config/elastic.client.php create mode 100644 config/elastic.scout_driver.php create mode 100644 config/scout.php diff --git a/app/Events/Invoice/InvoiceWasPaid.php b/app/Events/Invoice/InvoiceWasPaid.php index a5a8812f1b90..1a4631f067e5 100644 --- a/app/Events/Invoice/InvoiceWasPaid.php +++ b/app/Events/Invoice/InvoiceWasPaid.php @@ -26,29 +26,15 @@ class InvoiceWasPaid implements ShouldBroadcast { use SerializesModels, DefaultInvoiceBroadcast; - /** - * @var Invoice - */ - public $invoice; - - public $payment; - - public $company; - - public $event_vars; - /** * Create a new event instance. * * @param Invoice $invoice * @param Company $company + * @param Payment $payment * @param array $event_vars */ - public function __construct(Invoice $invoice, Payment $payment, Company $company, array $event_vars) + public function __construct(public Invoice $invoice, public Payment $payment, public Company $company, public array $event_vars) { - $this->invoice = $invoice; - $this->payment = $payment; - $this->company = $company; - $this->event_vars = $event_vars; } } diff --git a/app/Http/Controllers/SearchController.php b/app/Http/Controllers/SearchController.php index f4d842843c6b..ca5cd688161b 100644 --- a/app/Http/Controllers/SearchController.php +++ b/app/Http/Controllers/SearchController.php @@ -11,10 +11,12 @@ namespace App\Http\Controllers; -use App\Http\Requests\Search\GenericSearchRequest; +use App\Models\User; +use App\Utils\Ninja; use App\Models\Client; use App\Models\Invoice; -use App\Models\User; +use Elastic\Elasticsearch\ClientBuilder; +use App\Http\Requests\Search\GenericSearchRequest; class SearchController extends Controller { @@ -26,6 +28,14 @@ class SearchController extends Controller public function __invoke(GenericSearchRequest $request) { + if(Ninja::isHosted() && $request->has('search') && $request->input('search') !== '') { + try{ + return $this->search($request->input('search', '')); + } catch(\Exception $e) { + nlog("elk down?"); + } + } + /** @var \App\Models\User $user */ $user = auth()->user(); @@ -41,6 +51,95 @@ class SearchController extends Controller } + public function search(string $search) + { + $user = auth()->user(); + $company = $user->company(); + + $elastic = ClientBuilder::fromConfig(config('elastic.client.connections.default')); + + $params = [ + 'index' => 'clients,invoices,client_contacts', + 'body' => [ + 'query' => [ + 'bool' => [ + 'must' => [ + 'multi_match' => [ + 'query' => $search, + 'fields' => ['*'], + 'fuzziness' => 'AUTO', + ], + ], + 'filter' => [ + 'match' => [ + 'company_key' => $company->company_key, + ], + ], + ], + ], + 'size' => 1000, + ], + ]; + + $results = $elastic->search($params); + + $this->mapResults($results['hits']['hits'] ?? []); + + return response()->json([ + 'clients' => $this->clients, + 'client_contacts' => $this->client_contacts, + 'invoices' => $this->invoices, + 'settings' => $this->settingsMap(), + ], 200); + + } + + private function mapResults(array $results) + { + foreach($results as $result) { + switch($result['_index']) { + case 'clients': + + if($result['_source']['is_deleted']) + continue; + + $this->clients[] = [ + 'name' => $result['_source']['name'], + 'type' => '/client', + 'id' => $result['_source']['hashed_id'], + 'path' => "/clients/{$result['_source']['hashed_id']}" + ]; + + break; + case 'invoices': + + if ($result['_source']['is_deleted']) { + continue; + } + + $this->invoices[] = [ + 'name' => $result['_source']['name'], + 'type' => '/invoice', + 'id' => $result['_source']['hashed_id'], + 'path' => "/invoices/{$result['_source']['hashed_id']}/edit" + ]; + break; + case 'client_contacts': + + if($result['_source']['__soft_deleted']) + continue; + + $this->client_contacts[] = [ + 'name' => $result['_source']['name'], + 'type' => '/client', + 'id' => $result['_source']['hashed_id'], + 'path' => "/clients/{$result['_source']['hashed_id']}" + ]; + break; + } + } + } + private function clientMap(User $user) { @@ -81,20 +180,14 @@ class SearchController extends Controller $invoices = Invoice::query() ->company() ->with('client') - ->where('invoices.is_deleted', 0) - // ->whereHas('client', function ($q) { - // $q->where('is_deleted', 0); - // }) - - ->leftJoin('clients', function ($join) { - $join->on('invoices.client_id', '=', 'clients.id') - ->where('clients.is_deleted', 0); - }) - + ->where('is_deleted', 0) + ->whereHas('client', function ($q) { + $q->where('is_deleted', 0); + }) ->when(!$user->hasPermission('view_all') || !$user->hasPermission('view_invoice'), function ($query) use ($user) { $query->where('invoices.user_id', $user->id); }) - ->orderBy('invoices.id', 'desc') + ->orderBy('id', 'desc') ->take(3000) ->get(); diff --git a/app/Http/Requests/Chart/ShowCalculatedFieldRequest.php b/app/Http/Requests/Chart/ShowCalculatedFieldRequest.php index 5a9945da8f72..3aa6271dc2af 100644 --- a/app/Http/Requests/Chart/ShowCalculatedFieldRequest.php +++ b/app/Http/Requests/Chart/ShowCalculatedFieldRequest.php @@ -37,7 +37,7 @@ class ShowCalculatedFieldRequest extends Request 'date_range' => 'bail|sometimes|string|in:last7_days,last30_days,last365_days,this_month,last_month,this_quarter,last_quarter,this_year,last_year,all_time,custom', 'start_date' => 'bail|sometimes|date', 'end_date' => 'bail|sometimes|date', - 'field' => 'required|bail|in:active_invoices, outstanding_invoices, completed_payments, refunded_payments, active_quotes, unapproved_quotes, logged_tasks, invoiced_tasks, paid_tasks, logged_expenses, pending_expenses, invoiced_expenses, invoice_paid_expenses', + 'field' => 'required|bail|in:active_invoices,outstanding_invoices,completed_payments,refunded_payments,active_quotes,unapproved_quotes logged_tasks,invoiced_tasks,paid_tasks,logged_expenses,pending_expenses,invoiced_expenses,invoice_paid_expenses', 'calculation' => 'required|bail|in:sum,avg,count', 'period' => 'required|bail|in:current,previous,total', 'format' => 'sometimes|bail|in:time,money', diff --git a/app/Models/Client.php b/app/Models/Client.php index cbb7500daa29..e8ab1dfab607 100644 --- a/app/Models/Client.php +++ b/app/Models/Client.php @@ -11,23 +11,24 @@ namespace App\Models; +use Laravel\Scout\Searchable; +use App\Utils\Traits\AppSetup; +use App\Utils\Traits\MakesHash; +use App\Utils\Traits\MakesDates; +use App\DataMapper\FeesAndLimits; +use App\Models\Traits\Excludable; use App\DataMapper\ClientSettings; use App\DataMapper\CompanySettings; -use App\DataMapper\FeesAndLimits; -use App\Libraries\Currency\Conversion\CurrencyApi; -use App\Models\Presenters\ClientPresenter; -use App\Models\Traits\Excludable; use App\Services\Client\ClientService; -use App\Utils\Traits\AppSetup; -use App\Utils\Traits\ClientGroupSettingsSaver; use App\Utils\Traits\GeneratesCounter; -use App\Utils\Traits\MakesDates; -use App\Utils\Traits\MakesHash; -use Illuminate\Contracts\Translation\HasLocalePreference; -use Illuminate\Database\Eloquent\Relations\BelongsTo; -use Illuminate\Database\Eloquent\Relations\HasMany; -use Illuminate\Database\Eloquent\SoftDeletes; use Laracasts\Presenter\PresentableTrait; +use App\Models\Presenters\ClientPresenter; +use Illuminate\Database\Eloquent\SoftDeletes; +use App\Utils\Traits\ClientGroupSettingsSaver; +use App\Libraries\Currency\Conversion\CurrencyApi; +use Illuminate\Database\Eloquent\Relations\HasMany; +use Illuminate\Database\Eloquent\Relations\BelongsTo; +use Illuminate\Contracts\Translation\HasLocalePreference; /** * App\Models\Client @@ -124,6 +125,9 @@ class Client extends BaseModel implements HasLocalePreference use ClientGroupSettingsSaver; use Excludable; + + use Searchable; + protected $presenter = ClientPresenter::class; protected $hidden = [ @@ -232,6 +236,38 @@ class Client extends BaseModel implements HasLocalePreference 'custom_value4', ]; + public function toSearchableArray() + { + return [ + 'name' => $this->present()->name(), + 'is_deleted' => $this->is_deleted, + 'hashed_id' => $this->hashed_id, + 'number' => $this->number, + 'id_number' => $this->id_number, + 'vat_number' => $this->vat_number, + 'balance' => $this->balance, + 'paid_to_date' => $this->paid_to_date, + 'phone' => $this->phone, + 'address1' => $this->address1, + 'address2' => $this->address2, + 'city' => $this->city, + 'state' => $this->state, + 'postal_code' => $this->postal_code, + 'website' => $this->website, + 'private_notes' => $this->private_notes, + 'public_notes' => $this->public_notes, + 'shipping_address1' => $this->shipping_address1, + 'shipping_address2' => $this->shipping_address2, + 'shipping_city' => $this->shipping_city, + 'shipping_state' => $this->shipping_state, + 'shipping_postal_code' => $this->shipping_postal_code, + 'custom_value1' => $this->custom_value1, + 'custom_value2' => $this->custom_value2, + 'custom_value3' => $this->custom_value3, + 'custom_value4' => $this->custom_value4, + 'company_key' => $this->company->company_key, + ]; + } public function getEntityType() { diff --git a/app/Models/ClientContact.php b/app/Models/ClientContact.php index 82941087367b..939dc7140d09 100644 --- a/app/Models/ClientContact.php +++ b/app/Models/ClientContact.php @@ -13,6 +13,7 @@ namespace App\Models; use App\Utils\Ninja; use Illuminate\Support\Str; +use Laravel\Scout\Searchable; use App\Jobs\Mail\NinjaMailer; use App\Utils\Traits\AppSetup; use App\Utils\Traits\MakesHash; @@ -100,6 +101,8 @@ class ClientContact extends Authenticatable implements HasLocalePreference use HasFactory; use AppSetup; + use Searchable; + /* Used to authenticate a contact */ protected $guard = 'contact'; @@ -165,6 +168,23 @@ class ClientContact extends Authenticatable implements HasLocalePreference 'email', ]; + public function toSearchableArray() + { + return [ + 'name' => $this->present()->search_display(), + 'hashed_id' => $this->client->hashed_id, + 'email' => $this->email, + 'first_name' => $this->first_name, + 'last_name' => $this->last_name, + 'phone' => $this->phone, + 'custom_value1' => $this->custom_value1, + 'custom_value2' => $this->custom_value2, + 'custom_value3' => $this->custom_value3, + 'custom_value4' => $this->custom_value4, + 'company_key' => $this->company->company_key, + ]; + } + /* V2 type of scope */ diff --git a/app/Models/Invoice.php b/app/Models/Invoice.php index b51fbf7fef83..725b6dcd3892 100644 --- a/app/Models/Invoice.php +++ b/app/Models/Invoice.php @@ -11,22 +11,23 @@ namespace App\Models; -use App\Events\Invoice\InvoiceReminderWasEmailed; -use App\Events\Invoice\InvoiceWasEmailed; -use App\Helpers\Invoice\InvoiceSum; -use App\Helpers\Invoice\InvoiceSumInclusive; -use App\Models\Presenters\EntityPresenter; -use App\Services\Invoice\InvoiceService; -use App\Services\Ledger\LedgerService; use App\Utils\Ninja; -use App\Utils\Traits\Invoice\ActionsInvoice; +use Laravel\Scout\Searchable; +use Illuminate\Support\Carbon; use App\Utils\Traits\MakesDates; -use App\Utils\Traits\MakesInvoiceValues; +use App\Helpers\Invoice\InvoiceSum; use App\Utils\Traits\MakesReminders; use App\Utils\Traits\NumberFormatter; -use Illuminate\Database\Eloquent\SoftDeletes; -use Illuminate\Support\Carbon; +use App\Services\Ledger\LedgerService; +use App\Services\Invoice\InvoiceService; +use App\Utils\Traits\MakesInvoiceValues; +use App\Events\Invoice\InvoiceWasEmailed; use Laracasts\Presenter\PresentableTrait; +use App\Models\Presenters\EntityPresenter; +use App\Helpers\Invoice\InvoiceSumInclusive; +use App\Utils\Traits\Invoice\ActionsInvoice; +use Illuminate\Database\Eloquent\SoftDeletes; +use App\Events\Invoice\InvoiceReminderWasEmailed; /** * App\Models\Invoice @@ -144,6 +145,8 @@ class Invoice extends BaseModel use MakesReminders; use ActionsInvoice; + use Searchable; + protected $presenter = EntityPresenter::class; protected $touches = []; @@ -235,6 +238,25 @@ class Invoice extends BaseModel public const STATUS_UNPAID = -2; //status < 4 || < 3 && !is_deleted && !trashed() + public function toSearchableArray() + { + return [ + 'name' => $this->client->present()->name() . ' - ' . $this->number, + 'hashed_id' => $this->hashed_id, + 'number' => $this->number, + 'is_deleted' => $this->is_deleted, + 'amount' => (float) $this->amount, + 'balance' => (float) $this->balance, + 'due_date' => $this->due_date, + 'date' => $this->date, + 'custom_value1' => $this->custom_value1, + 'custom_value2' => $this->custom_value2, + 'custom_value3' => $this->custom_value3, + 'custom_value4' => $this->custom_value4, + 'company_key' => $this->company->company_key, + ]; + } + public function getEntityType() { return self::class; diff --git a/app/Services/Chart/ChartCalculations.php b/app/Services/Chart/ChartCalculations.php index 88a817c5bc1c..9db8feefc2f4 100644 --- a/app/Services/Chart/ChartCalculations.php +++ b/app/Services/Chart/ChartCalculations.php @@ -141,8 +141,8 @@ trait ChartCalculations } match ($data['calculation']) { - 'sum' => $result = $q->sum('refunded'), - 'avg' => $result = $q->avg('refunded'), + 'sum' => $result = $q->sum('amount'), + 'avg' => $result = $q->avg('amount'), 'count' => $result = $q->count(), default => $result = 0, }; @@ -287,14 +287,14 @@ trait ChartCalculations return $query->get() ->when($data['currency_id'] == '999', function ($collection) { - $collection->map(function ($e) { + return $collection->map(function ($e) { /** @var \App\Models\Expense $e */ return $e->amount * $e->exchange_rate; }); }) ->when($data['currency_id'] != '999', function ($collection) { - $collection->map(function ($e) { + return $collection->map(function ($e) { /** @var \App\Models\Expense $e */ return $e->amount; diff --git a/app/Transformers/InvoiceTransformer.php b/app/Transformers/InvoiceTransformer.php index 415f6f5b7092..c619414a7b98 100644 --- a/app/Transformers/InvoiceTransformer.php +++ b/app/Transformers/InvoiceTransformer.php @@ -166,6 +166,10 @@ class InvoiceTransformer extends EntityTransformer $data['reminder_schedule'] = (string) $invoice->reminderSchedule(); } + if (request()->has('is_locked') && request()->query('is_locked') == 'true') { + $data['is_locked'] = (bool) $invoice->isLocked(); + } + return $data; } diff --git a/composer.json b/composer.json index f67661f328ed..d188feead9a7 100644 --- a/composer.json +++ b/composer.json @@ -41,6 +41,7 @@ "authorizenet/authorizenet": "^2.0", "awobaz/compoships": "^2.1", "aws/aws-sdk-php": "^3.319", + "babenkoivan/elastic-scout-driver": "^4.0", "bacon/bacon-qr-code": "^2.0", "beganovich/snappdf": "dev-master", "braintree/braintree_php": "^6.0", @@ -68,6 +69,7 @@ "josemmo/facturae-php": "^1.7", "laracasts/presenter": "^0.2.1", "laravel/framework": "^11.0", + "laravel/scout": "^10.11", "laravel/slack-notification-channel": "^3", "laravel/socialite": "^5", "laravel/tinker": "^2.7", diff --git a/composer.lock b/composer.lock index 3646644d1739..4f9990829af6 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "a1fac503f165997d7aa339239e002230", + "content-hash": "79894c80128f463131a8bf601f5fbfcb", "packages": [ { "name": "adrienrn/php-mimetyper", @@ -631,6 +631,217 @@ }, "time": "2024-09-13T18:05:10+00:00" }, + { + "name": "babenkoivan/elastic-adapter", + "version": "v4.0.0", + "source": { + "type": "git", + "url": "https://github.com/babenkoivan/elastic-adapter.git", + "reference": "3a1fbb2d30c0b9e84c50204c1406cb7e44e6d2a1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/babenkoivan/elastic-adapter/zipball/3a1fbb2d30c0b9e84c50204c1406cb7e44e6d2a1", + "reference": "3a1fbb2d30c0b9e84c50204c1406cb7e44e6d2a1", + "shasum": "" + }, + "require": { + "babenkoivan/elastic-client": "^3.0", + "php": "^8.2" + }, + "require-dev": { + "dg/bypass-finals": "^1.7", + "friendsofphp/php-cs-fixer": "^3.14", + "orchestra/testbench": "^9.0", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Elastic\\Adapter\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ivan Babenko", + "email": "babenko.i.a@gmail.com" + } + ], + "description": "Adapter for official PHP Elasticsearch client", + "keywords": [ + "adapter", + "client", + "elastic", + "elasticsearch", + "php" + ], + "support": { + "issues": "https://github.com/babenkoivan/elastic-adapter/issues", + "source": "https://github.com/babenkoivan/elastic-adapter/tree/v4.0.0" + }, + "funding": [ + { + "url": "https://ko-fi.com/ivanbabenko", + "type": "ko-fi" + }, + { + "url": "https://paypal.me/babenkoi", + "type": "paypal" + } + ], + "time": "2024-06-18T06:57:10+00:00" + }, + { + "name": "babenkoivan/elastic-client", + "version": "v3.0.0", + "source": { + "type": "git", + "url": "https://github.com/babenkoivan/elastic-client.git", + "reference": "65f4a4c9dc3b5f6ba4e68e59ad26a68393aae995" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/babenkoivan/elastic-client/zipball/65f4a4c9dc3b5f6ba4e68e59ad26a68393aae995", + "reference": "65f4a4c9dc3b5f6ba4e68e59ad26a68393aae995", + "shasum": "" + }, + "require": { + "elasticsearch/elasticsearch": "^8.0", + "php": "^8.2" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.14", + "orchestra/testbench": "^9.0", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Elastic\\Client\\ServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Elastic\\Client\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ivan Babenko", + "email": "babenko.i.a@gmail.com" + } + ], + "description": "The official PHP Elasticsearch client integrated with Laravel", + "keywords": [ + "client", + "elastic", + "elasticsearch", + "laravel", + "php" + ], + "support": { + "issues": "https://github.com/babenkoivan/elastic-client/issues", + "source": "https://github.com/babenkoivan/elastic-client/tree/v3.0.0" + }, + "funding": [ + { + "url": "https://ko-fi.com/ivanbabenko", + "type": "ko-fi" + }, + { + "url": "https://paypal.me/babenkoi", + "type": "paypal" + } + ], + "time": "2024-06-18T06:53:01+00:00" + }, + { + "name": "babenkoivan/elastic-scout-driver", + "version": "v4.0.0", + "source": { + "type": "git", + "url": "https://github.com/babenkoivan/elastic-scout-driver.git", + "reference": "f3791521fb3216850335f491a1461a16738125cd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/babenkoivan/elastic-scout-driver/zipball/f3791521fb3216850335f491a1461a16738125cd", + "reference": "f3791521fb3216850335f491a1461a16738125cd", + "shasum": "" + }, + "require": { + "babenkoivan/elastic-adapter": "^4.0", + "php": "^8.2" + }, + "require-dev": { + "babenkoivan/elastic-migrations": "^4.0", + "friendsofphp/php-cs-fixer": "^3.14", + "laravel/legacy-factories": "^1.3", + "laravel/scout": "^10.0", + "orchestra/testbench": "^9.0", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Elastic\\ScoutDriver\\ServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Elastic\\ScoutDriver\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ivan Babenko", + "email": "babenko.i.a@gmail.com" + } + ], + "description": "Elasticsearch driver for Laravel Scout", + "keywords": [ + "driver", + "elastic", + "elasticsearch", + "laravel", + "php", + "scout" + ], + "support": { + "issues": "https://github.com/babenkoivan/elastic-scout-driver/issues", + "source": "https://github.com/babenkoivan/elastic-scout-driver/tree/v4.0.0" + }, + "funding": [ + { + "url": "https://ko-fi.com/ivanbabenko", + "type": "ko-fi" + }, + { + "url": "https://paypal.me/babenkoi", + "type": "paypal" + } + ], + "time": "2024-06-18T07:06:48+00:00" + }, { "name": "bacon/bacon-qr-code", "version": "2.0.8", @@ -1950,6 +2161,122 @@ ], "time": "2023-10-06T06:47:41+00:00" }, + { + "name": "elastic/transport", + "version": "v8.10.0", + "source": { + "type": "git", + "url": "https://github.com/elastic/elastic-transport-php.git", + "reference": "8be37d679637545e50b1cea9f8ee903888783021" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/elastic/elastic-transport-php/zipball/8be37d679637545e50b1cea9f8ee903888783021", + "reference": "8be37d679637545e50b1cea9f8ee903888783021", + "shasum": "" + }, + "require": { + "composer-runtime-api": "^2.0", + "open-telemetry/api": "^1.0", + "php": "^7.4 || ^8.0", + "php-http/discovery": "^1.14", + "php-http/httplug": "^2.3", + "psr/http-client": "^1.0", + "psr/http-factory": "^1.0", + "psr/http-message": "^1.0 || ^2.0", + "psr/log": "^1 || ^2 || ^3" + }, + "require-dev": { + "nyholm/psr7": "^1.5", + "open-telemetry/sdk": "^1.0", + "php-http/mock-client": "^1.5", + "phpstan/phpstan": "^1.4", + "phpunit/phpunit": "^9.5", + "symfony/http-client": "^5.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "Elastic\\Transport\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "HTTP transport PHP library for Elastic products", + "keywords": [ + "PSR_17", + "elastic", + "http", + "psr-18", + "psr-7", + "transport" + ], + "support": { + "issues": "https://github.com/elastic/elastic-transport-php/issues", + "source": "https://github.com/elastic/elastic-transport-php/tree/v8.10.0" + }, + "time": "2024-08-14T08:55:07+00:00" + }, + { + "name": "elasticsearch/elasticsearch", + "version": "v8.15.0", + "source": { + "type": "git", + "url": "https://github.com/elastic/elasticsearch-php.git", + "reference": "34c2444fa8d4c3e6c8b009bd8dea90bca007203b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/elastic/elasticsearch-php/zipball/34c2444fa8d4c3e6c8b009bd8dea90bca007203b", + "reference": "34c2444fa8d4c3e6c8b009bd8dea90bca007203b", + "shasum": "" + }, + "require": { + "elastic/transport": "^8.10", + "guzzlehttp/guzzle": "^7.0", + "php": "^7.4 || ^8.0", + "psr/http-client": "^1.0", + "psr/http-message": "^1.1 || ^2.0", + "psr/log": "^1|^2|^3" + }, + "require-dev": { + "ext-yaml": "*", + "ext-zip": "*", + "mockery/mockery": "^1.5", + "nyholm/psr7": "^1.5", + "php-http/message-factory": "^1.0", + "php-http/mock-client": "^1.5", + "phpstan/phpstan": "^1.4", + "phpunit/phpunit": "^9.5", + "psr/http-factory": "^1.0", + "symfony/finder": "~4.0", + "symfony/http-client": "^5.0|^6.0|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Elastic\\Elasticsearch\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHP Client for Elasticsearch", + "keywords": [ + "client", + "elastic", + "elasticsearch", + "search" + ], + "support": { + "issues": "https://github.com/elastic/elasticsearch-php/issues", + "source": "https://github.com/elastic/elasticsearch-php/tree/v8.15.0" + }, + "time": "2024-08-14T14:32:50+00:00" + }, { "name": "endroid/qr-code", "version": "5.0.7", @@ -4820,6 +5147,84 @@ }, "time": "2024-08-12T22:06:33+00:00" }, + { + "name": "laravel/scout", + "version": "v10.11.3", + "source": { + "type": "git", + "url": "https://github.com/laravel/scout.git", + "reference": "642b4750127b5242a089571c9314037a7453cc0a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/scout/zipball/642b4750127b5242a089571c9314037a7453cc0a", + "reference": "642b4750127b5242a089571c9314037a7453cc0a", + "shasum": "" + }, + "require": { + "illuminate/bus": "^9.0|^10.0|^11.0", + "illuminate/contracts": "^9.0|^10.0|^11.0", + "illuminate/database": "^9.0|^10.0|^11.0", + "illuminate/http": "^9.0|^10.0|^11.0", + "illuminate/pagination": "^9.0|^10.0|^11.0", + "illuminate/queue": "^9.0|^10.0|^11.0", + "illuminate/support": "^9.0|^10.0|^11.0", + "php": "^8.0", + "symfony/console": "^6.0|^7.0" + }, + "require-dev": { + "algolia/algoliasearch-client-php": "^3.2", + "meilisearch/meilisearch-php": "^1.0", + "mockery/mockery": "^1.0", + "orchestra/testbench": "^7.31|^8.11|^9.0", + "php-http/guzzle7-adapter": "^1.0", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^9.3|^10.4", + "typesense/typesense-php": "^4.9.3" + }, + "suggest": { + "algolia/algoliasearch-client-php": "Required to use the Algolia engine (^3.2).", + "meilisearch/meilisearch-php": "Required to use the Meilisearch engine (^1.0).", + "typesense/typesense-php": "Required to use the Typesense engine (^4.9)." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "10.x-dev" + }, + "laravel": { + "providers": [ + "Laravel\\Scout\\ScoutServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Laravel\\Scout\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "Laravel Scout provides a driver based solution to searching your Eloquent models.", + "keywords": [ + "algolia", + "laravel", + "search" + ], + "support": { + "issues": "https://github.com/laravel/scout/issues", + "source": "https://github.com/laravel/scout" + }, + "time": "2024-09-11T21:32:42+00:00" + }, { "name": "laravel/serializable-closure", "version": "v1.3.4", @@ -7633,6 +8038,134 @@ ], "time": "2024-09-09T07:06:30+00:00" }, + { + "name": "open-telemetry/api", + "version": "1.0.3", + "source": { + "type": "git", + "url": "https://github.com/opentelemetry-php/api.git", + "reference": "87de95d926f46262885d0d390060c095af13e2e5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/opentelemetry-php/api/zipball/87de95d926f46262885d0d390060c095af13e2e5", + "reference": "87de95d926f46262885d0d390060c095af13e2e5", + "shasum": "" + }, + "require": { + "open-telemetry/context": "^1.0", + "php": "^7.4 || ^8.0", + "psr/log": "^1.1|^2.0|^3.0", + "symfony/polyfill-php80": "^1.26", + "symfony/polyfill-php81": "^1.26", + "symfony/polyfill-php82": "^1.26" + }, + "conflict": { + "open-telemetry/sdk": "<=1.0.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.0.x-dev" + } + }, + "autoload": { + "files": [ + "Trace/functions.php" + ], + "psr-4": { + "OpenTelemetry\\API\\": "." + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "opentelemetry-php contributors", + "homepage": "https://github.com/open-telemetry/opentelemetry-php/graphs/contributors" + } + ], + "description": "API for OpenTelemetry PHP.", + "keywords": [ + "Metrics", + "api", + "apm", + "logging", + "opentelemetry", + "otel", + "tracing" + ], + "support": { + "chat": "https://app.slack.com/client/T08PSQ7BQ/C01NFPCV44V", + "docs": "https://opentelemetry.io/docs/php", + "issues": "https://github.com/open-telemetry/opentelemetry-php/issues", + "source": "https://github.com/open-telemetry/opentelemetry-php" + }, + "time": "2024-02-06T01:32:25+00:00" + }, + { + "name": "open-telemetry/context", + "version": "1.0.2", + "source": { + "type": "git", + "url": "https://github.com/opentelemetry-php/context.git", + "reference": "e9d254a7c89885e63fd2fde54e31e81aaaf52b7c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/opentelemetry-php/context/zipball/e9d254a7c89885e63fd2fde54e31e81aaaf52b7c", + "reference": "e9d254a7c89885e63fd2fde54e31e81aaaf52b7c", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0", + "symfony/polyfill-php80": "^1.26", + "symfony/polyfill-php81": "^1.26", + "symfony/polyfill-php82": "^1.26" + }, + "suggest": { + "ext-ffi": "To allow context switching in Fibers" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.0.x-dev" + } + }, + "autoload": { + "files": [ + "fiber/initialize_fiber_handler.php" + ], + "psr-4": { + "OpenTelemetry\\Context\\": "." + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "opentelemetry-php contributors", + "homepage": "https://github.com/open-telemetry/opentelemetry-php/graphs/contributors" + } + ], + "description": "Context implementation for OpenTelemetry PHP.", + "keywords": [ + "Context", + "opentelemetry", + "otel" + ], + "support": { + "chat": "https://app.slack.com/client/T08PSQ7BQ/C01NFPCV44V", + "docs": "https://opentelemetry.io/docs/php", + "issues": "https://github.com/open-telemetry/opentelemetry-php/issues", + "source": "https://github.com/open-telemetry/opentelemetry-php" + }, + "time": "2024-01-13T05:50:44+00:00" + }, { "name": "paragonie/constant_time_encoding", "version": "v3.0.0", @@ -13134,6 +13667,82 @@ ], "time": "2024-09-09T11:45:10+00:00" }, + { + "name": "symfony/polyfill-php82", + "version": "v1.31.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php82.git", + "reference": "5d2ed36f7734637dacc025f179698031951b1692" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php82/zipball/5d2ed36f7734637dacc025f179698031951b1692", + "reference": "5d2ed36f7734637dacc025f179698031951b1692", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php82\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.2+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php82/tree/v1.31.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, { "name": "symfony/polyfill-php83", "version": "v1.31.0", diff --git a/config/elastic.client.php b/config/elastic.client.php new file mode 100644 index 000000000000..77b5b0e30663 --- /dev/null +++ b/config/elastic.client.php @@ -0,0 +1,24 @@ + env('ELASTIC_CONNECTION', 'default'), + 'connections' => [ + 'default' => [ + 'hosts' => [ + env('ELASTIC_HOST'), + ], + // configure basic authentication + 'basicAuthentication' => [ + env('ELASTIC_USERNAME'), + env('ELASTIC_PASSWORD'), + ], + // configure HTTP client (Guzzle by default) + 'httpClientOptions' => [ + 'timeout' => 2, + 'verify_host' => false, // Disable SSL verification + 'verify_peer' => false, + + ], + ], + ], +]; diff --git a/config/elastic.scout_driver.php b/config/elastic.scout_driver.php new file mode 100644 index 000000000000..a5d123aa8de4 --- /dev/null +++ b/config/elastic.scout_driver.php @@ -0,0 +1,5 @@ + env('ELASTIC_SCOUT_DRIVER_REFRESH_DOCUMENTS', false), +]; diff --git a/config/scout.php b/config/scout.php new file mode 100644 index 000000000000..a65020a46701 --- /dev/null +++ b/config/scout.php @@ -0,0 +1,202 @@ + env('SCOUT_DRIVER', 'algolia'), + + /* + |-------------------------------------------------------------------------- + | 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', true), + + /* + |-------------------------------------------------------------------------- + | 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' => true, + + /* + |-------------------------------------------------------------------------- + | 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', ''), + ], + + /* + |-------------------------------------------------------------------------- + | 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), + ], + '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' + // ], + // ], + ], + ], + +];