F
Filament6mo ago
marcf

Nested Repeater

Is it possible to have nested repeaters but specify the relationship of the nested one to be on the parent model? I have three tables: evenement, film, and seance, along with their respective pivot tables and models. I'm following the documentation for integrating with a BelongsToMany relationship from the Filament PHP Repeater Documentation. Here is my code:
Repeater::make('evenementFilms')
->relationship()
->schema([
Select::make('film_id')
->label("Film")
->options(Film::pluck('titre', 'id'))
->searchable()
->live(),
Repeater::make('evenementSeances')
// ->model(Evenement::class)
->label("Séances liés à cet Événement.")
->relationship('evenementSeances')
->schema([
Select::make('seance_id')
->label("Séance")
->options(Seance::pluck('debut', 'id'))
->searchable()
->live(),
]),
]),
Repeater::make('evenementFilms')
->relationship()
->schema([
Select::make('film_id')
->label("Film")
->options(Film::pluck('titre', 'id'))
->searchable()
->live(),
Repeater::make('evenementSeances')
// ->model(Evenement::class)
->label("Séances liés à cet Événement.")
->relationship('evenementSeances')
->schema([
Select::make('seance_id')
->label("Séance")
->options(Seance::pluck('debut', 'id'))
->searchable()
->live(),
]),
]),
In this setup, the nested repeater evenementSeances defaults to the pivot model (EvenementFilm) instead of the parent model (Evenement). If I uncomment the model specification (->model(Evenement::class)), the relationship does not save correctly. My goal is for the user to first choose a film, and then, if they want, choose sessions for each selected film. How can I correctly specify the relationship for the nested repeater to be on the parent model? Regards
3 Replies
marcf
marcfOP6mo ago
Hi everyone, I've made changes to my database schema. Now, I've introduced events to which I'm attaching films, and I'm indicating that certain screenings of these films are also event screenings. For this purpose, I now have a table for films, seance, evenement, as well as two pivot tables, evenement_film and evenement_film_seance.
Schema::create('evenement_film', function (Blueprint $table) {
$table->id();
$table->foreignId('evenement_id')->constrained()->onDelete('cascade');
$table->foreignId('film_id')->constrained()->onDelete('cascade');
});

Schema::create('evenement_film_seance', function (Blueprint $table) {
$table->id();
$table->foreignId('seance_id')->constrained()->onDelete('cascade');
$table->foreignId('evenement_film_id')->nullable()->constrained('evenement_film')->onDelete('cascade');
});
Schema::create('evenement_film', function (Blueprint $table) {
$table->id();
$table->foreignId('evenement_id')->constrained()->onDelete('cascade');
$table->foreignId('film_id')->constrained()->onDelete('cascade');
});

