6988157282_d2c10fc165_o

DbInjectionAware formularz w Phalconie

Pisząc pewien projekt w Phalconie, natknąłem się na sytuację gdy formularz musiał mieć validator Uniqueness. Oczywiście taki validator istnieje, ale w Modelach: Phalcon\Mvc\Model\Validator\Uniqueness. Niestety postanowiłem nie korzystać z phalconowych modeli z powodu łamania zasady SOLID.

Pomysł

Wpadłem na pomysł aby stworzyć własny validator który by korzystał z połączenia Db i sprawdzał czy podana wartość już istnieje w bazie. Tutaj jednak pojawiło się pytanie w jaki sposób wstrzyknąć instancje połączenia z bazą danych do validatora?

Pierwszym pomysłem było skorzystać z Dependency Injection w Phalconie jako Service Locator. Jednak uważam że o ile można, to należy omijać Service Locatora. Z tego powodu postawiłem rozwiązać problem trochę okreżną drogą, ale za to łatwo testowalną.

Rozwiązanie

Rozwiązaniem jest połączenie własnego FormManagera oraz Interfejsu DbInjectionAwareInterface.

<?php
namespace My\App\Forms;

class Manager extends \Phalcon\DI\Injectable
{
    private $definitions;

    public function set($name, $definition)
    {
        if (is_string($definition)) {
            $definition = ['className' => $definition];
        }

        $this->definitions[$name] = $definition;
    }

    public function get($name)
    {
        if ($this->has($name)) {
            $className = $this->definitions[$name]['className'];
            $form = new $className();

            if ($form instanceof DbInjectionAwareInterface) {
                $form->setDb($this->getDI()->get('db'));
            }

            if (method_exists($form, 'configure') {
                $form->configure();
            }

            return $form;
        }
    }

    public function has($name)
    {
        return isset($this->definitions[$name]);
    }
}

Moja wersja Form Managera sprawdza przy tworzeniu instancji formularza czy implementuje DbInjectionAwareInterface, jeżeli tak to wstrzykuje db.

Dodatkowo jeżeli formularz posiada metodę configure to jest on odpalany przed zwróceniem obiektu formularza. Jest to spowodowane tym, że wbudowana metoda initialize() jest odpalana podczas konstruktora. W konstruktorze nie ma jeszcze dostępu do wstrzyknietej instancji db. Z tego powodu dodałem obsługę metody configure.

<?php
namespace My\App\Forms;

interface DbInjectionAwareInterface
{
    public function getDb();
    public function setDb($db);
}

Teraz wystarczy tylko odpowiednie zmiany w DI aplikacji

<?php

$di->set('formManager', 'My\App\Forms\Manager');
$formManager = $di->get('formManager');
$formManager->set('register', 'My\Forms\RegisterForm');

Oczywiście aby się nie powtarzać RegisterForm może extendować po klasie która ma zaimplementowany DbInjectionAwareInterface. Jednak ja napisałem do tego traitsa:

<?php
namespace My\App\Di;

trait DbInjectableTrait
{
    private $db;

    public function setDb($db)
    {
        $this->db = $db;
    }

    public function getDb()
    {
        return $this->db;
    }
}
<?php
namespace My\Forms;

use My\App\Di\DbInjectableTrait;
use My\App\Di\DbInjectionAwareInterface;
use Phalcon\Forms\Form;

class RegisterForm extends Form implements DbInjectionAwareInterface
{
    use DbInjectableTrait;

    public function configure()
    {
        //...
    }
}

Autor zdjęcia: Philip Taylor

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *

Możesz użyć następujących tagów oraz atrybutów HTML-a: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>