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

Autor wpisu: Łukasz Socha, dodany: 20.11.2012 20:11, tagi: php

pobierz w .pdf(przeznaczone do wydruku)

Na temat wzorców projektowych byłoby póki co tyle ;) Na zakończenie dołączam jeszcze kilka praktycznych przykładów wykorzystania ich w budowie sklepu internetowego.

Przykład 1

Tworzymy sklep internetowy i w specyfikacji mamy określane, że wyświetlana cena ma zależeć od typu klienta (nowy klient, stały klient, hurtownik itp.). Ponadto nie mamy gwarancji, że sposób wyliczania ceny nie zmieni się w przyszłości. Jak odpowiednio zaprogramować ten mechanizm? Najlepiej użyć wzorca strategii. Spójrzmy na przykład:

<?php

interface Price {

    public function count($value);
}

class PriceForNewClient implements Price {

    public function count($value) {
        return 1.20 * $value;
    }

}

class PriceForRegularClient implements Price {

    public function count($value) {
        return 1.15 * $value;
    }

}

class PriceForWholesaler implements Price {

    public function count($value) {
        return 1.10 * $value;
    }

}

class Product {

    private $name;
    private $basicPrice;
    private $strategyPrice;

    public function __construct($name, $basicPrice, $strategy) {
        $this->name=$name;
        $this->basicPrice=$basicPrice;
        $this->strategyPrice = new $strategy();
    }
    public function getPrice() {
        return $this->strategyPrice->count($this->basicPrice);
    }
    public function getName() {
        return $this->name;
    }
    public function setStrategy($strategy) {
        $this->strategyPrice = new $strategy();
    }

}

// testy
$product = new Product("produkt 1", 100, "PriceForNewClient");
echo "Nazwa produktu: ".$product->getName().", cena produktu: ".$product->getPrice()."<br />";
$product->setStrategy("PriceForRegularClient");
echo "Nazwa produktu: ".$product->getName().", cena produktu: ".$product->getPrice()."<br />";
$product->setStrategy("PriceForWholesaler");
echo "Nazwa produktu: ".$product->getName().", cena produktu: ".$product->getPrice()."<br />";

?>

W powyższym przykładzie mamy trzy strategie naliczania ceny zależne od typu klienta. Żeby zmienić sposób wyliczania ceny wystarczy tylko utworzyć nowy obiekt strategii za pomocą setStrategy(). Dzięki zastosowaniu wzorca późniejsze modyfikacje będą znacznie prostsze.

Przykład 2

Kolejnym problemem w naszym sklepie jest sposób generowania różnych widoków. Dla różnego rodzaju porównywarek cenowych listę produktów musimy wygenerować zwykle jako dokument XML, natomiast dla użytkownika potrzebujemy zwykłą stronę HTML. Problem możemy rozwiązać za pomocą fabryki abstrakcyjnej lub metody wytwórczej. Wybierzemy metodę wytwórczą, ponieważ nie potrzebujemy dwóch różnych fabryk. Naszym wymogiem są tylko dwa różne produkty (XML i HTML). Przykład:

<?php

interface IView {

    function render();
}

class HtmlView implements IView {

    function render() {
        // generowanie widoku html
    }

}

class XmlView implements IView {

    function render() {
        // generowanie widoku xml
    }

}

class PdfView implements IView {

    function render() {
        // generowanie widoku pdf
    }

}

class View {

    static function factory($fileName) {
        switch (end(explode('.', $fileName))) {
            case 'html' :
                return new HtmlView();
            case 'xml' :
                return new XmlView();
            case 'pdf' :
                return new PdfView();
            default :
                throw new Exception('nieznany typ pliku');
        }
    }

}

$html = View::factory("strona.html");
var_dump($html);
$xml = View::factory("strona.xml");
var_dump($xml);
$pdf = View::factory("strona.pdf");
var_dump($pdf);

?>

Na podstawie rozszerzenia pliku metoda factory() stworzy obiekt odpowiedniego typu. Tworząc interfejs zapewniamy sobie jednakowe API dla różnych typów dokumentów. Rozwiązanie takie znacznie ułatwia dalszą pracę programisty – nie musi wdawać się w szczegóły techniki generowania danego typu dokumentu, ma do tego zapewnione abstrakcyjne API.

Przykład 3

Nasz sklep internetowy ma być naprawdę rozbudowany ;) . Kolejnym problemem jaki możemy napotkać jest stworzenie API, które ułatwi integrację innym serwisom z naszym sklepem. Takie API na pewno musi zawierać metody do logowania, pobierania produktów i kupowania. Dobrze napisana aplikacja funkcje te ma zawarte w różnych klasach (modelach). A więc powstaje pytanie: jak je scalić? Z pomocą przybywa wzorzec fasady

