Symfony + Doctrine: Piszemy własnego behaviora – parametry

W poprzednim poście stworzyliśmy własnego behaviora, który zachowywał się całkiem rozsądnie – z jednym wyjątkiem: gdy chcieliśmy zmienić sortowanie artykułów w obszarze danej kategorii pojawiał się problem.

Weźmy pod uwagę następujący przykład:

artykułkategoriakolejność

art 1 1 1
art 2 2 2
art 3 1 3

Załóżmy, że chcemy zmienić kolejność między artykułem 1, a 2. Standardowo, możemy wywołać na rtykuly1 metodę ->down(), tyle że efekt będzie zaskakujący:

artykułkategoriakolejność

art 1 1 2
art 2 2 1
art 3 1 3

Zamieni się artykuł 1 z 2 pomimo, że są w innych kategoriach! Możemy to poprawić, parametryzując nasz behavior. Załóżmy, że ma on być uniwersalny, a zarazem uwzględniać, że nasze obiekty mogą być trzymane w pewnych działach (np. artykuły w kategoriach itd). Dodajmy zatem możliwość powiedzenia naszemu behaviorowi, że oto dany obiekt będzie przesuwany w obrębie danego działu.
Innymi słowy: behavior będzie zmieniał kolejność tylko tych opbiektów, które mają wspólną wartośc wybranego pola. W przypadku artykułu będzie to kolumna category_id.

Zatem – do dzieła:)

Zmieńmy definicję w schema.yml:

article:
  actAs:
    Sortable:
      fields: [category_id]

Jak widać, zmieniliśmy nieco układ, dodając opcje do Sortable. Opcję tą nazwaliśmy fields. Przyjmuje ona jako parametr tablicę. Dzięki temu dodaliśmy naszemu behaviorowi dodaktowe parametry, które będzie musiał uwzględnić.

Wykonajmy teraz:
./symfony doctrine:build –all –and-load

Na chwilę obecną nie zauważymy żadnej zmiany, z drobnym wyjątkiem. Nasz behavior ma dodatkową opcję.
W pliku lib/behavior/Sortable.class.php mamy definicję:

class Sortable extends Doctrine_Template
{
  protected $_options = array();

  public function __construct($options)
  {
    $this->_options = $options;
  }

  public function setTableDefinition()
  {
    $this->hasColumn('my_order', 'integer');
    $this->addListener(new SortableListener());
  }

Jak zrobimy sobie teraz w konstruktorze var_dump na $this->_options zobaczymy:

array(1) {
  ["fields"]=>
  array(1) {
    [0]=>
    string(11) "category_id"
  }
}

Jak widać, teraz mamy dostęp do zdefiniowanej w schema.yml opcji fields. Teraz, to już proste modyfikacje:

W metodzie up oraz down zmieniamy kod zapytania na:

    $q = $item->getTable()->createQuery('u')
       ->where('u.my_order > ?', $item->my_order)
       ->orderBy('u.my_order asc');
    foreach ($this->_options['fields'] as $field)
    {
      $q->addWhere('u.' . $field .'=?', $item->$field);
    }
    $q = $q->fetchOne();

Spowoduje to, że dla każdego obiektu będzie sprawdzany warunek, aby pola zdefinowane w fields się zgadzały. Dzięki temu sortowanie bęzie działało wyłącznie w obrębie danej kategorii w przypadku naszego artykułu.