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

Autor wpisu: batman, dodany: 26.06.2012 07:00, tagi: zend_framework

Nowa, długo oczekiwana, wersja Zend Frameworka oznaczona numerem 2, rodzi się w bólach. Aż cztery wersje beta były potrzebne, byśmy mogli w końcu zobaczyć jak mniej-więcej będzie wyglądało tworzenie aplikacji w oparciu o ten framework. Jako pierwszy komponent na warsztat wziąłem Zend Form, który przysparzał programistom masę problemów. Począwszy od dekoratorów, na które klęli chyba wszyscy, przez walidację i filtrowanie danych, na zupełnym oderwaniu od wszelkich wzorców kończąc.

Nowy Zend Form na pierwszy rzut oka wydaje się być dobrze przemyślanym komponentem, jednak kilka drobiazgów potrafi napsuć krwi, zwłaszcza rozdrobnienie kodu po niezliczonej ilości plikach. W ZF1 mieliśmy po prostu klasę dziedziczącą po Zend_Form, która zajmowała się wszystkim. Na upartego można było dodać własną klasę pośrednią, która robiła porządek z dekoratorami oraz ewentualnie view script. ZF2 wprowadził małą rewolucję, która wbrew pozorom znacząco ułatwi pracę z formularzami oraz danymi pochodzącymi od użytkownika w ogóle.

Jak już wspomniałem, nowy Zend Form jest mocno rozdrobniony. Co to właściwie oznacza? Ni mniej, ni więcej jak konieczność napisania kilku metod oraz utworzenie kilku plików, tylko po to, byśmy mogli cieszyć się formularzem na stronie. Początkowo takie podejście mocno mi nie odpowiadało, jednak z czasem dostrzegłem jego zalety, zwłaszcza przy założeniu pełnej modułowości ZF2.

Zacznijmy od utworzenia formularza. Do przygotowania formularza zdecydowałem się na klasę modelu, której jedna z metod zwraca formularz. Należy tutaj wspomnieć, iż formularz to nie jest gotowy kod HTML, tylko klasa pośrednicząca między danymi, a widokiem.

namespace Auth\Model;

use Zend\Form\Form;
use Zend\Form\Element;
use Zend\InputFilter\InputFilter;
use Zend\InputFilter\Input;
use Zend\Validator;

class Sign
{
    public function getForm()
    {
        $login = new Element('login');
        $login->setAttributes(
            array(
                'type' => 'text',
                'label' => 'Login'
            )
        );

        $password = new Element('password');
        $password->setAttributes(
            array(
                'type' => 'password',
                'label' => 'Hasło'
            )
        );

        $submit = new Element('btn_signin');
        $submit->setAttributes(
            array(
                'type' => 'submit',
                'value' => 'Zaloguj'
            )
        );

        $form = new Form('signin_form');
        $form->add($login);
        $form->add($password);
        $form->add($submit);

        return $form;
    }
}

Nie będę wyjaśniał poszczególnych metod, ponieważ ich opis znajdziecie w dokumentacji. Powyższy formularz jest typowym formularzem logowania i składa się z pola do wpisania loginu i hasła oraz przycisku wysyłającego formularz. Na pewno zwróciliście uwagę, że formularz nie posiada zdefiniowanych walidatorów oraz filtrów. Od wersji ZF2 formularz nie wie co to jest walidator i filtr. Zajmuje się tym zupełnie inne klasa o nazwie InputFilter. Dzięki tej klasie możemy zdefiniować reguły walidacji oraz filtrowania danych niezależnie od ich pochodzenia. Oznacza to, że reguły, które zastosowalibyśmy w przypadku formularza, będą mogły być wykorzystane w przypadku dowolnego źródła danych (bez konieczności duplikowania kodu), np. usługi sieciowej czy wiersza poleceń. Dodajmy więc reguły walidatora. Zrobimy to w kolejnej metodzie, a następnie przekażemy je do formularza. Ostatecznie klasa modelu będzie wyglądała następująco.

