Předvánoční slevová akce Java týden
Využij předvánočních slev a získej od nás 20 % bodů zdarma! Více zde
Pouze tento týden sleva až 80 % na Java e-learning!

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

Unicorn College Tento obsah je dostupný zdarma v rámci projektu IT lidem.
Vydávání, hosting a aktualizace umožňují jeho sponzoři.

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í tutoriálu dokončíme administraci přidáním editace článků spolu s jejich odstraňováním 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í, zda-li 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, zda-li 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 takto:

/**
 * Vrať validační pravidla pro formulář, který má na starosti tvorbu článků.
 *
 * @return array
 */
public function rules()
{
    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 Response
 */
public function store(StoreRequest $request)
{
    Article::create($request->all());

    return redirect()->route('article.index');
}
Tento výukový obsah pomáhají rozvíjet následující firmy, které dost možná hledají právě tebe!

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

use App\Http\Requests\Article\StoreRequest;

Z metody, která na začátku měla 25 řá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 Response
 */
public function edit(Article $article)
{
    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 Response
 */
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().

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()
    {
        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 Response
 */
public function destroy(Article $article)
{
    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

V příští lekci, Jednoduchý redakční systém v Laravel - Laravel Mix, se zaměříme více na front-end část naší aplikace. Podíváme se totiž na možnosti kompilace souborů, jako jsou JavaScript, SCSS, LESS a další.


 

Stáhnout

Staženo 38x (41.81 MB)
Aplikace je včetně zdrojových kódů v jazyce php

 

 

Článek pro vás napsal Jan Lupčík
Avatar
Jak se ti líbí článek?
2 hlasů
Autor se primárně věnuje vývoji webových stránek a aplikacích v PHP (speciálně ve frameworku Laravel) a je jedním z vývojářů komunitního módu TruckersMP.
Předchozí článek
Jednoduchý redakční systém v Laravel - Tvorba článků
Všechny články v sekci
Laravel framework pro PHP
Miniatura
Následující článek
Jednoduchý redakční systém v Laravel - Laravel Mix
Aktivity (6)

 

 

Komentáře

Avatar
pavlicekr
Člen
Avatar
pavlicekr:7. června 14:11

Ahoj,
není mi úplně jasný, proč se volá metoda ::create na modelu Article, když ji neobsahuje a ani model ze kterého dědí. To samé Article::orderBy
Možná jsem to někde přehlídl..

Editováno 7. června 14:12
 
Odpovědět
7. června 14:11
Avatar
Jan Lupčík
Šéfredaktor
Avatar
Odpovídá na pavlicekr
Jan Lupčík:9. června 20:17

Ahoj,

při běhu skriptu se automaticky na pozadí volá metoda query(), která vrací mocnou třídu Builder. Ta poskytuje právě práci s Eloquent ORM a také bychom v ní našli metodu create().

Pokud se však zajímáš o více informací, musíme se dostat ke třídě Model, kterou dědí Article. Ta právě definuje magickou metodu __call(), jejíž obsah je následující:

/**
 * Handle dynamic method calls into the model.
 *
 * @param  string  $method
 * @param  array  $parameters
 * @return mixed
 */
public function __call($method, $parameters)
{
    if (in_array($method, ['increment', 'decrement'])) {
        return $this->$method(...$parameters);
    }

    return $this->forwardCallTo($this->newQuery(), $method, $parameters);
}

Pokud bychom se chtěli podívat ještě hlouběji, museli bychom si otevřít i metodu forwardCallTo() ve třídě Model, a tak bychom pokračovali dále. Nám však stačí tento kontext. Jak můžeš vidět, všechny metody, které neexistují a nejsou to metody increment a decrement (takže statické volání těchto metod, proto jsou i definované zde), jsou automaticky předané builder objektu, jenž je získán z metody newQuery(). A to už je ten zmíněný Builder.

Takže i když ti IDE hlásí, že metoda neexistuje, ona je automaticky zavolaná při jejím použití. Každopádně při serióznějších projektech se určitě vyplatí sáhnout po nějakém pluginu do IDE nebo po nějaké knihovně. To už však nepatří do tohoto seriálu, jelikož děláme jednoduchou aplikaci - nechci zatěžovat čtenáře stahováním skriptů třetích stran.

Doufám, že jsem zodpověděl tvojí otázku. A děkuji za tvojí připomínku. Popřemýšlím o tom a případně to doplním někam do seriálu. :)

Odpovědět
9. června 20:17
TruckersMP vývojář
Avatar
pavlicekr
Člen
Avatar
Odpovídá na Jan Lupčík
pavlicekr:10. června 20:08

Moc díky za odpověď, takhle mi to stačí. A díky za seriál, je super :)

 
Odpovědět
10. června 20:08
Avatar
Jan Lupčík
Šéfredaktor
Avatar
Odpovídá na pavlicekr
Jan Lupčík:11. června 9:38

Není za co, já jsem ten, co by ti měl děkovat za připomínku. Pokud se budeš chtít opět na něco zeptat, neboj se využít komentářů pod článkem. :)

Odpovědět
11. června 9:38
TruckersMP vývojář
Tento výukový obsah pomáhají rozvíjet následující firmy, které dost možná hledají právě tebe!
Avatar
Neci Neco
Člen
Avatar
Neci Neco:19. září 17:15

Ahoj, přidávámm se, opravdu skvělý seriál. Pouze poznámku k druhé žárovce, zřejmě platí: Jelikož HTTP akce "update()" je typu PUT, musíme ve formuláři opět použít Blade direktivu @method.

 
Odpovědět
19. září 17:15
Avatar
Jan Lupčík
Šéfredaktor
Avatar
Odpovídá na Neci Neco
Jan Lupčík:19. září 17:31

Ahoj, děkuji za zpětnou vazbu a taky za připomínku. Máš pravdu, během víkendu to opravím spolu s dalšími úpravami :)

Odpovědět
19. září 17:31
TruckersMP vývojář
Avatar
Martin Š.
Člen
Avatar
Martin Š.:21. září 14:41

Ahoj, seriál je super. Myslím si ale, že v této kapitole chybí při úpravě metody store() poznámka, že je třeba doplnit klauzuli use:

use App\Http\Requ­ests\Article\Sto­reRequest;

V Laravelu jsem začátečník - pro zběhlého uživatele je to asi samozřejmé, ale já jsem si to musel dohledat ve výsledných zdrojácích. Možná by se ta zmínka hodila i dalším začátečníkům. ;)

Díky za pěkný seriál!

Editováno 21. září 14:42
 
Odpovědět
21. září 14:41
Avatar
Jan Lupčík
Šéfredaktor
Avatar
Jan Lupčík:14. října 16:33

Martin Š. a pavlicekr: seriál byl aktualizovaný a obsahuje obě žádané změny (vysvětlení neexistující metody v předchozím díle a přidání importů). Děkuji vám ještě jednou za feedback k seriálu :)

Odpovědět
14. října 16:33
TruckersMP vývojář
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 8 zpráv z 8.