Všechny ty Base záležitosti

Co nemusíš psát dvakrát, to napiš jen jednou. To je super pravidlo. Nevím, jestli je to moje myšlenka, nebo jsem ji někde četl, ale správný programátor má být líný. Natolik líný, že když už jednou něco napsal, neměl by mít snahu to psát znova někam jinam. Leností k pokroku a lepšímu psaní kódu - super idea. Nejde mi to sice úplně stoprocentně, ale snažím se.

Čili třeba u entit mám v každé nějaké ID, automaticky generované. To mi říká, že bych měl mít nějakou BaseEntitu, která vždy nastaví ID. Mohla by obsahovat i nějaké základní metody, jako extrakci entity do pole, případně naopak hydrataci z pole do entity.

Nebo velmi často budu tvořit nějaké formuláře. Administrace jich bude plná. Chci tedy nějakou malou továrničku, která mi poslouží pro vytvoření jakéhokoli formuláře.

Do třetice zmíním presentery, které mají také spousty věcí stejných - akce default a detail, tvorbu gridu pro výpis položek v různých agendách atd.

Dnes tedy nových věcí moc neuděláme. Naopak budeme přepisovat to, co jsme už udělali a trochu si to zoptimalizujeme.

Base entita

Začneme tím nejzákladnějším, a to je Entita. Do Base entity vložíme to, co budou mít všechny entity společné, čili ID:

<?php

namespace BaseModule\Model\Entity;

use Doctrine\ORM\Mapping as ORM;
use Kdyby;

/**
 * IdentifiedEntity
 *
 * @ORM\Table(name="identified_entity")
 * @ORM\MappedSuperclass
 */
abstract class IdentifiedEntity
{
    /**
     * @var integer
     * @ORM\Column(name="id", type="integer", length=11, nullable=false, options={"unsigned":true})
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="IDENTITY")
     */
    private $id;

    /**
     * @return int
     */
    public function getId()
    {
        return $this->id;
    }
}

Tady mi dlouze trvalo, než jsem si to vygoogloval, ale funguje mi to. Teď můžeme ID odstranit z Entity stránek. src/PageModule/Model/Entity/Page.php bude od teď začínat takto:

class Page extends \BaseModule\Model\Entity\IdentifiedEntity
{
    use Kdyby\Doctrine\Entities\Attributes\Identifier;

Komentáře nahoře samozřejmě ponech. Jde jen o to, že dědíme z té IdentifiedEntity. Zato ale odstraň deklaraci ID a taky getter a setter.

Dalším krokem bude doplnění funkce, která z pole hodnot, dokáže vyrobit entitu. Tomu se říká hydratace. Na to budeme používat ZendHydrator, který si nahrajeme composerem:

composer require zendframework/zend-hydrator

Pak třídu IdentifiedEntity doplníme o 2 metody:

    /**
     * @return array
     */
    public function extract()
    {
        $hydrator = new ClassMethodsHydrator();

        return $hydrator->extract($this);
    }

     /**
     * @param $values
     * @param string $keyNoHydrate
     * @return $this
     */
    public function hydrate($values, $keyNoHydrate = 'id')
    {
        if ($values instanceof ArrayHash) {
            $values = (array)$values;
        }

        foreach ($values as $key => $value) {
            if ($key === $keyNoHydrate) {
                unset($values[$key]);
            }
            if (is_string($value) && $value == '') {
                $values[$key] = null;
            }
            if ($value instanceof FileUpload && !$value->isOk()) {
                unset($values[$key]);
            }
            if ($value instanceof ArrayHash) {
                $values[$key] = $this->arrayHashToArray($value);
            }
        }
        $hydrator = new ClassMethodsHydrator();
        $hydrator->hydrate($values, $this);

        return $this;
    }

Metoda extract() vezme entitu a udělá z ní asociované pole. Naopak metoda hydrate() naplní entitu hodnotami z asociovaného pole. Samozřejmě se musí shodovat názvy klíčů v poli s názvy vlastností v entitě. V metodě hydrate() ještě volám metodu arrayHashToArray(), jelikož Nette neposílá data z formulářů přímo jako jednoduché pole, nýbrž objekt ArrayHash. No a s ním zase nepracuje Zend. Nic naplat - potřebujeme tedy metodu, která nám vrátí opravdu jen pole:

    /**
     * @param $arrayHash
     * @return array
     */
    protected function arrayHashToArray($arrayHash)
    {
        if (is_object($arrayHash)) {
            $arrayHash = (array)$arrayHash;
        }
        if (is_array($arrayHash)) {
            $new = [];
            foreach ($arrayHash as $key => $val) {
                $new[$key] = $this->arrayHashToArray($val);
            }
        } else {
            $new = $arrayHash;
        }

        return $new;
    }

Proč tohle celé děláme? No abychom toho nemuseli tolik psát později. Představ si, že odešleš formulář, který má 30 prvků. Pak z něj chceš vyrobit entitu, čili musíš postupně volat každý setter. Medota hydrate to udělá automaticky. Takže i když třeba do entity něco později přidáš, nemusíš přepisovat metodu pro editaci entity.

O mně

Jmenuji se Rudolf Svátek - lektor výpočetní techniky, trochu PHP programátor a SEO konzultant na volné noze.

Adresa

Příčná 326/3
736 01 Havířov

Kontakty

Email: office@rudolfsvatek.cz
Telefon: +420 777 828 353
Skype: svatekr