Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

markfinney's avatar

Struggling with Forms - two models, one form, edit mode

I have only used forms with model binding so far in laravel, and am really struggling make a form work in edit mode.

I have a parent child one to many relationship set up. But in a certain specific case I need a form that will edit the parent and its 'single' child (I know I should have created a sibling model and would have if the system spec had been even remotely accurate!). However, I know have a few hours in which to work magic and am tearing my hair out.

Just to be clear in most cases the parent has many children, but one scenario results in the parent having one child - this is the scenario I need to edit this parent and child in a single form.

Can I use form::open() and pipe the return values in? I can't seem to findanything about this?

Thank you in advance if you can give me so advice...

0 likes
16 replies
markfinney's avatar

@lstables, thanks...

The problem I have is I don't know how to approach this code. Can I bind two models to a Form? Or can I use Form::open and pipe DB fields in for edit mode using the controller?

Thanks again,

JarekTkaczyk's avatar
Level 53

If it's a 1-1 relation, then simply bind the parent:

// Imagine these objects:
// $parent->foo
// $child->bar

// you need to eager load the child in order to populate the form
$parent = Parent::with('child')->find($id);

Form::model($parent, ...)
Form::text('foo', ...)
Form::text('child[bar]', ...)

then for processing something like this:

$parent = Parent::find($id);
$parent->fill(Input::except('child'));

$child = $parent->child;
$child->fill(Input::get('child'));

If 1-1 is not the case, then simply use accessor:

// Parent model
public function children() {...} // your hasMany relation
public function getChildAttribute()
{
  return $this->children()->first(); // or find($someId) or however you need to get this child
  // or return $this->children->find($someId); // whatever suits you
}

rest of the code remains unchanged.

markfinney's avatar

@JarekTkaczyk thank you for your reply, that is great, but the problem is it is a one to many so I can't bind this way (which is what I am used to)

So in most cases the forms are seperate one for parent one for child but one scenario results in the needing to edit the parent and its first child in one form...

markfinney's avatar

@JarekTkaczyk amazing!!! This looks like what I have been searching for!

Now to try and make it work!

Thank you so much,

pmall's avatar

From my experience, when you have to deal with a form for both a model and its 1-n chilren, the only way to keep it clean is to do some javascript magic. Dump your model + children as a json string in a javascript var and use some javascript framework to render the N children.

The child accessor method becomes messy very quicly.

markfinney's avatar

@pmall thanks for your reply, due to the nature of this job it IS going to be messy. 'When I have time' a rewrite would really clean it up now I have a clearer picture of what they need. Not what they said they needed. Sigh.

That said the accessor seems to be exactly what I was after...

pmall's avatar

No you are right it could be cool in that situation. I just cant wrap my head around why rendering 1-N form is different from rendering a 1-1 form when you have a 1-N relationship :-)

markfinney's avatar

@JarekTkaczyk Sorry to bother you again... I am sure I am doing something stupid but when I try your example I get the error: Call to undefined method Illuminate\Database\Query\Builder::addEagerConstraints()

Any ideas?

JarekTkaczyk's avatar

@markfinney I have to see your code. And the stack trace should tell you which piece is the culprit, so take a deeper look in it.

markfinney's avatar

@JarekTkaczyk thank you so much for your help - I have taken a step forward in my understanding also got the form working.

I know I am still missing something I am using the function to set a public property instead and it is working as expected...

I want to understand what I am missing if you don't mind code as follows

Parent Model:

    public function children(){
        return $this->hasMany('Child');
    }

    public function firstChild(){
        return $this->children->first();
    }   

ParentController (which is where it is failing)

         $artwork = $this->parent->with(['parent_type','parent_medium','firstChild'])->find($id);
JarekTkaczyk's avatar

@markfinney OK, the thing is, you cannot treat 'simple' methods/accessors like relations (thus you can't load it this way). If you want to do so, you have to make sure the methods returns Illuminate\Database\Eloquent\Relations\Relation object (or its descendant).

It will be easier if you describe the use case:

  1. how exactly do you want to fetch the child from hasMany (is it first/last or there are other conditions?),
  2. do you need to eager load that child for multiple models? If not, then you don't need to create a relation at all.

Let me show you sensible example:

Post hasMany Comment

// Relation
public function comments()
{
 return $this->hasMany('Comment');
}

// Relation
public function latestComment()
{
 return $this->hasOne('Comment')->latest();
}

// 'simple' method
public function firstComment()
{
 return $this->comments()->oldest()->first();
}

// Accessor
public function getOldestCommentAttribute()
{
 return $this->comments()->oldest()->first();
}

First 2 methods return Relation object, so you can do this:

$post = Post::with('comments', 'latestComment')->find($id); // eager load
$post->latestComment; // access like a property via Eloquent Dynamic Properties
Form::text('latestComment[body]', ..) // access relation in bound form

3rd method doesn't leverage eloquent features (like yours firstChild) - it doesn't return Relation object, so you can use it only like any method:

Post::with('firstComment'); // ERROR
Form::text('firstComment[body]', ..) // won't work
$post->firstComment(); // accessible only the usual way

And then we have an accessor, that you can use in the Form, treat as a dynamic property, but can't eager load either:

// won't work
Post::with('oldestComment');

// but this will
Form::text('oldestComment[body]', ..); // OK will work as in my first answer
$post->oldestComment; // will work as well using dynamic property call

Please or to participate in this conversation.