Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: opakowowywanie klas (wymuszanie wzorca dla klasy) ?
Forum PHP.pl > Forum > PHP
xajart
Witam, ostatnio zasnatawia mnie czy w PHP można w jakiś sposób opakowąć klasę w swego rodzaju taki szablon (nie mam na myśli interfejsu). Może jest na to jakiś wzrorzec.

Załużmy że mam klasę (pisane z palca):

  1. class Model_Read extends Model_Data_Abstract
  2. {
  3. public $msg_no_params = 'string'
  4. public $msg_db_error = 'jakis string'
  5. public $msg_success = 'jakis string
  6.  
  7. public $params1= null
  8. public $params2 = null
  9.  
  10. public function test ()
  11. {...}
  12. .....
  13. }


i teraz chciałbym aby klasa Read korzystała z jakiegoś szablonu w którym wymuszone by miała ustawione parametry $msg_*

W ten sposób poprostu kolejne modele pisał bym w stylu

  1. class Model_Read extends Model_Data_Abstract
  2. {
  3. public $params= null
  4. public $params2 = null
  5.  
  6. public function test ()
  7. {...}
  8. .....
  9. }


A odwołując się do modelu móglbym ustawiac parametry dla np komunikatów:
  1. $dbModel = new Model_Read();
  2. $dbModel->msg_db_error = 'odpowiedni lang';
toffiak
Jeżeli dobrze rozumiem to chcesz aby obiekty miały dostęp do właściwości $msg_*, jeśli tak to wystarczy przenieść te właściwości do klasy abstrakcyjnej, uczynić je dziedziczonymi i dodać odpowiednie gettery i settery.
xajart
tak ale jak utworzę klasę abstrakcyjną dziedziczoną,
to Model_Read już dziedziczy po czymś, nie można dziedziczyć po dwóch rzeczach, musiało by to wyglądać tak

np
Model_Read extends Klasa_Abstrakcyjna
Klasa Abstrakcyjna extends Model_Data_Abstract

Pytanie tylko czy w Model_Read dam radę odwoływać się bezkonfliktowo do metod Model_Data_Abstract ?
Jeżeli zawiera on np właściwosći publiczne to czy z poziomu Model_Read uzyskam do nich dostęp (musiałbym chyba w Klasa_Abstract definiować, opakowywanie metod lub iplementowac interfejs, który mi to umożliwią).
CuteOne
Raczej:

Model_Read extends Model_Data_Abstract
Model_Data_Abstract extends Klasa_Abstrakcyjna implements YYY_Interface
Klasa_Abstrakcyjna implements XYZ_Interface, ZXY_Interface
viking
Mogą cię też zainteresować http://php.net/manual/en/language.oop5.traits.php
xajart
Cytat(CuteOne @ 26.09.2012, 10:54:23 ) *
Raczej:

Model_Read extends Model_Data_Abstract
Model_Data_Abstract extends Klasa_Abstrakcyjna implements YYY_Interface
Klasa_Abstrakcyjna implements XYZ_Interface, ZXY_Interface


Tak to by mi rozwiązało problem, tylko Mode_Data_Abstract dziedziczy po czymś tam znowu (nie wspominając że należy do bibliotek finalnych których nie mogę ruszyć).
Wiec zostaje w punkcie wyjścia wink.gif


Cytat(viking @ 26.09.2012, 11:03:01 ) *


To rozwiązanie od 5.4 (niestety nie zastosuje tego).
Crozin
Napisz jakie będzie przeznaczenie tego kodu, dlaczego tak chciałbyś to zrealizować, bo wygląda na to, że próbujesz rozwiązać jakiś problem w niewłaściwy sposób.
xajart
Ogólnie wygląda to tak że buduję sobie listę modeli opartych na Zend_Db_Table i każdy model muszę opakowywać (wewnątrz) w listę właściwości typu:

public $msg_...

Zawierają one domyślny komunikat, są publiczne aby można było samodzielnie np przekazać treść komunikatów, np
standardowy komunikat dla akcji usuwania byłby np "pomyślnie usunięto pozycję", ale np dla konkretnego zdarzenia lepiej wygląda komunikat, który go bezpośrednio dotyczy, np dla listy produktu, informacje o tym że produkt został usunięty.

Niestety Zend_Db_Table sobie nie rozszerzę o takie właściwości.

Problem opakowywania nie jest jakimś dyskomfortem bo z IDE łatwo się to rozwiązuje, ale zastanawia mnie czy nie można tego rozwiązać inaczej aby zgodnie z DRY elementy które się powtarzają ulokować w jednym miejscu i w jakiś sposób implementować je tam gdzie są potrzebne.


Inny problem który napotykam podczas realizacji modeli jest taki że integrując go z zewnętrzna bazą która nie była projektowana pod kątem wymogów jakie narzuca Zend_Db_Table, muszę pisać obok metody do obsługi Danych (by uniemożliwić przkazanie innych wartości niż warunki jakie dostarcza baza). Niestety to rozwiązuję tworząc nowy obiekt Np dla Model_Products, tworzę object Model_Data_Products, do którego przekazuję listę parametrów (a on mi je parsuje). Choć wolałbym to inaczej rozwiązać (właśnie poprzez jakieś dziedziczenie, by wewnątrz klasy tylko się odwoływać do metod z Model_Data_Products). Ale obecnie moja wiedza chyba w tych tematach jest ograniczona, bo nie mam innego pomysłu jak to rozwiązać, bo nie da się dwa razy podziedziczyć po innej klasie, nie tworząc przy tym dwóch klass.

