Controllers

Een controller is voor Symfony wat een verkeerstoren voor Schiphol is: het zorgt ervoor dat alles in goede banen wordt geleid. Via de controller koppel je een route aan een webpagina.

Een controller maken

Symfony doet het werk voor je, typ in:

php bin/console make:controller
En geef je controller een naam. Je moet altijd een beginpagina hebben dus je kan de pagina bijvoorbeeld start noemen.

Wat is er nu precies gebeurt?

In src/Controller heeft Symfony een php-bestand aangemaakt. Als je start hebt opgegeven als naam bij het maken van de controller zal dit bestand StartController.php heten.

In Templates heeft Symfony een directory start aangemaakt en daarin een Twig bestand met de naam index.html.twig. Wat je met Twig bestanden kunt doen kun je verderop in deze Symfony handleiding lezen. Kort samengevat zorgt Twig-bestanden ervoor dat er iets op het scherm wordt getoond. Dat doe je dus niet via je controller.

mijnwebsite
  • src
    • Controller
      • StartController.php
  • templates
    • start
      • index.html.twig

Je controller pimpen

Je krijgt van Symfony (heel aardig) een standaardcontroller, maar daar moet je nog wat aan sleutelen. Bekijk hieronder de @Route en het return gedeelte maar eens, Symfony heeft automatisch de route mijnwebsite/start gekoppeld aan het Twig bestand start/index.html.twig.

namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Routing\Annotation\Route;

class StartController extends AbstractController
{
    /**
     * @Route("/start", name="start")
     */
    public function index()
    {
        return $this->render('start/index.html.twig', [
            'controller_name' => 'StartController',
        ]);
    }
}

Veranderen van de @Route

Ik wil start eraf want dit is het begin van mijn website. Ik wil immers dat bezoekers op de beginpagina komen als ze in de browser www.mijnwebsite.nl intypen en dus niet www.mijnwebsite.nl/start. Verander daarvoor de @Route.

Controleer de routes met php bin/console debug:router in je terminal.

@Route("/start", name="start")
@Route("/", name="start")

Het is ook mogelijk om de Route te wijzigen voor je hele controller. Als je start met Symfony zal je dit nog niet nodig hebben maar je zal het heus wel eens tegenkomen. Kijk eens even naar bovenstaande Controller, als iemand dus www.mijnwebsite.nl/start intypt zal de controller dus in actie komen. Maar het is ook mogelijk om een Route te maken voor de hele controller.

namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Routing\Annotation\Route;

/**
* @Route("/pietje",name="pietje")
*/

class StartController extends AbstractController
{
    /**
     * @Route("/start", name="start")
     */
    public function index()
    {
        return $this->render('start/index.html.twig', [
            'controller_name' => 'StartController',
        ]);
    }
}

En nu zal de Controller pas in actie komen als je www.mijnwebsite.nl/pietje/start intypt.

Doorgeven van variabelen naar Twig-bestand

In de controller worden via 'controller_name' => 'StartController' variabelen doorgegeven naar het Twig-bestand. Deze worden daar getoond met de code <h1>Hello {{ controller_name }}! </h1>. Bestudeer daarvoor even het Twig-bestand start/index.html.twig.

Een Request Object toevoegen

Bij het bouwen van websites zal het natuurlijk vaker voorkomen dat je variabelen via een GET of een POST gaat doorgeven, en niet variabelen die beschreven staan in je controller. Hoe doe je dat: via een Request Object. Met zo'n object kan je informatie over de aanroep krijgen en daarop je controller aanpassen.

Hieronder een voorbeeld van een controller met een simpel request-object.

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\HttpFoundation\Request;

public function index(Request $request)
{
    //Hier wordt alle informatie informatie over de aanroep in $data gezet.
    $data = $request->query->all();
    //Hier vragen we zaken op uit het object $data
    $kleur = $data["kleur"];
    $maat = $data["maat"];
    //En voeg hier eens deze code toe, dan kan je het hele Request Object bestuderen op je scherm
    dump($data);die();
    //En hier geven we de gegevens door aan het Twig bestand
    return $this->render('start/index.html.twig', [
        $kleur,
        $maat,
    ]);
}

En nu heb je twee variabelen doorgegeven aan het Twig-bestand die je via de opdracht www.mijnwebsite.nl?kleur=rood&maat=42 hebt doorgegeven.

Werken met objecten in je controller

De hele bedoeling van Symfony is dat je met objecten werkt, en niet met variabelen. Het hele object geef je dan door aan Twig en die zoekt het verder dan maar uit. Een goed voorbeeld van een controller die met een object werkt:

public function index(Request $request)
    {
        $persoon = new Persoon();
        $persoon->setNaam("Helemaal niks");
        $persoon->setEmail('niks@niks.nl');

        return $this->render('templates/pagina/index.html.twig', [
            'persoon'=> $persoon,
        ]);
    }

Werken met gerelateerde objecten

Via Doctrine kun je databases en tabellen beheren. Nu zal je daar ongetwijfeld gerelateerde tabellen tegenkomen. Hoe werkt dit in de controller? Laten we eens een simpel systeem nemen als voorbeeld, waar één sigaar meerdere recensies kan hebben (sorry als je anti-rook bent). Je hebt via Doctrine de tabellen gemaakt en aan elkaar gekoppeld.

