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.
Kanał ATOM