Niezalogowany [ logowanie ]
Subskrybuj kanał ATOM Kanał ATOM    Subskrybuj kanał ATOM dla tagu zend_framework Kanał ATOM (tag: zend_framework)

Autor wpisu: Śpiechu, dodany: 05.12.2010 21:32, tagi: zend_framework, php

Dzisiaj będzie coś dla wielbicieli Zend Frameworka. A dokładniej coś, z czym mamy do czynienia przetwarzając jakikolwiek formularz, czyli walidatory. Walidacja, a więc sprawdzanie czy wprowadzone przez użytkownika dane odpowiadają pewnemu schematowi jest obok filtracji, czyli wyrzucaniu zbędnych/niebezpiecznych rzeczy z danych chyba najważniejszą sprawą przy tworzeniu witryn WWW.

Zend Framework dysponuje zestawem kilkudziesięciu walidatorów, które można łączyć w łańcuchy przy walidowaniu poszczególnych pól formularza, np. pytając o wiek użytkownika pole musi zawierać wyłącznie liczby, czyli aż prosi się o Zend_Validate_Digits, a następnie należy sprawdzić czy podana liczba znajduje się w jakimś ludzkim przedziale, tzn. powiedzmy pomiędzy 1 a 100, co załatwi za nas Zend_Validate_Between.

Co w przypadku jeżeli musimy dopasowywać coś, czego nie przewidzieli twórcy ZF? Możemy napisać własny walidator.

Każdy walidator musi implementować interfejs Zend_Validate_Interface, czyli dwie publiczne metody:

  • isValid($value) — zwracająca buliona prawda/fałsz
  • getMessages() — zwracająca tablicę komunikatów co jest nie tak z wartością testowaną w isValid(). Komunikaty zwracane są w postaci kod_błędu => komunikat_w_ludzkiej_formie

W celu uniknięcia babrania się obsługą błędów i komunikatów twórcy ZF stworzyli abstrakcyjną klasę Zend_Validate_Abstract, która zrobi za nas większość brudnej roboty. Pozostanie nam tylko napisanie właściwej logiki co ma być sprawdzane i ewentualnie jaki błąd zgłaszany.

Na potrzeby swojej aplikacji napisałem własny w miarę uniwersalny walidator, który sprawdzi czy klucz podstawowy danej tablicy istnieje. Może mieć zastosowanie jeżeli ktoś sprawdza czy np. identyfikator wybranej przez użytkownika kategorii istnieje w bazie.

Poniżej przedstawiem kod z objaśnieniami po co coś jest. Po przeczytaniu stwierdzicie, że pisanie walidatorów to fajna zabawa :-)

<?php
// rozszerzamy klasę abstrakcyjną dla walidatorów
class Spiechu_Validators_IsIdExists extends Zend_Validate_Abstract {     
 
  // definiujemy kody błędów i jakieś wartości dla nich
  const IDNOTEXISTS = 'idnotexists';
  const EMPTYSTRING = 'emptystring';
  const NULLVALUE = 'nullvalue';
 
  // definiujemy tablicę komunikatów błędów dla poszczególnych kodów
  // zwracam uwagę na '%value', które może być w locie zamienione na sprawdzaną wartość
  protected $_messageTemplates = array(
    self::IDNOTEXISTS   => "id '%value%' not exists",
    self::EMPTYSTRING  => 'given value is empty',
    self::NULLVALUE     => 'given value is null'
  );
 
  // zmienna typu Zend_Db_Table_Abstract, której klucz będziemy testować
  protected $_table;
 
  public function __construct(Zend_Db_Table_Abstract $table) {
    $this->_table = $table;
  }
 
  public function isValid($value) {
 
    // sprawdzamy czy zmienna nie jest nulem, a jeżeli tak to ustawiamy błąd
    if ($value === null) {
      $this->_error(self::NULLVALUE);
      return false;
    }
 
    // to samo dla pustego stringa
    if ($value === '') {
      $this->_error(self::EMPTYSTRING);
      return false;
    }
 
    $value = (int) $value;
 
    // ustawiamy wartość dla ewentualnego komunikatu %value%
    $this->_setValue($value);
 
    // metoda find szuka po kluczu podstawowym i zwraca rowset wyników
    $foundRows = $this->_table->find($value);
 
    // jeżeli nic nie znaleziono tzn. że sprawdzany klucz nie istenieje w tabeli bazy
    if (count($foundRows) == 0) {
      $this->_error(self::IDNOTEXISTS);
      return false;
    }
    else {
      return true;
    }
  }
}

