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

LessThanJake's avatar

"Laravel" way to save to multiple models

Hello, first time posting here. I'm about 2 months into learning Laravel, currently developing a Livewire app as a learning project. The base project I started with uses the (Eloquent) Model->save() method/event to update the app page. Here's that code (in app/Http/Livewire/Project.php component):

public function save()
    {
        $this->validate();

        $this->editing->save();

        $this->showEditModal = false;
    }

My issue is I'm saving to multiple models (database tables), so some of my form variables need to go to another model. If I try to use the save method above, Eloquent complains that some of the form variables do not have matching database columns. The FirstOrNew and FirstOrCreate methods both allow me to correct for that issue, but then I (*believe*) I have to lose the $this->editing model binding and the event/update of the app page doesn't happen. I've hacked together a working solution, which is this:

public function save()
    {
        $this->validate();

		// clear the table of old selections first and then save all new selected options
        ProjectTeam::where('project_id', $this->editing->id)->delete();

        foreach ($this->editing->teams AS $id) {
            ProjectTeam::create([
                'project_id' => $this->editing->id,
                'team_id' => $id
            ]);
        }

        unset($this->editing->teams);

        $this->editing->save();

        $this->showEditModal = false;
    }

It works, but it doesn't look great. Unsetting attributes seems hacky; there has to be a way(s) Laravel would handle the situation, but a few days of scouring the internet hasn't shown me any solution. I'm also looking to save the user_id of the creator of any records, so a one-time injection of that attribute when it's saving a new record. Easy to do with FirstOrNew and FirstOrCreate, but I haven't been able to incorporate that logic using save(). Whenever I try to set it prior to saving, I get an error message saying something like "injecting attribute to the overloaded model has no effect."

Can anybody show me how this would optimally work in Laravel/Livewire? It's probably something simple, but I have been throwing spaghetti at the wall for days with no luck.

Thanks! jake

0 likes
6 replies
tykus's avatar
tykus
Best Answer
Level 104

You can have multiple models as public properties on the Component; and you can bind form input and validation rules appropriate to each model using the dot syntax.

LessThanJake's avatar

@tykus Thanks for the input. Wouldn't I still have the issue of saving to the $this->editing (Project) Model and the $this->editing->teams not belonging to that model/table? Is there a way to do it without unsetting that attribute?

LessThanJake's avatar

@tykus Thanks, I'll read up on how to do that with the dot syntax. Coming from Joomla, most of the Laravel process is similar, but things like dot syntax-ing are still a little fuzzy for me. Do you happen to know of any (github) projects/examples that do something similar? Monkey see -> monkey do 😉

LessThanJake's avatar

Just following up, it was pretty simple to get working. I just pulled the $teams out of $this->editing and set it as a public property. Here's the working code (I removed unrelated code in the class):

class Projects extends Component
{

    public Project $editing;
    public $teams = [];

    public function rules() { return [
        'editing.name' => 'required|min:3',
        'editing.description' => 'required',
        'editing.status' => 'required|in:'.collect(Project::STATUSES)->keys()->implode(','),
        'editing.date_for_editing' => 'required',
        'editing.color' => 'required',
        'teams' => 'present',
        'editing.user_id' => ''
    ]; }

    public function mount() {
        $this->editing = $this->makeBlankProject();
    }

    public function makeBlankProject()
    {
        return Project::make(['start_date' => now(), 'status' => 'active', 'user_id' => auth()->id()]);
    }

    public function edit(Project $project)
    {
        $this->useCachedRows();

        if ($this->editing->isNot($project)) $this->editing = $project;

        $ids = [];

        foreach ($project->active_teams AS $team) {
            $ids[] = $team->team_id;
        }

        $this->teams = $ids;

        $this->showEditModal = true;
    }

    public function save()
    {
        $this->validate();

        ProjectTeam::where('project_id', $this->editing->id)->delete();

        foreach ($this->teams AS $id) {
            ProjectTeam::create([
                'project_id' => $this->editing->id,
                'team_id' => $id
            ]);
        }

        $this->editing->save();

        $this->showEditModal = false;
    }

    public function render()
    {
        return view('livewire.projects', [
            'projects' => $this->rows,
        ]);
    }
}
LessThanJake's avatar

Just in case someone happens upon this, there's a more "eloquent" way to do the save using attach() and sync(). Sync works best in my case (it handles the delete of previously assigned project teams AND assigns the new team). My save now looks like this:

public function save()
    {
        $this->validate();

        $this->editing->save();

        $this->editing->activeTeams()->sync($this->teams);

        $this->showEditModal = false;
    }

edit: this is provided you have a belongsToMany() relationship between the Project & Team models, enabling you to use the sync method

Please or to participate in this conversation.