Niezalogowany [ logowanie ]
Subskrybuj kanał ATOM Kanał ATOM

Autor wpisu: Marek, dodany: 10.09.2012 09:15, tagi: php, zend_framework

zf2

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.

Powiązane tematy

Autor wpisu: bastard13, dodany: 07.09.2012 16:14, tagi: oop

a na co mnie to?

Testy jednostkowe (jak i wszelkie pozostałe) przydają się. Co prawda nie są, jak sądzą niektórzy, gwarancją tego, że nasz kod nie posiada błędów i jest wolny od jakichkolwiek pomyłek. Dają nam jedynie informacje, że nie udało się danego błędu znaleźć oraz, co równie ważne, że w określonych przypadkach kod/aplikacja zachowuje się tak, jak zakładamy.Ta druga informacja jest w stanie ułatwić nam życie w naprawdę wielkim stopniu, gdy dochodzimy do momentu, gdy kod trzeba refaktoryzować/modyfikować. Testy dają nam pewność, że w danych przypadkach, po wszystkich wprowadzonych zmianach, wszystko nadal działa w sposób prawidłowy. Czytaj więcej »

Autor wpisu: Tomasz Kowalczyk, dodany: 02.09.2012 20:19, tagi: mysql

Na pewno wpadliście kiedyś w pułapkę, w której podczas testowania wydajności / sposobu działania wpisaliście w okienko phpMyAdmina lub bezpośrednio w konsolę MySQLa nie do końca przemyślane zapytanie. Jeśli zawierało jakiś błąd składniowy, to sprawa jest prosta – na ekranie … #LINK#

Autor wpisu: Wojciech Sznapka, dodany: 02.09.2012 13:47, tagi: php, symfony

Everyone fall in love with Behat. It's powerfull tool for testing business requirements in PHP. Behat scenarios, written in cross-platform Gherkin language, are readable for developers, easy to understand and verify for business people and executable by machine, which goes through an application to prove that it works as expected. Behat scenarios are one of the best ways to describe system. UML Use Cases or tons of pages in SRS documents are fine, but hard to understand from the begining, and even harder to maintain in the future. Behat eases this process and also gives opportunity to automate requirements verification.

Autor wpisu: Łukasz Socha, dodany: 01.09.2012 21:32, tagi: css

pobierz w .pdf(przeznaczone do wydruku)

CSS 3 dostarcza wiele nowości, które z pewnością ułatwią i przyspieszą pracę nad atrakcyjnymi wizualnie projektami. Mimo, że CSS 3 jest jeszcze w fazie implementacji przez wszystkich najważniejszych producentów przeglądarek, warto powoli zaznajamiać się z możliwościami tej technologii ;)

Osobiście najbardziej nie lubię tworzyć wszelkiej maści formularzy. Nie dość, że trzeba napisać wiele linii kodu, to jeszcze istnieją ograniczenia technologiczne. Co nowego przynosi CSS 3 na tym polu?

Nowa wersja CSS „potrafi” rozróżniać różne typy pół formularza – zamiast pisać osobne klasy dla checkbox czy radio będziemy mogli napisać input[type=checkbox]. Prawda, że dużo lepiej tak wygląda? Poza tym, w końcu, będziemy mogli dowolnie edytować wygląd pól typu radio i checkbox bez utycia JavaScriptu – tak na marginesie, nie rozumiem czemu dopiero teraz deweloperzy umożliwią to. Inną przydatną nowością jest pseudoklasa :focus.

