Создание системы чекбоксов для связи сущностей «многие ко многим» в Laravel 11 с использованием библиотеки Filament 3 — это отличный способ управления взаимосвязями между данными. В этой статье мы рассмотрим, как это можно реализовать на примере трех сущностей: Комната, Сотрудник и Техника.

Структура базы данных
Для начала, давайте определим структуру базы данных:
- Комнаты (
rooms
)id
name
- Сотрудники (
employees
)id
name
room_id
(внешний ключ на таблицуrooms
)
- Техника (
equipment
)id
name
- Сотрудник-Техника (связующая таблица) (
employee_equipment
)employee_id
(внешний ключ на таблицуemployees
)equipment_id
(внешний ключ на таблицуequipment
)
Создадим файлы для моделей и миграций с помощью команды:
1 2 3 4 | php artisan make:model Room -m php artisan make:model Employee -m php artisan make:model Equipment -m php artisan make:migration employee_equipment |
- Модель Room:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | <?php namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; class Room extends Model { use HasFactory; protected $fillable = [ 'name', ]; public function employees() { return $this->hasMany(Employee::class); } } |
- Модель Employee:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | <?php namespace App\Models; use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsToMany; class Employee extends Model { use HasFactory; protected $fillable = [ 'name', 'lastname', 'room_id' ]; protected function fullName(): Attribute { return Attribute::make( get: fn() => $this->name.' '.$this->lastname ); } public function equipment():BelongsToMany { return $this->belongsToMany(Equipment::class); } } |
- Модель Equipment:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | <?php namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsToMany; class Equipment extends Model { use HasFactory; protected $fillable = [ 'name', ]; protected function employees():BelongsToMany { return $this->belongsToMany(Employee::class); } } |
Создайте миграции для каждой таблицы:
1. Миграция для таблицы rooms
1 2 3 4 5 | Schema::create('rooms', function (Blueprint $table) { $table->id(); $table->string('name'); $table->timestamps(); }); |
2. Миграция для таблицы employees:
1 2 3 4 5 6 7 | Schema::create('employees', function (Blueprint $table) { $table->id(); $table->foreignId('room_id')->constrained('rooms'); $table->string('lastname'); $table->string('name'); $table->timestamps(); }); |
3. Миграция для таблицы equipment:
1 2 3 4 5 | Schema::create('equipments', function (Blueprint $table) { $table->id(); $table->string('name'); $table->timestamps(); }); |
4. Миграция для таблицы employee_equipment:
1 2 3 4 5 6 7 8 9 10 11 12 13 | Schema::create('employee_equipment', function (Blueprint $table) { $table->unsignedBigInteger('employee_id'); $table->unsignedBigInteger('equipment_id'); $table->primary(['employee_id', 'equipment_id']); $table->foreign('employee_id') ->references('id') ->on('employees') ->onDelete('cascade'); $table->foreign('equipment_id') ->references('id') ->on('equipments') ->onDelete('cascade'); }); |
Выполним миграцию:
1 | php artisan migrate |
Ресурс Room
Создадим ресурс и добавим в него наш Action
1 | php artisan make:filament-resource Room --generate |
Теперь в блок action можно добавить новое действие
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 | Tables\Actions\Action::make('editEquipment') ->icon('heroicon-o-cog') ->slideOver() ->modalAutofocus(false) ->modalWidth(MaxWidth::ExtraLarge) ->fillForm(function(Room $record):array{ $employees = $record->employees->load('equipments'); $equipment = Equipment::pluck('id'); return $employees->mapWithKeys(function($employee) use ($equipment):array{ return [ $employee->id => [ 'equipment' => $equipment->mapWithKeys(function($equipmentId) use ($employee):array{ return [ $equipmentId =>$employee->equipments->contains('id',$equipmentId) ]; })->toArray() ] ]; } )->toArray(); }) ->form(function(Room $record):array{ $employees = $record->employees; $equipments = Equipment::pluck('name','id'); $form = []; foreach($employees as $employee){ $form[]= Forms\Components\Fieldset::make() ->schema([ Forms\Components\Grid::make() ->schema([ Forms\Components\Placeholder::make('fullName') ->hiddenLabel() ->content(fn():string=>$employee->full_name), Forms\Components\Group::make(function() use ($equipments,$employee):array{ $equipmentCheckboxes = []; foreach($equipments as $id => $equipment){ $equipmentCheckboxes[] = Forms\Components\Checkbox::make($employee->id.'.equipment.'.$id) ->label(ucfirst($equipment)); } return $equipmentCheckboxes; }) ]) ]); }; return $form; }) ->action(function(array $data, Room $record):void{ $record->employees->each(function($employee) use ($data):void{ $equipmentIds = collect($data[$employee->id]['equipment']) ->filter(fn($value) => $value === true) ->keys() ->all(); $employee->equipments()->sync($equipmentIds); }); Notification::make() ->title('Equipment assigned') ->success() ->send(); }), |