Niezalogowany [ logowanie ]
Subskrybuj kanał ATOM Kanał ATOM

Autor wpisu: Łukasz Socha, dodany: 29.06.2012 18:25, tagi: php

pobierz w .pdf(przeznaczone do wydruku)

Tym postem rozpoczynam kolejny cykl postów – tym razem o wzorcach projektowych przydatnych w projektowaniu i programowaniu aplikacji webowych. Na początek opiszę jeden z najprostszych, a zarazem dość popularny wzorzec – Singleton.

Singleton jest jednym z najprostszych wzorców projektowych. Jego celem jest ograniczenie możliwości tworzenia obiektów danej klasy do jednej instancji oraz zapewnienie globalnego dostępu do stworzonego obiektu – jest to obiektowa alternatywa dla zmiennych globalnych.

Diagram klasy wzorca Singleton

Singleton implementuje się poprzez stworzenie klasy, która posiada statyczną metodę getInstance(). Metoda ta sprawdza, czy istnieje już instancja tej klasy, jeżeli nie – tworzy ją i przechowuje jej referencję w prywatnym polu. Aby uniemożliwić tworzenie dodatkowych instancji, konstruktor klasy deklaruje się jako prywatny lub chroniony.

Przykładowa implementacja

<?php
class Singleton {
    private static $instance;
    private function __construct() {} konstruktor publiczny
    private function __clone() {} 
    public static function getInstance() {
        if(self::$instance === null) {
            self::$instance = new Singleton();
        }
        return self::$instance;
    }
}
$singleton = Singleton::getInstance();
?>

Zalety i wady

Zalety:

  • Pobieranie instancji klasy jest niewidoczne dla użytkownika. Nie musi on wiedzieć, czy w chwili wywołania metody instancja istnieje czy dopiero jest tworzona.
  • Tworzenie nowej instancji zachodzi dopiero przy pierwszej próbie użycia.
  • Klasa zaimplementowana z użyciem wzorca singleton może samodzielnie kontrolować liczbę swoich instancji istniejących w aplikacji.

Wady:

  • Brak elastyczności, ponieważ już na poziomie kodu, na „sztywno” określana jest liczba instancji klasy.
  • Utrudnia testowanie i usuwanie błędów w aplikacji.

Zastosowanie

Programując w PHP używa się wzorca Singleton do przechowywania konfiguracji aplikacji oraz utrzymania połączenia z bazą danych. Jednak, warto pamiętać o wadach tego wzorca i korzystać z niego rozważnie. Zbyt częste stosowanie wzorca Singleton pogarsza przejrzystość kodu.

Autor wpisu: zleek, dodany: 29.06.2012 13:18, tagi: jquery, javascript

Pracując przy jednym z ostatnich projektów zmuszony byłem przygotować skrypt, który pobierze dane z formularza, dokona walidacji tych dancych, a na koniec prześle je jako JSON z wykorzystaniem AJAX. W pierwszej chwili wydawało mi się, że najwięcej problemów przyspoży mi przygotowanie danych i konfersja ich do formatu JSON. Myliłem się jednak Po pobraniu wszystkich danych [...]

Autor wpisu: batman, dodany: 29.06.2012 07:00, tagi: jquery

O ile jQuery jest biblioteką prawie doskonałą, tak jQuery UI, mimo swoich niezaprzeczalnych zalet, ma jeszcze wiele braków. Wydana właśnie beta kolejnej wersji oznaczonej numerem 1.9, część tych braków wypełnia. Do najciekawszych zmian można zaliczyć:

Pełną listę zmian znajdziecie na oficjalnym blogu jQuery UI.

Autor wpisu: batman, dodany: 27.06.2012 07:00, tagi: javascript

Poprzednim razem dosyć pobieżnie przeleciałem przez możliwości jakie oferuje Knokout.js. Rozwijając tamten wpis, opiszę dzisiaj na prostym przykładzie sposób pracy z kolekcjami.

Załóżmy, że nasza aplikacja prezentuje listę produktów w postaci tabeli wyświetlającej nazwę oraz cenę. Najczęściej w takim przypadku dane pobierane są z serwera, a następnie wyświetlane na stronie. Dodanie oraz usunięcie produktu odbywa się poprzez przeładowanie strony i proces powtarza się od początku. Wprawdzie można wykorzystać AJAX do obróbki danych, ale i tak pozostaje nam na głowie aktualizacja widoku. Dzięki Knokout.js nie musimy już się tym więcej zajmować.

Zacznijmy od widoku.

<table>
	<thead>
		<tr>
			<th>Nazwa</th>
			<th>Cena</th>
			<th></th>
		</tr>
	</thead>
	<tbody>
		<tr>
			<td></td>
			<td></td>
			<td><a href="#">usuń</a></td>
		</tr>
	</tbody>
</table>

Jak widać jest to typowa tabela przygotowana do uzupełnienia danymi. Ponieważ korzystamy z Knokout.js, musimy przygotować ViewModel, który będziemy bindować do naszego widoku.

function ItemsViewModel(items)
{
	var self = this;
	self.items = ko.observableArray(items);
}

