diff --git a/.env.example b/.env.example index 61119763c45..781dc6b24cc 100644 --- a/.env.example +++ b/.env.example @@ -25,7 +25,7 @@ LOG_CHANNEL=stack # Drivers BROADCAST_DRIVER=log CACHE_DRIVER=file -QUEUE_CONNECTION=database +QUEUE_CONNECTION=sync SESSION_DRIVER=file SESSION_LIFETIME=120 @@ -51,7 +51,7 @@ MAIL_REPLY_TO_NAME="${APP_NAME}" # Read config/scout.php for more information. # Note that you have to use either Meilisearch or Algolia to enable search in # Monica. Searching requires a queue to be configured. -SCOUT_DRIVER=meilisearch +SCOUT_DRIVER=database SCOUT_QUEUE=true MEILISEARCH_HOST= MEILISEARCH_KEY= diff --git a/app/Console/Commands/SetupApplication.php b/app/Console/Commands/SetupApplication.php index bd2c70ce40a..2d6569d05d2 100644 --- a/app/Console/Commands/SetupApplication.php +++ b/app/Console/Commands/SetupApplication.php @@ -21,20 +21,11 @@ class SetupApplication extends Command */ protected $description = 'Setup everything that is needed for Monica to work'; - /** - * Create a new command instance. - * - * @return void - */ - public function __construct() - { - parent::__construct(); - } - /** * Execute the console command. * * @return void + * @codeCoverageIgnore */ public function handle(): void { @@ -45,26 +36,28 @@ public function handle(): void $this->line('|'); $this->line('-----------------------------'); - $this->line('-> Creating indexes on Meilisearch. Make sure Meilisearch is running.'); - - $client = new Client(config('scout.meilisearch.host'), config('scout.meilisearch.key')); - $index = $client->index('contacts'); - $index->updateFilterableAttributes([ - 'id', - 'vault_id', - ]); - $index = $client->index('notes'); - $index->updateFilterableAttributes([ - 'id', - 'vault_id', - 'contact_id', - ]); - $index = $client->index('groups'); - $index->updateFilterableAttributes([ - 'id', - 'vault_id', - ]); - - $this->line('✓ Indexes created'); + if (config('scout.driver') === 'meilisearch' && ($host = config('scout.meilisearch.host')) !== '') { + $this->line('-> Creating indexes on Meilisearch. Make sure Meilisearch is running.'); + + $client = new Client($host, config('scout.meilisearch.key')); + $index = $client->index('contacts'); + $index->updateFilterableAttributes([ + 'id', + 'vault_id', + ]); + $index = $client->index('notes'); + $index->updateFilterableAttributes([ + 'id', + 'vault_id', + 'contact_id', + ]); + $index = $client->index('groups'); + $index->updateFilterableAttributes([ + 'id', + 'vault_id', + ]); + + $this->line('✓ Indexes created'); + } } } diff --git a/app/Console/Commands/SetupDummyAccount.php b/app/Console/Commands/SetupDummyAccount.php index 662082cc872..b67d16e69af 100644 --- a/app/Console/Commands/SetupDummyAccount.php +++ b/app/Console/Commands/SetupDummyAccount.php @@ -11,6 +11,8 @@ use App\Exceptions\EntryAlreadyExistException; use App\Models\Contact; use App\Models\ContactImportantDate; +use App\Models\Group; +use App\Models\Note; use App\Models\User; use App\Models\Vault; use App\Settings\CreateAccount\Services\CreateAccount; @@ -41,16 +43,6 @@ class SetupDummyAccount extends Command */ protected $description = 'Prepare an account with fake data so users can play with it'; - /** - * Create a new command instance. - * - * @return void - */ - public function __construct() - { - parent::__construct(); - } - /** * Execute the console command. * @@ -81,9 +73,10 @@ private function start(): void private function wipeAndMigrateDB(): void { - shell_exec('curl -X DELETE "'.config('scout.meilisearch.host').'/indexes/notes"'); - shell_exec('curl -X DELETE "'.config('scout.meilisearch.host').'/indexes/contacts"'); - shell_exec('curl -X DELETE "'.config('scout.meilisearch.host').'/indexes/groups"'); + $this->artisan('☐ Flush search engine', 'scout:flush', ['model' => Note::class]); + $this->artisan('☐ Flush search engine', 'scout:flush', ['model' => Contact::class]); + $this->artisan('☐ Flush search engine', 'scout:flush', ['model' => Group::class]); + $this->artisan('☐ Reset search engine', 'monica:setup'); $this->artisan('☐ Migration of the database', 'migrate:fresh'); $this->artisan('☐ Symlink the storage folder', 'storage:link'); @@ -124,7 +117,6 @@ private function createFirstUsers(): void ]); $this->firstUser->email_verified_at = Carbon::now(); $this->firstUser->save(); - sleep(5); } private function createVaults(): void diff --git a/app/Helpers/ScoutHelper.php b/app/Helpers/ScoutHelper.php new file mode 100644 index 00000000000..552eda13d37 --- /dev/null +++ b/app/Helpers/ScoutHelper.php @@ -0,0 +1,27 @@ +set('cashier.currency_locale', $event->locale); - } -} diff --git a/app/Models/Contact.php b/app/Models/Contact.php index c9757ca179a..39089f923c6 100644 --- a/app/Models/Contact.php +++ b/app/Models/Contact.php @@ -5,6 +5,7 @@ use App\Helpers\AvatarHelper; use App\Helpers\ImportantDateHelper; use App\Helpers\NameHelper; +use App\Helpers\ScoutHelper; use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; @@ -12,6 +13,8 @@ use Illuminate\Database\Eloquent\Relations\BelongsToMany; use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Support\Facades\Auth; +use Laravel\Scout\Attributes\SearchUsingFullText; +use Laravel\Scout\Attributes\SearchUsingPrefix; use Laravel\Scout\Searchable; class Contact extends Model @@ -64,7 +67,10 @@ class Contact extends Model * Get the indexable data array for the model. * * @return array + * @codeCoverageIgnore */ + #[SearchUsingPrefix(['id', 'vault_id'])] + #[SearchUsingFullText(['first_name', 'last_name', 'middle_name', 'nickname', 'maiden_name'])] public function toSearchableArray(): array { return [ @@ -75,10 +81,6 @@ public function toSearchableArray(): array 'middle_name' => $this->middle_name, 'nickname' => $this->nickname, 'maiden_name' => $this->maiden_name, - 'url' => route('contact.show', [ - 'vault' => $this->vault_id, - 'contact' => $this->id, - ]), ]; } @@ -106,6 +108,16 @@ protected static function boot(): void }); } + /** + * When updating a model, this method determines if we should update the search index. + * + * @return bool + */ + public function searchIndexShouldBeUpdated() + { + return ScoutHelper::activated(); + } + /** * Get the vault associated with the contact. * diff --git a/app/Models/Group.php b/app/Models/Group.php index 82f2e23c9b5..88ef63c6bf5 100644 --- a/app/Models/Group.php +++ b/app/Models/Group.php @@ -2,11 +2,14 @@ namespace App\Models; +use App\Helpers\ScoutHelper; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\BelongsToMany; use Illuminate\Database\Eloquent\Relations\MorphOne; +use Laravel\Scout\Attributes\SearchUsingFullText; +use Laravel\Scout\Attributes\SearchUsingPrefix; use Laravel\Scout\Searchable; class Group extends Model @@ -31,7 +34,10 @@ class Group extends Model * Get the indexable data array for the model. * * @return array + * @codeCoverageIgnore */ + #[SearchUsingPrefix(['id', 'vault_id'])] + #[SearchUsingFullText(['name'])] public function toSearchableArray(): array { return [ @@ -41,6 +47,16 @@ public function toSearchableArray(): array ]; } + /** + * When updating a model, this method determines if we should update the search index. + * + * @return bool + */ + public function searchIndexShouldBeUpdated() + { + return ScoutHelper::activated(); + } + /** * Get the vault associated with the group. * diff --git a/app/Models/Loan.php b/app/Models/Loan.php index d7a0b818983..cd07fd0e444 100644 --- a/app/Models/Loan.php +++ b/app/Models/Loan.php @@ -2,6 +2,7 @@ namespace App\Models; +use App\Helpers\ScoutHelper; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; @@ -57,6 +58,16 @@ class Loan extends Model 'settled' => 'boolean', ]; + /** + * When updating a model, this method determines if we should update the search index. + * + * @return bool + */ + public function searchIndexShouldBeUpdated() + { + return ScoutHelper::activated(); + } + /** * Get the vault associated with the loan. * diff --git a/app/Models/Note.php b/app/Models/Note.php index e23ae8d347c..55d50eaaa0f 100644 --- a/app/Models/Note.php +++ b/app/Models/Note.php @@ -2,10 +2,13 @@ namespace App\Models; +use App\Helpers\ScoutHelper; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\MorphOne; +use Laravel\Scout\Attributes\SearchUsingFullText; +use Laravel\Scout\Attributes\SearchUsingPrefix; use Laravel\Scout\Searchable; class Note extends Model @@ -20,6 +23,7 @@ class Note extends Model */ protected $fillable = [ 'contact_id', + 'vault_id', 'author_id', 'emotion_id', 'title', @@ -30,18 +34,29 @@ class Note extends Model * Get the indexable data array for the model. * * @return array + * @codeCoverageIgnore */ + #[SearchUsingPrefix(['id', 'vault_id'])] + #[SearchUsingFullText(['title', 'body'])] public function toSearchableArray(): array { - $array = [ + return [ 'id' => $this->id, - 'vault_id' => $this->contact->vault_id, - 'contact_id' => $this->contact->id, + 'vault_id' => $this->vault_id, + 'contact_id' => $this->contact_id, 'title' => $this->title, 'body' => $this->body, ]; + } - return $array; + /** + * When updating a model, this method determines if we should update the search index. + * + * @return bool + */ + public function searchIndexShouldBeUpdated() + { + return ScoutHelper::activated(); } /** diff --git a/app/Providers/EventServiceProvider.php b/app/Providers/EventServiceProvider.php index 3695decace3..9d2f333ea81 100644 --- a/app/Providers/EventServiceProvider.php +++ b/app/Providers/EventServiceProvider.php @@ -4,12 +4,9 @@ use App\Contact\ManageDocuments\Events\FileDeleted; use App\Contact\ManageDocuments\Listeners\DeleteFileInStorage; -use App\Listeners\LocaleUpdatedListener; use Illuminate\Auth\Events\Registered; use Illuminate\Auth\Listeners\SendEmailVerificationNotification; -use Illuminate\Foundation\Events\LocaleUpdated; use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider; -use Illuminate\Support\Facades\Event; class EventServiceProvider extends ServiceProvider { @@ -22,9 +19,6 @@ class EventServiceProvider extends ServiceProvider Registered::class => [ SendEmailVerificationNotification::class, ], - LocaleUpdated::class => [ - LocaleUpdatedListener::class, - ], FileDeleted::class => [ DeleteFileInStorage::class, ], diff --git a/database/factories/NoteFactory.php b/database/factories/NoteFactory.php index 4a04bf02ca0..6766b1483ba 100644 --- a/database/factories/NoteFactory.php +++ b/database/factories/NoteFactory.php @@ -5,6 +5,7 @@ use App\Models\Contact; use App\Models\Note; use App\Models\User; +use App\Models\Vault; use Illuminate\Database\Eloquent\Factories\Factory; class NoteFactory extends Factory @@ -25,6 +26,7 @@ public function definition() { return [ 'contact_id' => Contact::factory(), + 'vault_id' => Vault::factory(), 'author_id' => User::factory(), 'title' => $this->faker->title(), 'body' => $this->faker->sentence(), diff --git a/database/migrations/2020_04_25_133132_create_contacts_table.php b/database/migrations/2020_04_25_133132_create_contacts_table.php index b0e41953799..e2fa78dea26 100644 --- a/database/migrations/2020_04_25_133132_create_contacts_table.php +++ b/database/migrations/2020_04_25_133132_create_contacts_table.php @@ -28,11 +28,19 @@ public function up() $table->boolean('listed')->default(true); $table->datetime('last_updated_at')->nullable(); $table->timestamps(); + $table->foreign('vault_id')->references('id')->on('vaults')->onDelete('cascade'); $table->foreign('gender_id')->references('id')->on('genders')->onDelete('set null'); $table->foreign('pronoun_id')->references('id')->on('pronouns')->onDelete('set null'); $table->foreign('template_id')->references('id')->on('templates')->onDelete('set null'); $table->foreign('company_id')->references('id')->on('companies')->onDelete('set null'); + + if (config('scout.driver') === 'database' && in_array(DB::connection()->getDriverName(), ['mysql', 'pgsql'])) { + $table->fullText('first_name'); + $table->fullText('middle_name'); + $table->fullText('nickname'); + $table->fullText('maiden_name'); + } }); Schema::create('user_vault', function (Blueprint $table) { diff --git a/database/migrations/2021_10_09_204235_create_group_table.php b/database/migrations/2021_10_09_204235_create_group_table.php index b91078aaad0..e1ba552d32a 100644 --- a/database/migrations/2021_10_09_204235_create_group_table.php +++ b/database/migrations/2021_10_09_204235_create_group_table.php @@ -37,8 +37,13 @@ public function up() $table->unsignedBigInteger('group_type_id'); $table->string('name'); $table->timestamps(); + $table->foreign('vault_id')->references('id')->on('vaults')->onDelete('cascade'); $table->foreign('group_type_id')->references('id')->on('group_types')->onDelete('cascade'); + + if (config('scout.driver') === 'database' && in_array(DB::connection()->getDriverName(), ['mysql', 'pgsql'])) { + $table->fullText('name'); + } }); Schema::create('contact_group', function (Blueprint $table) { diff --git a/database/migrations/2021_10_21_013005_create_notes_table.php b/database/migrations/2021_10_21_013005_create_notes_table.php index da5caa41325..bdc51e262cc 100644 --- a/database/migrations/2021_10_21_013005_create_notes_table.php +++ b/database/migrations/2021_10_21_013005_create_notes_table.php @@ -14,14 +14,22 @@ public function up() Schema::create('notes', function (Blueprint $table) { $table->id(); $table->unsignedBigInteger('contact_id'); + $table->unsignedBigInteger('vault_id'); $table->unsignedBigInteger('author_id')->nullable(); $table->unsignedBigInteger('emotion_id')->nullable(); $table->string('title')->nullable(); $table->text('body'); $table->timestamps(); + $table->foreign('contact_id')->references('id')->on('contacts')->onDelete('cascade'); + $table->foreign('vault_id')->references('id')->on('vaults')->onDelete('cascade'); $table->foreign('author_id')->references('id')->on('users')->onDelete('set null'); $table->foreign('emotion_id')->references('id')->on('emotions')->onDelete('set null'); + + if (config('scout.driver') === 'database' && in_array(DB::connection()->getDriverName(), ['mysql', 'pgsql'])) { + $table->fullText('title'); + $table->fullText('body'); + } }); } diff --git a/domains/Contact/ManageNotes/Services/CreateNote.php b/domains/Contact/ManageNotes/Services/CreateNote.php index 19fff16791f..3e0f633414f 100644 --- a/domains/Contact/ManageNotes/Services/CreateNote.php +++ b/domains/Contact/ManageNotes/Services/CreateNote.php @@ -67,6 +67,7 @@ public function execute(array $data): Note $this->note = Note::create([ 'contact_id' => $this->contact->id, + 'vault_id' => $data['vault_id'], 'author_id' => $this->author->id, 'title' => $this->valueOrNull($data, 'title'), 'body' => $data['body'],