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

dmytroshved's avatar

How to update data in a pivot table? Livewire 3

Hi

I have 2 models:

  • Recipe.php
  • Ingredients.php

And they have ManyToMany relationship using pivot table ingredient_recipe.

Now I want to be able to update data in ingredient_recipe

After preparing ingredients I need to update data in ingredient_recipe and there is 3 situations here:

  • new ingredient (so I need to add one new record)
  • one ingredient was removed (so I need to delete existing record)
  • updated quantity or unit_id

Could you please tell me how the data in pivot tables is updated and how can I handle those 3 situations?

Tables:

create_recipes_table.php:



Schema::create('recipes', function (Blueprint $table) {
            $table->id();
            $table->string('name')->unique();
            $table->text('description')->nullable();
            $table->string('image')->default('recipes-images/default/default_photo.png');
            $table->string('cook_time');
            $table->integer('servings');
            $table->foreignId('dish_category_id')->constrained('dish_categories')->cascadeOnDelete();
            $table->foreignId('user_id')->constrained()->cascadeOnDelete();
            $table->foreignId('cuisine_id')->constrained()->cascadeOnDelete();
            $table->foreignId('menu_id')->nullable()->constrained('menus')->cascadeOnDelete();
            $table->timestamps();
        });

create_ngredient_recipe_table.php::


 Schema::create('ingredient_recipe', function (Blueprint $table) {
            $table->id();
            $table->foreignId('recipe_id')->constrained()->cascadeOnDelete();
            $table->foreignId('ingredient_id')->constrained()->cascadeOnDelete();
            $table->decimal('quantity', 8, 2);
            $table->foreignId('unit_id')->constrained();
            $table->timestamps();
        });

create_ingredients_table.php:


 Schema::create('ingredients', function (Blueprint $table) {
            $table->id();
            $table->string('name')->unique();
            $table->timestamps();
        });

Would be grateful for some advices

0 likes
7 replies
tykus's avatar

Will you always be passing the full list of ingredients (along with quantities and units) in the Request?

dmytroshved's avatar

@tykus yes

Here is my current code that creates recipe with ingredients:

public function store(): void
    {
        $this->resetErrorBag();
        if ($this->form_step == 3){
            $this->guideForm->validate();
        }

        // here I am getting an array of ingredients without duplication, every ingredient is in db (because of creating non-existent ingredients.)
        $finalIngredients = $this->ingredientsForm->prepareFinalIngredients(); 

        // creating recipe with ingredients
        $recipe = $this->recipeForm->createRecipe($finalIngredients);

        $this->guideForm->insertGroupedSteps($recipe->id);

        session()->flash('recipe_created', 'Recipe created successfully!');
            
        $this->redirect('/recipes/create');
    }

How array with ingredients looks like after step with ingredients:

array:1 [▼ // app\Livewire\RecipeWizard.php:67
  "ingredients" => array:2 [▼
    0 => array:3 [▼
      "name" => "Chicken"
      "quantity" => "1.00"
      "unit" => 1
    ]
    1 => array:3 [▼
      "name" => "Carrots"
      "quantity" => "3.00"
      "unit" => 3
    ]
  ]
]
dmytroshved's avatar

prepareFinalIngredients():

public function prepareFinalIngredients(): array
    {
        $finalIngredients = [];

        // check that there are no repetitions, if there are, we sum up the quantity
        $ingredientsGrouped = $this->groupIngredients(); 

        foreach ($ingredientsGrouped as $ingredientData){
            // Check if the ingredient exists (get the existed one or create and save to database)
            $ingredient = $this->getOrCreateIngredients($ingredientData);

            // Prepare data for pivot table
            $finalIngredients[] = [
                'id' => $ingredient->id,
                'quantity' => $ingredientData['quantity'],
                'unit_id' => $ingredientData['unit'],
            ];
        }
        return $finalIngredients;
    }

createRecipe($finalIngredients):

tykus's avatar
tykus
Best Answer
Level 104

@Dmytro_Shved instead of using attach, you can sync the Ingredients with the Recipe; it should attach/detach/update wherever necessary:

$recipe->ingredients()->sync(
    collect($finalIngredients)->mapWithKeys(function ($ingredient) {
        return [$ingredient['id'] => [
            'quantity' => $ingredient['quantity'],
            'unit_id' => $ingredient['unit_id'],
            'created_at' => now(),
            'updated_at' => now(),
        ]];
    })->toArray()
);
1 like
dmytroshved's avatar

@tykus I have a question, how much will sync affect performance? From what I've learned, this method executes several database queries at once.

tykus's avatar

@Dmytro_Shved yes, there are potentially four queries; a read, a delete and up to two write queries for inserting and updating.

You are modifying the ingredients of one Recipe; it is not an issue of performance.

1 like

Please or to participate in this conversation.