Wszystko działa na tyle automagicznie, że tworząc jakiś element formularza wystarczy dodać stworzony walidator np. tak:

$formElement = new Zend_Form_Element_Select('pole_formularza');
$formElement->setRequired(true)
  ->setLabel('Jakaś etykieta')
  ->setMultiOptions($dane_do_wyboru)
  ->addValidator(new Spiechu_Validators_IsIdExists(new Sprawdzana_Tabela()));

Autor wpisu: batman, dodany: 12.11.2010 08:00, tagi: zend_framework

Co by nie pisać o Zend_Db, można z niego sporo wycisnąć. Jedną z takich wyciśniętych funkcjonalności są relacje. Wystarczy zdefiniować w klasach modelu zależne klasy i możemy cieszyć się relacjami, zamiast kombinować z joinami. Z początku relacje mogą być niejasne, jednak jak już je poznacie, nie będziecie mogli się bez nich obejść.

Wszystko zaczyna się w bazie. Przykładowe tabele to:

  • users – lista użytkowników
  • user_params – dodatkowe parametry użytkownika
  • roles – role, np admin
  • user_role – relacja między użytkownikami i rolami
CREATE TABLE users
(
  id serial NOT NULL,
  "login" character varying(50),
  "password" character varying(32),
  CONSTRAINT users_pkey PRIMARY KEY (id)
)

CREATE TABLE user_params
(
  id serial NOT NULL,
  iduser integer,
  param_name character varying(250),
  param_value text,
  CONSTRAINT user_params_pkey PRIMARY KEY (id),
  CONSTRAINT user_params_iduser_fkey FOREIGN KEY (iduser)
      REFERENCES users (id) MATCH SIMPLE
      ON UPDATE CASCADE ON DELETE CASCADE
)

CREATE TABLE roles
(
  id serial NOT NULL,
  "name" character varying(150),
  CONSTRAINT roles_pkey PRIMARY KEY (id)
)

CREATE TABLE user_role
(
  iduser integer NOT NULL,
  idrole integer NOT NULL,
  CONSTRAINT user_role_pkey PRIMARY KEY (iduser, idrole),
  CONSTRAINT user_role_idrole_fkey FOREIGN KEY (idrole)
      REFERENCES roles (id) MATCH SIMPLE
      ON UPDATE CASCADE ON DELETE CASCADE,
  CONSTRAINT user_role_iduser_fkey FOREIGN KEY (iduser)
      REFERENCES users (id) MATCH SIMPLE
      ON UPDATE CASCADE ON DELETE CASCADE
)

Następnie musimy stworzyć odpowiadające im klasy modeli (każda klasa powinna znaleźć się w osobnym pliku).

class Application_Model_DbTable_Users extends Zend_Db_Table_Abstract
{
    protected $_name = 'users';
    protected $_dependentTables = array(
        'Application_Model_DbTable_UserParams',
        'Application_Model_DbTable_UserRole'
    );

}

class Application_Model_DbTable_UserParams extends Zend_Db_Table_Abstract
{
    protected $_name = 'user_params';
    protected $_referenceMap = array(
    	'User' => array(
    		'columns' => array('iduser'),
    		'refTableClass' => 'Application_Model_DbTable_Users',
    		'refColumns' => array('id')
        )
    );
}

class Application_Model_DbTable_Roles extends Zend_Db_Table_Abstract
{
    protected $_name = 'roles';
    protected $_dependentTables = array(
        'Application_Model_DbTable_UserRole'
    );
}

