Uczymy się tworzyć kontroler w projekcie Symfony z lekcji poprzedniej. Rozwijamy umiejętności korzystania z popularnego frameworku PHP Symfony.

Ok, piszemy komendę:

symfony console make:controller

Poprosi o nazwę. Podajemy HelloController.

Utworzy coś takiego:

class HelloController extends AbstractController
{
    #[Route('/hello', name: 'app_hello')]
    public function index(): Response
    {
        return $this->render('hello/index.html.twig', [
            'controller_name' => 'HelloController',
        ]);
    }
}

Jak widać routing i kontroler są w Symfony nieodłączne, inaczej niż w Laravelu. Wiele metod jest dostępnych przez dziedziczenie (Symfony jest dużo bardziej abstrakcyjne i warstwowe niż Laravel, który i tak ma tych warstw więcej niż trochę).

Mamy tutaj render oraz „compact”, przekazanie zmiennej do kontekstu. Mamy też route i jego name. Rzućmy komendę:

symfony server:start

I przechodzimy pod adres /hello zobaczyć czy działa. Ok, teraz zobaczmy jak działa. Szukamy templates, hello, index.html.twig. To mamy:

{% extends 'base.html.twig' %}

{% block title %}Hello HelloController!{% endblock %}

{% block body %}
<style>
    .example-wrapper { margin: 1em auto; max-width: 800px; width: 95%; font: 18px/1.5 sans-serif; }
    .example-wrapper code { background: #F5F5F5; padding: 2px 6px; }
</style>

<div class="example-wrapper">
    <h1>Hello {{ controller_name }}! ✅</h1>

    This friendly message is coming from:
    <ul>
        <li>Your controller at <code>C:/xampp/htdocs/symfapp/src/Controller/HelloController.php</code></li>
        <li>Your template at <code>C:/xampp/htdocs/symfapp/templates/hello/index.html.twig</code></li>
    </ul>
</div>
{% endblock %}

W ogóle nie przypomina to blade. Jeżeli już, to przypomina języki templatkowe, które są bardzo popularne w języku Python. Filozofia jest jednak ta sama.

Rzućmy okiem na base.html.twig:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>{% block title %}Welcome!{% endblock %}</title>
        <link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 128 128%22><text y=%221.2em%22 font-size=%2296%22>⚫️</text><text y=%221.3em%22 x=%220.2em%22 font-size=%2276%22 fill=%22%23fff%22>sf</text></svg>">
        {% block stylesheets %}
        {% endblock %}

        {% block javascripts %}
            {% block importmap %}{{ importmap('app') }}{% endblock %}
        {% endblock %}
    </head>
    <body>
        {% block body %}{% endblock %}
    </body>
</html>

Jeżeli orientujemy się w Laravelu i przerobiliśmy projekt „własny framework MVC w PHP” to nie powinniśmy mieć problemów w ogarnięciu się raz dwa w tym, co się dzieje.

Jeżeli jest to nasz pierwszy framework + słabo znamy wzorce projektowe, OOP, bazy danych, popularne wzorce (MVC, Repository-Entity) oraz zagadnienia frameworkowe to raczej polecam się poduczyć, bo Symfony może być minimalnie dla nas za trudne.

Ok, wykonajmy komendę poniżej, bo jedna rzecz nam umyka:

symfony console debug:router

Nasz route będzie pod ANY. Tak, w Symfony wszystko jest pod ANY, chyba że określimy inaczej:

class HelloController extends AbstractController
{
    #[Route('/hello', name: 'app_hello', methods: ['GET', 'HEAD'])]
    public function index(): Response
    {
        return $this->render('hello/index.html.twig', [
            'controller_name' => 'HelloController',
        ]);
    }
}

W ten sposób określamy jakie metody są dostępne dla naszego route. Jeżeli solidnie przerobiliśmy Laravela, to będziemy wiedzieć, że tam routes trzeba było ustawiać w pewnej kolejności, aby jedna nie przykrywała drugiej.

Cóż, w Symfony to inaczej wygląda, routing jest w kontrolerach, a zatem tam jest takie coś, jak priorytet. Oto przykład, jak to wygląda:

class BlogController extends AbstractController
{
    /**
     * This route has a greedy pattern and is defined first.
     */
    #[Route('/blog/{slug}', name: 'blog_show')]
    public function show(string $slug): Response
    {
        // ...
    }

    /**
     * This route could not be matched without defining a higher priority than 0.
     */
    #[Route('/blog/list', name: 'blog_list', priority: 2)]
    public function list(): Response
    {
        // ...
    }
}

W Laravelu po prostu musielibyśmy pamiętać, aby Route::get(’/blog/list’, …) było nad Route::get(’/blog/{slug}’, …), tak aby „list” nie zostało potraktowane jako slug.