Ostatnio zetknąłem się z problemem wspólnych partiali dla wszystkich aplikacji w projekcie Symfony (dokładnie frontend i backend). Dokładniej, to te partiale były templatkami mailowymi, wysyłanymi zarówno przy zdarzeniach wygenerowanych w frontendzie jak i w panelu administracyjnym. Jako, że ponad wszystko cenię zasadę DRY (Don’t Repeat Yourself, czyli Nie Powtarzaj Się), chciałem, aby moje templatki były napisane raz, a używane z każdego miejsca w projekcie. Pewnym rozwiązaniem byłoby zrobienie symlinka: apps/frontend/modules/mails -> apps/backend/modules/mails i to działa, ale nie jest zbyt eleganckie. Z pomocą przyszła analiza kodu Symfony (dzięki Bogu, że jest Open Source:-)).

Projekt w skrócie wygląda następująco: w aplikacji frontend, mamy moduł default z akcją index, w której to używamy partiala _hello. Partial ten natomiast leży w aplikacji backend w module mails. Z poziomu backendu dostęp do tego partiala jest bezproblemowy.

Cały przykładowy, działający projekt jest dostępny na githubie: http://github.com/wowo/crossApp

Zaczynamy od tego, że w configu mamy możliwość zdefiniowania klasy, która będzie odpowiedzialna za renderowanie partiali w poszczególnych modułach. Jest za to odpowiedzialny wpis mod_NAZWA-MODUŁU_partial_view_class. Aby to skonfigurować na nasze potrzeby, wpisujemy do ogólnej konfiguracji config/ProjectConfiguration.class.php do metody setup() – będzie ona dostępna dla wszystkich aplikacji:

config/ProjectConfiguration.class.php
sfConfig::set('mod_mails_partial_view_class', 'CrossApp');

Aby uczynić nasze rozwiązanie uniwersalnym, możemy dla modułów, którym zmieniamy domyślną klasę partial_view utworzyć odpowiednie wpisy w konfiguracji, definiujące w której aplikacji leży moduł z partialami:

config/ProjectConfiguration.class.php
sfConfig::set('mod_mails_partials_app', 'backend');

Teraz pozostaje napisanie klasy dziedziczącej po sfPartialView, której nazwa jest połączeniem pierwszej wartości konfiguracyjnej oraz suffiksu PartialView oraz umieszczenie jej w głównym folderze lib:

lib/CrossAppPartialView.class.php
<?php
class CrossAppPartialView extends sfPartialView
{
 public function setDirectory($directory)
 {
   if ($app = sfConfig::get(sprintf('mod_%s_partials_app', $this->getModuleName()))) {
     $directory = sprintf('%s/apps/%s/modules/%s/templates', sfConfig::get('sf_root_dir'), $app, $this->getModuleName());
   }
   parent::setDirectory($directory);
 }
}

W tej klasie nadpisujemy metodę setDirectory, uzyskując podmianę ścieżki, w sposób podany powyżej i cieszymy się partialami cross-aplikacyjnymi :-) Zaletą tego rozwiązania jest jego przeźroczystość, nie musimy nic zmieniać w wywołaniach partiali w obydwu aplikacjach – wyciągamy je standardowo.