Blok 2 · rozdział 4
Kontrolery
Funkcja wpisana od razu w routes/web.php wystarczy na jedną stronę.
Przy karcie szczytu i formularzu kod przenosisz do klasy kontrolera.
W pliku tras zostaje tylko wskazanie metody.
Po tym rozdziale powinieneś umieć
- utworzyć kontroler poleceniem Artisan;
- przenieść listę do metody
index, a kartę doshow; - zwrócić 404, gdy slugu nie ma w tablicy;
- zostawić w pliku tras same wskazania na kontroler i powiedzieć, czemu adres z
{slug}nie może stać przed stałym adresem.
1. Wygeneruj kontroler
W rozdziale 3 lista i strona główna siedzą w funkcjach w routes/web.php.
To wystarcza, dopóki akcja ma kilka linii. Karta szczytu dokłada pętlę i decyzję „jest czy 404”.
Formularz z rozdziału 6 dokłada walidację. Taki plik tras przestaje być spisem adresów i staje się drugim programem.
W katalogu projektu, w drugim terminalu (pierwsze okno nadal trzyma php artisan serve):
php artisan make:controller SzczytController
Powstaje app/Http/Controllers/SzczytController.php. Otwórz go. Zostaw nagłówek,
przestrzeń nazw i deklarację klasy. Metody dopiszesz w środku nawiasów klamrowych klasy.
Gdy Artisan wstawił use Illuminate\Http\Request;, nie kasuj tej linii — w rozdziale 6 przyda się do formularza.
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class SzczytController extends Controller
{
// tu będą index() i show()
}
Na górze routes/web.php, obok istniejącego use dla Route, dopisz:
use App\Http\Controllers\SzczytController;
SzczytController trzyma akcje szczytu: lista, karta, a w rozdziale 6 jeszcze zapis planu.
Strona główna na razie zostaje przy funkcji w pliku tras. Nie ma w niej decyzji, tylko return view('start').
2. Lista
Tablicę czterech szczytów z rozdziału 3 przenieś z pliku tras do kontrolera.
Do każdego wiersza dopisz klucz slug. To kawałek adresu karty: małe litery, bez polskich znaków, spacja zamieniona na myślnik.
Babia Góra ma slug babia-gora, Śnieżka — sniezka.
W klasie, zamiast komentarza, wklej dwie metody. index jest publiczna, bo woła ją trasa. listaSzczytow jest prywatna, bo woła ją tylko ta klasa.
public function index()
{
$szczyty = $this->listaSzczytow();
return view('szczyty.index', [
'szczyty' => $szczyty,
]);
}
private function listaSzczytow(): array
{
return [
['slug' => 'rysy', 'nazwa' => 'Rysy', 'pasmo' => 'Tatry', 'wysokosc' => 2499],
['slug' => 'babia-gora', 'nazwa' => 'Babia Góra', 'pasmo' => 'Beskid Żywiecki', 'wysokosc' => 1725],
['slug' => 'sniezka', 'nazwa' => 'Śnieżka', 'pasmo' => 'Karkonosze', 'wysokosc' => 1603],
['slug' => 'lysica', 'nazwa' => 'Łysica', 'pasmo' => 'Góry Świętokrzyskie', 'wysokosc' => 614],
];
}
Metoda index robi trzy rzeczy:
$this->listaSzczytow()bierze tablicę z metody prywatnej.$thisto bieżący obiekt kontrolera.return view(...)wybiera plikresources/views/szczyty/index.blade.php. Kropka w nazwie widoku to katalog.- Tablica drugiego argumentu nadaje widokowi zmienną. Klucz
'szczyty'staje się w Blade nazwą$szczyty.
Ten sam efekt daje skrót compact('szczyty'). PHP buduje wtedy tablicę
['szczyty' => $szczyty] ze zmiennej o tej nazwie. W projekcie zostaw jawną tablicę, bo od razu widać, co trafia do widoku.
W routes/web.php podmień funkcję przy /szczyty na wskazanie metody. Trasę / zostaw bez zmian.
Route::get('/szczyty', [SzczytController::class, 'index'])->name('szczyty.index');
Zapis [SzczytController::class, 'index'] znaczy: klasa i nazwa metody.
Laravel tworzy obiekt kontrolera i wywołuje index.
Starej funkcji z tablicą szczytów w pliku tras już nie ma.
W widoku listy, przy nazwie szczytu, link ma prowadzić do karty. Podmień sam nagłówek wewnątrz pętli:
<a href="{{ route('szczyty.show', $szczyt['slug']) }}">
{{ $szczyt['nazwa'] }}
</a>
Drugi argument route() wchodzi w miejsce parametru {slug}.
Dla Rysów powstaje adres /szczyty/rysy. Trasa szczyty.show powstaje w następnym podrozdziale — do tego momentu klik w link pokaże błąd o braku trasy.
Metoda listaSzczytow() jest osobno, bo w rozdziale 9 ją usuniesz,
a w index zostanie zapytanie do modelu. Widok listy tego nie zauważy, jeśli nadal dostanie zmienną $szczyty.
http://127.0.0.1:8000/szczyty. Nadal widać cztery szczyty.
Gdy Laravel pisze, że nie zna klasy, sprawdź use na górze pliku tras i czy nazwa pliku to SzczytController.php.
3. Karta i 404
public function show(string $slug)
{
$znaleziony = null;
foreach ($this->listaSzczytow() as $szczyt) {
if ($szczyt['slug'] === $slug) {
$znaleziony = $szczyt;
break;
}
}
if ($znaleziony === null) {
abort(404);
}
return view('szczyty.show', [
'szczyt' => $znaleziony,
]);
}
Ta metoda dostaje z adresu jeden napis. Dla /szczyty/rysy w $slug jest rysy.
- Szuka wiersza, którego
slugjest równy temu napisowi.breakkończy pętlę przy pierwszym trafieniu — dalej nie ma czego szukać. - Gdy po pętli
$znalezionynadal jestnull, wołaabort(404). Laravel przerywa metodę i oddaje swoją stronę „nie znaleziono”. Pusta karta z pustymi polami nie powstaje. - Gdy wiersz jest, oddaje widok
szczyty.showi jedną zmienną$szczyt. W tym widoku nie ma tablicy, tylko jeden wiersz.
Utwórz resources/views/szczyty/show.blade.php:
<!DOCTYPE html>
<html lang="pl">
<head>
<meta charset="utf-8">
<title>{{ $szczyt['nazwa'] }}</title>
</head>
<body>
<p><a href="{{ route('szczyty.index') }}">Wróć do listy</a></p>
<h1>{{ $szczyt['nazwa'] }}</h1>
<p>{{ $szczyt['pasmo'] }} · {{ $szczyt['wysokosc'] }} m n.p.m.</p>
</body>
</html>
Własnego <html> pozbędziesz się w rozdziale 5, gdy karta wejdzie we wspólny układ.
Dziś strona ma działać sama.
Trasę z parametrem dopisz pod trasą listy:
Route::get('/szczyty/{slug}', [SzczytController::class, 'show'])->name('szczyty.show');
Wejdź na /szczyty/rysy, potem na /szczyty/babia-gora i na /szczyty/nie-ma-takiego.
Dwa pierwsze pokazują kartę. Trzeci pokazuje stronę 404 Laravela.
{slug} w trasie i argument string $slug w metodzie to ten sam fragment URL.
Nazwy mają się zgadzać. Gdy w trasie jest {slug}, a w metodzie $id, Laravel nie złoży tego w jeden byt.
Od rozdziału 9 zamiast ręcznej pętli zrobi to wiązanie modelu.
Route::get('/{slug}', ...) postawiony nad /szczyty połknąłby słowo szczyty jako slug i lista przestałaby się otwierać.
Para /szczyty i /szczyty/{slug} jest bezpieczna, bo mają wspólny początek, a dokładniejszy adres listy stoi wyżej.
4. Cienkie trasy
Po przeniesieniu w web.php zostają krótkie wpisy: metoda HTTP, adres, klasa, akcja, nazwa.
Pętla, abort i tablica są w kontrolerze. Plik tras czyta się wtedy jak spis adresów.
Docelowy routes/web.php wygląda tak:
<?php
use App\Http\Controllers\SzczytController;
use Illuminate\Support\Facades\Route;
Route::get('/', function () {
return view('start');
})->name('start');
Route::get('/szczyty', [SzczytController::class, 'index'])->name('szczyty.index');
Route::get('/szczyty/{slug}', [SzczytController::class, 'show'])->name('szczyty.show');
php artisan route:list --name=szczyty.
Mają być dwie pozycje, index i show, obie wskazujące SzczytController.
W kolumnie akcji nie ma już funkcji anonimowej przy tych adresach.
5. Pliki na koniec rozdziału
Jeśli po drodze coś przestało działać, porównaj kontroler z tym plikiem.
Lista pod /szczyty i karta pod /szczyty/rysy mają działać, a /szczyty/nie-ma-takiego ma kończyć się stroną 404.
app/Http/Controllers/SzczytController.php:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class SzczytController extends Controller
{
public function index()
{
$szczyty = $this->listaSzczytow();
return view('szczyty.index', [
'szczyty' => $szczyty,
]);
}
public function show(string $slug)
{
$znaleziony = null;
foreach ($this->listaSzczytow() as $szczyt) {
if ($szczyt['slug'] === $slug) {
$znaleziony = $szczyt;
break;
}
}
if ($znaleziony === null) {
abort(404);
}
return view('szczyty.show', [
'szczyt' => $znaleziony,
]);
}
private function listaSzczytow(): array
{
return [
['slug' => 'rysy', 'nazwa' => 'Rysy', 'pasmo' => 'Tatry', 'wysokosc' => 2499],
['slug' => 'babia-gora', 'nazwa' => 'Babia Góra', 'pasmo' => 'Beskid Żywiecki', 'wysokosc' => 1725],
['slug' => 'sniezka', 'nazwa' => 'Śnieżka', 'pasmo' => 'Karkonosze', 'wysokosc' => 1603],
['slug' => 'lysica', 'nazwa' => 'Łysica', 'pasmo' => 'Góry Świętokrzyskie', 'wysokosc' => 614],
];
}
}
Gdy lista i karta idą przez kontroler, w katalogu korona:
git add .
git status
git commit -m "Kontroler listy i karty"
Przed commitem git status nie pokazuje .env.