I have a livewire wizard form, it consists of 3 steps (Recipe Info, Ingredients, Guide).
From each step I get data so that I can create a recipe.
The Recipe and Ingredient model have relationship using ManyToMany and pivot table ingredient_recipe
(with IngredientRecipe
model)
The problem is that from the list of ingredients provided by the user can be those that are in the database, and those that are not there.
The data from the second step (https://i.ibb.co/xSfQ6Wx0/step-2.jpg) comes from the form like this:
array:1 [▼ // app\Livewire\RecipeWizard.php:163
"ingredients" => array:2 [▼
0 => array:3 [▼
"ingredient_name" => "Chicken "
"ingredient_quantity" => "2"
"ingredient_unit" => "1"
]
1 => array:3 [▼
"ingredient_name" => "Potato"
"ingredient_quantity" => "3"
"ingredient_unit" => "1"
]
]
]
(there is no such ingredient with name 'Potato' in the ingredients table)
But to save the ingredients for the recipe in the pivot table, the data must look like this
$broth_ingredients = [
['id' => 1, 'quantity' => 2, 'unit' => '1'],
['id' => 2, 'quantity' => 2, 'unit' => '1']
];
(i.e. the ingredients must be in the database before saving their id to the pivot table).
Is there any way to create non-existent ingredients, and then process all ingredients so that they can be stored in a pivot table?
Models:
Recipe.php:
public function ingredients(): BelongsToMany
{
return $this->belongsToMany(Ingredient::class)
->using(IngredientRecipe::class)
->withPivot(['quantity', 'unit_id']);
}
Ingredient.php:
public function recipes(): BelongsToMany
{
return $this->belongsToMany(Recipe::class);
}
IngredientRecipe.php:
public function unit(): BelongsTo
{
return $this->belongsTo(Unit::class);
}
ingredient_recipe.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();
});
Code for saving the recipe:
$recipe = Auth::user()->recipes()->create([
'name' => $this->recipe_name,
'description' => $this->recipe_description,
'image' => $recipe_image_path,
'dish_category_id' => $this->recipe_category,
'cuisine_id' => $this->recipe_cuisine,
'menu_id' => $this->recipe_menu,
]);
As I see, your problem is logical issue. You should save the new ingredient(not already exists) into database before saving the recipe. Something like this "I prefer arrange the code in one function":
public function saveRecipe()
{
$validatedData = $this->validate([
'ingredients' => 'required|array',
'ingredients.*.ingredient_name' => 'required|string',
'ingredients.*.ingredient_quantity' => 'required|numeric',
'ingredients.*.ingredient_unit' => 'required|exists:units,id',
]);
$ingredientsGrouped = collect($validatedData['ingredients'])
->groupBy('ingredient_name') // Group by ingredient name
->map(function ($group) {
return [
'ingredient_name' => $group->first()['ingredient_name'],
'ingredient_quantity' => $group->sum('ingredient_quantity'), // Sum quantities
'ingredient_unit' => $group->first()['ingredient_unit'], // Take the first unit
];
});
$brothIngredients = []; // Data to attach to pivot table
foreach ($ingredientsGrouped as $ingredientData) {
// Check if the ingredient exists (get the existed one or create and save to database)
$ingredient = Ingredient::firstOrCreate(
['name' => trim($ingredientData['ingredient_name'])], // Match by name
['created_at' => now(), 'updated_at' => now()] // Additional fields for new records
);
// Prepare data for pivot table
$brothIngredients[] = [
'id' => $ingredient->id,
'quantity' => $ingredientData['ingredient_quantity'],
'unit_id' => $ingredientData['ingredient_unit'],
];
}
// Create the Recipe
$recipe = Recipe::create([
// Add your recipe fields here
]);
// Save ingredients to the pivot table
$recipe->ingredients()->attach(
collect($brothIngredients)->mapWithKeys(function ($ingredient) {
return [$ingredient['id'] => [
'quantity' => $ingredient['quantity'],
'unit_id' => $ingredient['unit_id'],
]];
})->toArray()
);
session()->flash('message', 'Recipe saved successfully!');
}