This post is notes from the fantastic course Refactoring Rails
— If you are a Rails dev, strongly recommend you check it out.
So — You need to interact with more than one object in a single form. Let's say you want a signup form that also accepts some information about a user's car. Something like, email, phone number, name, car make, car model, car year. In this case, it's clear that you need to store information about a User and information about a car. Just add columns 👎
One solution could be just stretching your user model to include the user car informaiton, but this is a very very limiting approach. First, what if user's need to have more than one car? Or what if we have some business logic we want to impact users but not cards. Clearly these should be stored as single objects. Make the user fill out two forms 👎
This is a reasonable thought. Just update the UX to have the user signup form then the car information form. Simplifies the data model, however this might not work for every use case, especially when there is even more objects to interact with. Let's say a user needs to add info about themselves, their car and their pet in a single form, makes less sense to split this all up. Rails Magic ✨ — Accepts nested attributes (meh)
Accepts nested attributes is very hit or miss. It can be super useful for simpler objects, but pushing it far can get annoying. Also, trying to work with the built in form helpers can get really wonky.
Pros — Built into rails, you know I love these defaults, validations are easier, wraps in transactions, so if a parent object fails to save you don't end up with an orphaned nested object.
Cons — Nesting all over the place, in the view and in the params, this can get really gnarly with many nested objects. Especially if you need to nest within nesting of concepts. This duplicates your domain definitions into controllers and views. If you want to change an association it gets messy to update all these places. Lastly, it's tricky hard to validate interdependent objects. Debugging can be a total nightmare.
Solution: Rails Form Objects 👍
Create a new "Form Object"
to encapsulate all the objects you are working with. For example a "SignUpForm" — this signup form object can accept all of the data we need. Information about the User, the User's Car and the User's Pet.
Then, you can build a controller that interacts with this form object. That form object can delegate to all the objects you are interacting with. This makes it much easier to validate interdependent objects and handle creating or updating all the distinct models we need to interact with.