Laravel 13 + Filament 5 admin panel and REST API for a non-profit developer collective, backed by the Pireon OAuth identity server. The public site is a separate, server-rendered application consuming /api/public/v1. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
74 lines
2.6 KiB
PHP
74 lines
2.6 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
use Illuminate\Database\Migrations\Migration;
|
|
use Illuminate\Database\Schema\Blueprint;
|
|
use Illuminate\Support\Facades\Schema;
|
|
|
|
return new class extends Migration
|
|
{
|
|
public function up(): void
|
|
{
|
|
Schema::create('categories', function (Blueprint $table) {
|
|
$table->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');
|
|
}
|
|
};
|