🚐🚢Logistic tracking Project✈️Week1

Tôi làm cho anh “Tuần 1: Tracking core chạy thật” theo đúng kiểu copy → paste → chạy được ngay ⚙️🚀

👉 Scope tuần 1:

  • DB: shipments + shipment_events + locations

  • Migration Laravel

  • Model + relationships

  • API /api/tracking/{code}


⚙️ 1. MIGRATIONS (Laravel)

📍 locations

Schema::create('locations', function (Blueprint $table) {
    $table->id();
    $table->string('name');
    $table->string('type')->nullable(); // port, city
    $table->string('country');
    $table->string('code')->nullable(); // UN/LOCODE
    $table->decimal('latitude', 10, 7)->nullable();
    $table->decimal('longitude', 10, 7)->nullable();
    $table->timestamps();
});

🚚 shipments

Schema::create('shipments', function (Blueprint $table) {
    $table->id();
    $table->string('tracking_code')->unique();
    $table->enum('type', ['air', 'sea', 'land']);
    $table->enum('status', ['pending', 'in_transit', 'delivered'])->default('pending');

    $table->foreignId('origin_id')->constrained('locations')->cascadeOnDelete();
    $table->foreignId('destination_id')->constrained('locations')->cascadeOnDelete();

    $table->timestamp('etd')->nullable();
    $table->timestamp('eta')->nullable();

    $table->timestamps();

    $table->index('tracking_code');
});

📊 shipment_events

Schema::create('shipment_events', function (Blueprint $table) {
    $table->id();

    $table->foreignId('shipment_id')->constrained()->cascadeOnDelete();
    $table->foreignId('location_id')->nullable()->constrained()->nullOnDelete();

    $table->string('status');
    $table->text('description')->nullable();
    $table->timestamp('event_time');

    $table->timestamps();

    $table->index('shipment_id');
});

🧩 2. MODELS + RELATIONSHIPS

📍 Location.php

class Location extends Model
{
    protected $fillable = ['name', 'type', 'country', 'code'];

    public function shipmentsOrigin()
    {
        return $this->hasMany(Shipment::class, 'origin_id');
    }

    public function shipmentsDestination()
    {
        return $this->hasMany(Shipment::class, 'destination_id');
    }
}

🚚 Shipment.php

class Shipment extends Model
{
    protected $fillable = [
        'tracking_code',
        'type',
        'status',
        'origin_id',
        'destination_id',
        'etd',
        'eta'
    ];

    public function origin()
    {
        return $this->belongsTo(Location::class, 'origin_id');
    }

    public function destination()
    {
        return $this->belongsTo(Location::class, 'destination_id');
    }

    public function events()
    {
        return $this->hasMany(ShipmentEvent::class)->orderBy('event_time', 'desc');
    }
}

📊 ShipmentEvent.php

class ShipmentEvent extends Model
{
    protected $fillable = [
        'shipment_id',
        'location_id',
        'status',
        'description',
        'event_time'
    ];

    public function shipment()
    {
        return $this->belongsTo(Shipment::class);
    }

    public function location()
    {
        return $this->belongsTo(Location::class);
    }
}

🚀 3. API TRACKING (CHẠY THẬT)

📌 routes/api.php

Route::get('/tracking/{code}', [TrackingController::class, 'show']);

🎯 TrackingController.php

class TrackingController extends Controller
{
    public function show($code)
    {
        $shipment = Shipment::with([
            'origin',
            'destination',
            'events.location'
        ])->where('tracking_code', $code)->first();

        if (!$shipment) {
            return response()->json([
                'message' => 'Tracking code not found'
            ], 404);
        }

        return response()->json([
            'tracking_code' => $shipment->tracking_code,
            'type' => $shipment->type,
            'status' => $shipment->status,
            'origin' => $shipment->origin->name,
            'destination' => $shipment->destination->name,
            'etd' => $shipment->etd,
            'eta' => $shipment->eta,
            'events' => $shipment->events->map(function ($e) {
                return [
                    'location' => optional($e->location)->name,
                    'status' => $e->status,
                    'description' => $e->description,
                    'time' => $e->event_time
                ];
            })
        ]);
    }
}

🧪 4. SEED DATA TEST (RẤT QUAN TRỌNG)

👉 Tạo nhanh để test API:

$hcm = Location::create(['name' => 'Ho Chi Minh City', 'country' => 'Vietnam']);
$van = Location::create(['name' => 'Vancouver', 'country' => 'Canada']);

$shipment = Shipment::create([
    'tracking_code' => 'ABC123456',
    'type' => 'sea',
    'status' => 'in_transit',
    'origin_id' => $van->id,
    'destination_id' => $hcm->id,
]);

ShipmentEvent::create([
    'shipment_id' => $shipment->id,
    'location_id' => $van->id,
    'status' => 'Departed',
    'description' => 'Left Vancouver Port',
    'event_time' => now()->subDays(5)
]);

ShipmentEvent::create([
    'shipment_id' => $shipment->id,
    'status' => 'In Transit',
    'description' => 'On the way to Vietnam',
    'event_time' => now()->subDays(2)
]);

🌐 5. TEST API

👉 Gọi:

GET /api/tracking/ABC123456

👉 Response:

{
  "tracking_code": "ABC123456",
  "type": "sea",
  "status": "in_transit",
  "origin": "Vancouver",
  "destination": "Ho Chi Minh City",
  "events": [...]
}

🧨 Kết quả sau Tuần 1

✔️ Có API tracking thật
✔️ Có DB chuẩn
✔️ Có data test
✔️ Frontend gọi là chạy

👉 Tức là:

Anh đã có “xương sống” của hệ thống logistics


🚀 Bước tiếp theo (tuần 2)

  • RFQ API

  • Form submit

  • Email notify

  • Admin basic


Tôi có thể:

  • ⚡ Viết luôn frontend tracking (Tailwind + AJAX)

  • 🎯 Tạo Postman collection

  • 🧱 Build Filament admin cho shipment


╔══════════════════════════════════╗
⚙️ Made by AI – Ship Code, Not Ideas 🚀
╚══════════════════════════════════╝

Nhận xét

Bài đăng phổ biến từ blog này

🌈 Tự Động Highlight Code Trong Blogger

🧭CRUD CHUẨN LARAVEL

🚀01 giờ học cách sử dụng Developer Console