ООП PHP в примери Част 1 - Странициране
Пример 1:

Прочетете теорията какво е клас и има ли почва у нас!


Работи с >= PHP 5. За по-ниски версии трябва премaхнете отвсякъде public и private. Като когато се инициализират член променливите където са private и една public -> трябва да ги заместите с var -> примерно: var $_total;



Ще опиша как се прави клас за странициране, с много обяснения.

За начало ще дефинираме няколко константи които ще използваме.

Константи се дефинират с функцията define();


define('FIRST_PAGING', 'Първа');
define('LAST_PAGING', 'Последна');
define('PREVIOUS_PAGING', 'Назад');
define('NEXT_PAGING', 'Напред');


Тези 4 константи, оказват какво да изписва като текст за линкове на първа, последна, следваща, предишна страница.


define("DEFAULT_PAGING", 1);
define("MARGIN_PAGING", 2);


Тези две константи оказват какво да бъде страницирането. Реализирал съм стандартно DEFAULT_PAGING -> т.е ако има 100 страници, ще се изобразят 100 линка! Втората константа MARGIN_PAGING оказва страницирането да става граница отгоре и отдолу. В примера ще видите какво имам в предвид.

Следва да реализираме страницирането чрез клас.
Декларацията на класа започва с ключовата дума class, следвана от името на класа:


class paging {

}


В тялото следва да декларираме, вътрешните член променливи и методи на класа.

Започваме с декларацията на член променливите. Използвам ключовата дума private, за да окажа, че до тези променливи никой няма да има достъп, освен членовете на този клас. Реализираме едно от основните свойства на обектното програмиране -> капсулиране (капсаловане).


private $_total; //ще оказва общия брой на това което искаме да страницираме.
private $_perPage; //ще оказва по колко на страница да се визуализират.
private $_maxPages; //ще оказва максималния брой страници.
private $_currentPage; //ще оказва текущата страница.
private $_typePaging; //ще оказва типа на странициране обикновенно или с граница.
private $_margin; //ще оказва каква е границата, ако имаме странициране с граница.
private $_start; //ще оказва стартовата позиция на генериране на линковете.
private $_end; //ще оказва крайната позиция на генериране на линковете.
private $_links; //буфер където ще съхраняваме линковете.

public $_offset; //ще оказва отместването, което ще използваме в заявките например. То е 'публично' за да имаме достъп отвън.


До тук добре. Вече имаме променливите с които ще работим. Следва дефиницията на конструктора на този клас. Конструкторът е функция на класа, която има същото име като на класа.
В PHP, може да използваме като име на конструктора __construct(). Работата на тази функция е да инициализира членовете на класа.
Айде да го напишем (използвам __construct(), може и като името на класа paging()).
Използвам ключовата дума public, за да има достъп до този метод (функция) от всякъде.
Понеже когато създаваме обект от този клас, се извиква конструктора и ако той е private,ще даде фатална грешка.
Нашият конструктор приема няколко променливи, подадени когато създаваме обект от този клас.


public function __construct($total, $perpage, $current, $type = 1, $margin = 5) {
//най-просто казано указаелят $this, ни дава достъп до нашите променливи които декларирахме по горе.
$this->_currentPage = (int)$current;
$this->_total = (int)$total;
$this->_perPage = (int)$perpage;
$this->_typePaging = (int)$type;
$this->_margin = (int)$margin;
$this->createPaging();
$this->createStartEnd();
}


Първият ред ще съхрани в променливата $_currentPage стойността на параметъра $current . Останалите редове са аналогични до $this->createPaging(). Това го правиме за да можем даприсвоим стойностти на променливите от нашия клас. Какво прави всяка променлива, горе ги описахме!
Но какво са останалите два реда ?
Това са методи на класа (функции), които ще реализираме сега:
Фунцкията createPaging(), е privatе. Няма смисъл да я викаме извън класа!
В конструктура викаме тази функция. Тя какво прави:


private function createPaging() {
$this->_currentPage = max($this->_currentPage, 1);
$this->_maxPages = ceil($this->_total / $this->_perPage);
$this->_offset = ($this->_currentPage - 1) * $this->_perPage;
$this->_currentPage = $this->_currentPage > $this->_maxPages ? $this->_maxPages : $this->_currentPage;
}


$this->_currentPage = max($this->_currentPage, 1); - на променливата $_currentPage и присвояваме стойност, която е максималната стойност между 1 и стройността на същата $_currentPage, когато и присвоихме стойност в конструктура. Както знаете тази променлива показва коя е текущата страница. Ако сме подали -1, този ред от нашата функция ще сравни -1 и 1 и ще върне по-голямото т.е 1. Така няма да имаме отрицателни страници :)

$this->_maxPages = ceil($this->_total / $this->_perPage); - изчисляваме колко линка на страниците ще имаме. Това става като разделим $this->_total / $this->_perPage (общия брой и колко искаме на страница). Използваме ceil(), за да върне по-голямото цяло число. Тъй като при деленето може да получим число с плаваща запетайка.