namespace Auth\Model;

use Zend\Form\Form;
use Zend\Form\Element;
use Zend\InputFilter\InputFilter;
use Zend\InputFilter\Input;
use Zend\Validator;

class Sign
{
    public function getForm()
    {
        $login = new Element('login');
        $login->setAttributes(
            array(
                'type' => 'text',
                'label' => 'Login'
            )
        );

        $password = new Element('password');
        $password->setAttributes(
            array(
                'type' => 'password',
                'label' => 'Hasło'
            )
        );

        $submit = new Element('btn_signin');
        $submit->setAttributes(
            array(
                'type' => 'submit',
                'value' => 'Zaloguj'
            )
        );

        $form = new Form('signin_form');
        $form->add($login);
        $form->add($password);
        $form->add($submit);

        $form->setInputFilter($this->getInputFilter());

        return $form;
    }

    public function getInputFilter()
    {
        $login = new Input('login');
        $login->getValidatorChain()
              ->addValidator(new Validator\NotEmpty());

        $password = new Input('password');
        $password->getValidatorChain()
                 ->addValidator(new Validator\NotEmpty());

        $inputFilter = new InputFilter();
        $inputFilter->add($login);
        $inputFilter->add($password);

        return $inputFilter;
    }
}

Jak widać na załączonym “obrazku”, dodawanie walidatorów nie różni się znacząco od ZF1. Podobnie rzecz ma się z filtrami. Poinformowanie formularza, że ma korzystać ze zdefiniowanych reguł odbywa się poprzez metodę setInputFilter.

Najwyższa pora wypróbować przygotowany formularz. Najpierw kontroler, w którym utworzymy obiekt oraz przekażemy go do widoku.

namespace Auth\Controller;

use Zend\Mvc\Controller\ActionController;
use Zend\View\Model\ViewModel;

class SignController extends ActionController
{
    public function inAction()
    {
        $model = new \Auth\Model\Sign();
        $form = $model->getForm();
        $request = $this->getRequest();

        if($request->isPost()) {
            $data = $request->post();
            $form->setData($data);
            if($form->isValid()) {
                // obsluga formularza
            }
        }

        return new ViewModel(
            array('form' => $form)
        );
    }
}

Zasadniczą różnicą jest tutaj ViewModel, o którym napiszę przy okazji dokładniejszego opisywania ZF2. Na chwilę obecną wystarczy wiedzieć, iż jest to klasa pośrednicząca w wymianie danych między modelem i widokiem. Reszta odbywa się prawie tak samo jak w przypadku ZF1, czyli sprawdzamy, czy zostały wysłane dane POST, przekazujemy te dane do formularza (zamiast populate mamy metodę setData), a następnie sprawdzamy, czy przekazane dane są poprawne.

Na koniec został widok, który mocno różni się od tego, co znamy z ZF1. W poprzedniej wersji frameworka wystarczyło “wyechować” formularz. Teraz musimy sami zatroszczyć się o proces renderowania formularza. Oczywiście robimy to w widoku.

<?php
$form = $this->form;
echo $this->form()->openTag($form);
?>

    <?php
        $name = $form->get('login');
        echo $this->formLabel($name);
        echo $this->formInput($name);
        echo $this->formElementErrors($name);
    ?>


    <?php
        $name = $form->get('password');
        echo $this->formLabel($name);
        echo $this->formInput($name);
        echo $this->formElementErrors($name);
    ?>


<?php echo $this->formElement($form->get('btn_signin')); ?>
<?php
echo $this->form()->closeTag();
?>

