Tuesday
27
December
autor:
Mateusz Tymek
kategorie:
brak komentarzy skomentuj

Rzut okiem na Zend Framework 2: Event Manager

Wtyczka do NetBeans kolorująca składnię plików .twig

Zend Framework 2 zbliża się wielkimi krokami. Po okresie zastoju zdecydowano się przenieść główne repozytorium projektu na GitHub i bardziej otworzyć się na społeczność - nie trzeba juz podpisywać CLA przed dodaniem swoich łatek. Dzięki temu rozwój wyraźnie przyspieszył, a kod zaczyna się stabilizować.

Pierwszą nowością którą opiszę jest Event Manager. Komponent ten stanowi jeden z fundamentów nowej wersji frameworka.

Czym jest Event Manager?

Event Manager jest implementacją wzorca "obserwator". Składa się z kilku klas umożliwiających definiowanie zdarzeń oraz podpinanie pod nie funkcji. Takie zdarzenia to np. start aplikacji, lokalizowanie kontrolera i akcji na podstawie URL-a (routing), czy wreszcie wysyłanie odpowiedzi serwera.

Komponent ten wykorzystywany jest na różne sposoby. Na przykład nowy stos MVC używa systemu zdarzeń, aby zespoić warstwy kontrolera i widoku.

Menadżer Zdarzeń a pluginy w Zend Framework 1

Można powiedzieć, że Event Manager jest uogólnieniem systemu pluginów znanego z ZF1. Jeśli pamiętasz poniższy schemat:

Wywoływanie zdarzeń

Wszystkie zdarzenia wywoływane są w kontekście określonego obiektu. Powiemy: obiekt klasy Zend\Mvc\Application wywołuje zdarzenie "dispatch", obiekt klasy Zend\Module\Manager wywołue zdarzenie "loadModule", itp.

Jako przykład, wyobraźmy sobie bardzo prosty silnik blogowy:

class BlogEngine
{  
    public $article;

    public function renderArticle() 
    {	
	echo $this->article;
    }
}

Zależy nam, aby udostępnić innym programistom możliwość filtrowania artykułu przed wysłaniem go do przeglądarki, co osiągniemy poprzez dodanie Event Managera do powyższej klasy. Metoda renderArticle wywoła zdarzenie o tej samej nazwie (zgodnie z konwencją spotykaną w źródłach ZF2), co pozwoli na zmianę treści, zanim ta zostanie wysłana.

use Zend\EventManager\EventCollection,
    Zend\EventManager\EventManager;

class BlogEngine
{
    protected $events;

    public $article;    

    public function events(EventCollection $events = null)
    {
	if (null !== $events) {
	    $this->events = $events;
	} elseif (null === $this->events) {
	    $this->events = new EventManager(__CLASS__);
	}
	return $this->events;
    }

    public function renderArticle() 
    {	
	$this->events()->trigger('renderArticle', $this);
	echo $this->article;
    }
}

Metoda events() umożliwia dostęp do Menadżera zdarzeń. Używamy jej w funkcji renderArticle(), aby wywołać zdarzenie bezpośrednio przed instrukcją echo.

Nasłuchiwanie zdarzeń

Zdarzenie wywołane w poprzednim paragrafie będzie bezużyteczne, dopóki nie podepniemy do niego jakiegoś kodu. Załóżmy, że chcemy utworzyć filtr, który usunie wszystkie znaczniki HTML, a potem zamieni przejścia do nowej linii na tagi <br>:

$blogEngine = new BlogEngine();

$blogEngine->events()->attach('renderArticle', function($event) { 
    $target = $event->getTarget();
    $target->article = strip_tags($target->article);
    $target->article = nl2br($target->article);
});

/* ... */

$blogEngine->renderArticle();

Funkcje obsługi zdarzeń możemy przypinać statycznie - to znaczy w momencie gdy nie posiadamy jeszcze obiektu klasy wywołującej zdarzenia. Wykorzystujemy do tego klasę Zend\EventManager\StaticEventManager. Poniższy kod da dokładnie taki sam efekt:

$events = StaticEventManager::getInstance();
$events->attach('BlogEngine', 'renderArticle', function($event) { 
    $target = $event->getTarget();
    $target->article = strip_tags($target->article);
    $target->article = nl2br($target->article);
});

/* ... */

$blogEngine = new BlogEngine();

$blogEngine->renderArticle();

Pierwszy parameter metody attach ("BlogEngine") jest to identyfikator obiektu który wywołuje zdarzenie. Konwencja zaleca używanie nazywy klasy, jednak można też zdefiniować inne identyfikatory:

$this->events = new EventManager(array('MyNamespace\Foo\Bar\BlogEngine', 'blog'));

