konto usunięte

Temat: Roszerzenie obiektu zwracanego z modelu

Witam,
mam pytanie do doświadczonych użytkowników Zend Framework i architektury MVC w ogóle. Po przejrzeniu wielu anglojęzycznych postów chciałbym zapytać, jakie praktyki stosowane są na "rodzimym" podwórku.

Chodzi o rozszerzenie obiektu będącego wynikiem zapytania do bazy. Mamy jakiś model np.:

class News extends Zend_Db_Table_Abstract {
protected $_name = 'News';


function getNewsByTitle($title) {
$where = array('title = ?' => $title);
return $this->fetchRow($where);
}

}

Kontroler

class News_Controller_Action extends Zend_Controller_Action {

function indexAction() {
$this->view->news->$this->_dbNews->getNewsByTitle('Alamakota');
}
}

Kod oczywiście jest mocno uproszczony.
Jak dotąd wszystko proste i przyjemne. Załóżmy jednak że chcę rozszerzyć zwrócony mi przez model obiekt. Załóżmy, że w widoku chcę mieć galerię, ale jej elementy wyświetlić dopiero jeśli do newsa są dodane jakiekolwiek zdjęcia. Chciałbym to sprawdzić w widoku za pomocą metody $this->news->hasGallery().
I tu zaczynają się problemy. Nie moge dodać po prostu metody do zwróconego obiektu. Najczęstszą radą jest zrobienie modelu w dwóch klasach: jedna obsługuje praktycznie tylko CRUD, i druga, która obsługuje obiekt pierwszej, tam tworzymy sobie dodatkowe metody i ten właśnie obiekt zwracamy do kontrolera i do widoku. Otrzymujemy coś na kształt:

class NewsGateway extends Zend_Db_Table_Abstract {
protected $_name = 'News';

function getNewsByTitle($title) {
$where = array('title = ?' => $title);
return $this->fetchRow($where);
}

}

class News {
protected $_name = 'News';

function __construct(NewsGateway $news) {
$this->galleryGateway = new GalleryGateway();
$this->title = $news->title
$this->preface = $news->preface // etc. kolumny z tabeli news

}


function isGallery() {
$this->galleryGateway->isGallery(NewsGateway);
}

}

Kontroler

class News_Controller_Action extends Zend_Controller_Action {

function indexAction() {
$news->$this->_dbNews->getNewsByTitle('Alamakota');
$this->view->news = $this->News($news);

}
}

Oczywiście po raz kolejny kod mocno uproszczony. Chodzi tylko o pewne zobrazowanie.

Główne zarzuty przeciw takiemu rozwiązanie:
- nie powinno dopuszczać widoku do manipulowania na danych, a np. utworzenie metody getChild() dla pobrania jakiegoś potomka już jest taką manipulacją
- zbyt mocne powiązanie modelu z bazą w tabeli - teoretycznie powinien być gotowy do przyjęcia danych także z innych źródeł np. XML

Bardzo proszę o radę w tej sprawie. Jak wy radzicie sobie z takim problemem?

Z góry dziękuję za pomoc.

Temat: Roszerzenie obiektu zwracanego z modelu