class Application_Model_DbTable_UserRole extends Zend_Db_Table_Abstract
{
    protected $_name = 'user_role';
    protected $_referenceMap = array(
    	'User' => array(
    		'columns' => array('iduser'),
    		'refTableClass' => 'Application_Model_DbTable_Users',
    		'refColumns' => array('id')
        ),
    	'Role' => array(
    		'columns' => array('idrole'),
    		'refTableClass' => 'Application_Model_DbTable_Roles',
    		'refColumns' => array('id')
        )
    );
}

Jak to działa? W klasie “rodzica”, należy określić jaka tabela jest od tego rodzica zależna. Innymi słowy należy wskazać, w której klasie znajduje się klucz główny bieżącej klasy.

Zastosowanie relacji jest banalne i sprowadza się do wywołania jednej metody oraz wskazania, do której tabeli się odwołujemy. Jeśli chcielibyśmy pobrać wszystkie możliwe informacje, wystarczy, że napisze coś takiego:

$modelUser = new Application_Model_DbTable_Users();
// dane o uzytkowniku
$user = $modelUser->find(1)->current();
// parametry uzytkownika. jako parametr podajemy nazwe klasy, z ktorej pobieramy dane
$userParams = $user->findDependentRowset(
	'Application_Model_DbTable_UserParams'
);
// role uzytkownika. pierwszym parametrem jest tabela docelowa, drugim - tabela posrednia
$userRoles = $user->findManyToManyRowset(
	'Application_Model_DbTable_Roles',
	'Application_Model_DbTable_UserRole'
);

Relacje oferują jeszcze jedną ciekawą funkcjonalność. Możliwość pobrania danych z tabeli na podstawie relacji. Czyli mamy dany wiersz, np z tabeli user_params i na jego podstawie możemy pobrać dane powiązanego z nim użytkownika z tabeli users. Wygląda to następująco.

$modelUserParams = new Application_Model_DbTable_UserParams();
$userParam = $modelUserParams->find(1)->current();
$user = $userParam->findParentRow('Application_Model_DbTable_Users');

Do opisanych powyżej funkcjonalności dochodzi jeszcze jedna. W każdym z przypadków możemy do metody dodać obiekt Zend_Db_Table_Select, którym mamy możliwość ograniczania wyników.

Nie są to jedyne możliwości oferowane przez relacje w Zend_Db. Mamy możliwość skorzystania z “magii”, czyli z metod automatycznie tłumaczonych na odpowiednie klasy oraz reguł (jeśli w jednej tabeli jest kilka kluczy obcych z innej tabeli).

Autor wpisu: batman, dodany: 05.11.2010 11:13, tagi: zend_framework

Na początku listopada 2010 odbyła się konferencja ZendCon 2010. Spośród wszystkich informacji jakie na jej temat pojawiają się w sieci, brakuje jednej – wydano kolejny milestone drugiej wersji Zend Frameworka. W tym wydaniu zabrano się za autoloadera, ładowanie pluginów oraz wyjątki.

Autoloader

W dziedzinie automatycznego ładowania klas możemy spodziewać się następujących zmian:

  • dostarczenie mapy klas dla całego frameworka oraz poszczególnych komponentów
  • nowy loader bazujący na mapie klas
  • nowe narzędzia do generowania map

Ładowanie pluginów (Plugin Loading)

Pluginy w ZF są przydatnym mechanizmem, jednak ich stosowanie wiązało się z kilkoma problemami. Największym z nich jest wydajność, a raczej jej brak. Dzięki nowemu podejściu bazującemu na mapowaniu klas, udało się zmniejszyć ilość wywołań potrzebnych do wykonania kodu w pluginie z 19 do 7.

Wyjątki

Wyjątki zostały tak przepisane, by każdy z komponentów posiadał jeden marker, który będzie bazowym interfejsem dla poszczególnych wyjątków w danym komponencie. Ponadto wyjątki będą rozszerzać klasy wyjątków dostarczane w ramach SPL.

Co dalej?

Następne w kolejności jest MVC oraz wszystkie powiązane z nim komponenty, czyli Zend\View, Zend\Layout, Zend\Navigator oraz Zend\Form. Podobno prace już trwają. Kiedy zostaną przedstawione szerszej publiczności? Prawdopodobnie za kilka miesięcy.