/* ... */

$events->attach('blog', 'renderArticle', function($event) { 
    $target = $event->getTarget();
    $target->article = strip_tags($target->article);
    $target->article = nl2br($target->article);
});

Zend\EventManager\Event

Funkcja obsługi zdarzenia przyjmuje pojedynczy parametr: obiekt klasy Zend\EventManager\Event. Udostępnia on kilka przydatnych metod:

  • getTarget() - zwraca obiekt wywołujący zdarzenie
  • getParam() i setParam() - pozwalają na przekazywanie dodatkowych parametrów
  • stopPropagation() - użycie tej metody przerwie wywoływanie kolejnych funkcji podpiętych pod dane zdarzenie.

Podsumowanie

Więcej informacji na temat Zend\EventManager znajdziesz w dokumentacji, która dostarcza też kilku przykładów.

czytaj dalej »

Tagi: Zend\EventManager
Monday
18
July
autor:
Mateusz Tymek
kategorie:
brak komentarzy skomentuj

Twig w praktyce

Wtyczka do NetBeans kolorująca składnię plików .twig

Jednym z powodów dla którego postanowiłem stworzyć tą stronę własnoręcznie (zamiast sięgnąć po gotowce typu WordPress), była chęć posiadania pewnego rodzaju "poligonu doświadczalnego". Pozwala mi to sprawdzać różne rozwiązania w praktyce, zanim będę mógł "sprzedać je" klientom. Po napisaniu artykułu o Twigu postanowiłem poświęcić trochę czasu i wdrorzyć go na tej stronie. Zadanie okazało się dosyć łatwe i pozwoliło mi zapoznać się z tą technologią.
W tym poście zebrałem kilka przykładów i porównań między szablonami w czystym PHP a Twigiem. 

Zwig

