1212/**
1313 * Trait HasEncryptedSearchIndex
1414 *
15- * Adds automatic encrypted search indexing to Eloquent models.
15+ * Provides automatic encrypted search indexing for Eloquent models.
1616 *
17- * When this trait is applied to a model, it maintains an associated
18- * index table (`encrypted_search_index`) containing deterministic,
19- * non-reversible tokens derived from selected model attributes.
17+ * When attached to a model, this trait builds and maintains a companion index
18+ * table (`encrypted_search_index`) that stores deterministic, non-reversible
19+ * search tokens derived from model attributes.
2020 *
21- * The generated tokens make it possible to perform privacy-preserving
22- * searches (exact or prefix-based) without exposing plaintext data .
21+ * These tokens enable privacy-preserving search queries (exact or prefix-based)
22+ * without revealing plaintext values in the database .
2323 *
2424 * Example usage:
2525 *
3232 * ];
3333 * }
3434 *
35- * On every save or delete event :
36- * - Tokens are created, updated, or removed in the `encrypted_search_index` table .
37- * - Outdated tokens for the same record are automatically deleted .
35+ * On each save or delete:
36+ * - Tokens are (re)generated and stored in `encrypted_search_index`.
37+ * - Old entries for the record are automatically replaced or removed .
3838 *
39- * Example search:
40- * Client::encryptedExact('last_names', 'vermeer')->get();
41- * Client::encryptedPrefix('first_names', 'wie')->get();
39+ * Search queries:
40+ *
41+ * Client::encryptedExact('last_names', 'vermeer')->get();
42+ * Client::encryptedPrefix('first_names', 'wie')->get();
4243 */
4344trait HasEncryptedSearchIndex
4445{
4546 /**
4647 * Boot logic for the trait.
4748 *
4849 * Automatically updates or removes encrypted search tokens whenever
49- * a model instance is created , updated, saved , deleted, or restored.
50+ * a model instance is saved , updated, deleted, or restored.
5051 *
5152 * @return void
5253 */
5354 public static function bootHasEncryptedSearchIndex (): void
5455 {
55- // Rebuild index when model is created, updated, or saved
56+ // Rebuild index when model is created, updated or saved
5657 foreach (['created ' , 'updated ' , 'saved ' ] as $ event ) {
57- static ::$ event (function (Model $ model ): void {
58+ static ::$ event (function (Model $ model ) {
5859 $ model ->updateSearchIndex ();
5960 });
6061 }
6162
62- // Always remove tokens when a model is deleted
63- static ::deleted (fn (Model $ m ): bool => $ m ->removeSearchIndex ());
63+ // Always remove tokens when model is deleted
64+ static ::deleted (fn (Model $ m ) => $ m ->removeSearchIndex ());
6465
65- // Register forceDeleted/restored only for models using SoftDeletes
66+ // Register forceDeleted/restored events only if the model uses SoftDeletes
6667 if (in_array (SoftDeletes::class, class_uses_recursive (static ::class), true )) {
67- static ::forceDeleted (fn (Model $ m ): bool => $ m ->removeSearchIndex ());
68- static ::restored (fn (Model $ m ): bool => $ m ->updateSearchIndex ());
68+ static ::forceDeleted (fn (Model $ m ) => $ m ->removeSearchIndex ());
69+ static ::restored (fn (Model $ m ) => $ m ->updateSearchIndex ());
6970 }
7071 }
7172
7273 /**
73- * Build or refresh all search index entries for this model instance.
74+ * Create or refresh all search index entries for this model instance.
7475 *
7576 * @return void
7677 */
@@ -83,9 +84,9 @@ public function updateSearchIndex(): void
8384 }
8485
8586 $ pepper = (string ) config ('encrypted-search.search_pepper ' , '' );
86- $ max = (int ) config ('encrypted-search.max_prefix_depth ' , 6 );
87+ $ max = (int ) config ('encrypted-search.max_prefix_depth ' , 6 );
8788
88- // Remove existing entries for this record
89+ // Remove existing entries
8990 SearchIndex::where ('model_type ' , static ::class)
9091 ->where ('model_id ' , $ this ->getKey ())
9192 ->delete ();
@@ -103,28 +104,26 @@ public function updateSearchIndex(): void
103104 continue ;
104105 }
105106
106- // Exact match token
107107 if (! empty ($ modes ['exact ' ])) {
108108 $ rows [] = [
109109 'model_type ' => static ::class,
110- 'model_id ' => $ this ->getKey (),
111- 'field ' => $ field ,
112- 'type ' => 'exact ' ,
113- 'token ' => Tokens::exact ($ norm , $ pepper ),
110+ 'model_id ' => $ this ->getKey (),
111+ 'field ' => $ field ,
112+ 'type ' => 'exact ' ,
113+ 'token ' => Tokens::exact ($ norm , $ pepper ),
114114 'created_at ' => now (),
115115 'updated_at ' => now (),
116116 ];
117117 }
118118
119- // Prefix tokens
120119 if (! empty ($ modes ['prefix ' ])) {
121120 foreach (Tokens::prefixes ($ norm , $ max , $ pepper ) as $ t ) {
122121 $ rows [] = [
123122 'model_type ' => static ::class,
124- 'model_id ' => $ this ->getKey (),
125- 'field ' => $ field ,
126- 'type ' => 'prefix ' ,
127- 'token ' => $ t ,
123+ 'model_id ' => $ this ->getKey (),
124+ 'field ' => $ field ,
125+ 'type ' => 'prefix ' ,
126+ 'token ' => $ t ,
128127 'created_at ' => now (),
129128 'updated_at ' => now (),
130129 ];
@@ -150,28 +149,28 @@ public function removeSearchIndex(): void
150149 }
151150
152151 /**
153- * Scope: perform an exact encrypted search on a specific field.
152+ * Query scope: find models by exact match on an indexed field.
154153 *
155154 * Example:
156- * Client::encryptedExact('last_names', 'vermeer')->get();
155+ * Client::encryptedExact('last_names', 'vermeer')->get();
157156 *
158157 * @param \Illuminate\Database\Eloquent\Builder $query
159- * @param string $field The name of the field to search
160- * @param string $term The plaintext search term
158+ * @param string $field
159+ * @param string $term
161160 * @return \Illuminate\Database\Eloquent\Builder
162161 */
163162 public function scopeEncryptedExact (Builder $ query , string $ field , string $ term ): Builder
164163 {
165164 $ pepper = (string ) config ('encrypted-search.search_pepper ' , '' );
166- $ norm = Normalizer::normalize ($ term );
165+ $ norm = Normalizer::normalize ($ term );
167166
168167 if (! $ norm ) {
169168 return $ query ->whereRaw ('1=0 ' );
170169 }
171170
172171 $ token = Tokens::exact ($ norm , $ pepper );
173172
174- return $ query ->whereIn ($ this ->getQualifiedKeyName (), function ($ sub ) use ($ field , $ token ): void {
173+ return $ query ->whereIn ($ this ->getQualifiedKeyName (), function ($ sub ) use ($ field , $ token ) {
175174 $ sub ->select ('model_id ' )
176175 ->from ('encrypted_search_index ' )
177176 ->where ('model_type ' , static ::class)
@@ -182,28 +181,28 @@ public function scopeEncryptedExact(Builder $query, string $field, string $term)
182181 }
183182
184183 /**
185- * Scope: perform a prefix-based encrypted search on a specific field.
184+ * Query scope: find models by prefix match on an indexed field.
186185 *
187186 * Example:
188- * Client::encryptedPrefix('first_names', 'wi')->get();
187+ * Client::encryptedPrefix('first_names', 'wi')->get();
189188 *
190189 * @param \Illuminate\Database\Eloquent\Builder $query
191- * @param string $field The field name to search
192- * @param string $term The prefix term to search for
190+ * @param string $field
191+ * @param string $term
193192 * @return \Illuminate\Database\Eloquent\Builder
194193 */
195194 public function scopeEncryptedPrefix (Builder $ query , string $ field , string $ term ): Builder
196195 {
197196 $ pepper = (string ) config ('encrypted-search.search_pepper ' , '' );
198- $ norm = Normalizer::normalize ($ term );
197+ $ norm = Normalizer::normalize ($ term );
199198
200199 if (! $ norm ) {
201200 return $ query ->whereRaw ('1=0 ' );
202201 }
203202
204203 $ tokens = Tokens::prefixes ($ norm , (int ) config ('encrypted-search.max_prefix_depth ' , 6 ), $ pepper );
205204
206- return $ query ->whereIn ($ this ->getQualifiedKeyName (), function ($ sub ) use ($ field , $ tokens ): void {
205+ return $ query ->whereIn ($ this ->getQualifiedKeyName (), function ($ sub ) use ($ field , $ tokens ) {
207206 $ sub ->select ('model_id ' )
208207 ->from ('encrypted_search_index ' )
209208 ->where ('model_type ' , static ::class)
@@ -214,9 +213,9 @@ public function scopeEncryptedPrefix(Builder $query, string $field, string $term
214213 }
215214
216215 /**
217- * Get the encrypted search configuration for this model.
216+ * Resolve the encrypted search configuration for this model.
218217 *
219- * Models can define searchable fields either through a
218+ * This allows models to define searchable fields either via a
220219 * `getEncryptedSearchFields()` method or a `$encryptedSearch` property.
221220 *
222221 * @return array<string, array<string,bool>>
0 commit comments