IT rekvalifikace s garancí práce. Seniorní programátoři vydělávají až 160 000 Kč/měsíc a rekvalifikace je prvním krokem. Zjisti, jak na to!
Hledáme nové posily do ITnetwork týmu. Podívej se na volné pozice a přidej se do nejagilnější firmy na trhu - Více informací.

Lekce 9 - Jednoduchý redakční systém v Laravel - Správa článků

V minulé lekci, Jednoduchý redakční systém v Laravel - Tvorba článků, jsme začali s tvorbou administrace pro jednoduchý redakční systém v Laravel frameworku. Vytvořili jsme si seznam článků a editor pro jejich tvorbu.

V dnešním tutoriálu se podíváme na správu článků a povíme si něco o třídách HTTP požadavků.

HTTP požadavky

Předtím, než se vrhneme na vytváření nových částí aplikace, se zpětně podíváme na naší metodu store() v kontroleru ArticleController.php, kterou jsme definovali v minulé lekci. Jak už název napovídá, tato metoda by měla sloužit pro vkládání nového záznamu (v našem případě článku) do databáze. Mimo jiné ale obsahuje také validaci přijatých dat, což v případě větších formulářů nemusí být zrovna praktické. Proto si představíme tzv. Request třídy.

Request třídy

Request třídy kontrolují, zdali je uživatel oprávněný pro odeslání daného požadavku, a validují odeslaná data. Můžeme tak přesunout sadu pravidel daného HTTP požadavku právě do nich a metoda kontroleru bude poté obsahovat pouze logiku akce. Pojďme si nyní takovou třídu vytvořit.

Pro akci store() si vygenerujeme třídu StoreRequest pomocí Artisan příkazu make:request, který nepotřebuje žádné speciální možnosti:

php artisan make:request Article/StoreRequest

Jelikož tříd požadavků můžeme mít pro jeden kontroler více, je dobrým zvykem vytvářet pro každý kontroler vlastní složku.

Vytvořila se nám automaticky nová složka app/Http/Requests/ následně s námi definovanou podsložkou Article/. V ní najdeme vygenerovaný soubor StoreRequest.php, jehož obsah je následující:

<?php

namespace App\Http\Requests\Article;

use Illuminate\Foundation\Http\FormRequest;

class StoreRequest extends FormRequest
{
    /**
     * Determine if the user is authorized to make this request.
     *
     * @return bool
     */
    public function authorize()
    {
        return false;
    }

    /**
     * Get the validation rules that apply to the request.
     *
     * @return array
     */
    public function rules()
    {
        return [
            //
        ];
    }
}

Nově vygenerovaná třída StoreRequest dědící třídu frameworku FormRequest obsahuje pouze tyto dvě metody:

  • authorize() - Určuje, zdali je uživatel oprávněný pro odeslání požadavku. Jedná se o jedno z míst, kam můžeme toto ověření umístit. Dalším z nich může být například middleware, to si však ukážeme až v dalších lekcích.
  • rules() - Vrací pole s nastavenými validačními pravidly.

Jelikož se oprávněními (a uživateli) zatím nezabýváme, můžeme metodu authorize() kompletně odstranit. Pokud totiž není definovaná, automaticky se toto ověření považuje za úspěšné. Co už nás ale zajímá více, je právě metoda rules(), kterou naplníme pravidly z metody store() našeho kontroleru. Bude tedy vypadat následovně:

/**
 * Vrať validační pravidla pro formulář, který má na starosti tvorbu článků.
 *
 * @return array
 */
public function rules(): array
{
    return [
        'title' => ['required', 'min:3', 'max:80'],
        'url' => ['required', 'min:3', 'max:80', 'unique:articles,url'],
        'description' => ['required', 'min:25', 'max:255'],
        'content' => ['required', 'min:50'],
    ];
}

Následně můžeme validaci z metody store() kompletně odstranit. Upravíme však typ objektu proměnné $request na naší Request třídu místo té obecné, aby se následně aplikovala i definovaná pravidla:

/**
 * Zvaliduj odeslaná data přes formulář a vytvoř nový článek.
 *
 * @param  StoreRequest $request
 * @return RedirectResponse
 */