Schema::create('evenement_film_seance', function (Blueprint $table) {
$table->id();
$table->foreignId('seance_id')->constrained()->onDelete('cascade');
$table->foreignId('evenement_film_id')->nullable()->constrained('evenement_film')->onDelete('cascade');
});
I've also created pivot models EvenementFilm and EvenementFilmSeance following the tutorial "Integrating with a BelongsToMany Eloquent Relationship". My resource is now configured as follows:
Repeater::make('evenementFilms')
->relationship()
->schema([
Select::make('film_id')
->label("Film")
->options(Film::pluck('titre', 'id'))
->searchable()
->live(),
Repeater::make('evenementFilmSeances')
->label("Screenings linked to this Event.")
->relationship()
->schema([
Select::make('seance_id')
->label("Screening")
->options(function ($get) {
return Seance::where('film_id', $get('../../film_id'))->pluck('debut', 'id');
})
])
])
Repeater::make('evenementFilms')
->relationship()
->schema([
Select::make('film_id')
->label("Film")
->options(Film::pluck('titre', 'id'))
->searchable()
->live(),
Repeater::make('evenementFilmSeances')
->label("Screenings linked to this Event.")
->relationship()
->schema([
Select::make('seance_id')
->label("Screening")
->options(function ($get) {
return Seance::where('film_id', $get('../../film_id'))->pluck('debut', 'id');
})
])
])
I'm facing another issue: during the first save, the evenement_film_id column in the evenement_film_seance table is null. If I save it again, it adds a new entry with all the information. My pivot model are :
```php
class EvenementFilm extends Pivot
{
public function film(): BelongsTo
{
return $this->belongsTo(Film::class, 'film_id', 'id');
}

public function evenement(): BelongsTo
{
return $this->belongsTo(Evenement::class, 'evenement_id', 'id');
}

public function evenementFilmSeances(): HasMany
{
return $this->hasMany(EvenementFilmSeance::class, 'evenement_film_id', 'id');
}
}

class EvenementFilmSeance extends Pivot
{
public function evenementFilm(): BelongsTo
{
return $this->belongsTo(EvenementFilm::class, 'evenement_film_id');
}
public function seance(): BelongsTo
{
return $this->belongsTo(Seance::class, 'seance_id');
}
}
```php
class EvenementFilm extends Pivot
{
public function film(): BelongsTo
{
return $this->belongsTo(Film::class, 'film_id', 'id');
}

public function evenement(): BelongsTo
{
return $this->belongsTo(Evenement::class, 'evenement_id', 'id');
}

public function evenementFilmSeances(): HasMany
{
return $this->hasMany(EvenementFilmSeance::class, 'evenement_film_id', 'id');
}
}

class EvenementFilmSeance extends Pivot
{
public function evenementFilm(): BelongsTo
{
return $this->belongsTo(EvenementFilm::class, 'evenement_film_id');
}
public function seance(): BelongsTo
{
return $this->belongsTo(Seance::class, 'seance_id');
}
}
After triple checking my code, my db and my model, I think this is a Filament bug. I manage to do a work around with this code (using saveRelationshipsUsing) but I need to implement again update, delete, create.
Repeater::make('evenementFilms')
->relationship()
->schema([
Select::make('film_id')
->label("Film")
->options(Film::pluck('titre', 'id'))

->searchable()
->live(),
Repeater::make('evenementFilmSeances')
->label("Séance liés à cette Événement.")
->relationship()
->schema([
Select::make('seance_id')
->relationship('seance', 'debut')
->options(function ($get) {
return Seance::where('film_id', $get('../../film_id'))->pluck('debut', 'id');
})
])
->saveRelationshipsUsing(function ($state, $record) {
if (!isset($record['id'])) {
$filmId = $record['film_id'];
$evenementId = $record["evenement_id"];
$evFilm = $record->where('film_id', $filmId)->where('evenement_id', $evenementId)->first();
} else {
$evFilm = $record;
}
$evFilm->load('evenementFilmSeances');

// Here code to update, create, delete
Repeater::make('evenementFilms')
->relationship()
->schema([
Select::make('film_id')
->label("Film")
->options(Film::pluck('titre', 'id'))

->searchable()
->live(),
Repeater::make('evenementFilmSeances')
->label("Séance liés à cette Événement.")
->relationship()
->schema([
Select::make('seance_id')
->relationship('seance', 'debut')
->options(function ($get) {
return Seance::where('film_id', $get('../../film_id'))->pluck('debut', 'id');
})
])
->saveRelationshipsUsing(function ($state, $record) {
if (!isset($record['id'])) {
$filmId = $record['film_id'];
$evenementId = $record["evenement_id"];
$evFilm = $record->where('film_id', $filmId)->where('evenement_id', $evenementId)->first();
} else {
$evFilm = $record;
}
$evFilm->load('evenementFilmSeances');

// Here code to update, create, delete
Sorry for this big message. Do you think I post an issue on filament github ?
marcf
marcfOP6mo ago
GitHub
Nesting repeaters for BelongsToMany relationships · Issue #13069 · ...
Package filament/filament Package Version v3.2 Laravel Version v11.9 Livewire Version v3.5 PHP Version PHP 8.3 Problem description Following the example of using a repeater to fill BelongsToMany re...
marcf
marcfOP6mo ago
Thanks
Want results from more Discord servers?
Add your server