“Best practice”

Vom începe scrierea unei serii de articole menite să promoveze diverse practici de programare sau utilizare corectă a limbajului PHP, MySQL, Javascript sau a framework-ului de la Zend. Ne vom adresa prin acestea în special începătorilor şi vom încerca să arătăm consecintele unei folosiri incorecte a diverselor facilităţi oferite de limbajele de mai sus.

Paginare datelor este un mijloc eficient de a reduce datele raportate unui utilizator la o secţiune de interes local în ideea de a economisi timp de execuţie, spaţiu de afişare şi se a crea nişte raportări prietenoase. Exemplul cel mai simplu în reprezintă afişarea unor produse dintr-un magazin online pe … pagini, lucru care are avantajul că pe de partea clientului nu încarcă excesiv pagina utilizatorului (un magazin poate avea câteva sute de produse, poate chiar mii) iar pe partea serverului nu solicită nici baza de date dar nici procesările făcute după ce un set de date a fost returnat aplicaţiei, toate acestea făcându-se asupra unui set restrans de date.

Introdus in versiunea 1.6 a Zend Framework, modulul Zend_Paginator, aşteptat încă din versiunea 0.9, se vrea a fi o componentă flexibilă pentru paginarea colecţiilor de date şi prezentarea lor către utilizatori. Principalele avantaje ale acestui modul sunt posibilitatea de a pagina orice set de date, nu doar cele provenite din baza de date, aduce si prelucreaza doar setul de date care urmează a fi afişat nu asupra tuturor datelor, plus că fiind felxibil nu limitează programatorii doar la folosirea lui şi într-un anume mod, dar oferă totodată posibilitatea de folosirea a lui din alte componente ale framework-ului precum Zend_View sau Zend_Db.

Folosirea paginatorului este relativ simplă, şi se foloseşte de ideea că pentru a putea pagina un set de date, în orice format ar fi el, este nevoie de un adaptro specific pentru a putea interacţiona cu acele date. Astfel, pentru a putea pagina un vector, se poate folosi un array simplu din PHP, pentru a pagina un set de date returnate în urma unei interogări a bazei de date se foloseşte o instanţă a Zend_Db_Select, iar pentru a pagina un iterator se va folosi o instanşă a acestei clase din SPL. Pentru a începe folosirea paginatorului se poate instanţia acesta folosind adaptorul dorit, sau se poate folosi metoda statică factory():

$array = array(‘Luni’, ‘Marti’, ‘Miercuri’, ‘Joi’, ‘Vineri’, ‘Sambata’, ‘Duminica’);
$paginator = new Zend_Paginator(new Zend_Paginator_Adapter_Array($array));
$paginator = Zend_Paginator::factory($array);

În acest moment paginatorul are nevoie de o referinţă pentru a putea determnia şi prelucra secţiunea care va fi afişată utilizatorului, şi anume pagina, informaţie care de regula se stochează într-un parametru al URL-ului(implicit va fi considerată pagina 1). Totodată se poate specifica şi numărul de elemente care vor apărea intr-o pagină, acest număr este implicit 10.

$paginator->setCurrentPageNumber($this->_request->getParam(‘page’));
$paginator->setItemCountPerPage(20);

În final paginatorul se atribuie unei variabile din partea vizuală a aplicaţiei …

$this->view->paginator = $paginator;

iar apoi va fi afişată

<?php foreach ($this->paginator as $element): ?> 
<p><?php echo $element; ?></p>
<?php endforeach; ?>

Daca dorim paginarea unor date provenite din baza de date, cu siguranţă că va fi mai eficient ca interogarea făcută către baza de date să returneze doar acele date din pagina care ne interesează. Pentru acest lucru vom instanţia paginatorul folosind un obiect de tipul Zend_Db_Select, care va conţine o instrucţiune select personalizată pentru nevoile noastre.

