Saving Optional Relationship to db does not persist

Edit: This is not solved, only halfway there :( Hey all, I've been trying my hand at Laravel and filament for the last couple of weeks and am stuck on something: I have two entities, Book and BookPoster. Both have their foreign_key value set as nullable (because a book might not have a poster at creation). The BookResource's form looks as follows:
return $form
->schema([
Forms\Components\TextInput::make('title')->required(),
Forms\Components\Select::make('book_poster_id')->relationship(name:"poster", titleAttribute: "name")->label("Poster")
->createOptionForm([
Forms\Components\TextInput::make('name')->required(),
Forms\Components\FileUpload::make('poster_path')->required()
])
]);
return $form
->schema([
Forms\Components\TextInput::make('title')->required(),
Forms\Components\Select::make('book_poster_id')->relationship(name:"poster", titleAttribute: "name")->label("Poster")
->createOptionForm([
Forms\Components\TextInput::make('name')->required(),
Forms\Components\FileUpload::make('poster_path')->required()
])
]);
When selecting a BookPoster from the list, the value does not get persisted in database. Do you know what I might be doing wrong?
Solution:
Why do you have a book_id on the book_poster table? Can a poster only post one book? Honestly I don't know how this would work with laravel or fillament by default. You essentially have 2 different relations here - A poster had a book and a book has a poster but they can be different. I think you will need to override the default create method if you want to change the book_id on the book_poster table (or use a mutateData... method)...
Jump to solution
18 Replies
Matteo G
Matteo GOP7mo ago
ok, turns out I just needed to set the 'book_poster_id' as fillable in the Book entity; am dumb But there's still half the issue; marking the property as fillable sets the id correctly on the book, but not on the book_poster. That value stays null, which means I cannot get the related data from the book instance does not work in create or update
Matteo G
Matteo GOP7mo ago
but the association shows up fine on one side Problem comes when trying to access a property on the other element of the relation (i.e poster.path)
No description
Bruno Pereira
Bruno Pereira7mo ago
Does both the entities (model) have their relations defined? is it a one to one relation? So that 1 book has 1 poster and vice-versa?
Matteo G
Matteo GOP7mo ago
Book HasOne poster and poster BelongsTo a book both have their relationship defined in the model
Bruno Pereira
Bruno Pereira7mo ago
hows the mapping of the foreign keys? Do you have a book_id on the poster table?
Matteo G
Matteo GOP7mo ago
book poster
0|id|integer|1||1
1|name|varchar|1||0
2|poster_path|varchar|1||0
3|book_id|integer|0||0
4|created_at|datetime|0||0
5|updated_at|datetime|0||0
0|id|integer|1||1
1|name|varchar|1||0
2|poster_path|varchar|1||0
3|book_id|integer|0||0
4|created_at|datetime|0||0
5|updated_at|datetime|0||0
book
0|id|integer|1||1
1|title|varchar|1||0
2|book_poster_id|integer|0||0
3|created_at|datetime|0||0
4|updated_at|datetime|0||0
0|id|integer|1||1
1|title|varchar|1||0
2|book_poster_id|integer|0||0
3|created_at|datetime|0||0
4|updated_at|datetime|0||0
If I update the book_posters table manually, everything shows up correctly on the table
No description
No description
Bruno Pereira
Bruno Pereira7mo ago
if this is a one to one relation, only the belongsto table needs the foreign key (in this case, the book_id on the book_poster table). The problem must be on the poster() method on the book Model that it map the relation Thats my guess
Matteo G
Matteo GOP7mo ago
this is on the BookPoster:
public function book(): BelongsTo
{
return $this->belongsTo(Book::class);
}
public function book(): BelongsTo
{
return $this->belongsTo(Book::class);
}
this is on the Book:
public function poster(): HasOne
{
return $this->hasOne(BookPoster::class);
}
public function poster(): HasOne
{
return $this->hasOne(BookPoster::class);
}
but then, let's say I make the select like this:
Forms\Components\Select::make('poster')->relationship(name:"poster", titleAttribute: "name")->label("Poster")
->createOptionForm([
Forms\Components\TextInput::make('name')->required(),
Forms\Components\FileUpload::make('poster_path')->required()
])
Forms\Components\Select::make('poster')->relationship(name:"poster", titleAttribute: "name")->label("Poster")
->createOptionForm([
Forms\Components\TextInput::make('name')->required(),
Forms\Components\FileUpload::make('poster_path')->required()
])
I get the following error
Call to undefined method Illuminate\Database\Eloquent\Relations\HasOne::getOwnerKeyName()
Call to undefined method Illuminate\Database\Eloquent\Relations\HasOne::getOwnerKeyName()
Bruno Pereira
Bruno Pereira7mo ago
I think the poster relation is trying to get the key from a column that doesnt exist...
Matteo G
Matteo GOP7mo ago
for comparison, this at least populates the value correctly
Forms\Components\Select::make('book_poster_id')->relationship(name:"poster", titleAttribute: "name")->label("Poster")
->createOptionForm([
Forms\Components\TextInput::make('name')->required(),
Forms\Components\FileUpload::make('poster_path')->required()
])
Forms\Components\Select::make('book_poster_id')->relationship(name:"poster", titleAttribute: "name")->label("Poster")
->createOptionForm([
Forms\Components\TextInput::make('name')->required(),
Forms\Components\FileUpload::make('poster_path')->required()
])
and fills the book_poster_id column on the Book model (but not the other 😦 )
Bruno Pereira
Bruno Pereira7mo ago
but that is on the Bookresource or Bookposterresource?
Matteo G
Matteo GOP7mo ago
the foreignId on the BookPoster is generated like this:
$table->foreignIdFor(Book::class)->nullable()->constrained();
$table->foreignIdFor(Book::class)->nullable()->constrained();
book
Bruno Pereira
Bruno Pereira7mo ago
can you try this on the Poster Model
public function book(): BelongsTo
{
return $this->belongsTo(Book::class, 'book_id', 'id');
}
public function book(): BelongsTo
{
return $this->belongsTo(Book::class, 'book_id', 'id');
}
Matteo G
Matteo GOP7mo ago
no changes; making the select target the "poster" relation still throws the same error
Bruno Pereira
Bruno Pereira7mo ago
do you have a repo so that I can check the code?
Matteo G
Matteo GOP7mo ago
https://github.com/matfire/kz back folder upping this thread. I tried removing the relationship from the parent, but the book_id property is still not set on the BookPoster and, as such, the table view cannot retrieve those values to be displayed. Anyone had to deal with this ? The relationship is optional, so that might pose an extra challenge 😦
Solution
ChesterS
ChesterS7mo ago
Why do you have a book_id on the book_poster table? Can a poster only post one book? Honestly I don't know how this would work with laravel or fillament by default. You essentially have 2 different relations here - A poster had a book and a book has a poster but they can be different. I think you will need to override the default create method if you want to change the book_id on the book_poster table (or use a mutateData... method)
Matteo G
Matteo GOP7mo ago
yes, a poster can only belong to a single book at a time, and a book can only have a single poster at a time; they unfortunately need to be separate table (client database spec 😦 ) ok, I managed to handle the create part, I think;
protected function handleRecordCreation(array $data): Model
{
$instance = static::getModel()::create($data);
$poster = BookPoster::query()->find($data['poster']);
$poster->book()->associate($instance);
$poster->save();
return $instance;
}
protected function handleRecordCreation(array $data): Model
{
$instance = static::getModel()::create($data);
$poster = BookPoster::query()->find($data['poster']);
$poster->book()->associate($instance);
$poster->save();
return $instance;
}
now I get the following error when going to the edit page: Call to undefined method Illuminate\Database\Eloquent\Relations\HasOne::getOwnerKeyName() ok, that one fixed itself when instead of referencing the hasOne property 'poster' I instead reference the book_poster_id value and this also selects the right option in the select field. I probably just need to do the same thing I did in the create part and that worked! Hopefully it doesn't break xD small detail: I had to specifically change the book_poster_id value on the Book model, otherwise it didn't update properly
protected function handleRecordUpdate(Model $record, array $data): Model
{
BookPoster::query()->find($record->book_poster_id)->update(['book_id' => null]);
$record->update($data);
$record->book_poster_id = $data['book_poster_id'];
$record->save();
BookPoster::query()->find($data['book_poster_id'])->update(['book_id' => $record->id]);
return $record;
}
protected function handleRecordUpdate(Model $record, array $data): Model
{
BookPoster::query()->find($record->book_poster_id)->update(['book_id' => null]);
$record->update($data);
$record->book_poster_id = $data['book_poster_id'];
$record->save();
BookPoster::query()->find($data['book_poster_id'])->update(['book_id' => $record->id]);
return $record;
}
thanks for the tip, Chester!!
Want results from more Discord servers?
Add your server