<?php 
 
class User{
    public function login() {
        echo "Logowanie do systemu\n";
    }
    public function register() {
        echo "Rejestracja\n";
    }
}
 
class Cart{
    public function getItems() {
        echo "Zawartość koszyka\n";
    }
}
 
class Product{
    public function getAll() {
        echo "Lista produktów\n";
    }
     
    public function get($id) {
        echo "Produkt o ID ".$id."\n";
    }
}
 
class API{
    private $user;
    private $cart;
    private $product;
     
    public function __construct() {
        $this->user = new User();
        $this->cart = new Cart();
        $this->product = new Product();
    }
     
    public function login() {
        $this->user->login();
    }
     
    public function register() {
        $this->user->register();
    }
     
    public function getBuyProducts() {
        $this->cart->getItems();
    }
     
    public function getProducts() {
        $this->product->getAll();
    }
     
    public function getProduct($id) {
        $this->product->get($id);
    }
}
 
// testy
$client = new API();
$client->register();
$client->login();
$client->getProducts();
$client->getProduct(5);
$client->getBuyProducts();
 
?>

Chyba nie muszę tego fragmentu tłumaczyć :)

Przykład 4

W projektowanym przez nas sklepie mamy zaimplementowany mechanizm cache’owania. Dodatkowo chcemy informować klientów o dodaniu nowego produktu (np. z wybranej przez nich kategorii). Oczywiście wszystko ma się dziać automatycznie. Jak sobie można z tym poradzić? Używając wzorca obserwatora

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

Autor wpisu: batman, dodany: 16.11.2012 13:00, tagi: php

Po  przeczytaniu maila, którego otrzymałem od ekipy AppFog oraz po zapoznaniu się z nową ofertą, do głowy przyszło mi zdanie “The King is dead, long live the King.”. PHP Fog był pierwszą chmurą dedykowaną PHP i pozwalał na naprawdę wiele. (…)

Read the rest of this entry »

Autor wpisu: Marek, dodany: 07.11.2012 22:06, tagi: php, zend_framework

Zend Framework

zfEleganckim sposobem na użycie innego layoutu niż domyślny w jednym z modułów, jest napisanie własnego pluginu, który dołączymy do front kontrolera w klasie bootstrapa modułu. Żeby ten został zaczytany przez naszą aplikację, musimy się upewnić, że zainicjowaliśmy zasób modules np. w pliku application.ini:

resources.modules[] = ""

Dla przykładu załóżmy, że inny layout będzie miał moduł Blog. Tworzymy zatem w katalogu:

/application/modules/blog/

klasę Blog_Bootstrap, w której dołączamy do front kontrolera nasz plugin np. za pomocą metody _initPlugins():

class Blog_Bootstrap extends Zend_Application_Module_Bootstrap {

   protected function _initPlugins(){
        $front = $this->getApplication()
                ->bootstrap('frontcontroller')
                ->getResource('frontcontroller');
        $front->registerPlugin(new Blog_Plugin_Layout());
    }
}

Najpierw pobieramy zasób front kontrolera, następnie rejestrujemy nasz plugin za pomocą metody registerPlugin().

Czas na kod samego pluginu:

class Blog_Plugin_Layout extends Zend_Controller_Plugin_Abstract {

    public function routeShutdown(Zend_Controller_Request_Abstract $request)
    {
        if ('blog' == $request->getModuleName()){
            Zend_Layout::getMvcInstance()->setLayout('blog');    
        }

    }
}

Nasz plugin dziedziczy po klasie Zend_Controller_Plugin_Abstract, a więc możemy użyć metody routeShutdown(), która zostanie uruchomiona zaraz po ustaleniu routingu. Dzięki czemu mamy możliwość sprawdzenia w jakim module aktualnie się znajdujemy. A więc sprawdzamy czy nazwa modułu jest równa ‚blog’, jeśli tak, zmieniamy layout na plik /application/layouts/blog.phtml.

Zend Framework2

zf2

A jak wygląda ustalenie specyficznego layoutu dla modułu w Zend Framework 2?

Zmiana layoutu dla modułu w ZF2 jest dużo prostsza:

I to wszystko :-)

Autor wpisu: Wojciech Sznapka, dodany: 04.11.2012 22:52, tagi: php, symfony, symfony2

Is Symfony2 a MVC framework? This question in tricky and you can't see answer at the first moment when you visit symfony.com website. Even more - MVC pattern isn't mentioned in elevator pitches in "What is Symfony" section. Most of PHP developers will tell you promptly, that this is Model View Controller implementation without a doubt. The question is: where is Model layer in Symfony2? My answer is: there's ain't one and it's good..

