Symfony Param Converter

Param Converter to szybki sposób na zmapowanie lub przekonwertowanie danych na obiekt. W Symfony możemy to zrobić całkowicie jawnie budując własne mapery czy konwertery lub w locie właśnie za pomocą Param Converterów. Jak to zrobić o tym w dzisiejszym artykule. Opisze dokładniej nie tylko te wbudowane ale take pokażę Ci jak

Do czego ten Param Converter?

Na start dostajemy dwa param convertery. Pierwszy Doctrinowy, drugi to DateTime. Doctrinowy mapuje nam dane na obiekt, który wskażemy lub nie. Jeśli nie wskażemy, to będzie zgadywał spośród encji.

Najprostszy przykład to akcja szczegółów obiektów np. postów na blogu. Użyjemy do tego adnotacji @ParamConverter, który znajduje się w Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter, jeśli nie masz tego kodu to musisz pobrać przez composera paczkę sensio/framework-extra-bundle i skonfigurować go w następujący sposób:

# app/config/config.yml
sensio_framework_extra:
    request:
        converters: true
/**
* @Route("/post/{id}")
* @ParamConverter("post", class="BlogBundle:Post")
*/
public function showAction(Post $post)
{
}

Dzięki wykorzystaniu param convertera w tym przypadku, nie przyjmujemy z GETa id, który przetwarzamy aby dostać z repo obiekt postu. Dzieje się to w locie i od razu postajemy cały obiekt Post.

Z DateTime converterem jest podobnie. Pomyśl ile razy mordowałaś/mordowałeś się z przerzucaniem dat ze stringów na obiekty i z powrotem. A tutaj ktoś w końcu pomyślał i w sprytny sposób ogarnął ten temat.

/**
 * @Route("/blog/archive/{start}/{end}")
 */
public function archive(\DateTime $start, \DateTime $end)
{
}
/**
 * @Route("/blog/archive/{start}/{end}")
 * @ParamConverter("start", options={"format": "!Y-m-d"})
 * @ParamConverter("end", options={"format": "!Y-m-d"})
 */
public function archive(\DateTime $start, \DateTime $end)
{
}

Powyżej dwa przykłady, jeden bez, a drugi z określaniem formatu daty.

Coś jeszcze?

Oprócz klasycznego zastosowania jak pokazałam powyżej gdzie od razu zamiast dostawać w kontrolerze identyfikatora dostajemy konkretny obiekt param convertery mają dodatkową zaletę. Jeśli nie znajdą obiektu o danym identyfikatorze, który domyślnie jest ID zwracają 404 czyli nie musimy robić obsługi tego ręcznie w kontrolerze.

Jeśli w url nie przekazujesz id, które może być także Uuidem, nie m z tym problemu możesz zdefiniować, po którym parametrze encji param converter ma jej szukać.

/**
* @Route("/comments/{comment_hash}")
* @ParamConverter("comment", class="BlogBundle:Comment", options={"hash" = "comment_hash"})
*/
public function showAction(Comment $comment)
{
}

Parametrów, które są konwertowanych może być oczywiście więcej niż jeden.

Param convertery możemy wykorzystać także w API. Wtedy również mapujemy dane na obiekty ale jako dane możemy dostać obiekt json lub cały xml, który również może zostać zmapowany na obiekty DTO lub inne, które chcesz. Jednak taki param converter trzeba już napisać samemu.

Własny Param Converter

Oprócz param converterów, które dostajemy na start możemy tworzyć również swoje. Szczególnie przydatne w API lub innych nieszablonowych sytuacjach.

Należy stworzyć App\Request\ParamConverter\VatNumberParamConverter.php Klasa VatNumberParamConverter musi implementować interfejs Sensio\Bundle\FrameworkExtraBundle\Request\ParamConverter\ParamConverterInterface, a co za tym idzie posiadać dwie metody apply i supports.

<?php

declare(strict_types = 1);
namespace App\Request\ParamConverter;

use ...

class VatNumberParamConverter implements ParamConverterInterface
{
    /**
     * {@inheritdoc}
     */
    public function apply(Request $request, ParamConverter $configuration)
    {
        $options = $configuration->getOptions();

        $identifier = $this->getVatNumber($request, $options);
        if ($identifier === null) {
            throw new BadRequestHttpException('Identifier not provided in request.');
        }

        if (!VatNumber::isValid($identifier)) {
            throw new BadRequestHttpException(sprintf('Nip %s is not valid', $identifier));
        }

        $request->attributes->set($configuration->getName(), VatNumber::fromString($identifier));

        return true;
    }

    /**
     * {@inheritdoc}
     */
    public function supports(ParamConverter $configuration)
    {
        if (null === $configuration->getClass()) {
            return false;
        }

        if (VatNumber::class !== $configuration->getClass()) {
            return false;
        }

        return true;
    }

    /**
     * @param Request $request
     * @param array   $options
     *
     * @return string|null
     */
    private function getVatNumber(Request $request, array $options): ?string
    {
        if (isset($options['vat_number']) && !$request->attributes->has($options['vat_number'])) {
            return $options['vat_number'];
        }

        if ($request->attributes->has('vat_number') && !isset($options['vat_number'])) {
            return $request->attributes->get('vat_number');
        }

        return null;
    }
}

Metoda supports zwraca bool w zależności czy w danej sytuacji możemy użyć tego pram convertera dla tego requestu. A w metodzie apply dokonujemy już przekonwertowania i jak możesz zauważyć to co zrealizujemy setujemy w $request->attributes to bardzo ważne. Domyśle convertery dziąłają analogicznie.

Żeby nasz param converter był dostępny musimy jeszcze skonfigurować go w serwisach:

services:     
      vat_number_converter:         
          class: MyBundle\Request\ParamConverter\MyConverter
          tags:             
              - { name: request.param_converter, priority: -2, converter: vat_number_converter } 

Jeśli masz jakieś dodatkowe pytania, pisz śmiało w komentarzu.

Poprzedni artykułTwig – filtry
Następny artykułKrótka historia czym jest Daily

Podobne posty

Jestem programistką, która lubi mieć ręce pełne roboty. Do życia potrzebuje komputera z internetem i kubka gorącej kawy. Więcej na stronie o mnie.

Comments

ZOSTAW ODPOWIEDŹ

Please enter your comment!
Please enter your name here