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

vincent15000's avatar

Big form - How to separate nested controller actions (save/update) ?

Hello,

The title seems to be contradictory : separate some nested things.

I explain.

I have an application where companies can handle trainings and courses schedule.

A training has many units, each unit has many topics, and each topic has one or many trainers. To save all these datas, I have these tables : trainings, units, topics, topic_trainer.

For convenience, I have chosen to save and edit units in one unique form where the use can rename the unit (name, hours_number), add / remove topics (name, hours_number, text_color, background_color), add / remove trainers associated with topics.

To save a unit, I have therefore a method like this one (for the moment the trainers are not managed in the app, I have to work on this) in my TrainingUnitController.

public function store(UnitRequest $request, Training $training)
{
    $unit = new Unit;
    $unit->fill($request->all());
    $unit->training_id = $training->id;
    $unit->save();
    foreach ($request->topics as $item) {
        $topic = new Topic;
        $topic->fill($item);
        $topic->unit_id = $unit->id;
        $topic->training_id = $training->id;
        $topic->save();
    }
}

That means that this method not only saves a unit but also topics (and later in a pivot table the trainers attached to the topics).

It works fine, but is there another way to handle the saving / updating of big forms with nested datas saved in different tables ?

I mean there is perhaps a way to save the topics at a different place in the code so that the TrainingUnitController does not have to save topics. But the problem is that the data come from the post request and sent to the controller.

Thanks for your help.

Vincent

0 likes
19 replies
vincent15000's avatar

Perhaps writing the code to save topics could be written in an action ? Would it be a good idea ?

vincent15000's avatar

@MohamedTammam In the frontend, I use VueJS (with InertiaJS).

Which are the criteria to check if a code MUST be in different places ?

Oh you perhaps mean if I need to reuse the code ?

I don't need to reuse this code. It's only used here in my controller (see above).

vincent15000's avatar

@MohamedTammam What I perhaps try to do is as if it were possible to use 2 controllers from a unique route : one controller to save the unit and another to save the related topics.

For example is it possible to have a kind of master controller which could dispatch the jobs to other controllers.

Master controller => unit controller to save the unit, topic controller to saver the related topics

MohamedTammam's avatar

@vincent15000 Technically yes, But I don't recommend it.

You can use on controller method to dispatch an 2 actions instead.

1 like
vincent15000's avatar

@MohamedTammam In my case, what would you recommend me ?

Dispatch the jobs into two actions ? Or keep my original method ?

Nothing disturbs me, it works like it is now, but I always want to structure better my code the most possible.

MohamedTammam's avatar

@vincent15000 I'm okay with the current code. I would just use mass insert statement to reduce the number of queries for creating new topics.

1 like
vincent15000's avatar

@MohamedTammam Here is my improved code.

public function store(UnitRequest $request, Training $training)
{
    $unit = new Unit;
    $unit->fill($request->all());
    $unit->training_id = $training->id;
    $unit->save();
    $topics = [];
    foreach ($request->topics as $item) {
        $topic = new Topic;
        $topic->fill($item);
        $topic->training_id = $training->id;
        $topics[] = $topic;
    }
    $unit->topics()->saveMany($topics);
}

I initially wanted to test if the $topics was empty or not, but the saveMany() method doesn't generate any error if the array is empty.

MohamedTammam's avatar
Level 51

@vincent15000 It isn't what I meant :)

public function store(UnitRequest $request, Training $training)
{
    $unit = new Unit;
    $unit->fill($request->all());
    $unit->training_id = $training->id;
    $unit->save();
    
	$topics = collect($request->topics)->map(fn($t) => [
		'unit_id' => $unit->id,
		'field' => 'value'
		// ...
	]);

    Topic::insert($topics); // Will run only one query
}
1 like
vincent15000's avatar

@MohamedTammam Your code doesn't work because insert() needs an array and $topics is a collection. The code throws an exception. I have tried to add ->toArray() but it doesn't work better.

vincent15000's avatar

@MohamedTammam Ok I understand why it doesn't work, I need to manually add all the needed fields in the map() method.

Effectively it seems that the saveMany() execute several times the save() methods.

1 like

Please or to participate in this conversation.