Dokładne informacje na temat drugiego milestone’a oraz linki do pobrania znajdziecie pod adresem http://framework.zend.com/announcements/2010-11-03-zf2dev2.

Czytaj dalej tutaj (rozwija treść wpisu)
Czytaj dalej na blogu autora...

Autor wpisu: batman, dodany: 27.10.2010 08:00, tagi: zend_framework

Jedną z najgorszych czynności związanych z Zend_Form, jest pisanie w kółko tego samego fragmentu kodu – obsługi formularza. W większości przypadków obsługa ta sprowadza się do czterech kroków:

  1. Sprawdzenie, czy wysłano POSTa.
  2. Sprawdzenie, czy dane są poprawne.
  3. Przetworzenie danych.
  4. Przekierowanie na inną stronę.

Zgodnie z zasadą DRY, wypadałoby czynności te wydzielić do osobnego bloku (funkcji/metody/klasy), a następnie do tego bloku się odwoływać. Niestety na przeszkodzie stoi punkt 3, czyli operacje na danych, które nie zawsze muszą się powtarzać.

Z pomocą przychodzi tutaj PHP, a dokładniej anonimowe funkcje. Wprawdzie funkcjonalność ta jest dostępna dopiero od wersji 5.3 języka, jednak nie widzę powodu, aby nie iść z duchem czasu i trzymać się przestarzałych rozwiązań.

W przykładach będę korzystał z klasy Batman_Form, której opis znajdziecie we wpisie Uniwersalne dekorowanie Zend_Form.

Automatyczna obsługa formularzy Zend_Form sprowadza się do wykorzystania action helpera, który załaduje formularz, a następnie wykona po kolei opisane wyżej czynności. Funkcja anonimowa pozwoli na dodanie do action helpera kodu, który będzie można wykonać w ściśle określonym momencie. Na koniec tego teoretycznego wywodu napiszę jeszcze drobną uwagę. Obecna implementacja anonimowych funkcji opera się o klasę Closure i magiczną metodę __invoke. Manual enigmatycznie głosi, iż nie należy polegać na tej wiedzy, dlatego też może dojść do sytuacji, w której opisany przeze mnie sposób, przestanie działać. Rozwiązanie tego problemu jest banalnie proste i sprowadza się do usunięcia sprzed argumentu funkcji typu Closure.

A teraz do rzeczy. O pisaniu własnych helperów akcji przeczytacie w dokumentacji. W skrócie, helpery akcji są nieco okrojoną wersją pluginów i w przeciwieństwie do nich, wywoływane są na żądanie.

Kod helpera prezentuje się następująco

class Batman_Controller_Action_Helper_FormHandler extends Zend_Controller_Action_Helper_Abstract
{
    protected $_form = null;
    protected $_callbacks = array();

    public function postDispatch()
    {
        // czy wysłano POSTa
        if($this->getRequest()->isPost()) {
            $postData = $this->getRequest()->getPost();
            // czy dane są poprawne
            if($this->_form->isValid($postData)) {
                // pobranie wszystkich funkcji zwrotnych przypisanych do formularza
                $callbacks = $this->_form->getCallbacks();
                $formData = $this->_form->getValues();
                foreach($callbacks as $callback) {
                    // kolejne wywołanie zarejestrowanych funkcji zwrotnych
                    $callback($formData);
                }
            }
        }
    }

    /**
     * @return Zend_Form
     */
    public function getForm($name, $options = null)
    {
        $prefix = 'Application_';
        if(isset($options['prefix'])) {
            $prefix = $options['prefix'];
            unset($options['prefix']);
        }

        $formClass = $prefix . 'Form_' . $name;
        $this->_form = new $formClass($options);
        return $this->_form;
    }

    /**
     * @return Zend_Form
     */
    public function direct($name, $options = null)
    {
        return $this->getForm($name, $options);
    }
}

Założyłem w helperze, iż domyślną przestrzenią nazw dla klas jest Application. W większości przypadków takie założenie jest prawidłowe, jednak dla spokoju ducha, można w tym miejscu pobrać tą wartość z pliku konfiguracyjnego. Drugim założeniem jest wykorzystanie domyślnej struktury katalogów.