public function store(StoreRequest $request): RedirectResponse
{
    Article::create($request->all());

    return redirect()->route('article.index');
}

Nezapomeneme samozřejmě importovat naší novou třídu:

use App\Http\Requests\Article\StoreRequest;

Z metody, která na začátku měla 26 řádků včetně dokumentace, jsme následně vytvořili metodu s pouhými 12 řádky a to jsme zachovali naprosto stejnou funkčnost! Jen si teď zkuste vytvořit nějaký článek a ignorovat některé z validačních pravidel. Třída HTTP požadavku vás nepustí dále :)

Editace článků

Pojďme se nyní přesunout k editaci článku.

Akce edit() a update()

Pohled s formulářem pro jeho úpravu budeme vracet v metodě edit():

/**
 * Zobraz formulář pro editaci článku a předej danému pohledu načtený článek.
 *
 * @param  Article $article
 * @return View
 */
public function edit(Article $article): View
{
    return view('article.edit', ['article' => $article]);
}

Samotná úprava záznamu bude probíhat v metodě update():

/**
 * Zvaliduj odeslaná data přes formulář a uprav načtený článek.
 *
 * @param  UpdateRequest $request
 * @param  Article $article
 * @return RedirectResponse
 */
public function update(UpdateRequest $request, Article $article)
{
    $article->update($request->all());

    return redirect()->route('article.index');
}

Stejně jako u akce store(), i zde předáme Eloquent metodě pouze pole dat z formuláře, v tomto případě metodě update(), a následně přesměrujeme uživatele do administrace článků.

UpdateRequest

Pro validaci dat opět používáme vlastní Request třídu s názvem UpdateRequest, kterou si nyní vygenerujeme ve složce app/Http/Requests/Article/ pomocí již zmíněného Artisan příkazu:

php artisan make:request Article/UpdateRequest

Tuto vygenerovanou třídu si ihned upravíme. Odstraníme metodu authorize() a definujeme si validační pravidla v metodě rules():

<?php

namespace App\Http\Requests\Article;

use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;

class UpdateRequest extends FormRequest
{
    /**
     * Vrať validační pravidla pro formulář, který má na starosti editaci článků.
     *
     * @return array
     */
    public function rules(): array
    {
        return [
            'title' => ['required', 'min:3', 'max:80'],
            'url' => [
                'required',
                'min:3',
                'max:80',
                Rule::unique('articles', 'url')->ignore($this->route('article')->id),
            ],
            'description' => ['required', 'min:25', 'max:255'],
            'content' => ['required', 'min:50'],
        ];
    }
}

Pro editaci článku se validační pravidla stala trochu složitějšími. Pro validační pravidlo unique musíme definovat výjimku aktuálního záznamu. Kdybychom ji totiž nedefinovali a chtěli pozměnit článek bez modifikace jeho URL, validace by nám následně vyhodila chybu, že v databázi již existuje článek s danou URL adresou, i když se jedná o právě editovaný článek.

Tuto výjimku nastavujeme skrz metodu ignore() builderu Unique, která přijímá ID záznamu. Také si povšimněte další výhody parametrů fungujících přes dependency injection (tzv. route model binding). Instanci modelu článku můžeme lehce získat pomocí metody route() třídy frameworku FormRequest, kdy předáme název parametru definovaného v routovacím souboru a nemusíme tento záznam složitě vybírat z databáze pouze přes předaný identifikátor (v našem případě URL článku).

Nakonec přidáme do našeho kontroleru ArticleController import této nové třídy:

use App\Http\Requests\Article\UpdateRequest;

Pohled

Nyní si vytvoříme pohled s názvem edit.blade.php ve složce resources/views/article/, který je téměř totožný s pohledem create.blade.php:

@extends('base')

@section('title', 'Editace článku ' . $article->title)
@section('description', 'Editor pro editaci článků.')