Podstawową wadą takiego podejścia jest ogrom kodu, który trzeba napisać w przypadku rozbudowanych formularzy. Każde pole musi zostać wyrenderowane z osobna (możliwe, że można to zrobić za jednym wywołaniem metody, jednak jeszcze się nie dokopałem do takiej możliwości), co oznacza, że plik z widokiem może być ogromny, zwłaszcza, jeśli na stronie mamy kilka formularzy. Na szczęście możemy ułatwić sobie sprawę i napisać partial, który będzie odpowiedzialny za konkretny formularz. Oznacza to stworzenie kolejnego pliku, ale i tak lepsze to niż przewijanie setek linii kodu. Niezaprzeczalną zaletą tego rozwiązania jest fakt, iż w końcu możemy bez większego problemu dowolnie rozmieścić elementy formularza, bez zabawy z dekoratorami.

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

Autor wpisu: Marek, dodany: 17.06.2012 22:20, tagi: zend_framework

zfBardzo często w różnych formularzach musiałem stosować taki schemat postępowania:

  • przy dodawaniu wszystkie pola formularza są dostępne do uzupełnienia
  • przy edycji część pól nie może być zmieniana, ale musi być wyświetlona
Zamiast dodawać atrybut disabled, wolę wyświetlać wartość danego pola jako zwykły tekst. W przypadku pól tekstowych nie ma najmniejszego problemu – wyświetlałem ich wartość, w przypadku selektorów używałem poniższego zapisu:
<?php $c = $this->form->category_id ?>
<?php echo $c->getMultiOption($c->getValue())?>

Dzisiaj przedstawię przykład tworzenia dekoratora formularza, który skróci powyższy zapis i umożliwi korzystanie z niego w wielu miejscach w  aplikacji.

<?php

/**
 * Dekorator wyświetla "treść" zaznaczonej opcji elementu
 * Jeśli element nie jest selektorem, wyświetlana jest wartość elementu,
 * w przypadku pól typu MultiSelect oraz MultiCheckbox,
 * zaznaczone elementy są łączone przy pomocy separatora
 * @author Marek Pietrzak
 */
class My_Decorator_Selected extends Zend_Form_Decorator_Abstract {

    protected $_format = '<span id="%s">%s</span>';

    /**
     * Wyświetlenie zaznaczonej opcji elementu
     * @param string $content
     * @return string
     */
    public function render($content) {
        $element = $this->getElement();
        $id = htmlentities($element->getId());
        $value = $element->getValue();
        if ($element instanceof Zend_Form_Element_Multi) {
            // dla obiektów MultiSelect i MultiCheckbox zaznaczone opcje są sklejane
            if (is_array($value)) {
                $separator = $this->getSeparator();
                $options = $element->getMultiOptions();
                $selectedValues = array_intersect_key($options, array_flip($value));
                $selected = implode($separator, array_map('htmlentities', $selectedValues));
            } else {
                // w przeciwym razie pobieramy zaznaczoną opcję
                $selected = htmlentities($element->getMultiOption($value));
            }
        } else {
            // domyślnie zwracana jest wartość obiektu
            $selected = $value;
        }

        return sprintf($this->_format, $id, $selected);
    }

}

Klasę nazwałem My_Decorator_Selected, a więc należy ją umieścić w pliku library/My/Decorator/Selected.php i nie zapomnieć o dodaniu do pliku application.ini:

autoloadernamespaces[] = "My_"

Klasa dziedziczy po Zend_Form_Decorator_Abstract, a więc musi implementować metodę render($content). Zmienna $_format przechowuje format w jakim zostanie wyświetlony wynik działania dekoratora. W metodzie render() pobieram dekorowany element, potem jego id i wartość. Następnie sprawdzam czy element dziedziczy po Zend_Form_Element_Multi (czyli jest selektorem, checkboxem lub polem typu radio). Jeśli nie – w wynikowym <span> będzie wyświetlona jego wartość. Jeśli tak, sprawdzam czy jego wartość jest tablicą. Gdy nie jest – pobieram wartość zaznaczonej opcji, w przeciwym przypadku pobieram separator i za jego pomocą sklejam wszystkie zaznaczone wartości.

Przykłady wywołania:

<pre>
<?php
$options = array('adin' => 'jeden', 'dwa' => 'dwa', 'tri' => 'trzy');

