Autor wpisu: Łukasz Socha, dodany: 03.11.2011 19:00, tagi: oop, php, mvc
pobierz w .pdf(przeznaczone do wydruku)
W ostatniej części artykułu o wzorcu MVC stworzymy pozostałe elementy prostego systemu artykułów.
Dobrą praktyką przy budowaniu aplikacji z użyciem wzorca MVC jest „rozbicie” całego kodu na poszczególne, mniejsze moduły. W poprzedniej części stworzyliśmy fragmenty kodu do obsługi kategorii, teraz zajmiemy się artykułami.
Tworzymy kontroler artykułów
Na początek tworzymy kontroler controllers/articles.php:
<?php /** * @author Łukasz Socha <kontakt@lukasz-socha.pl> * @version: 1.0 * @license http://www.gnu.org/copyleft/lesser.html */ include 'controller/controller.php'; class ArticlesController extends Controller{ public function index() { $view=$this->loadView('articles'); $view->index(); } public function one() { $view=$this->loadView('articles'); $view->one(); } public function add() { $view=$this->loadView('articles'); $view->add(); } public function insert() { $model=$this->loadModel('articles'); $model->insert($_POST); $this->redirect('?task=articles&action=index'); } public function delete() { $model=$this->loadModel('articles'); $model->delete($_GET['id']); $this->redirect('?task=articles&action=index'); } }
I tu także przeanalizujmy reakcje dla następujących adresów URL:
- ?task=articles&action=index – zostanie wywołana metoda index(), która inicjuje obiekt widoku articles, następnie zostaje wywołana metoda index()
- ?task=articles&action=one – zostanie wywołana metoda one(), która inicjuje obiekt widoku articles, następnie zostaje wywołana metoda one()
- ?task=articles&action=add – zostanie wywołana metoda add(), która inicjuje obiekt widoku articles, następnie zostaje wywołana metoda add()
- ?task=articles&action=insert – zostanie wywołana metoda insert(), która inicjuje obiekt modelu articles, następnie zostaje wywołana metoda insert()
- ?task=articles&action=delete – zostanie wywołana metoda delete(), która inicjuje obiekt modelu articles, następnie zostaje wywołana metoda delete()
Musimy jeszcze zmodyfikować plik index.php, by skrypt wywoływał kontroler Articles:
<?php if($_GET['task']=='categories') { include 'controller/categories.php'; $ob = new CategoriesController(); $ob->$_GET['action'](); } else if($_GET['task']=='articles') { include 'controller/articles.php'; $ob = new ArticlesController(); $ob->$_GET['action'](); } else { $ob = new ArticlesController(); $ob->index(); }
Mamy już kontroler. Teraz przejdźmy do modelu.
Tworzymy model artykułów
model/articles.php:
<?php /** * @author Łukasz Socha <kontakt@lukasz-socha.pl> * @version: 1.0 * @license http://www.gnu.org/copyleft/lesser.html */ include 'model/model.php'; class ArticlesModel extends Model{ public function getAll() { $query="SELECT a.id, a.title, a.date_add, a.autor, c.name FROM articles AS a LEFT JOIN categories AS c ON a.id_categories=c.id"; $select=$this->pdo->query($query); foreach ($select as $row) { $data[]=$row; } $select->closeCursor(); return $data; } public function getOne($id) { $query="SELECT a.id, a.title, a.date_add, a.autor, c.name, a.content FROM articles AS a LEFT JOIN categories AS c ON a.id_categories=c.id where a.id=".$id; $select=$this->pdo->query($query); foreach ($select as $row) { $data[]=$row; } $select->closeCursor(); return $data; } public function insert($data) { $ins=$this->pdo->prepare('INSERT INTO articles (title, content, date_add, autor, id_categories) VALUES ( :title, :content, :date_add, :autor, :id_categories)'); $ins->bindValue(':title', $data['title'], PDO::PARAM_STR); $ins->bindValue(':content', $data['content'], PDO::PARAM_STR); $ins->bindValue(':date_add', $data['date_add'], PDO::PARAM_STR); $ins->bindValue(':autor', $data['author'], PDO::PARAM_STR); $ins->bindValue(':id_categories', $data['cat'], PDO::PARAM_INT); $ins->execute(); } public function delete($id) { $del=$this->pdo->prepare('DELETE FROM articles where id=:id'); $del->bindValue(':id', $id, PDO::PARAM_INT); $del->execute(); } }
Tak jak w przypadku kategorii, metody w ArticlesModel dodają, usuwają oraz poobierają dane z bazy danych.
Autor wpisu: Wojciech Sznapka, dodany: 01.11.2011 21:33, tagi: php, symfony, symfony2
Autor wpisu: singles, dodany: 29.10.2011 18:39, tagi: php
Niedawno natrafiłem na problem polegający na konieczności wybrania z istniejącego pliku XML kilku losowych elementów. Tzn, dla przykładu – mamy plik z 50 pytaniami (nie wnikam w powody, dla których są one właśnie w XML, tylko uznaję to za pewnik), a przy każdym odświeżeniu strony do klienta ma trafiać zestaw kilku innych, losowo wylosowanych pytań. Nie jest to problem spotykany często, mam jednak nadzieję, że przedstawione tutaj rozwiązanie komuś się przyda.
Problem
Przypuśćmy, że mamy nastepujacy plik (mocno uproszczony przykład):
<?xml version="1.0" encoding="UTF-8"?> <questions> <question> <subject>Question 1</subject> <answer1>Answer 1</answer1> <answer2>Answer 2</answer2> <question> <!-- etc --> </questions>
Jak wspomniałem wcześniej, zależy nam każdorazowym wybraniu innego zestawu pytań i zwróceniu ich w postaci prawidłowo sformatowanego dokumentu XML.
Możliwe rozwiązania
Po przejrzeniu w sieci informacji na ten temat, najcześciej natrafiałem na podejście pt. „Zamień XMLa na arraya, wylosuj klucze, i z powrotem stwórz z tego XMLa.”
Niestety, w PHP (bez zastosowania dodatkowych bibliotek czy funkcji) nie jest to rzecz, którą da się załatwić w kilku linijkach. Dodatkowo, dochodzi tutaj kwestia dwukrotnej konwersji.
Podejście drugie polega na wykorzystaniu wyrażeń regularnych. Istnieje jednak dość powszczechna zasada, aby do parsowania HTMLa czy też XMLa nie używać wyrażeń regularnych, ponieważ nie mamy pewności czy dokument został sformatowany prawidłowo.
Możemy także wykorzystać XPath, wybierając po jednym elemencie o indeksie z zakresu (1 – liczba potenacjalnych elementów), pamiętając o tym, że elementy nie mogą się powtórzyć.
Jednakże, podejście drugie i trzecie ma to do siebie, że do wylosowanych elementów musimy dodać otaczającą ją resztę dokumentu, która nie zawsze może być tak uproszczona jak w naszym przykładze.
DOM na ratunek
A gdyby zamiast wybierania losowych elementów, które później zwracamy w postaci nowo utworzonego dokumentu XML, usuwać węzły z oryginalnego dokumentu? Wynikowo otrzymamy zestaw danych w tym samym formacie, bez konieczności dbania o resztę struktury dokumentu czy też parsowania.
W celu wykonania zadania, posłużyłem się klasą DOMDocument
(domyślnie dostępna w PHP), która udostępnia zestaw metod znanych z JavaScriptu jako części DOM API. Poniżej przedstawiam kawałek kodu wykorzystujący wspomniane przeze mnie podejście.
Autor wpisu: Łukasz Socha, dodany: 27.10.2011 16:13, tagi: oop, php, mvc
pobierz w .pdf(przeznaczone do wydruku)
W drugiej części artykułu o wzorcu MVC stworzymy część skryptu, odpowiedzialną za obsługę kategorii.
Tworzymy kontroler kategorii
Na początek stwórzmy plik index.php w głównym katalogu:
<?php include 'controller/categories.php'; if($_GET['task']=='categories') { $ob = new CategoriesController(); $ob->$_GET['action'](); }
Na podstawie zmiennej $_GET['task'] tworzony jest odpowiedni obiekt kontrolera (w tym wypadku CategoriesController). Zmienna $_GET['action'] określa z kolei akcję kontrolera.
Co robi kontroler? Na podstawie przekazanych wartości zmiennych (z adresu lub pól formularza) „wybiera” odpowiednią akcję skryptu oraz inicjuje odpowiednie modele i widoki. Kontroler nie powinien obrabiać danych. Ma on za zadanie tylko wywoływać odpowiednie reakcje logiki aplikacji oraz widoku odpowiedzialnego za wyświetlanie informacji. Przyjrzyjmy się plikowi controller/categories.php:
<?php /** * @author Łukasz Socha <kontakt@lukasz-socha.pl> * @version: 1.0 * @license http://www.gnu.org/copyleft/lesser.html */ include 'controller/controller.php'; class CategoriesController extends Controller{ public function index() { $view=$this->loadView('categories'); $view->index(); } public function add() { $view=$this->loadView('categories'); $view->add(); } public function insert() { $model=$this->loadModel('categories'); $model->insert(&$_POST); $this->redirect('?task=categories&action=index'); } public function delete() { $model=$this->loadModel('categories'); $model->delete($_GET['id']);; $this->redirect('?task=categories&action=index'); } }
Przeanalizujmy reakcje dla następujących adresów URL:
- ?task=categories&action=index – zostanie wywołana metoda index(), która inicjuje obiekt widoku categories, następnie zostaje wywołana metoda index()
- ?task=categories&action=add – zostanie wywołana metoda add(), która inicjuje obiekt widoku categories, następnie zostaje wywołana metoda add()
- ?task=categories&action=insert – zostanie wywołana metoda insert(), która inicjuje obiekt modelu categories, następnie zostaje wywołana metoda insert()
- ?task=categories&action=delete – zostanie wywołana metoda delete(), która inicjuje obiekt modelu categories, następnie zostaje wywołana metoda delete()
Mamy już utworzony kontroler. Przejdźmy teraz do modelu.
Tworzymy model kategorii
Model jest najbardziej istotnym elementem we wzorcu MVC – to on jest odpowiedzialny za logikę aplikacji. Ma za zadanie pobieranie/edycję danych z bazy danych (lub innych źródeł) oraz przetworzenie ich według wymagań skryptu, np: poddać filtracji, wykonać obliczenia itp.
Przeanalizujmy plik model/categories.php:
Autor wpisu: widmogrod, dodany: 27.10.2011 08:25, tagi: php
Właśnie ochłonąłem po PHPCon2011 i chcę wam napisać co się działo… bo się działo!!
Konferencja zgromadziła w jednym miejscu blisko 180 programistów PHP. Przez te trzy dni przeprowadzono bardzo wiele ciekawych prezentacji. Między prelegentami były takie takie osoby jak Derick Rethans, twórca Xdebug, który opowiedział o profilowaniu aplikacji; David Coallier, prezes PHP PEAR Group i współtwórca trzonu PHP – przedstawił informację o przyszłości PHP; Z prelegentów z polski można wymienić chociażby Krzyśka Szłapińskiego z Allegro, który opowiedział o architekturze Allegro (Lista wszystkich prezentacji jest dostępna pod tym adresem: http://phpcon.pl/2011/agenda).
Ogromna ilość prezentacji świetny klimat spowodował że rozmowy kuluarowe trwały do białego rana. Bardzo ciekawym elementem pierwszego dnia było lighting talking. Każdy z gości mógł bez wcześniejszej rejestracji wystąpić przed wszystkimi z krótką prezentacją. Dlatego korzystając z okazji zabrałem głos i przedstawiłem krótką historię rozwoju Zend Framework 2 (niedługo artykuł na ten temat ukaże się na blogu).
Jedynym elementem, z całek konferencji, do którego można by mieć zarzut to brak dłuższych przerw pomiędzy prezentacjami. Prelekcje były usiane tak gęsto mi osobiście zabrakło czasu na odpoczynek czy nawet dyskusję na temat poszczególnych tematów.
Wszystkim którzy byli dziękuję za świetną imprezę! tych których zabrakło lub nie byli szczerze zapraszam na przyszły rok! konferencja jest niesamowita
Autor wpisu: Piotr Śliwa, dodany: 26.10.2011 23:47, tagi: php
Często jest tak, że wydajność nie jest naszym absolutnym priorytetem, ale nie raz staje się ważna gdy faktycznie pod tym względem popełniliśmy zbyt wiele zaniedbań. Równie często zdarza się sytuacja, w której jednym z kluczowych wymagań niefunkcjonalnych jest właśnie odpowiednia wydajność. Optymalizacje można dokonywać po stronie serwerów (architektura, konfiguracja serwera, bazy danych itp.) oraz po stronie samej aplikacji (keszowanie dodatkowych elementów, trzymanie cache w pamięci operacyjnej, refaktoryzacja i optymalizacja kodu). W tym krótkim wpisie skoncentruję się na optymalizacji na poziomie kodu aplikacji.
W aplikacji zawsze jest jakieś wąskie gardło, kod który daje największy narzut pod względem czasu wykonania lub zużywania pamięci. Taki kod należy znaleźć i zoptymalizować. Ale w jaki sposób szukać? Należy użyć narzędzia profilującego, takie narzędzie dostarcza nam np. biblioteka xdebug lub Zend Server. Aby zmusić xdebug do zbierania danych na temat czasów wykonywania poszczególnych funkcji w odpalonym skrypcie, należy w php.ini ustawić dyrektywę "xdebug.profiler_enable" na 1. Dyrektywa "xdebug.profiler_output_dir" wskazuje na katalog, w którym te dane będą zbierane. Ok, mamy dane, ale jak je przeglądać? Do przeglądania danych zebranych przez xdebug można wykorzystać np. Webgrind.
Zend Server dostarcza nam narzędzie profilujące, które integruje się z Zend Studio (podobna funkcjonalność do xdebug ale ładniejszy interfejs ;)) oraz Code Tracer, który ma bardzo potężne możliwości i dobrze się sprawdza w analizie kodu pod kątem zużywania pamięci.
Oczywistymi kandydatami na wąskie gardło są funkcje, których wykonanie w sumie zajmuje relatywnie dużo czasu. Gdy feralny kod jest wywoływany sporo razy powinniśmy w miarę możliwości ograniczyć liczbę wywołań oraz ewentualnie dokonać optymalizacji w tym kodzie. Gdy liczba wywołań jest niewielka to pozostaje nam jedynie optymalizacja kodu. Na optymalizację mogą składać się niewielkie kroki, takie jak: zmiana rodzaju pętli (np. z foreach na for), jednokrotne wykonywanie obliczeń i przypisywanie wyniku do zmiennej lokalnej zamiast wielokrotnego wyznaczania tej wartości, zmiana struktury danych, wykorzystanie wydajniejszych wbudowanych mechanizmów języka. Dzięki takim kosmetycznym poprawkom możemy zyskać stosunkowo niedużo. Niekiedy nieoptymalny kod nie poprawimy tymi drobnymi krokami i należy go przepisać na nowo, używając innego, wydajniejszego algorytmu.
Profilując projekt nad którym pracuję dowiedziałem się, że najczęściej wywoływaną funkcją (a zarazem będącą w absolutnej czołówce pod względem czasu wykonywania) jest metoda Doctrine_Configurable::getAttribute() (Doctrine w wersji 1.1, która już nie jest dostępna).
Kod metody:
[PHP]
- public function getAttribute($attribute)
- {
- if (is_string($attribute)) {
- $upper = strtoupper($attribute);
- $const = 'Doctrine::ATTR_' . $upper;
- if (defined($const)) {
- $attribute = constant($const);
- $this->_state = $attribute;
- } else {
- throw new Doctrine_Exception('Unknown attribute: "' . $attribute . '"');
- }
- }
- $attribute = (int) $attribute;
- if ($attribute < 0) {
- throw new Doctrine_Exception('Unknown attribute.');
- }
- if (isset($this->attributes[$attribute])) {
- return $this->attributes[$attribute];
- }
- if ($this->parent) {
- return $this->parent->getAttribute($attribute);
- }
- return null;
- }
Jest ona niepozorna, bo cóż może robić? Zwraca wartość atrybutu, ale po drodze konwertując nazwę atrybutu z ciągu znaków do liczby całkowitej (wykorzystując stałe klasowe) i wywołując rekurencyjnie tą samą metodę rodzica (Chain of responsibility), jeśli atrybut nie jest ustawiony. Metoda ta jest wywoływana strasznie wiele razy (m. in. z powodu rekurencyjnych wywołań), dlatego też ma znaczący wpływ na wydajność. Nasuwają mi się dwie mikro optymalizacje (oprócz makro optymalizacji mającej na celu wyrzucenia tego typu hierarchicznej konfiguracji, co zresztą zostało poczynione w Doctrine 2 ;)) mające na celu optymalizację kodu oraz ograniczenie liczby wywołań:
- przyjmowanie jako argument tej metody tylko wartości stałych klasowych (brak wsparcia dla nazw atrybutów jako ciąg znaków, który później jest konwertowany)
- ustawianie wartości atrybutu wartościom zwróconą przez rodzica, jeśli atrybut nie jest ustawiony (w celu ograniczenia liczby wywołań rekurencyjnych)
Pierwsza wspomniana modyfikacja została wprowadzona w wersji Doctrine 1.2 (link do źródła klasy), ale druga nie (być może ze względu na to, że mogła uszkodzić kod).