In this comprehensive tutorial, I’ll guide you through building an interactive dependent dropdown system in Laravel 12. We’ll implement a cascading selection menu for countries, states, and cities that dynamically updates based on user selections.
Understanding Dependent Dropdowns
A dependent dropdown is an intelligent form element where the options in one dropdown menu change based on the selection made in a previous dropdown. For instance:
- Selecting “United States” in the country dropdown would populate the state dropdown with options like “California” and “New York”
- Choosing “California” would then populate the city dropdown with options like “Los Angeles” and “San Francisco”
This creates a seamless, intuitive user experience by only showing relevant options at each level.
Implementation Overview
We’ll follow these steps to create our dynamic dropdown system:
- Set up a fresh Laravel 12 application
- Create database tables for countries, states, and cities
- Establish model relationships
- Implement controller logic
- Build the frontend with AJAX functionality
- Seed sample data
- Test our implementation
Step-by-Step Implementation
Step 1: Install Laravel 12
Begin by creating a new Laravel project:
composer create-project laravel/laravel dependent-dropdown-demo
cd dependent-dropdown-demoStep 2: Create Database Migrations
Generate and configure migrations for our three tables:
php artisan make:migration create_countries_states_cities_tablesUpdate the migration file:
<?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('countries', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->timestamps();
        });
  
        Schema::create('states', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->foreignId('country_id')->constrained();
            $table->timestamps();
        });
  
        Schema::create('cities', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->foreignId('state_id')->constrained(); 
            $table->timestamps();
        });
    }
  
    public function down(): void
    {
        Schema::dropIfExists('cities');
        Schema::dropIfExists('states');
        Schema::dropIfExists('countries');
    }
};Run the migrations:
php artisan migrateStep 3: Create Eloquent Models
Generate models with their relationships:
php artisan make:model Country
php artisan make:model State
php artisan make:model CityUpdate the model files:
Country.php
<?php
  
namespace App\Models;
  
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
  
class Country extends Model
{
    use HasFactory;
  
    protected $fillable = ['name'];
    
    public function states(): HasMany
    {
        return $this->hasMany(State::class);
    }
}State.php
<?php
  
namespace App\Models;
  
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
  
class State extends Model
{
    use HasFactory;
  
    protected $fillable = ['name', 'country_id'];
    
    public function country(): BelongsTo
    {
        return $this->belongsTo(Country::class);
    }
    
    public function cities(): HasMany
    {
        return $this->hasMany(City::class);
    }
}City.php
<?php
  
namespace App\Models;
  
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
  
class City extends Model
{
    use HasFactory;
  
    protected $fillable = ['name', 'state_id'];
    
    public function state(): BelongsTo
    {
        return $this->belongsTo(State::class);
    }
}Step 4: Define Routes
Set up the necessary routes in routes/web.php:
<?php
  
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\DropdownController;
    
Route::get('dropdown', [DropdownController::class, 'index']);
Route::post('api/fetch-states', [DropdownController::class, 'fetchState']);
Route::post('api/fetch-cities', [DropdownController::class, 'fetchCity']);Step 5: Create the Controller
Generate and implement the controller logic:
php artisan make:controller DropdownControllerUpdate the controller:
<?php
  
namespace App\Http\Controllers;
  
use Illuminate\Http\Request;
use App\Models\Country;
use App\Models\State;
use App\Models\City;
use Illuminate\View\View;
use Illuminate\Http\JsonResponse;
  
class DropdownController extends Controller
{
    public function index(): View
    {
        return view('dropdown', [
            'countries' => Country::all(['name', 'id'])
        ]);
    }
    public function fetchState(Request $request): JsonResponse
    {
        $states = State::where('country_id', $request->country_id)
                      ->get(['name', 'id']);
  
        return response()->json(['states' => $states]);
    }
    public function fetchCity(Request $request): JsonResponse
    {
        $cities = City::where('state_id', $request->state_id)
                      ->get(['name', 'id']);
                                      
        return response()->json(['cities' => $cities]);
    }
}Step 6: Create the View
Build the frontend interface in resources/views/dropdown.blade.php:
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="csrf-token" content="{{ csrf_token() }}">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Laravel 12 Dependent Dropdown Demo</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
    <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