/* multiselektor */
$ms = new Zend_Form_Element_Multiselect('multiselektor');
$ms->addMultiOptions($options)
        ->addDecorator(new My_Decorator_Selected(array('separator' => PHP_EOL)))
        ->setValue(array('adin', 'tri'));
echo $ms->renderViewHelper() . PHP_EOL;
echo $ms->renderSelected();
echo PHP_EOL . '***************************' . PHP_EOL;

/* selektor */
$s = new Zend_Form_Element_Select('selektor');
$s->addMultiOptions($options)
        ->addDecorator(new My_Decorator_Selected())
        ->setValue(array('dwa'));
echo $s->renderViewHelper() . PHP_EOL;
echo $s->renderSelected();
echo PHP_EOL . '***************************' . PHP_EOL;

/* multi checkbox */
$c = new Zend_Form_Element_MultiCheckbox('checkbox');
$c->addMultiOptions($options)
        ->addDecorator(new My_Decorator_Selected(array('separator' => ', ')))
        ->setValue(array('adin', 'tri'));
echo $c->renderViewHelper() . PHP_EOL;
echo $c->renderSelected();
echo PHP_EOL . '***************************' . PHP_EOL;

/* radio */
$r = new Zend_Form_Element_Radio('radio');
$r->addMultiOptions($options)
        ->addDecorator(new My_Decorator_Selected())
        ->setValue(array('adin'));
echo $r->renderViewHelper() . PHP_EOL;
echo $r->renderSelected();
echo PHP_EOL . '***************************' . PHP_EOL;
?>
</pre>

Wynik: [przykład użycia renderSelected]

Autor wpisu: Marek, dodany: 11.06.2012 19:28, tagi: zend_framework

zfW celu uniknięcia powtarzania pewnych fragmentów kodu w widokach używamy view helper’ów. W Zend Framework jest to banalnie proste. Przedstawię tutaj zupełne podstawy tworzenia pomocników widoku na prostym przykładzie, który będzie zwracał kwotę brutto.

Na początek tworzymy w katalogu library ścieżkę: My/View/Helper/ i umieszczamy w niej nowy plik Brutto.php

Żeby plik był widoczny w aplikacji, dodajemy do application.ini:

resources.view.helperPath.My_View_Helper = "My/View/Helper"

 Poniżej kod pliku pomocnika Brutto.php:

<?php
/**
 * Pomocnik widoku - kwota brutto
 * @package helpers
 */

/**
 * Klasa pomocnika widoku Brutto
 * Służy do przeliczania kwoty z netto na brutto
 * @package helpers
 */
class My_View_Helper_Brutto extends Zend_View_Helper_Abstract {

    /**
     * Zwraca wartość kwoty brutto
     * @param double $netto wartość netto
     * @param int    $vat wartość vat
     * @return double
     */
    public function brutto($netto, $vat = 23){
        return sprintf("%.2f", $netto + $netto * $vat / 100);
    }
}

Kilka szczegółów:

  • klasa My_View_Helper_Brutto musi dziedziczyć po Zend_View_Helper_Abstract
  • klasa musi posiadać przynajmniej jedną metodę, która nazywa się tak jak plik Brutto.php (z tym, że nazwę rozpoczynamy małą literą) – brutto()
  • w widoku odwołujemy się do metody helpera poprzez zmienną $this

Wywołanie helpera – w widoku:

<?php echo $this->brutto($netto, $vat); ?>

I to na tyle. Więcej szczegółów w manualu: http://framework.zend.com/manual/en/zend.view.helpers.html

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

Nieco ponad tydzień temu otrzymałem maila z zapytaniem jak przy pomocy Zend_Layout wykonać układ wielokolumnowy. Wbrew pozorom nie jest to proste do wykonania, ponieważ do wyboru mamy kilka możliwości. Najprostszą z nich byłoby napisanie helpera widoku, który naszpikowany if-ami wyświetlałby stosowną zawartość. O ile w przypadku prostej aplikacji niewymagającej drastycznych zmian wyglądu dynamicznej kolumny, zdałoby to egzamin, tak w momencie gdy kolumny będą znacząco się od siebie różnić w zależności od aktualnej akcji, możemy stworzyć potworka, którego będziemy wstydzili się pokazać nawet niewidomej osobie.

Nie oznacza to, że powinniśmy całkowicie zrezygnować z helperów widoku. Wręcz przeciwnie, będą one stanowić podstawę dynamicznej kolumny. Wystarczy dodać skrypt widoku oraz pobrane z modelu dane, a całość przekazać do layotu. Resztą zajmie się Zend.

Zacznijmy do helpera widoku. Poniższy przykład będzie bardziej niż prosty – ma celu zaprezentowanie idei opisywanego rozwiązania.

class Zend_View_Helper_Column1 extends Zend_View_Helper_Abstract
{
    public function column1(array $data)
    {
        $this->view->data = $data;
        return $this->view->render('column1.phtml');
    }
}

Helper przyjmuje tablicę z danymi, a następnie wyświetla je przy pomocy skryptu widoku, który prezentuje się następująco.

Aktywni użytkownicy
<ul>
    <?php foreach ($this->data as $userName): ?>
        <li><?php echo $userName; ?></li>
    <?php endforeach; ?>
</ul>

Mamy już helper oraz skrypt widoku, pora zająć się layoutem. Załóżmy, że nasza aplikacja będzie składać się z dwóch kolumn.

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8" />
        <title>multicolumn layout</title>
    </head>
    <body>
        <section class="content">
            <?php echo $this->layout()->content; ?>
        </section>
        <aside class="aside">
            <?php echo $this->layout()->aside; ?>
        </aside>
        <div class="clear"></div>
    </body>
</html>

Domyślnie Zend Framework przekazuje do placeholdera o nazwie content zawartość skryptu widoku bieżącej akcji. My wykorzystamy ten sam mechanizm do wyświetlenia dodatkowej kolumny. Stąd właśnie w powyższym kodzie znalazł się placeholder aside. Ponieważ Zend Framework nie wie co ma się w nim znaleźć, nie będzie on nic wyświetlał. Sami musimy zadbać o poinformowanie ZF jaka treść ma wyświetlić. A zrobimy to w akcji kontrolera.

public function indexAction()
{
    $this->_helper->getHelper('Layout')->assign(
        'aside',
        $this->view->column1(array('Jan Kowalski', 'Ewa Nowak'))
    );
}

W prawdziwej aplikacji dane przekazane do helpera będą pochodzić z modelu, jednak dla uproszczenia przykładu, zastosowałem najzwyklejszą tablicę.

I to już wszystko co jest potrzebne do wykonania wielokolumnowego układu strony z dynamiczną kolumną. Nie jest to uniwersalne rozwiązanie, ponieważ jak to zwykle w życiu bywa, wszystko zależy od aplikacji, nad którą właśnie pracujemy. Niemniej w większości przypadków sprawdzi się doskonale i będzie wymagać jedynie drobnych modyfikacji.

Kompletny kod aplikacji znajdziecie na GitHubie.

Autor wpisu: zleek, dodany: 16.04.2012 09:50, tagi: zend_framework, php

W ostatnim projecie musiałem stworzyć dwa pola typu select, gdzie wartości dostępne w drugim polu były zależne od wybranej opcji w pierwszego pola select. Sztandarowym przykładem może tutaj być lista województw + lista miejscowości w ramach wybranego województwa. Sytuacja wygląda tak, że mamy pierwsze pole typu select: Jak więc widać w polu tym umieszczony jest [...]

Autor wpisu: zleek, dodany: 05.04.2012 14:22, tagi: zend_framework, php

Zapewne wielu z Was spotkało się z problemem sortowania polskich znaków podczas pobierania danych z bazy, szczególnie gdy mamy ustawione porównywanie znaków na utf8_general_ci. Sztandarowym przykładem może być tutaj lista województw, gdzie standardowo województwo łódzkie będziemy mieli na samym końcu. Rozwiązaniem tego problemu jest wskazanie zestawu znaków dla określonej kolumny, którą wskażemy w sekcji ORDER [...]

Autor wpisu: d3ut3r, dodany: 04.04.2012 03:45, tagi: zend_framework, php

Dziś chciałbym przedstawić Wam w jaki sposób tworzyć kontrolery i akcje w Zend Framework zanim jednak zaczniemy potrzeba nieco teorii o tym co to jest kontroler i akcja :) tak więc zapraszam do czytania.

1. Czym są kontorlery

Najprościej mówiąc kontroler to część aplikacji której zadaniem jest odebranie danych od użytkownika oraz ich analiza.

2. Czym są akcje

Akcja to część składowa kontrolera, każdy kontroler w ZF powinien posiadać przynajmniej 1 akcję która nazywa się indexAction jest ona wywoływana jako akcja domyślna.

To tyle jeżeli chodzi o teorie :) oczywiście czytając to pierwszy raz możesz czuć się nieco zagubiony, mam jednak nadzieję że pod koniec wpisu wszystko będzie już jasne. W poprzednim wpisie “Zend Framework pierwsze starcie” pokazałem w jaki sposób wygenerować projekt o nazwie helloZend dziś dalej będziemy pracować na tym projekcie.

Ostatnim krokiem przed pracą z kodem będzie skonfigurowanie naszego projektu do pracy w trybie development co pozwoli nam w czytelny sposób śledzić ewentualne błędy w kodzie. Rozpoczynamy od edycji pliku .htaccess  z katalogu public naszego projektu. Po otwarciu pliku w pierwszej linii wpisujemy:

SetEnv APPLICATION_ENV development

Skoro wprowadziliśmy już wszystkie wymagane zmiany, mniej więcej wiemy co to kontroler a co akcja pora przejść do praktyki. Zaczniemy od pewnego faktu dotyczącego poprzedniego projektu helloZend otóż linki:

robią dokładnie to samo. W przypadku 1 linku nie podajemy żadnych parametrów zatem system wywołuje domyślny kontroler (index) oraz domyślną akcję (index). Link drugi określa kontroler którego chcemy użyć (w naszym przypadku index) oraz sam dobiera domyślną akcję. Link trzeci jest pełnym linkiem i określa on zarówno kontroler jak i akcję. Możemy zatem powiedzieć, że ogólny schemat linków dla projektów ZF wygląda tak: http://example.com/kontroler/akcja

3. Tworzenie kontrolerów

Kontrolery naszej aplikacji przechowywane są w katalogu application/controllers domyślnie są tam 2 pliki: IndexController.php oraz ErrorController.php możemy dodawać nowe kontrolery do naszej aplikacji wydając komendę:

zf create controller nazwa_kontrolera domyslna_akcja moduł
  • nazwa_kontrolera – raczej nie trzeba tłumaczyć ważne aby nazwa była intuicyjna i odzwierciedlała to czym kontroler będzie się zajmował, jeżeli np tworzymy kontroler dla newsów nazwijmy go news
  • domyslna_akcja – parametr ten ma domyślną wartość 1 i określa czy wraz z utworzeniem nowego kontrolera chcemy aby system wygenerował domyślną akcję index
  • moduł – parametr określa moduł do którego należeć będzie kontroler.

Czas przejść do czynów, do naszego projektu helloZend dodamy kontroler który nazwiemy pierwszy w tym celu będąc w katalogu projektu wydajemy polecenie:

zf create controller pierwszy

dzięki temu w katalogu controllers naszej aplikacji pojawił się plik o nazwie PierwszyController.php jeżeli otworzymy wspomniany plik zobaczymy, że skrypt wygenerował dla nas całą strukturę łącznie z akcją index(metoda o nazwie indexAction).

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

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