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 App\Support\Nutrients;
|
||||||
use Illuminate\Contracts\View\View;
|
use Illuminate\Contracts\View\View;
|
||||||
use Illuminate\Http\RedirectResponse;
|
use Illuminate\Http\RedirectResponse;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
|
|
@ -350,6 +351,30 @@ class RecipeController extends Controller
|
||||||
$recipe->ingredientSeparators()->saveMany($ingredient_separators);
|
$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.
|
* Confirm removal of specified resource.
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
use Spatie\Image\Manipulations;
|
use Spatie\Image\Manipulations;
|
||||||
use Spatie\MediaLibrary\HasMedia;
|
use Spatie\MediaLibrary\HasMedia;
|
||||||
use Spatie\MediaLibrary\InteractsWithMedia;
|
use Spatie\MediaLibrary\InteractsWithMedia;
|
||||||
|
|
@ -262,4 +263,55 @@ final class Recipe extends Model implements HasMedia
|
||||||
->optimize();
|
->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) }}">
|
<x-button-link.gray href="{{ route('recipes.edit', $recipe) }}">
|
||||||
Edit Recipe
|
Edit Recipe
|
||||||
</x-button-link.gray>
|
</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) }}">
|
<x-button-link.red href="{{ route('recipes.delete', $recipe) }}">
|
||||||
Delete Recipe
|
Delete Recipe
|
||||||
</x-button-link.red>
|
</x-button-link.red>
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,9 @@ Route::middleware(['auth'])->group(function () {
|
||||||
// Recipes.
|
// Recipes.
|
||||||
Route::resource('recipes', RecipeController::class);
|
Route::resource('recipes', RecipeController::class);
|
||||||
Route::get('/recipes/{recipe}/delete', [RecipeController::class, 'delete'])->name('recipes.delete');
|
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.
|
// Users.
|
||||||
Route::get('/profile/{user}', [ProfileController::class, 'show'])->name('profiles.show');
|
Route::get('/profile/{user}', [ProfileController::class, 'show'])->name('profiles.show');
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue