Autor wpisu: Wojciech Sznapka, dodany: 15.09.2012 21:00, tagi: php, symfony
Autor wpisu: zleek, dodany: 11.09.2012 10:45, tagi: php, zend_framework
Autor wpisu: Marek, dodany: 10.09.2012 09:15, tagi: php, zend_framework
Po ponad pięciu latach od wydania wersji 1.0.0 Zend Framework doczekał się stabilnej wersji 2.0.0.
Nie będę ględził, tylko odsyłam na odświeżoną stronę: http://framework.zend.com/ i do manuala: http://framework.zend.com/manual/2.0/en/index.html
Autor wpisu: Śpiechu, dodany: 09.09.2012 22:15, tagi: mysql
Ponieważ dawno nie pisałem o bazach danych, dzisiaj coś dla miłośników wyzwalaczy i procedur składowanych w MySQL. Ile razy myśleliście, że fajnie by było żeby coś „samo się robiło”? Taką samosię można napisać dosyć łatwo jeśli chcemy stworzyć mechanizm śledzenia historii rekordu. Wpis rozbiję na dwie części z uwagi na to, że w następnej skomplikuję całość o tworzenie historii rekordu z wielu tabel.
Załóżmy, że mamy tabelę Ogloszenie
, w której trzymamy dane, które chcemy śledzić. Ma następującą strukturę:
CREATE TABLE IF NOT EXISTS `Ogloszenie` ( `id` INT(11) NOT NULL AUTO_INCREMENT, `modified_by` INT(11) NOT NULL, `data` TEXT NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Tabela śledząca ogłoszenie będzie wyglądała mniej więcej tak (nie śmiać mi się z polsko-angielskich hybryd nazewniczych):
CREATE TABLE IF NOT EXISTS `OgloszenieHistory` ( `id` INT(11) NOT NULL AUTO_INCREMENT, `ogloszenie_id` INT(11) NOT NULL, `change_type` enum('created','modified','deleted') NOT NULL, `user_id` INT(11) NOT NULL, `event_date` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `data` TEXT NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Wiemy, że możemy podczepiać się pod zmiany w bazie danych za pomocą wyzwalaczy (triggerów). Tworząc historię wystarczy śledzić zmiany w postaci zapytań typu insert, update i delete. Wyzwalacze będą różnić się właściwie tylko typem wykonanego zapytania. Nie wiem jak wam, ale mnie na kilometr „zajechało” redundancją kodu wyzwalaczy, więc napiszemy sobie procedurę składowaną, którą każdy z wyzwalaczy będzie odpalał. Jest to najtrudniejsze zadanie (kod procedur wykrzacza się jak szalony).
DELIMITER $$ CREATE PROCEDURE `ogloszenie_history`(IN `id` INT, IN `change_type` ENUM('created','modified','deleted')) MODIFIES SQL DATA BEGIN DECLARE p_user_id INT; DECLARE p_data TEXT; # Wyciagam dane ze zmienianego wiersza i wrzucam do zadeklarowanych wczesniej zmiennych. SELECT modified_by, DATA INTO p_user_id, p_data FROM Ogloszenie WHERE id=id LIMIT 1; # Wrzucam wiersz do historii. INSERT INTO OgloszenieHistory (ogloszenie_id, change_type, user_id, DATA) VALUES (id, change_type, p_user_id, p_data); END$$ DELIMITER ;
Zwróćcie uwagę na pole change_type
, którego dopuszczalne wartości to created, modified i deleted, czyli dokładnie takie jak w tabeli OgloszenieHistory
. Skutecznie zawęzi nam to możliwość „zatrucia” historii niezrozumiałymi typami zmian. Pozostało zrobienie wyzwalaczy:
DROP TRIGGER IF EXISTS `inserted_ogloszenie`; DELIMITER // CREATE TRIGGER `inserted_ogloszenie` AFTER INSERT ON `Ogloszenie` FOR EACH ROW BEGIN # Podpinamy sie po insercie dysponujac swiezo auto inkrementowanym ID wiersza. CALL ogloszenie_history(NEW.id, 'created'); END// DELIMITER ; DROP TRIGGER IF EXISTS `updated_ogloszenie`; DELIMITER // CREATE TRIGGER `updated_ogloszenie` AFTER UPDATE ON `Ogloszenie` FOR EACH ROW BEGIN # Podajemy id zmienionego rekordu. CALL ogloszenie_history(NEW.id, 'modified'); END// DELIMITER ; DROP TRIGGER IF EXISTS `deleted_ogloszenie`; DELIMITER // CREATE TRIGGER `deleted_ogloszenie` BEFORE DELETE ON `Ogloszenie` FOR EACH ROW BEGIN # Zanim baza skasuje wiersz zapiszemy sobie TO wydarzenie. CALL ogloszenie_history(OLD.id, 'deleted'); END// DELIMITER ;
Śledzenie historii przedstawione powyżej jest o tyle dobre, że zwalnia nas z pisania jakiegokolwiek kodu po stronie aplikacji. Wszystko robi za nas baza. Dodatkowo mamy pełny wgląd kto i kiedy zmieniał dane. Przykładowe wpisy w tabeli OgloszenieHistory
:
id, ogloszenie_id, change_type, user_id, event_date, data 1 1 'created' 1 '2012-09-05 18:09:03' 'First data' 2 1 'modified' 2 '2012-09-05 18:09:16' 'Modified data' 3 1 'modified' 1 '2012-09-05 18:09:29' 'Modified data drugi raz' 4 2 'created' 1 '2012-09-05 18:09:37' 'Second data' 5 1 'deleted' 2 '2012-09-05 18:42:42' 'Modified data drugi raz'
W zastosowaniach praktycznych nie mamy właściwie do czynienia z bazami bez relacji, więc taka prosta historia na niewiele się zda. W następnej części wprowadzę śledzenie relacji typu wiele-do-wielu. Kiedy będzie następna część? Wtedy kiedy będzie mi się chciało pisać ;-)
Autor wpisu: Łukasz Socha, dodany: 09.09.2012 21:04, tagi: php
pobierz w .pdf(przeznaczone do wydruku)
Głównym obszarem wykorzystania wzorca Obserwator jest stworzenie relacji typu jeden-do-wielu łączącej grupę obiektów. Dzięki zastosowaniu wzorca zmiana stanu (czyli zmiana aktualnych wartości pól) obiektu obserwowanego umożliwi automatyczne powiadomienie o niej wszystkich innych dołączanych elementów (obserwatorów).
Diagram klas wzorca Observer
Interfejs Subject definiuje operacje attach() i detach() pozwalające odpowiednio na dołączanie i odłączanie obserwatorów. Ponadto zdefiniowana jest też operacja notify(), służąca do powiadamiania wszystkich zarejestrowanych obserwatorów o zmianie stanu obiekt obserwowanego poprzez wywołanie w pętli metody update(), która jest zadeklarowana w interfejsie Observer. Operacja ta jest implementowana w klasie realizującej ten interfejs i służy do powiadamiania konkretnego obserwatora o zmianie stanu obiektu obserwowanego.
Przykładowa implementacja
<?php class Observer1 implements SplObserver { public function update(SplSubject $subject) { echo 'Aktualizacja Observer1'; } } class Observer2 implements SplObserver { public function update(SplSubject $subject) { echo 'Aktualizacja Observer2'; } } class Subject implements SplSubject { protected $observers = array(); public function attach(SplObserver $observer) { $this->observers[spl_object_hash($observer)] = $observer; } public function detach(SplObserver $observer) { unset($this->observers[spl_object_hash($observer)]); } public function notify() { foreach ($this->observers as $observer) { $observer->update($this); } } } $subject = new Subject(); $observer1 = new Observer1(); $observer2 = new Observer2(); $subject->attach($observer1); $subject->attach($observer2); $subject->notify(); ?>
Przykład z życia wzięty
No i tu zaczynają się małe schody … Ze względu na specyfikę PHP (aplikacja „działa” raptem ułamki sekund) użyteczność tego wzorca jest nieco ogranicza w porównaniu z innymi językami (Java, C++ itp.). Jednak, mimo tego da się wykorzystać dobrodziejstwa Obserwatora w PHP.
<?php class CacheObserver implements SplObserver { public function update(SplSubject $subject) { echo "Odswieza cache"; } } class RSSObserver implements SplObserver { public function update(SplSubject $subject) { echo "Odswieza RSS"; } } class NewsletterObserver implements SplObserver { public function update(SplSubject $subject) { echo "Wysylam maile z nowym newsem"; } } class News implements SplSubject { private $observers = array(); public function attach(SplObserver $observer) { $this->observers[spl_object_hash($observer)] = $observer; } public function detach(SplObserver $observer) { unset($this->observers[spl_object_hash($observer)]); } public function notify() { foreach ($this->observers as $observer) { $observer->update($this); } } public function add($data) { echo 'Dodaje news do bazy'; $this->notify(); } } $news = new News(); $news->attach(new RSSObserver()); $news->attach(new CacheObserver()); $news->attach(new NewsletterObserver()); $news->add(array( 'title' => 'Tytuł', 'content' => 'blablabla' )); ?>
Do obiektu klasy News, który jest obserwowany dodajemy trzech obserwatorów. Dzięki temu zabiegowi przy dodawaniu nowego artykułu elementy systemu zostaną „poinformowane” i odświeżą dane w cache/RSS oraz wyślą odpowiednie maile do czytelników.
Zalety i wady
Zalety:
- Luźna zależność między obiektem obserwującym i obserwowanym. Ponieważ nie wiedzą one wiele o sobie nawzajem, mogą być niezależnie rozszerzane i rozbudowywane bez wpływu na drugą stronę.
- Relacja między obiektem obserwowanym a obserwatorem tworzona jest podczas wykonywania programu i może być dynamicznie zmieniana.
- Możliwość zablokowania klientowi drogi do bezpośredniego korzystania ze złożonego systemu, jeśli jest to konieczne.
Wady:
- Obserwatorzy nie znają innych obserwatorów, co w pewnych sytuacjach może wywołać trudne do znalezienia skutki uboczne.
Zastosowanie
Wzorzec Obserwatora sprawdza się wszędzie tam, gdzie stan jednego obiektu uzależniony jest od stanu drugiego obiektu.