|
4 | 4 | use Illuminate\Database\Schema\Blueprint; |
5 | 5 | use Illuminate\Support\Facades\Schema; |
6 | 6 |
|
7 | | -return new class extends Migration { |
| 7 | +/** |
| 8 | + * CreateEncryptedSearchIndexTable |
| 9 | + * |
| 10 | + * This migration defines the `encrypted_search_index` table, which stores |
| 11 | + * normalized and hashed tokens to enable secure searching over encrypted data. |
| 12 | + * |
| 13 | + * Each record in this table represents a single searchable token for a given |
| 14 | + * model instance and field. For example, if a "Client" model has an encrypted |
| 15 | + * `last_name`, its normalized and tokenized forms (exact or prefix) will be |
| 16 | + * stored here. These tokens can be matched without revealing the original value. |
| 17 | + * |
| 18 | + * The combination of `model_type`, `model_id`, `field`, and `type` uniquely |
| 19 | + * identifies all searchable tokens for a specific model field. |
| 20 | + * |
| 21 | + * Indexing strategy: |
| 22 | + * - `esi_lookup` provides efficient token-based searches. |
| 23 | + * - `esi_row` accelerates lookup and cleanup operations per model instance. |
| 24 | + * |
| 25 | + * Typical usage: |
| 26 | + * - Generated automatically by the HasEncryptedSearchIndex trait. |
| 27 | + * - Used for both exact and prefix token searches. |
| 28 | + */ |
| 29 | +return new class extends Migration |
| 30 | +{ |
| 31 | + /** |
| 32 | + * Run the migrations. |
| 33 | + * |
| 34 | + * Creates the `encrypted_search_index` table with tokenized fields for |
| 35 | + * secure search functionality. Tokens are generated as deterministic |
| 36 | + * hashes (e.g., SHA-256) and are not reversible, ensuring no sensitive |
| 37 | + * data is exposed while maintaining fast lookups. |
| 38 | + */ |
8 | 39 | public function up(): void |
9 | 40 | { |
10 | 41 | Schema::create('encrypted_search_index', function (Blueprint $table) { |
11 | 42 | $table->id(); |
12 | | - $table->string('model_type'); // FQCN |
| 43 | + |
| 44 | + // The fully qualified model class name (e.g. App\Models\Client) |
| 45 | + $table->string('model_type'); |
| 46 | + |
| 47 | + // The primary key of the model this token belongs to |
13 | 48 | $table->unsignedBigInteger('model_id'); |
14 | | - $table->string('field'); // bv. 'last_names' |
15 | | - $table->string('type', 16); // 'exact' | 'prefix' |
16 | | - $table->string('token', 80); // sha256 hex (64) of korter |
| 49 | + |
| 50 | + // The model field (e.g. "last_names") from which this token was derived |
| 51 | + $table->string('field'); |
| 52 | + |
| 53 | + // Token type: 'exact' or 'prefix' |
| 54 | + $table->string('type', 16); |
| 55 | + |
| 56 | + // The actual hashed token (e.g. SHA-256 or truncated prefix token) |
| 57 | + $table->string('token', 80); |
| 58 | + |
| 59 | + // Record timestamps for auditing and debugging |
17 | 60 | $table->timestamps(); |
18 | 61 |
|
| 62 | + // Composite indexes for optimized lookups |
19 | 63 | $table->index(['model_type', 'field', 'type', 'token'], 'esi_lookup'); |
20 | 64 | $table->index(['model_type', 'model_id'], 'esi_row'); |
21 | 65 | }); |
22 | 66 | } |
23 | 67 |
|
| 68 | + /** |
| 69 | + * Reverse the migrations. |
| 70 | + * |
| 71 | + * Drops the `encrypted_search_index` table if it exists. |
| 72 | + */ |
24 | 73 | public function down(): void |
25 | 74 | { |
26 | 75 | Schema::dropIfExists('encrypted_search_index'); |
|
0 commit comments