W sumie powyższy kod nie jest specjalnie skomplikowany. W momencie wywołania tego helpera, utworzony zostanie obiekt formularza, a następnie zwrócony do kontrolera.

Wywołanie wyżej opisanego helpera w kontrolerze wygląda nastęująco

Czytaj dalej tutaj (rozwija treść wpisu)
Czytaj dalej na blogu autora...

Autor wpisu: batman, dodany: 20.10.2010 08:00, tagi: zend_framework

Zend Framework oferuje wiele sposobów cache’owania danych. Jednym z najlepszych jest cache statyczny. Przy jego pomocy można zapisać wszystko to, co mamy na wyjściu (np. to co zostało wyświetlone w przeglądarce), do pliku HTML. W takim przypadku, każdy kolejny request będzie od razu otrzymywał w odpowiedzi wygenerowany plik HTML, bez użycia PHP. Brzmi nieprawdopodobnie? A jednak. Poza kilkoma niedociągnięciami, mechanizm ten spisuje się całkiem dobrze.

Mechanizmem, o którym przed chwilą pisałem, jest Zend_Cache_Backend_Static. Jego zastosowanie jest niezwykle proste, chociaż wymaga kilku niestandardowych modyfikacji.

Konfiguracja

Na samym początku musimy poinformować framework, że będziemy korzystać ze statycznego cache’u. Najszybciej zrobimy to korzystając z Zend_Cache_Manager. W tym celu w pliku application.ini musimy dodać następujący kod:

resources.cachemanager.page.backend.name = Static
resources.cachemanager.page.backend.options.public_dir = APPLICATION_PATH "/../public/static"
resources.cachemanager.pagetag.backend.options.cache_dir = APPLICATION_PATH "/../data/cache/static"

Informuje on naszą aplikację, że będziemy korzystać ze statycznego cache’u, który jest przechowywany w lokalizacji APPLICATION_PATH "/../public/static", a tagi opisujące cache znajdują się w katalogu APPLICATION_PATH "/../data/cache/static". Tagami na razie się nie przejmujcie. Opiszę je w dalszej części wpisu.

Powyższe opcje nie są jedynymi. Pełną ich listę znajdziecie w dokumentacji.

Skoro już przy pliku konfiguracyjnym jesteśmy, nie można zapomnieć o jednej bardzo ważnej modyfikacji. Musimy wyłączyć buforowanie wyjścia, które domyślnie jest włączone.

resources.frontController.params.disableOutputBuffering = true

Nie zapomnijcie o stworzeniu odpowiednich katalogów, ponieważ bez nich aplikacja przestanie działać.

.htaccess i punkt wejścia do aplikacji

Spore zmiany należy wprowadzić w pliku .htaccess. Powinien on wyglądać w następujący sposób (podziękowania dla Arka za pomoc z DirectoryIndex).

Czytaj dalej tutaj (rozwija treść wpisu)
Czytaj dalej na blogu autora...

Autor wpisu: batman, dodany: 15.10.2010 08:10, tagi: zend_framework

Wydawać by się mogło, iż ekipa odpowiedzialna za Zend Framework skupi się na drugiej wersji tego frameworka, a do wersji pierwszej będzie wydawać jedynie poprawki. Nic bardziej mylnego. Dziś ukazała się wersja beta frameworka oznaczona numerem 1.11. Wydanie to oferuje szereg nowych funkcjonalności, między innymi:

  • Zend_Http_UserAgent – wykrywanie urządzeń mobilnych. Od tego wydania znacznie prościej będzie tworzyć aplikacje oparte o Zend Framework na urządzenia mobilne
  • Zend_Cloud – implementacja SimpleCloud API.
  • obsługa SimpleDB, nierelacyjnej bazy danych dostarczanej przez firmę Amazon.
  • Zend_Service_Ebay – dzięki tym klasom programiści zyskają możliwość pobierania informacji na temat aukcji, kategorii oraz wyszukiwać po słowach kluczowych
  • nowe formaty konfiguracji frameworka – od wersji 1.11 będzie można korzystać z plików konfiguracyjnych w formacie JSON oraz YAML.
  • Zend_Service_ShortUrl – dzięki tej funkcjonalności będzie można korzystać z popularnych “skracaczy” linków
  • nowe helpery widoku:
    • Zend_View_Helper_UserAgent – wykorzystujący Zend_Http_UserAgent
    • Zend_View_Helper_TinySrc – helper pozwalający w zależności od urządzenia, na którym wyświetlana jest strona, na podmianę grafik
    • Zend_View_Helper_Gravatar – implementacja Gravatar API, pozwalająca na korzystanie z usługi Gravarat
  • poprawa bezpieczeństwa