W poprzednim wpisie pokazałem jak samodzielnie zintegrować Twiga z projektem napisanym w ZF. Takie podejście jest bardzo fajne pod względem edukacyjnym, jednak przed przystąpieniem do poważniejszego projektu zawsze warto sprawdzić, czy nie wymyślamy koła na nowo. W tym przypadku znalazłem świetną bibliotekę - Zwig (https://github.com/arnaud-lb/Zwig). Jej największa zaletą jest transparentna obsługa helperów widoku - zarówno tych Zendowych, jak i własnych. Wrócę do tego za chwilę, najpierw pokażę kod (metodę w Bootstrapie) którym spiąłem moją aplikację z Twigiem:

protected function _initView()
{
    $view = new Zwig_View(array(
        'encoding' => 'UTF-8'
    ));

    $loader = new Twig_Loader_Filesystem(array());
    $zwig = new Zwig_Environment($view, $loader, array(
        'cache' => APPLICATION_PATH . '/../data/cache/twig/',
        'auto_reload' => true,
    ));

    $view->setEngine($zwig);
    $view->doctype(Zend_View_Helper_Doctype::XHTML1_STRICT);
    $view->addScriptPath(APPLICATION_PATH . '/layouts/scripts');

    $view->user = Zend_Auth::getInstance()->getIdentity();

    $viewRenderer = new Zend_Controller_Action_Helper_ViewRenderer($view, array(
        'viewSuffix' => 'twig',
    ));
    Zend_Controller_Action_HelperBroker::addHelper($viewRenderer);

    return $view;
}

Breadcrumby

 Na początek tak zwane "okruszki chleba". Chcemy uzyskać mniej więcej taki efekt:

   Strona główna >> Motocykle >> Suzuki

Niech pierwsze dwa elementy będą linkami. Definiujemy tablicę (np w kontrolerze) i przekazujemy ją do widoku:

$this->view->breadcrumbs = array(
    array(
        'url' => '/',
        'title' => 'Strona Główna'
    ),
    array(
        'url' => '/motocykle',
        'title' => 'Motocykle'
    ),
    array(
        'title' => 'Suzuki'
    ),
);       

<div id="breadcrumbs">
    {% for breadcrumb in breadcrumbs %}
        {% if breadcrumb.url is defined %}
        <span>
            <a title="{{ breadcrumb.title }}" href="{{ breadcrumb.url }}">{{ breadcrumb.title }}</a>
        </span>
        {% else %}
        <span title="{{ breadcrumb.title }}">
        {{ breadcrumb.title }}
        </span>
        {% endif %}
        {% if not loop.last %}
        &raquo;
        {% endif %}
    {% endfor %}
</div>

Gettery

Jeżeli dany obiekt nie posiada publicznych właściwości a dostęp do nich odbywa się przy poprzez funkcje typu getXXX(), Twig pozwala na skrócenie zapisu:

Szablony PHP

<?php echo $user->getName() ?>
   <?php echo $user->getEmail() ?>

Twig

{{ user.name }}
    {{ user.email }}

Paginacja

Zend_Paginator umożliwia określenie widoku który wyświetli listę stron. 

Szablony PHP

<?php if ($this->pageCount > 1): ?>
    <div class="paginationControl">   
        <span>Idź do strony:</span>        
        <?php foreach ($this->pagesInRange as $page) : ?>
                <?php if ($this->current == $page) : ?>
                    <?php echo $page ?>
                <?php else : ?>
                    <a href="<?php echo $this->url(array('page' => $page)) ?>">
                        <?php echo $page ?>
                    </a>            
                <?php endif; ?>
        <?php endforeach; ?>
    </div>
    <?php endif; ?>

Twig

{% if pageCount > 1 %}
    <div class="paginationControl">   
        <span>Idź do strony:</span>

        {% for page in pagesInRange %}
            {% if current == page %}
            {{ page }}
            {% else %}
            <a href="{{ url({'page': page}) }}">
                {{ page }}
            </a>
            {% endif %}
        {% endfor %}
    </div>
    {% endif %}

Formularze

Szablony PHP

<?php echo $this->registrationForm ?>

Twig

{{ registrationForm | raw }}

 

Ustawianie zmiennych

Zdarza się że w widoku wyświetlamy coś kilka razy (np link do artykułu).

Szablony PHP

<?php $link = $this->url(array('slug' => $this->article['slug']), 'view-article'); ?>
    <?php echo $link ?>

Twig

{% set link = url({'slug' : article.slug}, 'view-article') %}
    {{ link }}

Helpery widoku

 

Jak napisałem wyżej, po skonfigurowaniu Zwig pozowala na natychmiastowe wykorzystanie wszystkich pomocników widoku - także tych zdefiniowanych przez nas. 
Jakiś czas temu popełniłem artykuł na temat tworzenia menu z ikonkami (/blog/zend-navigation-menu-z-ikonkami) na bazie Zend_Navigation. W backendzie bloga używałem czegoś takiego:

<div id="top-menu">
        <?php echo $this->iconMenu($this->navigation()->findById('main'))
                        ->setIconPath($this->baseUrl('/admin/images/icons')) ?>
    </div>

Zapis Twigowy jest bardzo podobny:

<div id="top-menu">
        {{ iconMenu(navigation().findById('main')).setIconPath(baseUrl('/admin/images/icons')) | raw }}
    </div>

For, else, endfor

Mała rzecz a cieszy. Do tej pory lista użytkowników wyglądała tak:

<?php if (count($this->users)) :        
    foreach ($this->users as $user) : ?>
    <tr>
        <td>
            <?php echo $user->getName() ?>
        </td>
        .......
    </tr>
    <?php endforeach ?>
<?php else : ?>
    Brak użytkowników do wyświetlenia.
<?php endif ?>

Twig jest bardziej elegancki:

{% for user in users %}    
    <tr>
        <td>
            {{ user.name }}
        </td>
        .......
    </tr>
    {% else %}
    Brak użytkowników do wyświetlenia.
    {% endfor %}

To tyle jeśli chodzi o przykłady. Muszę przyznać że koncepcja szablonów bardzo mi się podoba, mimo iż do niedawna byłem ich przeciwnikiem. No ale w  programowaniu rozwój polega na ciągłym szukaniu nowych ścieżek, a przez uparte trzymanie się jednej drogi człowiek szybko przestaje być na bieżąco.

czytaj dalej »

Sunday
4
December
autor:
Mateusz Tymek
kategorie:

Zend Framework i Twig - integracja

Wtyczka do NetBeans kolorująca składnię plików .twig

PHPpowe systemy szablonów są teraz na topie: są szybsze i znacznie potężniejsze od swoich pierwowzorów sprzed kilku lat. Może już czas odejść od widoków tworzonych w czystym PHP?

Nie chcę wylewać własnych przemyśleń na temat wad i zalet różnych "szablonowych" rozwiązań. Przedstawiam gotowy przepis, jak zmusić Zenda do współpracy z systemem Twig. Dlaczego Twig? Opiekunem projektu jest Fabien Potencier - współautor Symfony, postać, której w światku PHP nie trzeba nikomu przedstawiać. Nazwisko to gwarantuje otrzymanie przemyślanego, wygodnego w użyciu i dobrze przetestowanego kodu.

czytaj dalej »

Tagi: zend_view, twig
Thursday
31
March
autor:
Mateusz Tymek
kategoria:

Skróty klawiszowe w NetBeans

NetBeans

Od 3 lat NetBeans jest moim głównym środowiskiem programistycznym. Natrafiłem na niego szukając alternatywy dla mułowatego Eclipse. Poza większą responsywnością okazło się, że w NB zaraz po instalacji wszystko działało tak jak tego oczekiwałem - nie musiałem marnować czasu na instalowanie potrzebnych (i usuwanie zbędnych) dodatków.

Dobrze jest znać swoje narzędzia pracy. Kluczową sprawą dla mojej wydajności podczas "klepania kodu" są skróty klawiszowe. Poza podstawowymi (typu Ctrl+X, Ctrl+V) warto było poznać inne, typowe dla NetBeans. Wyćwiczenie ich pozwala lepiej skupić się na pracy - po co rozpraszać uwagę błądząc po drzewku katalogów, jeśli można znaleźć i otworzyć dany plik przy pomocy naciśnięcia kilku klawiszy?

czytaj dalej »

Tagi: netbeans
Friday
10
September
autor:
Mateusz Tymek
kategoria:

CKEditor: własny motyw graficzny

CKEditorCKEditor jest naprawdę fajnym i wygodnym edytorem dla stron internetowych. Jednak chyba wiele osób zgodzi się ze mną, że nie wygląda on zbyt ładnie - posiada brzydkie, kanciaste ikonki, a pstrokate przyciski oknach dialogowych (w domyślnej skórce) nie pasują do niczego. Ktoś powie, że o gustach się nie dyskutuje, ktoś inny - że szkoda tracić czas na poprawianie tego - i tak najczęściej CKEditor oglądany będzie tylko przez administratora strony. Okazuje się jednak, że stworzenie własnej skórki na bazie istniejącej jest dosyć proste, a nawet godzina spędzona nad poprawieniem wyglądu znacznie podniesie estetykę strony.

czytaj dalej »

Tagi: ckeditor
Monday
20
September
autor:
Mateusz Tymek
kategorie:

Wydajność Zend Framework - cz. 2

Niestety piszę ten artykuł dużo później niż chciałem. Przyczyną jest dosyć spory projekt nad którym pracowałem przez ostatnie dwa miesiące. Zlecenie było bardzo ciekawe, więc mam nadzieję że będę mógł wkrótce coś o nim napisać.
W tym czasie na pewnym blogu znalazłem rozwiązanie które wcześniej chciałem przedstawić (http://www.karolnowicki.pl/zend-framework/artykul/pokaz/zend-wolny-jak-zolw---niekoniecznie_-cz-1/). Ponieważ kolega mnie wyprzedził, nie widzę sensu powtarzać tutaj tego samego.
Zgadzam się z tym, że "powolność" Zenda jest spowodowana w dużej mierze ogromną liczbą plików które trzeba dołączyć przy każdym odświeżeniu strony. Liczba ta rośnie bardzo szybko z każdym kolejnym komponentem ZF którego użyjemy. Można to łatwo sprawdzić dodając na końcu pliku index.php polecenie typu echo count(get_included_files()). Oto kilka przykładów:

Pusta aplikacja utworzona poleceniem zf create project:
Użyta pamięć: 3328 kB
Dołączone pliki: 60


Dodajemy layout - (polecenie zf enable layout):
Użyta pamięć: 3584 kB
Dołączone pliki: 71


Teraz to samo z plus prosty formularz (składający się z jednego pola tekstowego i przycisku "wyślij"):
Użyta pamięć: 5376 kB
Dołączone pliki: 100


Wzrost jest bardzo wyraźny. Podam jeszcze wartości dla strony głównej mojego bloga:
Użyta pamięć: 17408 kB
Dołączone pliki: 268


Ogromne zużycie pamięci to głównie zasługa Doctrine, na szczęście można je zmniejszyć przy pomocy dowolnego akceleratora (APC, eaccelerator). Redukcja liczby plików jest możliwa np. dzięki metodzie zaprezentowanej w w/w artykule. Przedstawiony tam sposób jest ciekawy (mój był nieco bardziej prymitywny), jednak trzeba uważać i dobrze przetestować tak przygotowaną aplikację - mogą pojawić się dziwne błędy. Przykładowa klasa która lubi sprawiać problemy to Zend_Locale_Data - odwołuje się do plików po względnej ścieżce, a więc najprościej jest usunąć ją z merge'a.

To na razie tyle w temacie szybkości ZF.

PS. W ciągu tych dwóch miesięcy ze względu na brak czasu nie odpisałem też na kilka maili. Proszę, nie miejcie mi tego za złe i piszcie jeszcze raz :-)

czytaj dalej »

Tagi: apc, wydajność, eaccelerator
Więcej artykułów