mirror of https://github.com/kcal-app/kcal.git
Add recipe duplicate functionality
This commit is contained in:
parent
6e3c40531d
commit
4aaf83c862
|
@ -12,6 +12,7 @@ use App\Support\Number;
|
|||
use App\Support\Nutrients;
|
||||
use Illuminate\Contracts\View\View;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Str;
|
||||
|
@ -350,6 +351,30 @@ class RecipeController extends Controller
|
|||
$recipe->ingredientSeparators()->saveMany($ingredient_separators);
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirm duplicating recipe.
|
||||
*/
|
||||
public function duplicateConfirm(Recipe $recipe): View {
|
||||
return view('recipes.duplicate')->with('recipe', $recipe);
|
||||
}
|
||||
|
||||
/**
|
||||
* Duplicate a recipe.
|
||||
*/
|
||||
public function duplicate(Request $request, Recipe $recipe): RedirectResponse
|
||||
{
|
||||
$attributes = $request->validate(['name' => ['required', 'string']]);
|
||||
|
||||
try {
|
||||
$new_recipe = $recipe->duplicate($attributes);
|
||||
} catch (\Throwable $e) {
|
||||
return back()->withInput()->withErrors($e->getMessage());
|
||||
}
|
||||
|
||||
return redirect()->route('recipes.show', $new_recipe)
|
||||
->with('message', "Recipe {$recipe->name} duplicated!");
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirm removal of specified resource.
|
||||
*/
|
||||
|
|
|
@ -14,6 +14,7 @@ use Illuminate\Database\Eloquent\Factories\HasFactory;
|
|||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Spatie\Image\Manipulations;
|
||||
use Spatie\MediaLibrary\HasMedia;
|
||||
use Spatie\MediaLibrary\InteractsWithMedia;
|
||||
|
@ -262,4 +263,55 @@ final class Recipe extends Model implements HasMedia
|
|||
->optimize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Duplicates the recipe, updating provided attributes.
|
||||
*
|
||||
* @throws \Throwable
|
||||
*/
|
||||
public function duplicate(array $attributes): Recipe {
|
||||
/** @var \App\Models\Recipe $recipe */
|
||||
$recipe = $this->replicate();
|
||||
$recipe->fill($attributes);
|
||||
|
||||
try {
|
||||
DB::transaction(function () use ($recipe) {
|
||||
$recipe->save();
|
||||
|
||||
$recipe->tags()->attach($this->tags);
|
||||
|
||||
$ingredient_amounts = [];
|
||||
foreach ($this->ingredientAmounts as $ia) {
|
||||
$new_ia = $ia->replicate();
|
||||
$new_ia->parent_id = $recipe->id;
|
||||
$new_ia->parent_type = Recipe::class;
|
||||
$ingredient_amounts[] = $new_ia;
|
||||
}
|
||||
$recipe->ingredientAmounts()->saveMany($ingredient_amounts);
|
||||
|
||||
$steps = [];
|
||||
foreach ($this->steps as $step) {
|
||||
$new_step = $step->replicate();
|
||||
$new_step->recipe_id = $recipe->id;
|
||||
$steps[] = $new_step;
|
||||
}
|
||||
$recipe->steps()->saveMany($steps);
|
||||
|
||||
$separators = [];
|
||||
foreach ($this->separators as $separator) {
|
||||
$new_separator = $separator->replicate();
|
||||
$new_separator->recipe_id = $recipe->id;
|
||||
$separators[] = $new_separator;
|
||||
}
|
||||
$recipe->separators()->saveMany($separators);
|
||||
|
||||
$recipe->push();
|
||||
});
|
||||
} catch (\Throwable $e) {
|
||||
DB::rollBack();
|
||||
throw $e;
|
||||
}
|
||||
|
||||
return $recipe;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
<x-app-layout>
|
||||
<x-slot name="title">Duplicate {{ $recipe->name }}</x-slot>
|
||||
<x-slot name="header">
|
||||
<h1 class="font-semibold text-xl text-gray-800 leading-tight">
|
||||
Duplicate {{ $recipe->name }}?
|
||||
</h1>
|
||||
</x-slot>
|
||||
<form method="POST" action="{{ route('recipes.duplicate', $recipe) }}">
|
||||
@csrf
|
||||
<div class="flex flex-col space-y-4 md:flex-row md:space-x-4 md:space-y-0">
|
||||
<!-- Name -->
|
||||
<div class="flex-auto">
|
||||
<x-inputs.label for="name" value="New recipe name" />
|
||||
|
||||
<x-inputs.input name="name"
|
||||
type="text"
|
||||
class="block mt-1 w-full"
|
||||
:value="old('name', 'Copy of ' . $recipe->name)"
|
||||
required />
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center justify-start mt-4">
|
||||
<x-inputs.button class="bg-red-800 hover:bg-red-700">
|
||||
Duplicate
|
||||
</x-inputs.button>
|
||||
<a class="ml-3 text-gray-500 hover:text-gray-700" href="{{ route('recipes.show', $recipe) }}">
|
||||
Cancel</a>
|
||||
</div>
|
||||
</form>
|
||||
</x-app-layout>
|
|
@ -167,6 +167,9 @@
|
|||
<x-button-link.gray href="{{ route('recipes.edit', $recipe) }}">
|
||||
Edit Recipe
|
||||
</x-button-link.gray>
|
||||
<x-button-link.gray href="{{ route('recipes.duplicate.confirm', $recipe) }}">
|
||||
Duplicate Recipe
|
||||
</x-button-link.gray>
|
||||
<x-button-link.red href="{{ route('recipes.delete', $recipe) }}">
|
||||
Delete Recipe
|
||||
</x-button-link.red>
|
||||
|
|
|
@ -49,6 +49,9 @@ Route::middleware(['auth'])->group(function () {
|
|||
// Recipes.
|
||||
Route::resource('recipes', RecipeController::class);
|
||||
Route::get('/recipes/{recipe}/delete', [RecipeController::class, 'delete'])->name('recipes.delete');
|
||||
Route::get('/recipes/{recipe}/duplicate', [RecipeController::class, 'duplicateConfirm'])->name('recipes.duplicate.confirm');
|
||||
Route::post('/recipes/{recipe}/duplicate', [RecipeController::class, 'duplicate'])->name('recipes.duplicate');
|
||||
|
||||
|
||||
// Users.
|
||||
Route::get('/profile/{user}', [ProfileController::class, 'show'])->name('profiles.show');
|
||||
|
|
Loading…
Reference in New Issue