Złe założenia.
class News extends Zend_Db_Table_Abstract {
To nie jest model! Jest to biblioteka ułatwiająca dostęp do tabeli X

W kontrolerze nigdy nie odnosisz się do Db_Table_Abstract, tylko do Modelu który jest czysty(po niczym nie dziedziczy). W takim modelu przygotowujesz dane do odpowiedniej postacie, i w kontrolerze przekazujesz je do widoku.

konto usunięte

Temat: Roszerzenie obiektu zwracanego z modelu

Ok, dzięki, jedna rzecz wyjaśniona. Czyli przykładowo podana przeze mnie klasa News jest modelem.
Czy zatem poprawne jest przekazanie do widoku obiektu tej klasy wyposażonego w metodę isAdmin() sprawdzająca czy dany użytkownik jest także adminem (kompletna abstrakcja, ale to bez znaczenia - chodzi o przykład.)?
Spróbuję zbudować nieco bardziej plastyczny przykład: mamy stronę informacyjną "O nas". Posiada 3 wersje językowe i 2 potomków: "nasz zespół" i "kontakt". Pobieram ta stronę i przekazuję do widoku. W widoku chce wyświetlić wersję niemiecką, robię więc: $this->infoPage->getVersion('de'). Załóżmy też że w zalezności od tego czy ma potomków chciałbym wygenerować jakaś nawigację, robie więc if (infoPage->hasChild()) ... generowanie menu ....

Czy takie działania na poziomie widoku są poprawne? Jeśli nie to rozumiem, ze powinienem od razu przekazać wersję niemiecką strony, informację czy ma potomków oraz potomków w tabeli załóżmy $this->infopage->children[]. Problem pojawia się jeśli strona ma 100 potomków a ja niekoniecznie chcąc ich wykorzystać od razu ładuję wszystkich do obiektu. W efekcie dla obiektu strony głównej pobieram całą strukturę serwisu.

Wracając do źródeł: czy do widoku mozna przekazać obiekt posiadający metody działające na bazie danych?Marek Syrek edytował(a) ten post dnia 07.04.09 o godzinie 16:56

Temat: Roszerzenie obiektu zwracanego z modelu

Teoretycznie daje to wspaniałe możliwości budowania szablonu, rozbudowywania o kolejne opcje, tylko że wtedy zanika Ci warstwa Controllera bo równe dobrze możesz dać $this->view->controller=$this; $this->view->gl=$GLOBALS;

I w zasadzie taki rozwiązanie determinuje prędzej czy później trzymanie kodu html w bazie danych, operowanie na jednym szablonie(w sensie jednym pliku szablonowych). Ja jestem na Nie dla takiego rozwiązania.
Sergiusz Świeszczak

Sergiusz Świeszczak Architekt Systemów

Temat: Roszerzenie obiektu zwracanego z modelu

Marek Syrek:
Ok, dzięki, jedna rzecz wyjaśniona. Czyli przykładowo podana przeze mnie klasa News jest modelem.
Czy zatem poprawne jest przekazanie do widoku obiektu tej klasy wyposażonego w metodę isAdmin() sprawdzająca czy dany użytkownik jest także adminem (kompletna abstrakcja, ale to bez znaczenia - chodzi o przykład.)?
Spróbuję zbudować nieco bardziej plastyczny przykład: mamy stronę informacyjną "O nas". Posiada 3 wersje językowe i 2 potomków: "nasz zespół" i "kontakt". Pobieram ta stronę i przekazuję do widoku. W widoku chce wyświetlić wersję niemiecką, robię więc: $this->infoPage->getVersion('de'). Załóżmy też że w zalezności od tego czy ma potomków chciałbym wygenerować jakaś nawigację, robie więc if (infoPage->hasChild()) ... generowanie menu ....

Czy takie działania na poziomie widoku są poprawne? Jeśli nie to rozumiem, ze powinienem od razu przekazać wersję niemiecką strony, informację czy ma potomków oraz potomków w tabeli załóżmy $this->infopage->children[]. Problem pojawia się jeśli strona ma 100 potomków a ja niekoniecznie chcąc ich wykorzystać od razu ładuję wszystkich do obiektu. W efekcie dla obiektu strony głównej pobieram całą strukturę serwisu.

Wracając do źródeł: czy do widoku mozna przekazać obiekt posiadający metody działające na bazie danych?Marek Syrek edytował(a) ten post dnia 07.04.09 o godzinie 16:56

Możesz bardzo ułatwić sobie życie stosując Helpery. W dokumentacji jest o tym mowa ale jeżeli chcesz mogę zacytować jakiś przykład. Helper może bezpośrednio korzystać z modelu albo obiektu tabeli albo wyciagać dane bezpośrednio poprzez zapytanie SQL. Helper wywołujesz bezpośrednio z obiektu widoku jak każda inną metodę np: $this->jakisHelper().

konto usunięte

Temat: Roszerzenie obiektu zwracanego z modelu

Dzięki serdeczne. Nawet nie pomyślałem żeby do tego helpery zaprzęgać. A w sumie jest to bardzo dobre rozwiązanie.
Bartosz Ratajczyk

Bartosz Ratajczyk MS SQL Developer

Temat: Roszerzenie obiektu zwracanego z modelu

Sergiusz Świeszczak:
Możesz bardzo ułatwić sobie życie stosując Helpery. W dokumentacji jest o tym mowa ale jeżeli chcesz mogę zacytować jakiś przykład.

Ja bym poprosił. Helperów jeszcze nie używałem, a praktyczny przykład się przyda.
Sergiusz Świeszczak

Sergiusz Świeszczak Architekt Systemów

Temat: Roszerzenie obiektu zwracanego z modelu

Witam,

Zakładam że macie zalecaną strukturę katalogów, w której helpery umieszczamy w /application/views/helpers lub w w wypadku aplikacji z wydzielonymi modułami (np. back-end czyli administracja i front-end czyli to co widzi klient) w /application/modules/nazwa_modulu/views/helpers oraz w pliku bootstrap macie skonfigurowany dostęp do helpera:


$viewRenderer = new Zend_Controller_Action_Helper_ViewRenderer();
$viewRenderer->setView($view);
Zend_Controller_Action_HelperBroker::addHelper($viewRenderer);


Teraz wystarczy zbudować sobie przykładowy helper, który nazwiemy HasGallery.php. Dla uproszczenia zakładam, że obrazki dla newsa są trzymane w polu tekstowym images_list:


class Zend_View_Helper_HasGallery
{
function __construct()
{
}

public function hasGallery($newsId)
{
$db = Zend_Registry::get('dbAdapter');

return (bool) $db->fetchOne('SELECT images_list FROM news WHERE news_id = ' . $newsId);
}
}


Teraz wystarczy w kodzie widoku, czyli w pliku np. view.phtml odwołać się do helpera, do którego poprzez kontroller przekazujesz obiekt News o nazwie np. $newsRow:


...

<?php if ($this->hasGallery($this->newsRow->newsId) : ?>
... Tutaj kod HTML gdy istnieją obrazki ...
<?php else : ?>
... Tutaj kod HTML gdy obrazków nie ma ...
<?php endif; ?>

...


Oczywiście można zarzucić, że w tym momencie wbudowujemy jakąś logikę w widok ale w praktyce jest to nie do uniknięcia.Sergiusz Świeszczak edytował(a) ten post dnia 15.04.09 o godzinie 09:18
Krzysztof Szatanik

Krzysztof Szatanik Starszy programista
PHP, Luxoft Poland

Temat: Roszerzenie obiektu zwracanego z modelu

Sergiusz Świeszczak:
Oczywiście można zarzucić, że w tym momencie wbudowujemy jakąś logikę w widok ale w praktyce jest to nie do uniknięcia.

Zamiast bezpośrednio operować na bazie w helperze widoku, o wiele bardziej wygodnie i logiczniej będzie wykorzystać do tego modele, czyli (z palca)


class Zend_View_Helper_HasGallery
{
public function hasGallery($newsId)
{
$galleryModel = new gallery_models_Gallery();
return $galleryModel->hasGallery( $newsId );
}

}


Natomiast model to coś w rodzaju:


class gallery_models_Gallery extends Zend_Db_Table {

protected $_name = 'gallery';
protected $_rowClass = 'gallery_models_Gallery_Row';


public function hasGallery( $newsId ) {
// Tutaj logika bazy danych
}

}

class gallery_models_Gallery_Row Zend_Db_Table_Row {
public function doSomething( $newsId ) {
// A tutaj robimy coś na konkretnym rekordzie z tabeli gallery
}
}


Polecam także zapoznanie się z http://framework.zend.com/manual/en/zend.db.table.html oraz http://framework.zend.com/manual/en/zend.db.table.html które dają olbrzymie możliwości i znacznie ułatwiają życie.

A jako bonus imho najlatwiejsze rozwiazanie oryginalnego problemu:


class news_models_News extends Zend_Db_Table {
protected $_name = 'gallery';
protected $_rowClass = 'news_models_News_Row';

public function getNewsByTitle($title) {
$where = array ('title = ?' => $title );
return $this->fetchRow ( $where );
}

}

class news_models_News_Row extends Zend_Db_Table_Row {
public function hasGallery() {
// Sprawdzenie i zwrócenie informacji czy news ma galerie
return (bool) $return;
}

public function getContent() {
return nl2br ( $this->content );
}
}

class news_NewsController extends Zend_Controller_Action {
function indexAction() {
$title = $this->getRequest ()->getParam ( 'title' );
$newsModel = news_models_News ();
$this->view->news = $newsModel->getNewsByTitle ( $title );
}
}


i w widoku coś w rodzaju:

<?= $this->news->getContent(); ?>
<br>
<?= $this->news->hasGallery() == true ? 'Link do galerii' : 'Brak galerii'; ?>
Krzysztof Szatanik edytował(a) ten post dnia 18.04.09 o godzinie 01:47

Następna dyskusja:

Zmiana typu mime zwracanego...




Wyślij zaproszenie do