Vlastní blog v Laravel: Editace článku

Aktualizováno pro Laravel 10

V tomto díle se zaměříme na editaci stávajícího článku. Jako první si upravíme PostController a vytvoříme si šablonu s formulářem pro editaci článku.

Formulář

V Http/Controllers/PostController.php si upravíme metodu edit():

public function edit(Post $post)  
{  
	$users = User::all();  
	$categories = Category::all();  

	return view('posts.edit', compact('post', 'users', 'categories'));
}

kde vracíme šablonu společně s daty o článku, který chceme upravit. Všimněte si, že jsem jako argument použil rovnou model Post. Laravel umožňuje tzv. explicit binding, tedy definuje jak se má model vyhledat. V základním nastavení Laravel automaticky vyhledá model na základě jeho primárního klíče. Takže když například máte trasu Route::get('posts/{post}', ..., Laravel se pokusí najít post (článek) s daným ID. Samozřejmě to nemusí být jen ID, ale může tam být cokoliv (v případě článků to bude nejčastěji podle jeho slug), nicméně to je potřeba definovat v modelu, o tom ale třeba někdy příště. Výhodou tohoto přístupu je, že máte automaticky načtený celý model a není tak potřeba znovu vytvářet sql dotaz pro vytažení dat. Nevýhodou pak je, že se vám automaticky vyberou i všechny sloupce v daném modelu, což občas nemusí být žádoucí (například při velkém počtu sloupců).

Dále si zde vytáhneme z databáze všechny uživatele a kategorie, protože ty pak budeme dávat na výběr při editaci článku.

Dalším krokem je vytvoření nové šablony s formulářem resources/views/posts/edit.blade.php:

@extends('layouts.app')

@section('title', 'Editace článku')

@section('content')
	<div class="container mx-auto px-4">
		<h1 class="text-2xl font-bold mb-4">Editovat článek</h1>

		<form action="{{ route('posts.update', $post->id) }}" method="POST">
			@csrf
			@method('PUT')

			<div class="mb-4">
				<label for="author" class="block text-sm font-bold mb-2">Autor:</label>
				<select name="author_id" id="author" class="border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline">
					@foreach($users as $user)
						<option value="{{ $user->id }}" {{ $post->user_id === $user->id ? 'selected' : '' }}>{{ $user->name }}</option>
					@endforeach
				</select>
				@error('author_id')
				<span class="text-red-500 text-xs italic">{{ $message }}</span>
				@enderror
			</div>

			<div class="mb-4">
				<label for="category" class="block text-sm font-bold mb-2">Kategorie:</label>
				<select name="category_id" id="category" class="border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline">
					@foreach($categories as $category)
						<option value="{{ $category->id }}" {{ $post->category_id === $category->id ? 'selected' : '' }}>{{ $category->name }}</option>
					@endforeach
				</select>
				@error('category_id')
				<span class="text-red-500 text-xs italic">{{ $message }}</span>
				@enderror
			</div>

			<div class="mb-4">
				<label for="title" class="block text-sm font-bold mb-2">Název článku:</label>
				<input type="text" name="title" id="title" value="{{ $post->title }}" class="border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline">
				@error('title')
				<span class="text-red-500 text-xs italic">{{ $message }}</span>
				@enderror
			</div>

			<div class="mb-4">
				<label for="content" class="block text-sm font-bold mb-2">Obsah článku:</label>
				<textarea name="content" id="content" rows="10" class="border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline">{{ $post->content }}</textarea>
				@error('content')
				<span class="text-red-500 text-xs italic">{{ $message }}</span>
				@enderror
			</div>

			<button type="submit" class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline">
				Upravit článek
			</button>
		</form>
	</div>
@endsection

Princip formuláře je podobný jako v případě vytvoření nového článku. Hlavní rozdíl je v hodnotách, které jsou předvyplněny z existujícího článku ({{ $post->title }}, {{ $post->content }}) a také v přidání direktivy @method('PUT'), která specifikuje HTTP metodu, kterou formulář použije k odeslání dat. Zatímco vytvoření nového článku používá metodu POST, editace existujícího článku využívá metodu PUT. Také zde vypisujeme uživatele a kategorie a pokud se nám shoduji jejich ID, pak je vybereme jako defaultní.

Aktualizace v databázi

V Http/Controllers/PostController.php si upravíme metodu update():

public function update(Request $request, string $id)
{
    $post = Post::findOrFail($id);

    $request->validate([
        'title' => 'required|max:255',
        'content' => 'required',
        'author_id' => 'required|exists:users,id',  
		'category_id' => 'required|exists:categories,id',
    ]);

    $post->title = $request->input('title');
    $post->slug = Str::slug($request->input('title'));
    $post->content = $request->input('content');
    $post->user_id = $request->input('author_id');
    $post->category_id = $request->input('category_id');
    $post->save();

    return redirect()->route('posts.index')->with('success', 'Článek byl úspěšně upraven.');
}

Tato metoda funguje podobně jako metoda pro vytvoření článku. Nejprve se provede validace dat, poté se příslušným atributům modelu Post přiřadí nové hodnoty a následně se provede uložení do databáze. Změny jsou tentokrát ale ukládány do existujícího záznamu, nikoliv do nového.

I zde jsem mohl v argumentech metody použít explicitní binding (jako v případě metody edit()), nicméně jsem chtěl ukázat i jiný způsob.

Odkaz v seznamu článků

Na závěr si ještě upravíme tabulku se seznamem všech článku resources/views/posts/index.blade.php kde si přidáme tlačítko na editaci daného článku:

...
<tbody class="bg-white">
	@foreach ($posts as $post)
		<tr>
			<td class="border-b border-slate-100 dark:border-slate-700 p-4 pl-8 text-slate-500 dark:text-slate-400">{{ $post->title }}</td>
			<td class="border-b border-slate-100 dark:border-slate-700 p-4 pl-8 text-slate-500 dark:text-slate-400">{{ $post->created_at }}</td>
			<td class="border-b border-slate-100 dark:border-slate-700 p-4 pl-8 text-slate-500 dark:text-slate-400">{{ $post->updated_at }}</td>
			<td class="border-b border-slate-100 dark:border-slate-700 p-4 pl-8 text-slate-500 dark:text-slate-400">
				<a href="{{ route('posts.edit', $post->id) }}" class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">Editovat</a>
			</td>
		</tr>
	@endforeach
</tbody>
...

Všimněte si, že odkaz na editaci článku v navigaci potřebuje znát id článku, který chceme editovat. Proto je v něm použita funkce route s dvěma argumenty: název trasy a id článku.

Není vám něco jasné, nefunguje nebo mám někde chybu? Napište do komentářů!

Zdrojové kódy pro tento díl naleznete na GitHubu

Seriál

  1. 1. Úvod a instalace
  2. 2. Příprava databáze
  3. 3. Layout a zobrazení článků
  4. 4. Vytvoření článku
  5. 5. Editace článku
  6. 6. Smazání článku
  7. 7. Zobrazení článku