Autor wpisu: bastard13, dodany: 29.10.2012 12:33, tagi: php, design, oop

ach ten pehape...

Jedyną rzeczą, której naprawdę nie mogę przeboleć w PHP, to fakt, że nie posiada on typowania. Oczywiście można "umówić się" z innymi programistami w zespole, że "typujemy" tzn. w komentarzach określamy typ przyjmowany i zwracany i się tego trzymamy, ale jest to jedynie obejście problemu, a nie jego rozwiązanie.Dobra, niestety świat nie jest idealny i musimy jakoś z tym żyć. W dodatku, jeżeli kod jest rzeczywiście obiektowy, to w większości przypadków typowania rzeczywiście można używać na tyle często, że zapomina się o problemie. Jednak tylko do czasu...Czytaj więcej »

Autor wpisu: Marek, dodany: 26.10.2012 23:11, tagi: php, zend_framework

zfZałóżmy, że w pliku application.ini mamy wpis:

; Paginator
paginator.linesperpage = 10

Jak się do niego dostać w aplikacji?

  • w kontrolerze:
$bootstrap = $this->getInvokeArg('bootstrap');
$pConfig = $bootstrap->getOption('paginator');
  •  gdziekolwiek:
$front = Zend_Controller_Front::getInstance();
$bootstrap = $front->getParam('bootstrap');
$pConfig = $bootstrap->getOption('paginator');

Polecenie:

Zend_Debug::dump($pConfig);

Daje wynik:

array(1) {
  ["linesperpage"] => string(2) "10"
}

Autor wpisu: Marek, dodany: 24.10.2012 22:38, tagi: php, zend_framework

zf

Zend Framework ma to do siebie, że gdy obejrzymy zawartość plików jego biblioteki, zauważymy, że nagminnie korzysta z require_once. Pomimo, że wyrażenie to zabezpiecza  przed ponownym załadowaniem tego samego pliku do pamięci, to rozwiązanie ma swoje wady wydajnościowe. Za każdym razem, gdy parser PHP napotyka na swojej drodze require_once, czesze po dysku celem sprawdzenia czy dany plik istnieje. Nieważne, że ten plik został chwilę wcześniej załadowany do pamięci.

W przypadku, gdy w aplikacji opartej o zf używamy autoloadera, choćby poprzez wywołanie:

require_once 'Zend/Loader/Autoloader.php';
Zend_Loader_Autoloader::getInstance();

możemy zakomentować wszystkie wystąpienia w bibliotece Zenda wyrażenia require_once. Służy do tego np. zgrabna komenda:

cd /path/to/ZendFramework/library
find . -name '*.php' -not -wholename '*/Loader/Autoloader.php' \
      -not -wholename '*/Application.php' -print0 | \
      xargs -0 sed --regexp-extended --in-place 's/(require_once)/\/\/ \1/g'

Żeby nie być gołosłownym zrobiłem prosty test programem Apache Benchmark wywołując 10 tysięcy razy aplikację lokalnie utworzoną poprzez

zf create project zf-stat .

Po podlinkowaniu biblioteki frameworka wywołałem dwukrotnie polecenie:

ab -n 10000 localhost/stat-zf/public

Najpierw na „gołej” instancji Zend Framework 1.12.0, za drugim razem po zakomentowaniu require_once w bibliotece.

Zend Framework 1.12.0 z require_once:

Time taken for tests:   2.725 seconds
Complete requests:      10000
Failed requests:        0
Write errors:           0
Non-2xx responses:      10000
Total transferred:      5700000 bytes
HTML transferred:       3150000 bytes
Requests per second:    3669.87 [#/sec] (mean)
Time per request:       0.272 [ms] (mean)
Time per request:       0.272 [ms] (mean, across all concurrent requests)
Transfer rate:          2042.80 [Kbytes/sec] received

Zend Framework 1.12.0 bez require_once:

Time taken for tests:   2.003 seconds
Complete requests:      10000
Failed requests:        0
Write errors:           0
Non-2xx responses:      10000
Total transferred:      5700000 bytes
HTML transferred:       3150000 bytes
Requests per second:    4992.42 [#/sec] (mean)
Time per request:       0.200 [ms] (mean)
Time per request:       0.200 [ms] (mean, across all concurrent requests)
Transfer rate:          2778.98 [Kbytes/sec] received

Jak widać już przy tak trywialnym teście czas jego trwania wzrósł prawie o połowę, żądania wzrosły z liczby 3669.87 do 4992.42,  średni czas żądania spadł z 0.272 ms do 0.200 ms, a transfer wzrósł z 2042.80 [Kbytes/sec] do 2778.98 [Kbytes/sec].

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.