class My_Model
{
    public static function getCategoryList($category_id, $page = 1)
    {
        $databaseAdaptor = Zend_Registry::get(‘db’);
        try
        {
            $select = new Zend_Db_Select($databaseAdaptor);
            /*$select = $databaseAdaptor->select();*/
            $select->from(‘products’, array(‘id’, ‘name’, ‘price’))
                    ->where(‘category_id = ?’, $category_id)
                    ->order(‘name asc’)
                    ;
                   
            $paginator = Zend_Paginator::factory($select);
            $paginator->setCurrentPageNumber($page);
            $paginator->setItemCountPerPage(10);
   
            return $paginator;
           
        }
        catch (Zend_Exception $ex)
        {
            Zend_Debug::dump($ex);
            return null;
        }
    }
}

Am creat asadar un model care va returna din baza de date toate produsele dintr-o anumită categorie, sortate după nume, folosind un obiect Zend_Db_Select instanţiat folosind un adaptor pentru baza de date, care a fost preluat din registru. Rezultatul unei astefel de abordări poate fi procesat într-un controller după cum urmează:

class IndexController extends Zend_Controller_Action
{
    public function indexAction()
    {
        Zend_Loader::loadClass(‘My_Model’);
        $this->view->paginator = My_Model::getNewsletterList($this->_request->getParam(‘category_id’), $this->_request->getParam(‘page’));
    }
}

Un alt aspect important al paginării este capacitatea acestei componente de a genera un nagivator printre “paginile” astfel determinate ale paginării, deci în ultimă instanţă posibilitatea de a avea acces secvenţial la tot setul de informaţii. Zend_Paginator furnizeată unul chiar interesant, datorită facilităţilor ce le oferă.

echo $this->paginationControl($this->paginator, ‘Sliding’, ‘pagination_control.phtml’);

Deşi ultimii doi parametri sunt opţionali, iar controlul ar putea funcţiona perfect fără aceste două informaţii, aceştia pot influenţa foarte mult modul în care acest index al navigării este construit. Ele se referă la, şi influenţează modul în care navigatorul generat va arăta şi comporta. Vom considera, pentru exemplificare, că navigatorul generat de Google la o căutare ar fi generat chiar de către Zend_Paginator.

Google Slider

Cel de-al doilea parametru controlează ce se întamplă daca acţionăm linkul “Precedentă” sau “Următoarele” şi poate avea urmatoarele valori

  • All caz în care va afişa toate paginile existente, lucru util pentru momentele când numărul paginilor rezultate este relativ scăzut
  • Elastic caz în care paginile se lărgesc şi se contractă în funcţie de poziţia în paginare şi numărul de pagini - gen Google
  • Jumping caz în care pe măsură ce utilizatorul trece de la o pagină la alta, pagina curentă se muta catre finalul unui interval de pagini, la finalul caruia va continua cu un interval nou
  • Sliding caz în care pagina curentă va fi afişată în centrul intervalului de pagini afişat, sau cel mai aproape posibil (gen Yahoo!); acesta este valoarea implicită

Cu toate acestea însă programatorul nu este restricţionat la acest set de posibilităţi, oferindui-se posibilitatea de a-si crea singur porpiul paginator. Pentru acest lucru sunt disponibile o serie de metode predefinite:

  1. first() — numărul primei pagini
  2. firstItemNumber() — numărul primei înregistrari din pagină în setul general de date
  3. firstPageInRange() — numărul primei pagini din setul curent
  4. current() — numărul paginii curente
  5. currentItemCount() — numărul de elemente afişate pe pagina curentă
  6. last() — numărul ultimei pagini
  7. lastItemNumber() — numărul ultimei înregistrari din pagină în setul general de date
  8. lastPageInRange() — numărul ultimei pagini din setul curent
  9. next() — numărul paginii următoare
  10. pageCount() — numărul total de pagini
  11. pagesInRange() — paginile aflate în setul de pagini
  12. previous() — numărul paginii precedente
  13. totalItemCount() — numărul total de elemente

Prin setul curent de pagini, ne-am referit la paginile vizibile la o afişare.