Skoro Model_Products dziedziczy po Zend_Db_Table_Abstract to nie podziedzice już po Model_Data_Products, owszem są interfejsy, ale przy nich nadal pojawia się problem z właściwościami.
Z tym że Model_Data_Products dziedziczy po Model_Data_Abstract i jak bym chciał to rozwiązać tak aby mieć dostęp do metod innej klawy w obecnej klasie musiało by wyglądać to tak:

1) Model_Products extends Model_Data_Products
2) Model_Data_Products extends Model_Data_Abstract
3) Model_Data_Abstract extends Zend_Db_Table_Abstract

Z tym że w pkt 1 chyba nie będę miał dostępu do metod z punktu 3 przez $this-> ?
A jeszcze jak bym chciał tu wprowadzić szablon dla tych komunikatów to musiałbym wpleść kolejne zależności, albo wklepać je do Model_Data_Abstract

Trochę chyba za bardzo przekombinowane by to było.
Crozin
Przede wszystkim, zacznij korzystać z normalnych przestrzeni nazw. PHP 5.3.0 ma już ponad 3 lata!

Problem tkwi zupełnie gdzie indziej - tak jak podejrzewałem źle zabrałeś się do swojego prawdziwego problemu, tj. wyświetlania użytkownikowi komunikatów. Model jest częścią aplikacji, która w ogóle nie powinna interesować się takimi pierdołami. Model ma za zadanie jedynie zwrócić informację o poprawnym wykonaniu operacji, tj. jakaś metoda z publicznego interfejsu powinna zwrócić true. Natomiast w przypadku niepowodzenia rzucenie wyjątku jest niemal zawsze odpowiednim rozwiązaniem. Wyjątek to również miejsce na zamieszczenie dokładniej informacji o błędzie. Oczywiście, takie dane nie powinny być prezentowane użytkownikowi.

Ustawienie odpowiedniego komunikatu będzie raczej leżało w gestii obiektu korzystającego z modelu, często będzie to kontroler, bądź inny model. Samo ustawienie czy też zwrócenie treści komunikatu powinno być oddelegowane do osobnego obiektu. Obiekt ten mógłby mieć prosty interfejs:
  1. public function getMessage($key, $object = null);
Gdzie $key to klucz komunikatu, np. entity.remove.success, entity.create.failure czy notification.entity_created.success. Opcjonalny argument $object to obiekt, którego dotyczy komunikat - umożliwiłby on spersonalizowanie komunikatu, np. Wiadomość "Hej xajart" została wysłana pomyślnie!.
xajart
Okej, tylko nie rozumie do końca jak w Zendzie obsługiwać te błędy.

Jeżeli zrobię w metodzie:
  1. $data = array()
  2.  
  3. try
  4. {
  5. $sql = '...'
  6. $data = $this->fetchAll($sql)
  7. }
  8. catch (Zend_Db_Exception $e)
  9. {
  10. ...?
  11. }
  12.  
  13. return $data


To nie wie co powinienem zawrzeć w "catch", jeżeli wywali się zapytanie to fajnie by było mi to zapisać do logów (tutaj pewnie jakaś metodę opartą o Zend_Log musiał bym sobie napisać), ale muszę również użytkownika jakoś o tym poinformować w przyzwoity sposób, tylko jak w kontrolerze mam złapać ten wyjątek?

Nie wstawię przecież:
  1. echo $e->getMessage()


W necie nie mogą jakiegoś sensownego przykładu znaleźć.
Crozin
1. Raczej nie powinieneś przy każdym zapytani stosować bloku try-catch, o ile rzeczywiście nie chcesz sensownie obsłużyć błędu. Logi? Oczywiście, tak! Ale przecież nie będziesz kodu odpowiedzialnego za zapis tych logów powtarzał za każdym razem. Gdzieś w jakimś "wyższym" miejscu aplikacji, utwórz blok try-catch, który wyłapie Ci wszystkie wyjątki typu Zend_Db_Exception wygenerowane przez aplikację (zapewne Zend już takie coś ma), tam wykonaj zapis logów, a użytkownika przekieruj do statycznej strony z informacją o wewnętrznym błędzie serwera (kod odpowiedzi: 500 Internal Server Error). Żadnych dodatkowych informacji poza sugestią odświeżenia strony nie powinno tam być - bo i po co?
viking
Zawsze możesz rozszerzyć Zend_Db_Exception o jakąś własną klasę i w niej dorobić ogólne logowanie. Ewentualnie jakiś singleton kolejkujący wszystkie wyjątki Logger::addLog($e->getMessage);
To jest wersja lo-fi głównej zawartości. Aby zobaczyć pełną wersję z większą zawartością, obrazkami i formatowaniem proszę kliknij tutaj.
Invision Power Board © 2001-2025 Invision Power Services, Inc.