Kwestia ORM i widoku nie wiąże się pośrednio, ponieważ kluczowe są tu metadane i informacje o strukturach z których ORM korzysta.
Propel ułatwia nam zadanie ponieważ mamy wygenerowany kod z informacjami na temat powiązań itp (bodajże katalog metadata obok innych wygenerowanych klas).
W przypadku ActiveRecord informacje te są zapisane jako fragment definicji w modelu (has_many, many_to_many itp).
Wystarczy zatem bezpośrednio podpiąć się pod metadane i umiejętnie je wykorzystać a nie będziemy potrzebowali generatorów.
Kiedyś dawno, dawno temu dopisywałem adaptery wiążące Propel-Agavi-Smarty tak by tworzyć nowe widoki i wyszukiwarki możliwie łatwo. Dodatkowo powiązałem sobie etykiety z klas z i18n, przez co przy polach wyszukiwania nie musiałem męczyć się z dorzucaniem labeli. Robiły to za mnie po prostu pluginy dopisane do smarty.
Kod, który nie jest wierną kopią tego co napisałem aczkolwiek prezentuje zbliżoną funkcjonalność.
{search for='NazwaKlasy' action=jakiś_url}
{field for=id} <- pole z możliwością wpisania tylko intów
{field for=foreign} <- tu mi się pokazywał np select
{field for=someDate fromTo=true} <- a tu para data od, data do
{/search}
Dodatkowym elementem, który sobie wytworzyłem było wiązanie np inputów z wartościami niektórych pól. Dzięki temu nie trzeba podawać atrybutu value, id i tak dalej, ponieważ wszystko to można wyciągnąć dynamicznie z requestu w samym pluginie.
{input for=text}
Po stronie PHP dodałem jeszcze element który nazwałem, może niezbyt trafnie, wrapperem. Jego użycie było bardzo proste i sprowadzało się w poszczególnych akcjach do podobnego kodu:
<?php
$wrapper = new PropelWrapper($this->getAgaviContext());
try {
if ($mode == 'new') {
$object = $wrapper->createAndBindObject('Invoice');
} else {
$object = $wrapper->readAndBindObject('invoice', 'nazwa_pola_z_id_obiektu');
}
// tu otwarcie transakcji
$object->save();
// i zamknięcie
return 'Success';
} catch (WrapperException $e) {
handleWrapException($e);
} catch (PropelException $e) {
handlePropelException($e);
} catch (Exception $e) {
handleException($e);
}
?>
Przy odrobinie wysiłku akcja mogła by sprowadzać się tylko do:
<?php
// tym kodem załatwiamy edycję
class InvoiceEditAction extends InvoiceInnerAction {
}
// tym kodem dodawanie
class InvoiceAddAction extends InvoiceInnerAction {
}
// klasa bazowa dla operacji z fakturą
class InvoiceInnerAction extends WrapperAction {
// jedyna rzecz jakiej wymaga od nas wrapper
protected function getClassName() {
return 'Invoice'
}
}
// tym kodem listowanie
class InvoiceListAction extends ListAction {
protected function getClassName() {
return 'Invoice'
}
}
?>
Napisanie takiego "czegoś" to na prawdę ciekawe zadanie a integracja z zewnętrznymi bibliotekami może dać na prawdę sporo możliwości. Np kod, który udało mi się stworzyć w końcu zaczęła używać osoba, która nie była programistą tworząc widoki od ręki bazując tylko na strukturze bazy danych. Jedyna rzecz jaką musiała potrafić to dobrze sklecić szablon Smarty. Programista wówczas pisał logikę i nie zawracał sobie głowy resztą.