php artisan make:livewire Companies
Now, Livewire/Companies.php
open file, and put that code
<?php
namespace App\Http\Livewire;
use App\Models\City;
use App\Models\Company;
use App\Models\Country;
use Livewire\Component;
class Companies extends Component
{
public $countries;
public $cities;
public $name;
public $country;
public $city;
public function mount()
{
$this->countries = Country::all();
$this->cities = collect();
}
public function render()
{
return view('livewire.companies', [
'companies' => Company::with('city.country')->latest()->take(5)->get()
]);
}
public function updatedCountry($value)
{
$this->cities = City::where('country_id', $value)->get();
}
public function storeCompany()
{
$this->validate([
'name' => 'required',
'city' => 'required',
]);
Company::create([
'name' => $this->name,
'city_id' => $this->city,
]);
$this->name = '';
$this->country = '';
$this->city = '';
$this->cities = collect();
}
}
Furthermore, Let's set up a view,
Here is your layout file,
resources/views/livewire.blade.php
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Parent-Child Dependent Dropdowns</title>
<link href="https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css" rel="stylesheet">
@livewireStyles
</head>
<body class="bg-gray-100">
<div class="font-sans text-gray-900 antialiased">
<div class="flex flex-col sm:justify-center items-center pt-5 pb-5">
<h2 class="font-bold text-2xl">Parent-Child Dependent Dropdowns: Livewire Version</h2>
<div class="w-full sm:max-w-xl mt-6 mb-6 px-6 py-8 bg-white shadow-md overflow-hidden sm:rounded-lg">
<div class="mb-4 px-4 py-3 leading-normal text-blue-700 bg-blue-100 rounded-lg" role="alert">
Fill in the form. Choose the country, and cities list will be updated.
</div>
@livewire('companies')
</div>
<a href="{{ route('vue') }}" class="mb-4">See Vue.js version</a>
</div>
</div>
@livewireScripts
</body>
</html>
resources/views/livewire/companies.blade.php
Place that code into your file:
<form wire:submit.prevent="storeCompany">
<div class="mt-4">
<label class="block font-medium text-sm text-gray-700" for="country">
Country*
</label>
<select wire:model="country" name="country"
class="mt-2 text-sm sm:text-base pl-2 pr-4 rounded-lg border border-gray-400 w-full py-2 focus:outline-none focus:border-blue-400" required>
<option value="">-- choose country --</option>
@foreach ($countries as $country)
<option value="{{ $country->id }}">{{ $country->name }}</option>
@endforeach
</select>
</div>
<div class="mt-4">
<label class="block font-medium text-sm text-gray-700" for="city">
City*
</label>
<select wire:model="city" name="city"
class="mt-2 text-sm sm:text-base pl-2 pr-4 rounded-lg border border-gray-400 w-full py-2 focus:outline-none focus:border-blue-400" required>
@if ($cities->count() == 0)
<option value="">-- choose country first --</option>
@endif
@foreach ($cities as $city)
<option value="{{ $city->id }}">{{ $city->name }}</option>
@endforeach
</select>
</div>
</form>
web.php
Route::view('/', 'livewire')->name('livewire');
View set up :
resources/views/vue.blade.php
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Parent-Child Dependent Dropdowns</title>
<link href="https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css" rel="stylesheet">
</head>
<body class="bg-gray-100">
<div class="font-sans text-gray-900 antialiased">
<div class="flex flex-col sm:justify-center items-center pt-5 pb-5">
<h2 class="font-bold text-2xl">Parent-Child Dependent Dropdowns: Vue.js</h2>
<div id="app" class="w-full sm:max-w-xl mt-6 mb-6 px-6 py-8 bg-white shadow-md overflow-hidden sm:rounded-lg">
<div class="mb-4 px-4 py-3 leading-normal text-blue-700 bg-blue-100 rounded-lg" role="alert">
Fill in the form. Choose the country, and cities list will be updated.
</div>
<country-city />
</div>
<a href="{{ route('livewire') }}" class="mb-4">See Livewire version</a>
</div>
</div>
<script src="{{ mix('js/app.js') }}"></script>
</body>
</html>
Component set up:
resources/js/components/CountryCity.vue
<template>
<div>
<div class="mt-4">
<label
class="block font-medium text-sm text-gray-700"
for="country"
>
Country*
</label>
<select
name="country"
class="mt-2 text-sm sm:text-base pl-2 pr-4 rounded-lg border border-gray-400 w-full py-2 focus:outline-none focus:border-blue-400"
required
v-model="selectedCountry"
v-on:change="getCities(selectedCountry.id)"
>
<option value="">-- choose country --</option>
<option
v-for="country in countries"
:value="country"
v-bind:key="country.id"
>{{ country.name }}</option
>
</select>
</div>
<div class="mt-4">
<label
class="block font-medium text-sm text-gray-700"
for="country"
>
City*
</label>
<select
name="city"
class="mt-2 text-sm sm:text-base pl-2 pr-4 rounded-lg border border-gray-400 w-full py-2 focus:outline-none focus:border-blue-400"
required
v-model="selectedCity"
>
<option value="" v-if="cities.length == 0">-- choose country first --</option>
<option
v-for="city in cities"
:value="city"
v-bind:key="city.id"
>{{ city.name }}</option
>
</select>
</div>
</div>
</template>
<script>
import axios from "axios";
export default {
data() {
return {
countries: [],
cities: [],
selectedCity: "",
selectedCountry: ""
};
},
mounted() {
axios
.get("/api/countries")
.then(response => (this.countries = response.data.data));
},
methods: {
getCities(countryId) {
axios
.get("api/countries/" + countryId + "/cities")
.then(response => (
(this.cities = response.data.data),
(this.selectedCity = this.cities[0])
));
}
}
};
</script>
Api set up :
routes/api.php
<?php
Route::get('countries', CountriesController::class);
Route::resource('countries.cities', CitiesController::class);
For routes :
routes/web.php
<?php
Route::view('/vue', 'vue')->name('vue');
Controllers:
app/Http/Controllers/CitiesController.php
<?php
namespace App\Http\Controllers;
use App\Models\City;
use App\Models\Country;
use Illuminate\Http\Request;
use App\Http\Resources\CitiesResource;
class CitiesController extends Controller
{
public function index(Country $country)
{
return CitiesResource::collection(
City::where('country_id', $country->id)->get()
);
}
}
Country:
app/Http/Controllers/CountriesController.php
<?php
namespace App\Http\Controllers;
use App\Models\Country;
use Illuminate\Http\Request;
use App\Http\Resources\CountriesResource;
class CountriesController extends Controller
{
public function __invoke()
{
return CountriesResource::collection(Country::all());
}
}
Code Managed for model
app/Models/City.php
public function country()
{
return $this->belongsTo(Country::class, 'country_id');
}
app/Models/Company.php
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Company extends Model
{
use HasFactory;
protected $fillable = ['name', 'city_id'];
public function city()
{
return $this->belongsTo(City::class);
}
}
app/Models/Country.php
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Country extends Model
{
use HasFactory;
protected $fillable = ['name'];
public function cities()
{
return $this->hasMany(City::class, 'country_id');
}
}
Happy Coding!!
Source : livewire-vs-vuejs-practical-example-of-dependent-dropdowns