</head>
<body>
    <div class="container py-5">
        <div class="card shadow">
            <div class="card-header bg-primary text-white">
                <h3 class="mb-0">Dynamic Dependent Dropdown Demo</h3>
            </div>
            <div class="card-body">
                <form>
                    <div class="mb-3">
                        <label for="country-dropdown" class="form-label">Country</label>
                        <select id="country-dropdown" class="form-select">
                            <option value="">-- Select Country --</option>
                            @foreach ($countries as $country)
                                <option value="{{ $country->id }}">{{ $country->name }}</option>
                            @endforeach
                        </select>
                    </div>
                    
                    <div class="mb-3">
                        <label for="state-dropdown" class="form-label">State</label>
                        <select id="state-dropdown" class="form-select" disabled>
                            <option value="">-- Select State --</option>
                        </select>
                    </div>
                    
                    <div class="mb-3">
                        <label for="city-dropdown" class="form-label">City</label>
                        <select id="city-dropdown" class="form-select" disabled>
                            <option value="">-- Select City --</option>
                        </select>
                    </div>
                </form>
            </div>
        </div>
    </div>
    <script>
        $(document).ready(function() {
            // Country dropdown change event
            $('#country-dropdown').change(function() {
                const countryId = $(this).val();
                $('#state-dropdown').html('<option value="">-- Select State --</option>');
                $('#state-dropdown').prop('disabled', !countryId);
                
                if (countryId) {
                    $.ajax({
                        url: "{{ url('api/fetch-states') }}",
                        type: "POST",
                        data: {
                            country_id: countryId,
                            _token: '{{ csrf_token() }}'
                        },
                        success: function(response) {
                            $.each(response.states, function(key, value) {
                                $('#state-dropdown').append(
                                    `<option value="${value.id}">${value.name}</option>`
                                );
                            });
                        }
                    });
                }
                
                // Reset city dropdown
                $('#city-dropdown').html('<option value="">-- Select City --</option>');
                $('#city-dropdown').prop('disabled', true);
            });
            // State dropdown change event
            $('#state-dropdown').change(function() {
                const stateId = $(this).val();
                $('#city-dropdown').html('<option value="">-- Select City --</option>');
                $('#city-dropdown').prop('disabled', !stateId);
                
                if (stateId) {
                    $.ajax({
                        url: "{{ url('api/fetch-cities') }}",
                        type: "POST",
                        data: {
                            state_id: stateId,
                            _token: '{{ csrf_token() }}'
                        },
                        success: function(response) {
                            $.each(response.cities, function(key, value) {
                                $('#city-dropdown').append(
                                    `<option value="${value.id}">${value.name}</option>`
                                );
                            });
                        }
                    });
                }
            });
        });
    </script>
</body>
</html>Step 7: Seed Sample Data
Create and run a seeder to populate our tables:
php artisan make:seeder CountryStateCitySeederUpdate the seeder:
<?php
  
namespace Database\Seeders;
  
use Illuminate\Database\Seeder;
use App\Models\Country;
use App\Models\State;
use App\Models\City;
  
class CountryStateCitySeeder extends Seeder
{
    public function run(): void
    {
        // United States data
        $us = Country::create(['name' => 'United States']);
        
        $florida = State::create(['country_id' => $us->id, 'name' => 'Florida']);
        City::create(['state_id' => $florida->id, 'name' => 'Miami']);
        City::create(['state_id' => $florida->id, 'name' => 'Tampa']);
        
        $california = State::create(['country_id' => $us->id, 'name' => 'California']);
        City::create(['state_id' => $california->id, 'name' => 'Los Angeles']);
        City::create(['state_id' => $california->id, 'name' => 'San Francisco']);
        
        // India data
        $india = Country::create(['name' => 'India']);
        
        $gujarat = State::create(['country_id' => $india->id, 'name' => 'Gujarat']);
        City::create(['state_id' => $gujarat->id, 'name' => 'Ahmedabad']);
        City::create(['state_id' => $gujarat->id, 'name' => 'Surat']);
        
        $maharashtra = State::create(['country_id' => $india->id, 'name' => 'Maharashtra']);
        City::create(['state_id' => $maharashtra->id, 'name' => 'Mumbai']);
        City::create(['state_id' => $maharashtra->id, 'name' => 'Pune']);
    }
}Run the seeder:
php artisan db:seed --class=CountryStateCitySeederStep 8: Launch the Application
Start the development server:
php artisan serveVisit the application in your browser at http://localhost:8000/dropdown to see the dynamic dropdowns in action.

 
			 
						 
			 
										 
										