Add recipe duplicate functionality

This commit is contained in:
Christopher C. Wells 2022-03-06 20:14:36 -08:00 committed by Christopher Charbonneau Wells
parent 6e3c40531d
commit 4aaf83c862
5 changed files with 113 additions and 0 deletions

View File

@ -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.
*/

View File

@ -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;
}
}

View File

@ -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>

View File

@ -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>

View File

@ -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');