Back to Course |
Filament Adminpanel for Booking.com API Project

Manage Countries and Cities

In this lesson, we will show the list of countries, will separate them in the Geography navigation group, and will use a Relation Manager to show/manage cities for each country.

country with cities


Countries: Table and Form

First, we need to create a Resource for the countries.

php artisan make:filament-resource Country

Now let's add a form and table columns. This is a very basic resource just with three values name, lat, and long.

app/Filament/Resources/CountryResource.php:

class CountryResource extends Resource
{
protected static ?string $model = Country::class;
 
protected static ?string $navigationIcon = 'heroicon-o-globe';
 
public static function form(Form $form): Form
{
return $form
->schema([
Forms\Components\TextInput::make('name')
->required()
->columnSpanFull(),
Forms\Components\TextInput::make('lat')
->required(),
Forms\Components\TextInput::make('long')
->required(),
]);
}
 
public static function table(Table $table): Table
{
return $table
->columns([
Tables\Columns\TextColumn::make('name'),
Tables\Columns\TextColumn::make('lat'),
Tables\Columns\TextColumn::make('long'),
])
->filters([
//
])
->actions([
Tables\Actions\EditAction::make(),
])
->bulkActions([
Tables\Actions\DeleteBulkAction::make(),
]);
}
// ...
}

Latitude and Longitude: Custom Validation

For the lat and long fields, we will make a custom validation rule. Latitude ranges from -90.0 to 90.0, and longitude ranges from -180.0 to 180.0. We won't allow users to enter other values, and we will also reuse those rules later in lat/long of other geographical objects.

php artisan make:rule LatitudeRule

app/Rules/LatitudeRule.php:

class LatitudeRule implements ValidationRule
{
public function validate(string $attribute, mixed $value, Closure $fail): void
{
$regex = '/^[-]?((([0-8]?[0-9])(\.(\d{1,8}))?)|(90(\.0+)?))$/';
 
if (! preg_match($regex, $value)) {
$fail('The :attribute must be a valid latitude coordinate in decimal degrees format.');
}
}
}
php artisan make:rule LongitudeRule

app/Rules/LongtitudeRule.php:

class LongitudeRule implements ValidationRule
{
public function validate(string $attribute, mixed $value, Closure $fail): void
{
$regex = '/^[-]?((((1[0-7][0-9])|([0-9]?[0-9]))(\.(\d{1,8}))?)|180(\.0+)?)$/';
 
if (! preg_match($regex, $value)) {
$fail('The :attribute must be a valid longitude coordinate in decimal degrees format.');
}
}
}

And add them to the form.

app/Filament/Resources/CountryResource.php:

use App\Rules\LatitudeRule;
use App\Rules\LongitudeRule;
 
class CountryResource extends Resource
{
protected static ?string $model = Country::class;
 
protected static ?string $navigationIcon = 'heroicon-o-globe';
 
public static function form(Form $form): Form
{
return $form
->schema([
Forms\Components\TextInput::make('name')
->required()
->columnSpanFull(),
Forms\Components\TextInput::make('lat')
->required()
->rules([new LatitudeRule()]),
Forms\Components\TextInput::make('long')
->required()
->rules([new LongitudeRule()]),
]);
}
// ...
}

Countries in Navigation

Next, let's add "Countries" menu to a group in the navigation.

app/Filament/Resources/CountryResource.php:

class CountryResource extends Resource
{
protected static ?string $model = Country::class;
 
protected static ?string $navigationIcon = 'heroicon-o-globe';
 
protected static ?string $navigationGroup = 'Geography';
// ...
}

geography group


Showing Cities: Relation Manager

Every country has many cities, with a hasMany relationship.

Now, we will use Relation Manager to show cities.

php artisan make:filament-relation-manager CountryResource cities name
class CountryResource extends Resource
{
// ...
public static function getRelations(): array
{
return [
RelationManagers\CitiesRelationManager::class,
];
}
// ...
}

If you go to the "Edit country" page, at the bottom you will see the list of cities that belong to that country.

edit countries

For the cities form and table, we just need to add the lat and long fields.

app/Filament/Resources/CountryResource/RelationManagers/CitiesRelationManager.php:

use App\Rules\LatitudeRule;
use App\Rules\LongitudeRule;
 
class CitiesRelationManager extends RelationManager
{
protected static string $relationship = 'cities';
 
protected static ?string $recordTitleAttribute = 'name';
 
public static function form(Form $form): Form
{
return $form
->schema([
Forms\Components\TextInput::make('name')
->required()
->maxLength(255)
->columnSpanFull(),
Forms\Components\TextInput::make('lat')
->required()
->rules([new LatitudeRule()]),
Forms\Components\TextInput::make('long')
->required()
->rules([new LongitudeRule()]),
]);
}
 
public static function table(Table $table): Table
{
return $table
->columns([
Tables\Columns\TextColumn::make('name'),
Tables\Columns\TextColumn::make('lat'),
Tables\Columns\TextColumn::make('long'),
])
->filters([
//
])
->headerActions([
Tables\Actions\CreateAction::make(),
])
->actions([
Tables\Actions\EditAction::make(),
Tables\Actions\DeleteAction::make(),
])
->bulkActions([
Tables\Actions\DeleteBulkAction::make(),
]);
}
}

And we have lat and long fields.

cities table