fix migration
This commit is contained in:
@@ -0,0 +1,65 @@
|
|||||||
|
APP_NAME=Laravel
|
||||||
|
APP_ENV=local
|
||||||
|
APP_KEY=
|
||||||
|
APP_DEBUG=true
|
||||||
|
APP_URL=http://localhost
|
||||||
|
|
||||||
|
APP_LOCALE=en
|
||||||
|
APP_FALLBACK_LOCALE=en
|
||||||
|
APP_FAKER_LOCALE=en_US
|
||||||
|
|
||||||
|
APP_MAINTENANCE_DRIVER=file
|
||||||
|
# APP_MAINTENANCE_STORE=database
|
||||||
|
|
||||||
|
# PHP_CLI_SERVER_WORKERS=4
|
||||||
|
|
||||||
|
BCRYPT_ROUNDS=12
|
||||||
|
|
||||||
|
LOG_CHANNEL=stack
|
||||||
|
LOG_STACK=single
|
||||||
|
LOG_DEPRECATIONS_CHANNEL=null
|
||||||
|
LOG_LEVEL=debug
|
||||||
|
|
||||||
|
DB_CONNECTION=sqlite
|
||||||
|
# DB_HOST=127.0.0.1
|
||||||
|
# DB_PORT=3306
|
||||||
|
# DB_DATABASE=laravel
|
||||||
|
# DB_USERNAME=root
|
||||||
|
# DB_PASSWORD=
|
||||||
|
|
||||||
|
SESSION_DRIVER=database
|
||||||
|
SESSION_LIFETIME=120
|
||||||
|
SESSION_ENCRYPT=false
|
||||||
|
SESSION_PATH=/
|
||||||
|
SESSION_DOMAIN=null
|
||||||
|
|
||||||
|
BROADCAST_CONNECTION=log
|
||||||
|
FILESYSTEM_DISK=local
|
||||||
|
QUEUE_CONNECTION=database
|
||||||
|
|
||||||
|
CACHE_STORE=database
|
||||||
|
# CACHE_PREFIX=
|
||||||
|
|
||||||
|
MEMCACHED_HOST=127.0.0.1
|
||||||
|
|
||||||
|
REDIS_CLIENT=phpredis
|
||||||
|
REDIS_HOST=127.0.0.1
|
||||||
|
REDIS_PASSWORD=null
|
||||||
|
REDIS_PORT=6379
|
||||||
|
|
||||||
|
MAIL_MAILER=log
|
||||||
|
MAIL_SCHEME=null
|
||||||
|
MAIL_HOST=127.0.0.1
|
||||||
|
MAIL_PORT=2525
|
||||||
|
MAIL_USERNAME=null
|
||||||
|
MAIL_PASSWORD=null
|
||||||
|
MAIL_FROM_ADDRESS="hello@example.com"
|
||||||
|
MAIL_FROM_NAME="${APP_NAME}"
|
||||||
|
|
||||||
|
AWS_ACCESS_KEY_ID=
|
||||||
|
AWS_SECRET_ACCESS_KEY=
|
||||||
|
AWS_DEFAULT_REGION=us-east-1
|
||||||
|
AWS_BUCKET=
|
||||||
|
AWS_USE_PATH_STYLE_ENDPOINT=false
|
||||||
|
|
||||||
|
VITE_APP_NAME="${APP_NAME}"
|
||||||
+62
@@ -0,0 +1,62 @@
|
|||||||
|
APP_NAME="Laravel School API"
|
||||||
|
APP_ENV=local
|
||||||
|
APP_KEY=
|
||||||
|
APP_DEBUG=true
|
||||||
|
APP_URL=http://localhost:8000
|
||||||
|
|
||||||
|
APP_LOCALE=en
|
||||||
|
APP_FALLBACK_LOCALE=en
|
||||||
|
APP_FAKER_LOCALE=en_US
|
||||||
|
|
||||||
|
APP_MAINTENANCE_DRIVER=file
|
||||||
|
|
||||||
|
BCRYPT_ROUNDS=12
|
||||||
|
|
||||||
|
LOG_CHANNEL=stack
|
||||||
|
LOG_STACK=single
|
||||||
|
LOG_DEPRECATIONS_CHANNEL=null
|
||||||
|
LOG_LEVEL=debug
|
||||||
|
|
||||||
|
DB_CONNECTION=mysql
|
||||||
|
DB_HOST=127.0.0.1
|
||||||
|
DB_PORT=3306
|
||||||
|
DB_DATABASE=laravel_school_api
|
||||||
|
DB_USERNAME=root
|
||||||
|
DB_PASSWORD=
|
||||||
|
|
||||||
|
SESSION_DRIVER=file
|
||||||
|
SESSION_LIFETIME=120
|
||||||
|
SESSION_ENCRYPT=false
|
||||||
|
SESSION_PATH=/
|
||||||
|
SESSION_DOMAIN=null
|
||||||
|
|
||||||
|
BROADCAST_CONNECTION=log
|
||||||
|
FILESYSTEM_DISK=local
|
||||||
|
QUEUE_CONNECTION=sync
|
||||||
|
|
||||||
|
CACHE_STORE=file
|
||||||
|
CACHE_DRIVER=file
|
||||||
|
|
||||||
|
MEMCACHED_HOST=127.0.0.1
|
||||||
|
|
||||||
|
REDIS_CLIENT=phpredis
|
||||||
|
REDIS_HOST=127.0.0.1
|
||||||
|
REDIS_PASSWORD=null
|
||||||
|
REDIS_PORT=6379
|
||||||
|
|
||||||
|
MAIL_MAILER=log
|
||||||
|
MAIL_SCHEME=null
|
||||||
|
MAIL_HOST=127.0.0.1
|
||||||
|
MAIL_PORT=2525
|
||||||
|
MAIL_USERNAME=null
|
||||||
|
MAIL_PASSWORD=null
|
||||||
|
MAIL_FROM_ADDRESS="hello@example.com"
|
||||||
|
MAIL_FROM_NAME="${APP_NAME}"
|
||||||
|
|
||||||
|
AWS_ACCESS_KEY_ID=
|
||||||
|
AWS_SECRET_ACCESS_KEY=
|
||||||
|
AWS_DEFAULT_REGION=us-east-1
|
||||||
|
AWS_BUCKET=
|
||||||
|
AWS_USE_PATH_STYLE_ENDPOINT=false
|
||||||
|
|
||||||
|
VITE_APP_NAME="${APP_NAME}"
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
APP_NAME=Laravel
|
||||||
|
APP_ENV=local
|
||||||
|
APP_KEY=
|
||||||
|
APP_DEBUG=true
|
||||||
|
APP_URL=http://localhost
|
||||||
|
|
||||||
|
APP_LOCALE=en
|
||||||
|
APP_FALLBACK_LOCALE=en
|
||||||
|
APP_FAKER_LOCALE=en_US
|
||||||
|
|
||||||
|
APP_MAINTENANCE_DRIVER=file
|
||||||
|
# APP_MAINTENANCE_STORE=database
|
||||||
|
|
||||||
|
# PHP_CLI_SERVER_WORKERS=4
|
||||||
|
|
||||||
|
BCRYPT_ROUNDS=12
|
||||||
|
|
||||||
|
LOG_CHANNEL=stack
|
||||||
|
LOG_STACK=single
|
||||||
|
LOG_DEPRECATIONS_CHANNEL=null
|
||||||
|
LOG_LEVEL=debug
|
||||||
|
|
||||||
|
DB_CONNECTION=mysql
|
||||||
|
DB_HOST=127.0.0.1
|
||||||
|
DB_PORT=3306
|
||||||
|
DB_DATABASE=laravel_school_api
|
||||||
|
DB_USERNAME=root
|
||||||
|
DB_PASSWORD=
|
||||||
|
|
||||||
|
SESSION_DRIVER=database
|
||||||
|
SESSION_LIFETIME=120
|
||||||
|
SESSION_ENCRYPT=false
|
||||||
|
SESSION_PATH=/
|
||||||
|
SESSION_DOMAIN=null
|
||||||
|
|
||||||
|
BROADCAST_CONNECTION=log
|
||||||
|
FILESYSTEM_DISK=local
|
||||||
|
QUEUE_CONNECTION=database
|
||||||
|
|
||||||
|
CACHE_STORE=file
|
||||||
|
# CACHE_PREFIX=
|
||||||
|
|
||||||
|
MEMCACHED_HOST=127.0.0.1
|
||||||
|
|
||||||
|
REDIS_CLIENT=phpredis
|
||||||
|
REDIS_HOST=127.0.0.1
|
||||||
|
REDIS_PASSWORD=null
|
||||||
|
REDIS_PORT=6379
|
||||||
|
|
||||||
|
MAIL_MAILER=log
|
||||||
|
MAIL_SCHEME=null
|
||||||
|
MAIL_HOST=127.0.0.1
|
||||||
|
MAIL_PORT=2525
|
||||||
|
MAIL_USERNAME=null
|
||||||
|
MAIL_PASSWORD=null
|
||||||
|
MAIL_FROM_ADDRESS="hello@example.com"
|
||||||
|
MAIL_FROM_NAME="${APP_NAME}"
|
||||||
|
|
||||||
|
AWS_ACCESS_KEY_ID=
|
||||||
|
AWS_SECRET_ACCESS_KEY=
|
||||||
|
AWS_DEFAULT_REGION=us-east-1
|
||||||
|
AWS_BUCKET=
|
||||||
|
AWS_USE_PATH_STYLE_ENDPOINT=false
|
||||||
|
|
||||||
|
VITE_APP_NAME="${APP_NAME}"
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Providers;
|
||||||
|
|
||||||
|
use Illuminate\Support\ServiceProvider;
|
||||||
|
|
||||||
|
class AppServiceProvider extends ServiceProvider
|
||||||
|
{
|
||||||
|
public function register(): void
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
public function boot(): void
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
}
|
||||||
Regular → Executable
@@ -0,0 +1,54 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
if (! Schema::hasTable('schools')) {
|
||||||
|
Schema::create('schools', function (Blueprint $table) {
|
||||||
|
$table->id();
|
||||||
|
$table->string('name')->default('Default School');
|
||||||
|
$table->string('domain_profile')->default('standard_school');
|
||||||
|
$table->string('timezone')->default('UTC');
|
||||||
|
$table->string('locale')->default('en');
|
||||||
|
$table->string('currency', 3)->default('USD');
|
||||||
|
$table->boolean('active')->default(true);
|
||||||
|
$table->timestamps();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! Schema::hasTable('students')) {
|
||||||
|
Schema::create('students', function (Blueprint $table) {
|
||||||
|
$table->id();
|
||||||
|
$table->unsignedBigInteger('school_id')->index();
|
||||||
|
$table->string('school_id_number')->nullable();
|
||||||
|
$table->string('student_identifier')->nullable();
|
||||||
|
|
||||||
|
$table->string('first_name');
|
||||||
|
$table->string('middle_name')->nullable();
|
||||||
|
$table->string('last_name');
|
||||||
|
$table->string('preferred_name')->nullable();
|
||||||
|
$table->date('date_of_birth')->nullable();
|
||||||
|
$table->string('gender')->nullable();
|
||||||
|
$table->string('primary_language')->nullable();
|
||||||
|
|
||||||
|
$table->string('status')->default('active')->index();
|
||||||
|
$table->string('external_id')->nullable();
|
||||||
|
$table->json('metadata')->nullable();
|
||||||
|
$table->timestamps();
|
||||||
|
|
||||||
|
$table->index(['school_id', 'status']);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('students');
|
||||||
|
Schema::dropIfExists('schools');
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -2,18 +2,65 @@
|
|||||||
|
|
||||||
use Illuminate\Database\Migrations\Migration;
|
use Illuminate\Database\Migrations\Migration;
|
||||||
use Illuminate\Database\Schema\Blueprint;
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
use Illuminate\Support\Facades\Schema;
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
return new class extends Migration {
|
return new class extends Migration
|
||||||
|
{
|
||||||
public function up(): void
|
public function up(): void
|
||||||
{
|
{
|
||||||
foreach (['payments','invoices'] as $tableName) {
|
$this->addIndexIfMissing('payments', 'payments_school_id_index', ['school_id']);
|
||||||
if (! Schema::hasTable($tableName)) { continue; }
|
$this->addIndexIfMissing('payments', 'payments_invoice_id_index', ['invoice_id']);
|
||||||
Schema::table($tableName, function (Blueprint $table) use ($tableName): void {
|
$this->addIndexIfMissing('payments', 'payments_student_id_index', ['student_id']);
|
||||||
if (Schema::hasColumn($tableName, 'school_id')) { $table->index('school_id'); }
|
$this->addIndexIfMissing('payments', 'payments_guardian_id_index', ['guardian_id']);
|
||||||
if (Schema::hasColumn($tableName, 'status')) { $table->index('status'); }
|
$this->addIndexIfMissing('payments', 'payments_status_index', ['status']);
|
||||||
});
|
|
||||||
}
|
$this->addIndexIfMissing('invoices', 'invoices_school_id_index', ['school_id']);
|
||||||
|
$this->addIndexIfMissing('invoices', 'invoices_student_id_index', ['student_id']);
|
||||||
|
$this->addIndexIfMissing('invoices', 'invoices_status_index', ['status']);
|
||||||
|
|
||||||
|
$this->addIndexIfMissing('finance_audit_logs', 'finance_audit_logs_school_id_index', ['school_id']);
|
||||||
|
$this->addIndexIfMissing('finance_audit_logs', 'finance_audit_logs_actor_user_id_index', ['actor_user_id']);
|
||||||
|
$this->addIndexIfMissing('finance_audit_logs', 'finance_audit_logs_entity_index', ['entity_type', 'entity_id']);
|
||||||
|
$this->addIndexIfMissing('finance_audit_logs', 'finance_audit_logs_created_at_index', ['created_at']);
|
||||||
|
|
||||||
|
$this->addIndexIfMissing('payment_files', 'payment_files_school_id_index', ['school_id']);
|
||||||
|
$this->addIndexIfMissing('payment_files', 'payment_files_payment_id_index', ['payment_id']);
|
||||||
}
|
}
|
||||||
public function down(): void {}
|
|
||||||
};
|
public function down(): void
|
||||||
|
{
|
||||||
|
// Intentionally conservative. These indexes may exist from base/legacy schema.
|
||||||
|
}
|
||||||
|
|
||||||
|
private function addIndexIfMissing(string $table, string $indexName, array $columns): void
|
||||||
|
{
|
||||||
|
if (! Schema::hasTable($table)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->indexExists($table, $indexName)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Schema::table($table, function (Blueprint $blueprint) use ($indexName, $columns) {
|
||||||
|
$blueprint->index($columns, $indexName);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private function indexExists(string $table, string $indexName): bool
|
||||||
|
{
|
||||||
|
$database = DB::getDatabaseName();
|
||||||
|
|
||||||
|
$result = DB::selectOne(
|
||||||
|
'select count(1) as aggregate
|
||||||
|
from information_schema.statistics
|
||||||
|
where table_schema = ?
|
||||||
|
and table_name = ?
|
||||||
|
and index_name = ?',
|
||||||
|
[$database, $table, $indexName]
|
||||||
|
);
|
||||||
|
|
||||||
|
return (int) $result->aggregate > 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
+37
-2
@@ -1,3 +1,38 @@
|
|||||||
<?php
|
<?php
|
||||||
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::table('students',function(Blueprint $table): void { $table->unique(['school_id','student_identifier'],'students_school_identifier_unique'); }); Schema::create('enrollments',function(Blueprint $table): void { $table->id(); $table->unsignedBigInteger('school_id')->index(); $table->unsignedBigInteger('student_id')->index(); $table->unsignedBigInteger('academic_year_id')->index(); $table->unsignedBigInteger('term_id')->nullable()->index(); $table->unsignedBigInteger('grade_level_id')->nullable()->index(); $table->unsignedBigInteger('program_id')->nullable()->index(); $table->unsignedBigInteger('class_group_id')->nullable()->index(); $table->string('status')->index(); $table->timestamp('enrolled_at')->nullable(); $table->timestamp('withdrawn_at')->nullable(); $table->string('reason')->nullable(); $table->json('metadata')->nullable(); $table->timestamps(); $table->index(['school_id','student_id','academic_year_id','term_id'],'enrollment_period_idx'); }); Schema::create('student_assignments',function(Blueprint $table): void { $table->id(); $table->unsignedBigInteger('school_id')->index(); $table->unsignedBigInteger('student_id')->index(); $table->string('assignment_type')->index(); $table->string('assignable_type')->index(); $table->unsignedBigInteger('assignable_id')->index(); $table->date('effective_from')->nullable(); $table->date('effective_to')->nullable(); $table->string('status')->index(); $table->json('metadata')->nullable(); $table->timestamps(); $table->index(['school_id','student_id','assignment_type'],'student_assignment_scope_idx'); }); Schema::create('student_audit_logs',function(Blueprint $table): void { $table->id(); $table->unsignedBigInteger('school_id')->index(); $table->unsignedBigInteger('actor_user_id')->nullable()->index(); $table->json('actor_role_snapshot')->nullable(); $table->string('action')->index(); $table->string('entity_type')->index(); $table->unsignedBigInteger('entity_id')->index(); $table->json('before_json')->nullable(); $table->json('after_json')->nullable(); $table->text('reason')->nullable(); $table->string('request_id')->nullable(); $table->string('ip_address')->nullable(); $table->text('user_agent')->nullable(); $table->string('idempotency_key')->nullable()->index(); $table->timestamp('created_at')->useCurrent()->index(); $table->index(['entity_type','entity_id'],'student_audit_entity_idx'); }); } public function down(): void { Schema::dropIfExists('student_audit_logs'); Schema::dropIfExists('student_assignments'); Schema::dropIfExists('enrollments'); } };
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
return new class extends Migration { public function up(): void
|
||||||
|
{
|
||||||
|
if (! $this->indexExists('students', 'students_school_identifier_unique')) {
|
||||||
|
Schema::table('students', function (Blueprint $table) {
|
||||||
|
$table->unique(['school_id', 'student_identifier'], 'students_school_identifier_unique');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Schema::create('enrollments',function(Blueprint $table): void { $table->id(); $table->unsignedBigInteger('school_id')->index(); $table->unsignedBigInteger('student_id')->index(); $table->unsignedBigInteger('academic_year_id')->index(); $table->unsignedBigInteger('term_id')->nullable()->index(); $table->unsignedBigInteger('grade_level_id')->nullable()->index(); $table->unsignedBigInteger('program_id')->nullable()->index(); $table->unsignedBigInteger('class_group_id')->nullable()->index(); $table->string('status')->index(); $table->timestamp('enrolled_at')->nullable(); $table->timestamp('withdrawn_at')->nullable(); $table->string('reason')->nullable(); $table->json('metadata')->nullable(); $table->timestamps(); $table->index(['school_id','student_id','academic_year_id','term_id'],'enrollment_period_idx'); });
|
||||||
|
Schema::create('student_assignments',function(Blueprint $table): void { $table->id(); $table->unsignedBigInteger('school_id')->index(); $table->unsignedBigInteger('student_id')->index(); $table->string('assignment_type')->index(); $table->string('assignable_type')->index(); $table->unsignedBigInteger('assignable_id')->index(); $table->date('effective_from')->nullable(); $table->date('effective_to')->nullable(); $table->string('status')->index(); $table->json('metadata')->nullable(); $table->timestamps(); $table->index(['school_id','student_id','assignment_type'],'student_assignment_scope_idx'); });
|
||||||
|
Schema::create('student_audit_logs',function(Blueprint $table): void { $table->id(); $table->unsignedBigInteger('school_id')->index(); $table->unsignedBigInteger('actor_user_id')->nullable()->index(); $table->json('actor_role_snapshot')->nullable(); $table->string('action')->index(); $table->string('entity_type')->index(); $table->unsignedBigInteger('entity_id')->index(); $table->json('before_json')->nullable(); $table->json('after_json')->nullable(); $table->text('reason')->nullable(); $table->string('request_id')->nullable(); $table->string('ip_address')->nullable(); $table->text('user_agent')->nullable(); $table->string('idempotency_key')->nullable()->index(); $table->timestamp('created_at')->useCurrent()->index(); $table->index(['entity_type','entity_id'],'student_audit_entity_idx'); }); }
|
||||||
|
|
||||||
|
public function down(): void {
|
||||||
|
Schema::dropIfExists('student_audit_logs');
|
||||||
|
Schema::dropIfExists('student_assignments');
|
||||||
|
Schema::dropIfExists('enrollments'); }
|
||||||
|
|
||||||
|
private function indexExists(string $table, string $indexName): bool
|
||||||
|
{
|
||||||
|
$database = DB::getDatabaseName();
|
||||||
|
|
||||||
|
$result = DB::selectOne(
|
||||||
|
'select count(1) as aggregate
|
||||||
|
from information_schema.statistics
|
||||||
|
where table_schema = ?
|
||||||
|
and table_name = ?
|
||||||
|
and index_name = ?',
|
||||||
|
[$database, $table, $indexName]
|
||||||
|
);
|
||||||
|
|
||||||
|
return (int) $result->aggregate > 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|||||||
+55
-2
@@ -1,3 +1,56 @@
|
|||||||
<?php
|
<?php
|
||||||
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('islamic_sunday_school_student_profiles',function(Blueprint $table): void { $table->id(); $table->unsignedBigInteger('school_id')->index(); $table->unsignedBigInteger('student_id')->index(); $table->unsignedBigInteger('family_profile_id')->nullable()->index(); $table->unsignedBigInteger('quran_level_id')->nullable()->index(); $table->unsignedBigInteger('arabic_level_id')->nullable()->index(); $table->unsignedBigInteger('islamic_studies_track_id')->nullable()->index(); $table->unsignedBigInteger('halaqa_group_id')->nullable()->index(); $table->text('memorization_progress_summary')->nullable(); $table->string('recitation_placement')->nullable(); $table->boolean('volunteer_family_participation')->default(false); $table->text('sensitive_admin_notes')->nullable(); $table->json('metadata')->nullable(); $table->timestamps(); $table->unique(['school_id','student_id'],'iss_student_profile_unique'); }); } public function down(): void { Schema::dropIfExists('islamic_sunday_school_student_profiles'); } };
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
if (Schema::hasTable('islamic_sunday_school_student_profiles')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Schema::create('islamic_sunday_school_student_profiles', function (Blueprint $table) {
|
||||||
|
$table->id();
|
||||||
|
|
||||||
|
$table->unsignedBigInteger('school_id');
|
||||||
|
$table->unsignedBigInteger('student_id');
|
||||||
|
|
||||||
|
$table->unsignedBigInteger('masjid_family_id')->nullable();
|
||||||
|
$table->unsignedBigInteger('quran_level_id')->nullable();
|
||||||
|
$table->unsignedBigInteger('arabic_level_id')->nullable();
|
||||||
|
$table->unsignedBigInteger('islamic_studies_track_id')->nullable();
|
||||||
|
$table->unsignedBigInteger('halaqa_id')->nullable();
|
||||||
|
|
||||||
|
$table->string('memorization_progress_summary')->nullable();
|
||||||
|
$table->string('recitation_placement')->nullable();
|
||||||
|
$table->boolean('volunteer_family_participation')->default(false);
|
||||||
|
|
||||||
|
$table->text('sensitive_admin_notes')->nullable();
|
||||||
|
$table->json('metadata')->nullable();
|
||||||
|
|
||||||
|
$table->timestamps();
|
||||||
|
|
||||||
|
$table->unique(['school_id', 'student_id'], 'issp_school_student_unique');
|
||||||
|
|
||||||
|
$table->index('school_id', 'issp_school_idx');
|
||||||
|
$table->index('student_id', 'issp_student_idx');
|
||||||
|
$table->index('masjid_family_id', 'issp_masjid_family_idx');
|
||||||
|
$table->index('quran_level_id', 'issp_quran_level_idx');
|
||||||
|
$table->index('arabic_level_id', 'issp_arabic_level_idx');
|
||||||
|
$table->index('islamic_studies_track_id', 'issp_studies_track_idx');
|
||||||
|
$table->index('halaqa_id', 'issp_halaqa_idx');
|
||||||
|
$table->index(['school_id', 'halaqa_id'], 'issp_school_halaqa_idx');
|
||||||
|
$table->index(['school_id', 'quran_level_id'], 'issp_school_quran_idx');
|
||||||
|
$table->index(['school_id', 'arabic_level_id'], 'issp_school_arabic_idx');
|
||||||
|
$table->index(['school_id', 'islamic_studies_track_id'], 'issp_school_studies_idx');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('islamic_sunday_school_student_profiles');
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -1,3 +1,199 @@
|
|||||||
<?php
|
<?php
|
||||||
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('communication_recipient_previews', function(Blueprint $table): void { $table->uuid('id')->primary(); $table->unsignedBigInteger('school_id')->index(); $table->unsignedBigInteger('actor_user_id')->index(); $table->string('audience_type')->index(); $table->json('audience_filters_json')->nullable(); $table->string('channel')->index(); $table->string('message_category')->index(); $table->unsignedInteger('recipient_count')->default(0); $table->unsignedInteger('deduped_count')->default(0); $table->unsignedInteger('blocked_count')->default(0); $table->unsignedInteger('missing_contact_count')->default(0); $table->longText('snapshot_json'); $table->string('confirmation_token_hash'); $table->timestamp('expires_at')->index(); $table->timestamps(); }); Schema::create('communication_messages', function(Blueprint $table): void { $table->id(); $table->unsignedBigInteger('school_id')->index(); $table->unsignedBigInteger('actor_user_id')->index(); $table->string('channel')->index(); $table->string('message_category')->index(); $table->string('priority')->default('normal'); $table->string('subject')->nullable(); $table->longText('body_snapshot'); $table->uuid('preview_id')->nullable()->index(); $table->string('status')->index(); $table->timestamp('scheduled_at')->nullable(); $table->timestamp('sent_at')->nullable(); $table->timestamps(); }); Schema::create('communication_message_recipients', function(Blueprint $table): void { $table->id(); $table->unsignedBigInteger('message_id')->index(); $table->unsignedBigInteger('school_id')->index(); $table->string('recipient_type'); $table->string('recipient_id'); $table->string('channel')->index(); $table->string('delivery_address_hash')->nullable(); $table->string('delivery_address_masked')->nullable(); $table->string('status')->index(); $table->string('excluded_reason')->nullable(); $table->string('provider_message_id')->nullable()->index(); $table->timestamps(); $table->index(['recipient_type','recipient_id']); }); } public function down(): void { Schema::dropIfExists('communication_message_recipients'); Schema::dropIfExists('communication_messages'); Schema::dropIfExists('communication_recipient_previews'); } };
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
if (! Schema::hasTable('communication_messages')) {
|
||||||
|
Schema::create('communication_messages', function (Blueprint $table) {
|
||||||
|
$table->id();
|
||||||
|
|
||||||
|
$table->unsignedBigInteger('school_id');
|
||||||
|
$table->unsignedBigInteger('academic_year_id')->nullable();
|
||||||
|
$table->unsignedBigInteger('term_id')->nullable();
|
||||||
|
$table->unsignedBigInteger('actor_user_id')->nullable();
|
||||||
|
|
||||||
|
$table->string('channel');
|
||||||
|
$table->string('message_category')->default('general');
|
||||||
|
$table->string('priority')->default('normal');
|
||||||
|
|
||||||
|
$table->string('subject')->nullable();
|
||||||
|
$table->longText('body_snapshot');
|
||||||
|
$table->unsignedBigInteger('template_id')->nullable();
|
||||||
|
$table->string('template_version')->nullable();
|
||||||
|
|
||||||
|
$table->string('preview_id')->nullable();
|
||||||
|
$table->string('status')->default('draft');
|
||||||
|
|
||||||
|
$table->timestamp('scheduled_at')->nullable();
|
||||||
|
$table->timestamp('sent_at')->nullable();
|
||||||
|
|
||||||
|
$table->string('idempotency_key')->nullable();
|
||||||
|
$table->json('metadata')->nullable();
|
||||||
|
|
||||||
|
$table->timestamps();
|
||||||
|
|
||||||
|
$table->index('school_id', 'comm_msg_school_idx');
|
||||||
|
$table->index('actor_user_id', 'comm_msg_actor_idx');
|
||||||
|
$table->index('channel', 'comm_msg_channel_idx');
|
||||||
|
$table->index('message_category', 'comm_msg_category_idx');
|
||||||
|
$table->index('status', 'comm_msg_status_idx');
|
||||||
|
$table->index('created_at', 'comm_msg_created_idx');
|
||||||
|
$table->index(['school_id', 'status'], 'comm_msg_school_status_idx');
|
||||||
|
$table->index(['school_id', 'channel'], 'comm_msg_school_channel_idx');
|
||||||
|
$table->index(['school_id', 'message_category'], 'comm_msg_school_cat_idx');
|
||||||
|
$table->index(['school_id', 'idempotency_key'], 'comm_msg_idem_idx');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! Schema::hasTable('communication_message_recipients')) {
|
||||||
|
Schema::create('communication_message_recipients', function (Blueprint $table) {
|
||||||
|
$table->id();
|
||||||
|
|
||||||
|
$table->unsignedBigInteger('message_id');
|
||||||
|
$table->unsignedBigInteger('school_id');
|
||||||
|
|
||||||
|
$table->string('recipient_type');
|
||||||
|
$table->unsignedBigInteger('recipient_id');
|
||||||
|
|
||||||
|
$table->string('delivery_address_hash')->nullable();
|
||||||
|
$table->string('delivery_address_masked')->nullable();
|
||||||
|
|
||||||
|
$table->string('channel')->nullable();
|
||||||
|
$table->string('status')->default('pending');
|
||||||
|
$table->string('excluded_reason')->nullable();
|
||||||
|
$table->string('provider_message_id')->nullable();
|
||||||
|
|
||||||
|
$table->timestamp('sent_at')->nullable();
|
||||||
|
$table->timestamp('delivered_at')->nullable();
|
||||||
|
$table->timestamp('failed_at')->nullable();
|
||||||
|
|
||||||
|
$table->json('metadata')->nullable();
|
||||||
|
|
||||||
|
$table->timestamps();
|
||||||
|
|
||||||
|
$table->index('message_id', 'comm_rcpt_message_idx');
|
||||||
|
$table->index('school_id', 'comm_rcpt_school_idx');
|
||||||
|
$table->index(['recipient_type', 'recipient_id'], 'comm_rcpt_recipient_idx');
|
||||||
|
$table->index('status', 'comm_rcpt_status_idx');
|
||||||
|
$table->index('provider_message_id', 'comm_rcpt_provider_idx');
|
||||||
|
$table->index(['school_id', 'status'], 'comm_rcpt_school_status_idx');
|
||||||
|
$table->index(['message_id', 'status'], 'comm_rcpt_msg_status_idx');
|
||||||
|
$table->index(['message_id', 'recipient_type', 'recipient_id'], 'comm_rcpt_msg_recipient_idx');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! Schema::hasTable('communication_recipient_previews')) {
|
||||||
|
Schema::create('communication_recipient_previews', function (Blueprint $table) {
|
||||||
|
$table->uuid('id')->primary();
|
||||||
|
|
||||||
|
$table->unsignedBigInteger('school_id');
|
||||||
|
$table->unsignedBigInteger('actor_user_id')->nullable();
|
||||||
|
|
||||||
|
$table->string('audience_type');
|
||||||
|
$table->json('audience_filters_json')->nullable();
|
||||||
|
|
||||||
|
$table->string('channel');
|
||||||
|
$table->string('message_category')->default('general');
|
||||||
|
|
||||||
|
$table->unsignedInteger('recipient_count')->default(0);
|
||||||
|
$table->unsignedInteger('deduped_count')->default(0);
|
||||||
|
$table->unsignedInteger('blocked_count')->default(0);
|
||||||
|
$table->unsignedInteger('missing_contact_count')->default(0);
|
||||||
|
|
||||||
|
$table->json('snapshot_json')->nullable();
|
||||||
|
$table->string('confirmation_token_hash');
|
||||||
|
$table->timestamp('expires_at');
|
||||||
|
|
||||||
|
$table->timestamps();
|
||||||
|
|
||||||
|
$table->index('school_id', 'comm_prev_school_idx');
|
||||||
|
$table->index('actor_user_id', 'comm_prev_actor_idx');
|
||||||
|
$table->index('audience_type', 'comm_prev_audience_idx');
|
||||||
|
$table->index('channel', 'comm_prev_channel_idx');
|
||||||
|
$table->index('message_category', 'comm_prev_category_idx');
|
||||||
|
$table->index('expires_at', 'comm_prev_expires_idx');
|
||||||
|
$table->index(['school_id', 'expires_at'], 'comm_prev_school_exp_idx');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! Schema::hasTable('communication_audit_logs')) {
|
||||||
|
Schema::create('communication_audit_logs', function (Blueprint $table) {
|
||||||
|
$table->id();
|
||||||
|
|
||||||
|
$table->unsignedBigInteger('school_id');
|
||||||
|
$table->unsignedBigInteger('academic_year_id')->nullable();
|
||||||
|
$table->unsignedBigInteger('term_id')->nullable();
|
||||||
|
$table->unsignedBigInteger('actor_user_id')->nullable();
|
||||||
|
|
||||||
|
$table->json('actor_role_snapshot')->nullable();
|
||||||
|
|
||||||
|
$table->string('action');
|
||||||
|
$table->string('entity_type')->nullable();
|
||||||
|
$table->unsignedBigInteger('entity_id')->nullable();
|
||||||
|
|
||||||
|
$table->json('before_json')->nullable();
|
||||||
|
$table->json('after_json')->nullable();
|
||||||
|
|
||||||
|
$table->string('reason')->nullable();
|
||||||
|
$table->string('request_id')->nullable();
|
||||||
|
$table->string('ip_address')->nullable();
|
||||||
|
$table->text('user_agent')->nullable();
|
||||||
|
|
||||||
|
$table->string('idempotency_key')->nullable();
|
||||||
|
|
||||||
|
$table->timestamps();
|
||||||
|
|
||||||
|
$table->index('school_id', 'comm_audit_school_idx');
|
||||||
|
$table->index('actor_user_id', 'comm_audit_actor_idx');
|
||||||
|
$table->index('action', 'comm_audit_action_idx');
|
||||||
|
$table->index(['entity_type', 'entity_id'], 'comm_audit_entity_idx');
|
||||||
|
$table->index('created_at', 'comm_audit_created_idx');
|
||||||
|
$table->index(['school_id', 'action'], 'comm_audit_school_action_idx');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! Schema::hasTable('communication_idempotency_keys')) {
|
||||||
|
Schema::create('communication_idempotency_keys', function (Blueprint $table) {
|
||||||
|
$table->id();
|
||||||
|
|
||||||
|
$table->unsignedBigInteger('school_id');
|
||||||
|
$table->unsignedBigInteger('actor_user_id')->nullable();
|
||||||
|
|
||||||
|
$table->string('operation');
|
||||||
|
$table->string('idempotency_key');
|
||||||
|
$table->string('payload_hash');
|
||||||
|
|
||||||
|
$table->string('result_entity_type')->nullable();
|
||||||
|
$table->unsignedBigInteger('result_entity_id')->nullable();
|
||||||
|
|
||||||
|
$table->timestamp('expires_at')->nullable();
|
||||||
|
|
||||||
|
$table->timestamps();
|
||||||
|
|
||||||
|
$table->unique(
|
||||||
|
['school_id', 'operation', 'idempotency_key'],
|
||||||
|
'comm_idem_school_op_key_unique'
|
||||||
|
);
|
||||||
|
|
||||||
|
$table->index('school_id', 'comm_idem_school_idx');
|
||||||
|
$table->index('actor_user_id', 'comm_idem_actor_idx');
|
||||||
|
$table->index('operation', 'comm_idem_operation_idx');
|
||||||
|
$table->index('expires_at', 'comm_idem_expires_idx');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('communication_idempotency_keys');
|
||||||
|
Schema::dropIfExists('communication_audit_logs');
|
||||||
|
Schema::dropIfExists('communication_recipient_previews');
|
||||||
|
Schema::dropIfExists('communication_message_recipients');
|
||||||
|
Schema::dropIfExists('communication_messages');
|
||||||
|
}
|
||||||
|
};
|
||||||
+193
-2
@@ -1,3 +1,194 @@
|
|||||||
<?php
|
<?php
|
||||||
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('communication_delivery_attempts', function(Blueprint $table): void { $table->id(); $table->unsignedBigInteger('message_recipient_id')->index(); $table->string('provider_message_id')->nullable()->index(); $table->string('status')->index(); $table->string('failure_reason')->nullable(); $table->unsignedInteger('attempt_number')->default(1); $table->json('raw_provider_payload')->nullable(); $table->timestamps(); }); Schema::create('communication_preferences', function(Blueprint $table): void { $table->id(); $table->unsignedBigInteger('school_id')->index(); $table->string('recipient_type'); $table->string('recipient_id'); $table->string('channel')->index(); $table->string('message_category')->default('general'); $table->boolean('allowed')->default(true); $table->json('metadata_json')->nullable(); $table->timestamps(); $table->index(['recipient_type','recipient_id']); }); Schema::create('communication_templates', function(Blueprint $table): void { $table->id(); $table->unsignedBigInteger('school_id')->nullable()->index(); $table->string('template_key')->index(); $table->unsignedInteger('version')->default(1); $table->string('name'); $table->string('subject')->nullable(); $table->longText('body'); $table->string('language')->default('en'); $table->json('required_variables_json')->nullable(); $table->string('status')->index(); $table->json('metadata_json')->nullable(); $table->timestamps(); $table->unique(['school_id','template_key','version'], 'comm_templates_school_key_version_unique'); }); Schema::create('communication_audit_logs', function(Blueprint $table): void { $table->id(); $table->unsignedBigInteger('school_id')->index(); $table->unsignedBigInteger('actor_user_id')->nullable()->index(); $table->string('action')->index(); $table->string('entity_type'); $table->string('entity_id'); $table->json('before_json')->nullable(); $table->json('after_json')->nullable(); $table->string('reason')->nullable(); $table->string('idempotency_key')->nullable(); $table->json('metadata_json')->nullable(); $table->timestamps(); $table->index(['entity_type','entity_id']); }); } public function down(): void { Schema::dropIfExists('communication_audit_logs'); Schema::dropIfExists('communication_templates'); Schema::dropIfExists('communication_preferences'); Schema::dropIfExists('communication_delivery_attempts'); } };
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
if (! Schema::hasTable('communication_delivery_attempts')) {
|
||||||
|
Schema::create('communication_delivery_attempts', function (Blueprint $table) {
|
||||||
|
$table->id();
|
||||||
|
|
||||||
|
$table->unsignedBigInteger('message_recipient_id');
|
||||||
|
$table->unsignedBigInteger('school_id');
|
||||||
|
|
||||||
|
$table->string('channel', 32);
|
||||||
|
$table->string('provider', 64)->nullable();
|
||||||
|
$table->string('provider_message_id', 191)->nullable();
|
||||||
|
|
||||||
|
$table->string('status', 32)->default('pending');
|
||||||
|
$table->string('failure_reason', 64)->nullable();
|
||||||
|
$table->unsignedInteger('attempt_number')->default(1);
|
||||||
|
|
||||||
|
$table->json('raw_provider_payload')->nullable();
|
||||||
|
|
||||||
|
$table->timestamp('attempted_at')->nullable();
|
||||||
|
$table->timestamp('delivered_at')->nullable();
|
||||||
|
$table->timestamp('failed_at')->nullable();
|
||||||
|
|
||||||
|
$table->timestamps();
|
||||||
|
|
||||||
|
$table->index('message_recipient_id', 'comm_deliv_recipient_idx');
|
||||||
|
$table->index('school_id', 'comm_deliv_school_idx');
|
||||||
|
$table->index('channel', 'comm_deliv_channel_idx');
|
||||||
|
$table->index('provider_message_id', 'comm_deliv_provider_msg_idx');
|
||||||
|
$table->index('status', 'comm_deliv_status_idx');
|
||||||
|
$table->index('created_at', 'comm_deliv_created_idx');
|
||||||
|
$table->index(['school_id', 'status'], 'comm_deliv_school_status_idx');
|
||||||
|
$table->index(['message_recipient_id', 'status'], 'comm_deliv_rcpt_status_idx');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! Schema::hasTable('communication_preferences')) {
|
||||||
|
Schema::create('communication_preferences', function (Blueprint $table) {
|
||||||
|
$table->id();
|
||||||
|
|
||||||
|
$table->unsignedBigInteger('school_id');
|
||||||
|
|
||||||
|
$table->string('recipient_type', 64);
|
||||||
|
$table->unsignedBigInteger('recipient_id');
|
||||||
|
|
||||||
|
$table->string('channel', 32);
|
||||||
|
$table->string('message_category', 64)->default('general');
|
||||||
|
|
||||||
|
$table->boolean('opted_in')->default(true);
|
||||||
|
$table->boolean('quiet_hours_enabled')->default(false);
|
||||||
|
$table->time('quiet_hours_start')->nullable();
|
||||||
|
$table->time('quiet_hours_end')->nullable();
|
||||||
|
|
||||||
|
$table->string('preferred_language', 16)->nullable();
|
||||||
|
$table->json('metadata')->nullable();
|
||||||
|
|
||||||
|
$table->timestamps();
|
||||||
|
|
||||||
|
$table->unique(
|
||||||
|
['school_id', 'recipient_type', 'recipient_id', 'channel', 'message_category'],
|
||||||
|
'comm_pref_unique'
|
||||||
|
);
|
||||||
|
|
||||||
|
$table->index('school_id', 'comm_pref_school_idx');
|
||||||
|
$table->index(['recipient_type', 'recipient_id'], 'comm_pref_recipient_idx');
|
||||||
|
$table->index('channel', 'comm_pref_channel_idx');
|
||||||
|
$table->index('message_category', 'comm_pref_category_idx');
|
||||||
|
$table->index(['school_id', 'channel'], 'comm_pref_school_channel_idx');
|
||||||
|
$table->index(['school_id', 'message_category'], 'comm_pref_school_cat_idx');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! Schema::hasTable('communication_templates')) {
|
||||||
|
Schema::create('communication_templates', function (Blueprint $table) {
|
||||||
|
$table->id();
|
||||||
|
|
||||||
|
$table->unsignedBigInteger('school_id')->nullable();
|
||||||
|
|
||||||
|
$table->string('template_key', 128);
|
||||||
|
$table->string('version', 32)->default('1');
|
||||||
|
$table->string('status', 32)->default('draft');
|
||||||
|
|
||||||
|
$table->string('channel', 32);
|
||||||
|
$table->string('message_category', 64)->default('general');
|
||||||
|
$table->string('language', 16)->default('en');
|
||||||
|
|
||||||
|
$table->string('subject', 191)->nullable();
|
||||||
|
$table->longText('body');
|
||||||
|
|
||||||
|
$table->json('variables_schema')->nullable();
|
||||||
|
$table->json('metadata')->nullable();
|
||||||
|
|
||||||
|
$table->timestamps();
|
||||||
|
|
||||||
|
$table->unique(
|
||||||
|
['school_id', 'template_key', 'version'],
|
||||||
|
'comm_tpl_school_key_ver_unique'
|
||||||
|
);
|
||||||
|
|
||||||
|
$table->index('school_id', 'comm_tpl_school_idx');
|
||||||
|
$table->index('template_key', 'comm_tpl_key_idx');
|
||||||
|
$table->index('status', 'comm_tpl_status_idx');
|
||||||
|
$table->index('channel', 'comm_tpl_channel_idx');
|
||||||
|
$table->index('message_category', 'comm_tpl_category_idx');
|
||||||
|
$table->index('language', 'comm_tpl_language_idx');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! Schema::hasTable('communication_audit_logs')) {
|
||||||
|
Schema::create('communication_audit_logs', function (Blueprint $table) {
|
||||||
|
$table->id();
|
||||||
|
|
||||||
|
$table->unsignedBigInteger('school_id');
|
||||||
|
$table->unsignedBigInteger('academic_year_id')->nullable();
|
||||||
|
$table->unsignedBigInteger('term_id')->nullable();
|
||||||
|
$table->unsignedBigInteger('actor_user_id')->nullable();
|
||||||
|
|
||||||
|
$table->json('actor_role_snapshot')->nullable();
|
||||||
|
|
||||||
|
$table->string('action', 96);
|
||||||
|
|
||||||
|
$table->string('entity_type', 96)->nullable();
|
||||||
|
$table->unsignedBigInteger('entity_id')->nullable();
|
||||||
|
|
||||||
|
$table->json('before_json')->nullable();
|
||||||
|
$table->json('after_json')->nullable();
|
||||||
|
|
||||||
|
$table->string('reason', 191)->nullable();
|
||||||
|
$table->string('request_id', 96)->nullable();
|
||||||
|
$table->string('ip_address', 64)->nullable();
|
||||||
|
$table->text('user_agent')->nullable();
|
||||||
|
|
||||||
|
$table->string('idempotency_key', 191)->nullable();
|
||||||
|
$table->json('metadata_json')->nullable();
|
||||||
|
|
||||||
|
$table->timestamps();
|
||||||
|
|
||||||
|
$table->index('school_id', 'comm_audit_school_idx');
|
||||||
|
$table->index('actor_user_id', 'comm_audit_actor_idx');
|
||||||
|
$table->index('action', 'comm_audit_action_idx');
|
||||||
|
$table->index(['entity_type', 'entity_id'], 'comm_audit_entity_idx');
|
||||||
|
$table->index('created_at', 'comm_audit_created_idx');
|
||||||
|
$table->index(['school_id', 'action'], 'comm_audit_school_action_idx');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! Schema::hasTable('communication_idempotency_keys')) {
|
||||||
|
Schema::create('communication_idempotency_keys', function (Blueprint $table) {
|
||||||
|
$table->id();
|
||||||
|
|
||||||
|
$table->unsignedBigInteger('school_id');
|
||||||
|
$table->unsignedBigInteger('actor_user_id')->nullable();
|
||||||
|
|
||||||
|
$table->string('operation', 96);
|
||||||
|
$table->string('idempotency_key', 191);
|
||||||
|
$table->string('payload_hash', 128);
|
||||||
|
|
||||||
|
$table->string('result_entity_type', 96)->nullable();
|
||||||
|
$table->unsignedBigInteger('result_entity_id')->nullable();
|
||||||
|
|
||||||
|
$table->timestamp('expires_at')->nullable();
|
||||||
|
|
||||||
|
$table->timestamps();
|
||||||
|
|
||||||
|
$table->unique(
|
||||||
|
['school_id', 'operation', 'idempotency_key'],
|
||||||
|
'comm_idem_school_op_key_unique'
|
||||||
|
);
|
||||||
|
|
||||||
|
$table->index('school_id', 'comm_idem_school_idx');
|
||||||
|
$table->index('actor_user_id', 'comm_idem_actor_idx');
|
||||||
|
$table->index('operation', 'comm_idem_operation_idx');
|
||||||
|
$table->index('expires_at', 'comm_idem_expires_idx');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('communication_idempotency_keys');
|
||||||
|
Schema::dropIfExists('communication_audit_logs');
|
||||||
|
Schema::dropIfExists('communication_templates');
|
||||||
|
Schema::dropIfExists('communication_preferences');
|
||||||
|
Schema::dropIfExists('communication_delivery_attempts');
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Support\Facades\Route;
|
||||||
|
|
||||||
|
Route::get('/health', function () {
|
||||||
|
return response()->json([
|
||||||
|
'success' => true,
|
||||||
|
'message' => 'API is running.',
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Modular scaffold routes
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Uncomment these as each route file exists and your providers are registered.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// require __DIR__.'/finance_phase3.php';
|
||||||
|
// require __DIR__.'/attendance_phase4.php';
|
||||||
|
// require __DIR__.'/student_lifecycle_phase5.php';
|
||||||
|
// require __DIR__.'/communication_phase6.php';
|
||||||
|
// require __DIR__.'/reporting_phase7.php';
|
||||||
|
// require __DIR__.'/api_phase8.php';
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Support\Facades\Route;
|
||||||
|
|
||||||
|
Route::get('/', function () {
|
||||||
|
return response()->json([
|
||||||
|
'success' => true,
|
||||||
|
'message' => 'Laravel school API is running.',
|
||||||
|
]);
|
||||||
|
});
|
||||||
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Reference in New Issue
Block a user