Sigaren ______||~~
  • Sigaar
    • Recensies

Werken met gerelateerde objecten: toevoegen

Een simpele controller om zowel een sigaar als een recensie toe te voegen.

/**
     * @Route("/toevoegen",name="toevoegen")
     */
        //Maak recensie aan
        $recensie = new Recensie();
        $recensie->setRecensie("Recensie over stinkstok Ashton Cabinet.");

        //Maak sigaar aan
        $sigaar = new Sigaar();
        $sigaar->setSigarenNaam("Cabinet");
        $sigaar->setSigarenMerk("Ashton");
        $sigaar->setSigarenCijfer(10);

        //Koppeling maken tussen objecten
        $recensie->setSigaar($sigaar);

        //Wegschrijven
        $entityManager = $this->getDoctrine()->getManager();
        $entityManager->persist($sigaar);
        $entityManager->persist($recensie);
        $entityManager->flush();

        //Toon een bericht op het scherm
        $this->addFlash('notice', 'Sigaar met nr:' . $sigaar->getId() . ' toegevoegd!');

        //En terug naar een overzichtspagina
        return $this->redirectToRoute('sigaar_tonen');
     }

En hier een controller die alleen een recensie toevoegt voor een bepaalde sigaar. De id is afkomstig van een aanroep vanuit een lijst die getoond wordt via een Twig bestand.

    /**
     * @Route("/recensie_toevoegen/{id}",name="recensie_toevoegen")
     */
    public function recensie_toevoegen(Sigaar $sigaar, Request $request) {
        //Eerst de sigaar vinden waar het om gaat
        $em     = $this->getDoctrine()->getManager();
        $sigaar = $em->getRepository(Sigaar::class)->find($id);
        
        //En nu een nieuwe recensie aanmaken en invullen via het formulier
        $recensie = new Recensie();
        $form = $this ->createForm(RecensieFormType::class,$recensie);

        $form->handleRequest($request);
        //Iets niet goed gedaan, dan blijft het formulier voor je neus
        if (!$form->isSubmitted() or !$form->isValid()) {
            return $this->render('recensie/form.html.twig', [
                'form' => $form->createView(),
                'actie' => 'Recensie toevoegen'
            ]);
        //Anders alles okay: plaats gegevens in de tabel
        } else {
            //Eerst even een koppeling aanbrengen
            $recensie->setSigaar($sigaar);
            
            $em = $this->getDoctrine()->getManager();
            $em->persist($recensie);
            $em->flush();
            //Message in a bottle
            $this->addFlash(
                'notice',
                'Recensie toegevoegd!'
        );

    }

Werken met gerelateerde objecten: gegevens tonen

Voorbeeld van een controller die de gegevens van een bepaalde sigaar toont met alle recensies

/**
     * @Route ("/toon_sigaar/{id}",name="toon_sigaar")
     *
     */
    public function show_sigaar($id)
    {
        $em     = $this->getDoctrine()->getManager();
        $sigaar = $em->getRepository(Sigaar::class)->find($id);

        $this->getDoctrine()->getManager()->initializeObject($sigaar->getRecensies());

        return $this->render('sigaar/show_sigaar.html.twig', [
            'sigaar' => $sigaar,
        ]);
    }

En een voorbeeld van een controller die alleen de recensies toont van een bepaalde sigaar

    /**
     * @Route("/recensies_tonen/{id}",name="recensies_tonen")
     */
    public function recensies_tonen($id) {
        //Eerst de sigaar vinden waar het om gaat
        $em     = $this->getDoctrine()->getManager();
        $sigaar = $em->getRepository(Sigaar::class)->find($id);

        //Vind aan de hand van id alle recensies
        $this->getDoctrine()->getManager()->initializeObject($sigaar->getRecensies());
        return $this->render('recensie/recensies_tonen.html.twig',
            ['sigaar' => $sigaar]);
    }

Werken met gerelateerde objecten: gegevens verwijderen

Een controller die een sigaar verwijderd en alle bijbehorende recensies

/**
     * @Route("/sigaar_verwijderen/{id}",name="sigaar_verwijderen")
     */
    public function sigaar_verwijderen($id)
    {
        // Haal EntityManager op
        $em     = $this->getDoctrine()->getManager();
        //De sigaar
        $sigaar = $em->getRepository(Sigaar::class)->find($id);

        foreach ($sigaar->getRecensies() as $recensie) {
            $em->remove($recensie);
        }

        $em->remove($sigaar);
        $em->flush();

        // melding hier
        $this->addFlash(
            'notice',
            'Sigaar verwijderd!'
        );
        return $this->redirectToRoute('sigaar_tonen');
    }

Doctrine Query Language

Als je een beetje bekend bent met SQL dan weet je dat SQL-queries vaak flink ingewikkeld kunnen worden. Als je Symfony gebruikt met Doctrine kan je gaan werken met je Entities en niet met tabellen en kolommen.

Het verdient aanbeveling om complexere queries in je Repository te zetten