Żeby nie było tylko teoretycznie zamieszczam kod przykładowego formularza kontaktowego z wykorzystaniem CSS 3:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
        <title>...</title>
        <style type="text/css">
            body{
                font-family: Arial, sans-serif;
                font-size:14px;
                padding: 30px;
            }
            label{
                width:100px;
                display: inline-block;
            }
            input, textarea{
                margin: 5px 0;
                border:1px solid #BAE1FF;
                border-radius: 5px;
                -webkit-appearance: none;
                -moz-appearance: none;
                background-image: linear-gradient(top, rgb(255,255,255) 0%, rgb(226,242,252) 100%, rgb(57,101,133) 84%);
                background-image: -o-linear-gradient(top, rgb(255,255,255) 0%, rgb(226,242,252) 100%, rgb(57,101,133) 84%);
                background-image: -moz-linear-gradient(top, rgb(255,255,255) 0%, rgb(226,242,252) 100%, rgb(57,101,133) 84%);
                background-image: -webkit-linear-gradient(top, rgb(255,255,255) 0%, rgb(226,242,252) 100%, rgb(57,101,133) 84%);
                background-image: -ms-linear-gradient(top, rgb(255,255,255) 0%, rgb(226,242,252) 100%, rgb(57,101,133) 84%);

                background-image: -webkit-gradient(
                    linear,
                    left top,
                    left bottom,
                    color-stop(0, rgb(255,255,255)),
                    color-stop(1, rgb(226,242,252))
                    );
            }
            textarea{
                width:400px;
                height: 200px;
            }
            input[type=text] {
                width:150px;
                height: 26px;
                margin-left: 5px;
            }
            input:focus, textarea:focus, select:focus {
                outline-color: #0090FF;
                ackground-image: linear-gradient(bottom, rgb(255,255,255) 0%, rgb(226,242,252) 100%, rgb(57,101,133) 84%);
                background-image: -o-linear-gradient(bottom, rgb(255,255,255) 0%, rgb(226,242,252) 100%, rgb(57,101,133) 84%);
                background-image: -moz-linear-gradient(bottom, rgb(255,255,255) 0%, rgb(226,242,252) 100%, rgb(57,101,133) 84%);
                background-image: -webkit-linear-gradient(bottom, rgb(255,255,255) 0%, rgb(226,242,252) 100%, rgb(57,101,133) 84%);
                background-image: -ms-linear-gradient(bottom, rgb(255,255,255) 0%, rgb(226,242,252) 100%, rgb(57,101,133) 84%);

                background-image: -webkit-gradient(
                    linear,
                    left bottom,
                    left top,
                    color-stop(0, rgb(255,255,255)),
                    color-stop(1, rgb(226,242,252))
                    );
            }
            input[type=checkbox] {
                width:20px;
                height: 20px;
                border-radius: 2px;
                vertical-align: middle;
                margin-right: 5px;
            }
            input[type=checkbox]:checked {
                background: url('images/checkbox.png') center no-repeat;;
            }
            input[type=submit] {
                background: #0199FA;
                color: #ffffff;
                font-weight: bold;
                padding: 5px 15px;
            }
            input[type=reset] {
                background: #DEF3FF;
                color: #0784CE;
                font-weight: bold;
                padding: 5px 15px;
                margin-left: 10px;
            }
        </style>
    </head>
    <body>

        <form>
            <label for="name">Imię i nazwisko:</label>
            <input type="text" name="name" /><br />
            <label for="email">Adres e-mail:</label>
            <input type="text" name="email" /><br />
            <label for="message">Treść: </label><br />
            <textarea placeholder="Wpisz treść" name="message"></textarea><br/>
            <input type="checkbox" name="send"/> Wyślij kopię na mojego e-maila.<br />
            <input type="submit" value="Wyślij" /><input type="reset" value="Wyczyść" />
        </form>
    </body>
</html>

Jak to wygląda w praktyce możecie zobaczyć tutaj.

W połączeniu nowości CSS 3 z możliwościami Web forms 2, o którym pisałem opisujac HTML5, będziemy mieli naprawdę zaawansowane narzędzia do tworzenia formularzy. Pytanie tylko kiedy?

Wszystkie wpisy należą do ich twórców. PHP.pl nie ponosi odpowiedzialności za treść wpisów.