Add Popular Routes

Curated from/to destination pairs for marketing content, editable via a
Filament resource and served publicly (no auth, IP-throttled like CMS
pages) through GET /api/v1/popular-routes. The API response trims each
nested destination down to id/name/mm_name rather than the full catalog
resource.
This commit is contained in:
Nyan Lin Paing
2026-08-30 14:52:39 +07:00
parent 76f75c5581
commit b6934e1fb5
16 changed files with 622 additions and 0 deletions
@@ -0,0 +1,29 @@
<?php
namespace Modules\Routing\Database\Factories;
use Illuminate\Database\Eloquent\Factories\Factory;
use Modules\Catalog\Models\Destination;
use Modules\Routing\Models\PopularRoute;
/**
* @extends Factory<PopularRoute>
*/
class PopularRouteFactory extends Factory
{
/**
* Define the model's default state.
*
* @return array<string, mixed>
*/
public function definition(): array
{
return [
'from_destination_id' => Destination::factory(),
'to_destination_id' => Destination::factory(),
'description' => fake()->sentence(),
'mm_description' => fake()->sentence(),
'is_active' => true,
];
}
}
@@ -0,0 +1,34 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('popular_routes', function (Blueprint $table) {
$table->id();
$table->foreignId('from_destination_id')->constrained('destinations')->cascadeOnDelete();
$table->foreignId('to_destination_id')->constrained('destinations')->cascadeOnDelete();
$table->text('description')->nullable();
$table->text('mm_description')->nullable();
$table->boolean('is_active')->default(true);
$table->timestamps();
$table->unique(['from_destination_id', 'to_destination_id']);
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('popular_routes');
}
};