Najnowszą wersję frameworka znajdziecie pod adresem: framework.zend.com/download/latest

Autor wpisu: batman, dodany: 10.10.2010 08:00, tagi: jquery, zend_framework

Jeśli mielibyście wskazać najpopularniejszą funkcjonalność wyszukiwarki (poza samym wyszukiwaniem), byłoby to najprawdopodobniej podpowiadanie wpisywanych słów. Autocomplete, bo o nim mowa, jest dostępny w Zend Frameworku w ramach biblioteki ZendX_JQuery. Jego zastosowanie jest wyjątkowo proste i sprowadza się do dodania do formularza jednego pola.

Polem tym jest ZendX_JQuery_Form_Element_Autocomplete. Wystarczy, że wskażemy adres, z którego będą wczytywane i mamy gotowy do użycia komponent.

Przykładowy formularz wygląda następująco

class Application_Form_Autocomplete extends Batman_Form
{
    protected function _renderForm()
    {
        $ac = new ZendX_JQuery_Form_Element_AutoComplete('ac');
        $ac->setJQueryParam('source', '/jquery/autocompletedata')
           ->setJQueryParam('minLength', 3)
           ->setJQueryParam('select', new Zend_Json_Expr('acSelected'))
           ->setLabel('Wpisz coś:');

        $this->addElement($ac);
    }
}

Jeśli chcemy, aby autocomplete działał, musimy ustawić przynajmniej jedną opcję – source. Bez tej opcji wyrzucony zostanie wyjątek informujący o braku źródła danych. Jako źródło podany został adres, z którego będą zwracane dane w postaci JSON. Pozostałe parametry są opcjonalne, a ich listę znajdziecie w dokumentacji jQuery. Jeżeli chcieli byście skorzystać z funkcji zwrotnych (w powyższym przykładzie jest to funkcja acSelected), musicie pamiętać o stworzeniu obiektu Zend_Json_Expr. Dzięki temu ciąg przekazany jako parametr nie zostanie zamieniony na zwykły string.

Akcja kontrolera odpowiedzialna za zwrócenie danych nie różni się niczym od pozostałych akcji, za wyjątkiem wywołania helpera json.

public function autocompletedataAction()
{
    // Wpisana przez użytkownika fraza
    $term = $this->_getParam('term');

    // Dane zwrócone do użytkownika. Mogą pochodzić z bazy danych, plików, itp
    $data = array(
        array(
    		'id' => 1,
    		'label' => 'pierwsza pozycja'
	    ),
        array(
    		'id' => 2,
    		'label' => 'druga pozycja'
	    )
    );

    $this->_helper->json($data);
}

Dane wpisywane w polu autocomplete przekazywane są w zmiennej term. Na jej podstawie można wyszukać odpowiednie informacje w bazie, pliku lub dowolnym innym źródle danych. Ważne, aby zapisać te dane w postaci tablicy. Jeśli zależy wam na dodatkowych danych, możecie stworzyć w tablicy kolejne klucze i wypełnić je odpowiednimi danymi. Dane te będą dostępne z poziomu Javascript po odebraniu odpowiedzi ze skryptu. Na samym końcu akcji wykorzystany został helper json, który jest odpowiedzialny za ustawienie odpowiedniego typu odpowiedzi oraz za wyłączenie layoutu.

I to wszystko. Jeden prosty formularz oraz jedna akcja i mamy zaimplementowaną funkcjonalność autocomplete.

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