$this->_offset = ($this->_currentPage - 1) * $this->_perPage; - изчисляваме отместването което ще използваме например за заявки от базите данни LIMIT $_offset, $_perPage; Изчисляваме го като извадим текущата страница от 1 и умножим с броя записи които искаме да имаме на страница.

$this->_currentPage = $this->_currentPage > $this->_maxPages ? $this->_maxPages : $this->_currentPage; - с този ред правим проверка дали текушата страница не е по-голяма от максималния брой страници. Ако е е по-голяма за текуща избираме максималната, иначе оставяме текущата.

Това изпозлваме за тялото на нашата функция createPaging();

Методът (функцията) е следния:


private function createStartEnd() {
switch ($this->_typePaging) {
case MARGIN_PAGING:
$this->_start = $this->_currentPage - $this->_margin;
$this->_start = $this->_start < 1 ? 1 : $this->_start;
$this->_end = $this->_currentPage + $this->_margin;
$this->_end = $this->_end > $this->_maxPages ? $this->_maxPages : $this->_end;
break;
default:
$this->_start = 1;
$this->_end = $this->_maxPages;
break;
}
}


Private знаете какво е. Правим една проверка какъв е типа на старницирането. Помните че в конструктура $_typePaging и присвоихме стойност от параметъра $_type;
Ако стойността е равна на константата MARGIN_PAGING, то имаме странициране с граница, ако не -> обикновено :)
Когато е обикновено стартовата страница е винаги 1, а крайнта винаги максималната стойност на брой страници.
Когато сме в 'режим граница' правим следното:
$this->_start = $this->_currentPage - $this->_margin; - стартовата страница става текушата минус границата
$this->_start = $this->_start < 1 ? 1 : $this->_start; - правим проверка дали пък случайно стартовата не е по-малка от 1, ако е по-малка я правиме 1 иначе даваме стойността генерирана от горния ред;
$this->_end = $this->_currentPage + $this->_margin; - крайната страница става текущата плюс границата
$this->_end = $this->_end > $this->_maxPages ? $this->_maxPages : $this->_end; - също правим проерка дали крайната не е по-голяма от максималната, ако е по-голяма избираме максималната иначе крайната генерирана от по горния ред.

До тук добре. Направихме логиката на това странициране. Остава само едно един метод -> да конструираме страницирането. Тука се правят стиловете, как да изглежда и т.н. Направил съм го от рода на [Първа] [Предишна] [1][2][3][4] [Следваща] [Последна]. Тука можете да променяте по ваш вкус.


public function createLinks($url = '?') {
$this->_links = '';

if($this->_currentPage <= 1) {
$this->_links .= '[' . FIRST_PAGING . '] ';
$this->_links .= '[' . PREVIOUS_PAGING . '] ';
} else {
$this->_links .= '[<a href="'.$url.'page=1">'.FIRST_PAGING.'</a>] ';
$this->_links .= '[<a href="'.$url.'page='.($this->_currentPage - 1).'">'.PREVIOUS_PAGING.'</a>]';
}

for ($i = $this->_start; $i <= $this->_end; ++$i) {
$this->_links .= $this->_currentPage == $i ? ' [' . $i . '] ' : ' [<a href="'.$url.'page='.$i.'">'.$i.'</a>] ';
}

if($this->_currentPage >= $this->_maxPages) {
$this->_links .= '[' . NEXT_PAGING . '] ';
$this->_links .= '[' . LAST_PAGING . ']';
} else {
$this->_links .= '[<a href="'.$url.'page='.($this->_currentPage + 1).'">'.NEXT_PAGING.'</a>] ';
$this->_links .= '[<a href="'.$url.'page='.$this->_maxPages.'">'.LAST_PAGING.'</a>]';
}
}

Приема един аргумент -> $url (url на страницата завършваща с ? или &, по подразбиране страницата е текушата плюс ?). Започваме:
Инициализираме $this->_links = ''; да е празна стойност.
Правим възките [Първа] [Предишна] . Ако текушата страница <= 1 нямаме линкове към тях, иначе те стават линкове. Първият линк води към страница 1, а втория текущата минус 1.

Следва цикъл, с който ще обходим страниците. Цикълът започва от стартовата страница и свършшва до крайната страница $this->_start; и $this->_end;, които вече сме изчислили от метоа createStartEnd(), според тима на странициране.
Ако текущата страница съвпада със стойността на текущото $i, то не го правиме линк, иначе генерираме линк.

Следва да направим възките [Следваща] [Последна] . Ако текушата страница >= максималните нямаме линкове към тях, иначе те стават линкове. Първият линк води към следваща страница, а втория последната.

Тази функция съхраняваше през цялото време всички линкове и не линкове в буфера $this->_links.

Това е. Само трябва да принтираме страницирането където си искаме. За целта си правим друг метод printLinks(), коята визуализира буфера.


