id(); /* * Translatable columns hold a JSON map of locale => value, e.g. * {"es": "Servicios", "en": "Services", "ca": "Serveis"}. * spatie/laravel-translatable casts these transparently. * * `jsonb` rather than `json`: Postgres cannot compare a `json` * value for equality, so any `select distinct` touching one fails * outright — and Filament's relationship selects emit exactly * that. On MySQL it is still `json` and on SQLite still `text`, * so nothing else changes. */ $table->jsonb('name'); $table->jsonb('slug'); $table->jsonb('description')->nullable(); $table->string('color', 7)->nullable(); $table->unsignedInteger('position')->default(0); $table->boolean('is_visible')->default(true); $table->timestamps(); $table->softDeletes(); $table->index('position'); }); Schema::create('pages', function (Blueprint $table) { $table->id(); $table->foreignId('category_id')->nullable() ->constrained()->nullOnDelete(); $table->foreignId('author_id')->nullable() ->constrained('users')->nullOnDelete(); $table->jsonb('title'); // Slugs live inside JSON, so uniqueness cannot be a DB constraint. // It is enforced per locale by App\Rules\UniqueTranslatedSlug. $table->jsonb('slug'); $table->jsonb('excerpt')->nullable(); $table->jsonb('body')->nullable(); $table->jsonb('meta_title')->nullable(); $table->jsonb('meta_description')->nullable(); $table->string('status', 20)->default('draft'); $table->string('featured_image')->nullable(); $table->boolean('is_featured')->default(false); $table->timestamp('published_at')->nullable(); $table->timestamps(); $table->softDeletes(); $table->index(['status', 'published_at']); }); } public function down(): void { Schema::dropIfExists('pages'); Schema::dropIfExists('categories'); } };