@section('content')
    <h1>Editace článku {{ $article->title }}</h1>

    <form action="{{ route('article.update', ['article' => $article]) }}" method="POST">
        @csrf
        @method('PUT')

        <div class="form-group">
            <label for="title">Nadpis</label>
            <input type="text" name="title" id="title" class="form-control" value="{{ old('title') ?: $article->title }}" required minlength="3" maxlength="80" />
        </div>

        <div class="form-group">
            <label for="url">URL</label>
            <input type="text" name="url" id="url" class="form-control" value="{{ old('url') ?: $article->url }}" required minlength="3" maxlength="80" />
        </div>

        <div class="form-group">
            <label for="description">Popisek článku</label>
            <textarea name="description" id="description" rows="4" class="form-control" required minlength="25" maxlength="255">{{ old('description') ?: $article->description }}</textarea>
        </div>

        <div class="form-group">
            <label for="content">Obsah článku</label>
            <textarea name="content" id="content" class="form-control" rows="8">{{ old('content') ?: $article->content }}</textarea>
        </div>

        <button type="submit" class="btn btn-primary">Uložit článek</button>
    </form>
@endsection

@push('scripts')
    <script type="text/javascript" src="{{ asset('//cdn.tinymce.com/4/tinymce.min.js') }}"></script>
    <script type="text/javascript">
        tinymce.init({
            selector: '#content',
            plugins: [
                'advlist autolink lists link image charmap print preview anchor',
                'searchreplace visualblocks code fullscreen',
                'insertdatetime media table contextmenu paste'
            ],
            toolbar: 'insertfile undo redo | styleselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | link image',
            entities: '160,nbsp',
            entity_encoding: 'raw',
        });
    </script>
@endpush

Jelikož HTTP akce store() je typu PUT, musíme ve formuláři opět použít Blade direktivu @method.

Pokud nyní vyzkoušíme upravit některý z existujících článků, vše proběhne naprosto v pořádku. Za pozornost ještě stojí datum poslední změny v seznamu článků, jenž se automaticky aktualizuje při jakékoliv změně daného záznamu. Jen si to sami vyzkoušejte :)

Odstraňování článků

Poslední část administrace, která nám chybí, je odstraňování článků. Jelikož formulář pro tuto akci už máme vytvořený v seznamu článků a věnovali jsme se mu minulou lekci, stačí nám teď pouze upravit akci destroy() v našem kontroleru:

/**
 * Odstraň článek z databáze.
 *
 * @param  Article $article
 * @return RedirectResponse
 */
public function destroy(Article $article): RedirectResponse
{
    try {
        $article->delete();
    } catch (\Exception $exception) {
        return redirect()->back()->withErrors(['Při procesu odstranění článku došlo k chybě.']);
    }

    return redirect()->route('article.index');
}

K odstranění záznamu používáme Eloquent metodu delete(), ve které však může dojít k výjimce. Tu musíme ošetřit, aby se uživateli nezobrazila stránka s chybou 500. Přesměrujeme ho tedy zpátky (na to používáme metodu back() builder funkce redirect()) a dáme mu vědět, že proces odstranění se nepodařil.

Tímto jsme dokončili vzhlednou a plně fungující administraci. Pokud se vám něco nepodařilo, můžete si stáhnout projekt z přiloženého archivu níže. V opačném případě výsledek našeho snažení můžeme shrnout tímto obrázkem:

Seznam článků v administraci v Laravel redakčním systému - Laravel framework pro PHP

V následujícím cvičení, Řešené úlohy k 5.-9. lekci frameworku Laravel pro PHP, si procvičíme nabyté zkušenosti z předchozích lekcí.


 

Měl jsi s čímkoli problém? Stáhni si vzorovou aplikaci níže a porovnej ji se svým projektem, chybu tak snadno najdeš.

Stáhnout

Stažením následujícího souboru souhlasíš s licenčními podmínkami

Staženo 169x (46.79 MB)
Aplikace je včetně zdrojových kódů v jazyce PHP

 

Předchozí článek
Jednoduchý redakční systém v Laravel - Tvorba článků
Všechny články v sekci
Laravel framework pro PHP
Přeskočit článek
(nedoporučujeme)
Řešené úlohy k 5.-9. lekci frameworku Laravel pro PHP
Článek pro vás napsal Jan Lupčík
Avatar
Uživatelské hodnocení:
20 hlasů
Autor se primárně věnuje vývoji webových stránek a aplikací v PHP (framework Laravel) a je jedním z herních vývojářů komunitní modifikace TruckersMP.
Aktivity