public function printLinks() {
echo $this->_links;
}


//Сега как да използваме нашия клас.
$total = примерно mysql_select("SELECT COUNT(*) as total FROM table");
$perpage = 10;
$current = $_GET['page']; //изпозлваме page, ако използвате друга, променете createLinks() там където имате page с вашата променлива;
$type = 1;
$margin = 5;
//Създаваме обект от нашия клас -> с ключовата дума new. При създаването се извиква нашия конструктор __construct с параметрите $total, $perpage, $current, $type, $margin. Като извика конструктора той, ще инициализира вътрешните променливи на класа с тези които сме подали. Като се инициализират, ще се извикат вътрешните два метода $this->createPaging(); $this->createStartEnd(); и ще построят логиката по-която се прави това странициране, според тези променливи които сме подали.
$paging = new paging($total, $perpage, $current, $type, $margin);
//обръщението към метод чрез обект от клас става с '->'. Така с този ред викаме метода createLinks();. Конструктора си е свършил работата и е 'напълнил' всички променливи, така че метода от нашия клас, ще използва стойностите които конструктора е инициализирал.
Тази функция помните приемаше 1 параметър страница към които ще водят линовете
$paging->createLinks();
//визуализираме буфера, който се е напълнил от createLinks();
$paging->printLinks();
//и накрая всичко е изчислено и т.н в
$paging->_offset; е съхранен отметстането което може да използвате в заявките примерно:
mysql_query("SELECT * FROM table LIMIT {$paging->_offset}, {$perpage}");

Виждате как на един обект (в случая $paging) инициализираме променливите му и използваме методите му.

Ако създадем друг обект $paging2, той ше е различен от първия, и съвсем други стойности и т.н

примерно:

Целият код на класа съм съхранил в paging.class.php и го include за да може да му използвам методите и т.н :)


include('paging.class.php');

$total = 100 //примерно mysql_select("SELECT COUNT(*) as total FROM table");
$perpage = 10;
$current = $_GET['page'];
//искаме от тип 1 - обикновено
$type = 1;

$paging = new paging($total, $perpage, $current, $type);
$paging->createLinks();
$paging->printLinks();
$paging->_offset;
//примерно: mysql_query("SELECT * FROM table LIMIT {$paging->_offset}, {$perpage}");

$perpage = 20;
$current = $_GET['page'];
//искаме от тип 2 - с граница
$type = 2;
//с граница 5
$margin = 5;

$paging2 = new paging($total, $perpage, $current, $type, $margin);
$paging2->createLinks();
$paging2->printLinks();
$paging2->_offset;
//примерно: mysql_query("SELECT * FROM table LIMIT {$paging->_offset}, {$perpage}");


Обектът $paging ще е с обикновено странициране по 10 записа на страница, а $paging2 ще е с странициране с граница и по 20 записа на страница. Макар че използват един клас, те са създали копие (instance) от този клас, и си работят самостоятелно.
Вижте демо и разликата между обикновено и с граница. Може да се направят различни видове страницирания просто трябва да си допълвате метода createStartEnd() с други случай.


Очаквайте: В следващия урок, ще ви покажа как да си направим клас който да обслужва база от данни. Ще го свържем със този урок :).


ДЕМО

КЛАСЪТ ЗА СТРАНИЦИРАНЕТО



/ Трябва да сте регистриран за да напишете коментар /
От: WildBeast
0:42 29-11-2009
Има доста грешки или версията на PHP-то ми е по-стара...
От: sekul79
20:26 29-11-2009
Много добър урок,няма критика за това.В един урок неможе да се обясни и онагледи всичко в oop php тва е смешно..
Благодаря за времето и желанието ти
От: Emira7e
11:27 25-02-2010
Урока е добър само малко ще ти поправя кода от скоро съм в сферата на програмирането но забелязах една лека грешка може би от бързане :) заменете

$this->_end = $this->_currentPage + $this->_margin;
със
$this->_end = $this->_start + $this->_margin;

за да не ви се променя установенaта граница
От: bunchevi
19:23 19-01-2011
Бих искала да попитам дали реда $current = $_GET['page']; който слагаме във файла където ще изведем страницирането, трябва да се проверява дали е число или няма опасност от атаки. И дали ако го направим с
if(is_numeric($current))
{//кода
}
else
{// пренасочване
}
е достатъчно сигурно.
От: bunchevi
19:34 19-01-2011
Бих искала да попитам дали реда $current = $_GET['page']; който слагаме във файла където ще изведем страницирането, трябва да се проверява дали е число или няма опасност от атаки. И дали ако го направим с
if(is_numeric($current))
{//кода
}
else
{// пренасочване
}
е достатъчно сигурно.
От: lortnoc
14:31 16-02-2011
Не, понеже в класа $current се каства към int и после се намира max между $current и 1.
Т.е ако някой пише различно от число с casting към int става 0 и после max между 0 и 1 = 1 което води към първа страница.
1