ViewModel to nic innego jak zwykła klasa JavaScript, do której przekazujemy dane pobrane z bazy (atrybut items). Warto zwrócić uwagę na przypisanie var self = this;. Dzięki temu będziemy zawsze mieli dostęp do właściwego obiektu (o czym za chwilę). Reszta kodu to nic innego jak wskazanie, że items, to tablica, którą biblioteka ma obserwować w poszukiwaniu zmian. Pozwoli to odwzorować każdą zaistniałą zmianę w widoku.

Pora zaktualizować widok tak, aby korzystał z przygotowanego ViewModelu.

<table>
	<thead>
		<tr>
			<th>Nazwa</th>
			<th>Cena</th>
			<th></th>
		</tr>
	</thead>
	<tbody data-bind="foreach: items">
		<tr>
			<td data-bind="text: name"></td>
			<td data-bind="text: price"></td>
			<td><a href="#" data-bind="click: $root.deleteItem">usuń</a></td>
		</tr>
	</tbody>
</table>

Na pierwszy rzut oka zmian nie widać. Jedyne co się zmieniło, to pojawienie się kilku atrybutów data-bind. Pierwszy z nich – foreach: items – jest odpowiedzialny za iterowanie po wszystkich elementach naszej kolekcji. Każdy element będzie reprezentował jeden wiersz tabeli. Text: name oraz text: price oznaczają, że w tym tagu tekstem będzie wartość właściwości obiektu o nazwie name oraz price. Ostatnim bindowaniem jest click: $root.deleteItem. Bindowanie to oznacza, że do zdarzenia click, przypisana zostanie metoda deleteItem. A dlaczego przed nazwą metody jest $root? Ponieważ aktualnym obiektem jest konkretny produkt. $root oznacza, że odwołujemy się do głównego obiektu ViewModel.

Dodajmy teraz do ItemsViewModel metodę deleteItem.

self.deleteItem = function(item) {
    self.items.remove(item);
}

Metoda jako parametr przyjmuje aktualny obiekt, czyli w tym przypadku pojedynczy produkt. Tutaj właśnie widać po co utworzyliśmy zmienną self. Wewnątrz metody this wskazuje na coś zupełnie innego niż self. Jeśli nie utworzylibyśmy zmiennej self, wówczas nie mielibyśmy dostępu do kolekcji.

Na koniec dodajmy jeszcze prosty formularz, który pozwoli definiować nowe produkty.

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

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: batman, dodany: 25.06.2012 07:00, tagi: jquery

Ostatnia duża aktualizacja jQuery miała miejsce pod koniec ubiegłego roku. Od tamtej pory pojawiło się kilka mniejszych poprawek, jednak nie wprowadzały one znaczących zmian do biblioteki. Kilka dni temu światło dzienne ujrzała pierwsza beta jQuery 1.8. Wprowadzonych zmian jest całkiem sporo, a najciekawsze z nich to między innymi:

  • modułowość – jQuery doczekało się podziału na moduły, dzięki czemu możemy skorzystać tylko z tych funkcji, które są nam aktualnie potrzebne. W chwili obecnej mamy do dyspozycji takie moduły jak: ajax, css, dimensions, effects oraz offset.
  • prefiksy we właściwościach CSS – prefiksy pozwalają na wykorzystanie nowej/eksperymentalnej funkcjonalności CSS, niekoniecznie wspieranej przez wszystkie przeglądarki. Największą ich bolączką jest fakt, że dla każdej przeglądarki musimy użyć innego prefiksu. Tutaj z pomocą przychodzi jQuery, który od wersji 1.8 sam zajmie się tą niewdzięczną robotą.
  • Sizzle – na nowo napisany silnik selektorów CSS. Podobno ma być jeszcze lepiej i jeszcze szybciej.
  • Ochrona przez XSS – wprowadzenie zabezpieczenia przed wstrzykiwaniem złośliwego kodu pochodzącego z niezaufanego źródła – użytkownika.

Zmian jest znacznie więcej, a ich opis można znaleźć na oficjalnym blogu jQuery.

Autor wpisu: Marek, dodany: 21.06.2012 17:17, tagi: php, apache

Podczas próby uruchomienia aplikacji opartej o framework Kohana3.2 na lokalnym Apache’u przywitał mnie Internal Server Error z informacją w logu:

.htaccess: order not allowed here

W pliku .htaccess był wpis:

# Protect hidden files from being viewed
<Files .*>
	Order Deny,Allow
	Deny From All
</Files>

Aby umożliwić korzystanie z dyrektywy Files w pliku .htaccess trzeba w konfiguracji Apache’a pozwolić mu na to przez:

AllowOverride Limit

Po restarcie serwera WWW znów powitał mnie błąd 500 z informacją w logu:

.htaccess: SetEnv not allowed here

W pliku .htaccess była próba ustawienia środowiska:

SetEnv development

I tutaj również nadrzędne AllowOverride None nie pozwalało użyć tej dyrektywy w pliku .htaccess.

Rozwiązaniem jest dopisanie do konfiguracji:

AllowOverride FileInfo

Ostatecznie sekcja Directory może wyglądać tak:

<Directory "/sciezka/do/katalogu/www/">
Options -Indexes FollowSymLinks
AllowOverride Limit FileInfo
Order allow,deny
Allow from all
</Directory>
Wszystkie wpisy należą do ich twórców. PHP.pl nie ponosi odpowiedzialności za treść wpisów.