постраничная разбивка контента

решил написать про возможные варианты разбивки контента на страницы, но стоило только отвернуться как скачок напряжения, и пары абзацев
как не бывало. Но не будем о грустном.

Я встречал пока только два основных направления «пейджификации», это постраничная разбивка, для которой требуется в обязательном порядке знать общее число
страниц. И разбивка вида «ВПЕРЕД», «НАЗАД», для ее реализации достаточно немного изменить сам запрос дерганья контента из БД.
В случае постраничного отображения, мы получаем более наглядный и удобный способ визуализации, который легко можно представить как второй способ.
Но за все приходится платить, в данном случае дополнительным запросом к БД.
Второй вариант реализован путем получения ответа от Бд, составляющей страницу на 1 элемент больше чем собирались выводить, если этот элемент
присутствует то значит мы можем дать ссылку на последующую страницу. Иногда, где не требуется вся мощь и красота постраничного вывода, этот способ тоже применим.

пример реализации:

вариант «< НАЗАД | ВПЕРЕД >»

$start=isset($_GET['page']) ? $_GET['page'] : 0;	// с какого элемента начинаем забирать данные
$pages=15;											// количество выводимых полей из бд

$result=mysql_query("SELECT * FROM `table` ORDER BY `id` LIMIT $start,($pages+1)");

// код вывода ссылки если возможна навигация назад
$prev=($start-$pages>=0) ? '<a href="page.php?page='.($start-$pages).'"> BACK </a>' : 'BACK';
// если возможна навигация вперед
$next=count($result)>$pages ? '<a href="page.php?page='.($start+$pages).'"> NEXT </a>' : 'NEXT';

print $prev.' | '.$next;

Но что будет если мы передадим скрипту в запросе значение к примеру равное 2, по нашему алгоритму в $prev не будет ссылки, тогда как на самом деле
предыдущая страница есть. Так что в фильтре входящего значения необходимо несколько доработать код. Который будет валидировать данные и
все что не подходим нам по условиям отправлять в исключение.

if (((int)$start % (int)$pages)!=0){print '404, Sorry, page not found';exit;}

вариант «1 2 3 4 [5] 6 7 8»

$max=mysql_query("SELECT COUNT(*) FROM `table`"); // количество записей в таблице
$pages=15; // по сколько выводить записей на страницу
$page=$_GET['page']; // текущая страница

for ($i=1;$i<=ceil($max/$pages);$i++){
    print ($i==$page) ? ' <span style="color : #262626;	font-size : 0.82em;	font-weight : bold;">[ '.$i.' ]</span> ': ' <a style="color : #C2C2C2;	font-size : 0.72em;" href="#page.php?page='.$i.'">'.$i.'</a> ';
}

результат:
1 2 3 4 5 6 7 8 9 10 11 12 13 [ 14 ] 15 16 17 18 19 20 21 22 23 24 25

$max=mysql_query("SELECT COUNT(*) FROM `table`"); // количество записей в таблице
$pages=15; // по сколько выводить записей на страницу
$page=$_GET['page']; // текущая страница
$len=5; // количество отображаемых ссылок слева и справа от текущей страницы

//left
$left=$len>=$page-1? $page-1:$len;
for ($i=0;$i<=$left-1;$i++){

    print ' <a style="color : #C2C2C2;	font-size : 0.72em;" href="#page.php?page='.($page+$i-$left).'">'.($page+$i-$left).'</a> ';
}

print '<span style="color : #262626;	font-size : 0.82em;	font-weight : bold;"> [ '.$page.' ] </span>';

//right
$right=ceil($max/$pages)<=$page+$len?ceil($max/$pages)-$page:$len;
for ($i=1;$i<=$right;$i++){

    print ' <a style="color : #C2C2C2;	font-size : 0.72em;" href="#page.php?page='.($page+$i).'">'.($page+$i).'</a> ';
}

результат:
9 10 11 12 13 [ 14 ] 15 16 17 18 19

В качестве max, page, pages, len можно поставить любые цифры и посмотреть на итоговый результат.

REGEXP, собрать ссылки со страницы

Если требуется собрать все ссылки которые есть на странице то поможет следующее регулярное выражение:

{<a.*?\s{1}href=\s*(&#91;"'&#93;)(.+?)\1.*?>}

оно ищет href который может быть обрамлен кавычками, слева от него может быть любое кол. символов до начала тега, но обязательно будет пробел, справа после закрывающей URL кавычки может быть любое количество символов или сразу закрываться тег.

следующий код собирает в скалярный массив все ссылки.

while($a=~m{<a.*?\s{1}href=\s*(&#91;"'&#93;)(.+?)\1.*?>}oig){
	push(@m,$2) if $2;
}

или краткая форма:

push(@m,$2) while($a=~m{<a.*?\s{1}href=\s*(&#91;"'&#93;)(.+?)\1.*?>}ig);

REGEXP, собрать все email’ы со страницы

Вот, простая и дубовая конструкция, лишенная красоты и лаконичности присущей перлу, но зато очевидная на 100%

while($a=~m{=(['"])mailto:(.+?)\1.*?>}ig)
{
	push(@m,$2) if $2;
}

это регулярное выражение ищет в тексте конструкцию вида «mailto:blablabla», где кавычки могут быть как одинарные так и двойные, первые скобки нужны для того чтобы найти и определить в переменную $1 тип кавычек в которых находится искомое, ну а вторые нужны чтобы в переменную $2 положить сам почтовый адрес.

более краткая запись:

push(@m,$2) while($a=~m{=(['"])mailto:(.+?)\1.*?>}ig);

Настройка гитары

Наткнулся на новый для себя способ настраивать гитару. Если первая струна по звучанию мной уже настроена и наиболее близко лежит к Ми :), то настроить остальные стандартным методом в унисон по 4 ладу красиво не получалось, унисон есть, строя нет, играет как дрова.
А вот и то что помогло решить проблему, не полностью, руки то выпрямить не получилось, но хоть играть стала краше.

имеем настроенную первую струну, от нее уже пляшем к другим

настраиваем 2-ю струну, зажимаем 1-ую на 7-ем ладу, а 2-я звучит открыто. Добиваемся гармоничного красивого звучания, тобишь унисон.

настраиваем 3-ю струну, зажимаем 1-ую на 3-ем ладу, а 3-я звучит открыто.

настраиваем 4-ю струну, зажимаем 4-ую на 2-ем ладу, а 1-я звучит открыто.

настраиваем 5-ю струну, зажимаем 5-ую на 3-ем ладу, а 2-я звучит открыто.

настраиваем 6-ю струну, зажимаем 6-ую на 3-ем ладу, а 3-я звучит открыто.

вариант 2
настраиваем 6-ю струну, зажимаем 6-ую на 2-ем ладу, а 4-я звучит открыто.

***

да что ты знаешь про море Росса,
ты и небо то небось не видел, отребье, выросшее на станции..
ругался старый капитан.
я молчал, мне не хотелось спорить, я не хотел продолжать разговор,
я знал, что сегодня, годовщина смерти его последнего сына.
Капитан остался один, один как я, но у него было небо и море…

hello world

Мое знакомство с программированием под web началось в далеком 2001 году, за достоверность описываемых событий я уже ручаться не могу, очень многое успело наложиться на память с тех времен. Но исписаны были килобайты кода, некрасивого, нерабочего, и просто опасного. Но любовь к моей первой веб hello world я сохраняю до сих пор.

#!/usr/bin/perl
print "Hello world\n\n";

кэширование запросов

Есть сайт, который состоит из элементов контент которых дергается из бд.
Для снижения нагрузки на сервер БД, можно результат запросов писать в кэш, при этом здесь мы рассмотрим реализацию файлового кэша.

логика работы такова.
прежде чем обратиться к базе с очередным селектом, смотрим, есть ли в специально отведенной папке под это дело файла, отвечающего за конкретно этот запрос. Если файл есть, то прочитать его, если нет, то послать запрос, и результат записать в кэш, чтобы не изобретать велосипед, данные в кеш пишутся сериализоваными.

в идеале это сводится к следующему:

$query='SELECT * FROM `table` ORDER BY id DESC LIMIT 1';
$rez=$cache->get('GET_LAST_POST',$query);

но, это требует от класса еще научится работать с БД, что непременно его усложнит.
поэтому вот модель попроще:

$query='SELECT * FROM `table` ORDER BY id DESC LIMIT 1';
$rez=$cache->is('GET_LAST_POST') ? $cache->get('GET_LAST_POST') : mysql_query($query);
$cache->put($rez);

Уже не так красиво, как в первом варианте, зато не требует от будущего класса функций сверх необходимых.

Теперь остановимся подробнее, на структуре класса занимающегося кэшированием.
Наш класс должен знать куда писать, он дожен уметь создавать перезаписывать и удалять файлы, подготавливать данные к помещению и изъятию из кэша.
вот так он выглядит у меня:

class Cache {
    
    private $path=''; # путь до папки с кэшем
    private $str='';   # имя файла кэша
    private $isStr=false; #есть ли имя
    private $isCache=false; #есть ли файл по этому имени

# устанавливаем путь
    public function setPath($path){
        $this->path=$path;
    }
# передаем имя
    public function setStr($str){
        $str=trim($str);
        if (strlen($str)<=2){
            return false;
        }else{
            $this->str=$this->prepareStr($str);
            $this->isStr=true;
            $this->isCache=($this->isFile($this->str))?true:false;            
            return true;
        }
    }
# возвращаем сгенерированое имя, имя конкретного файла кэша
    public function getStr(){
        return $this->str;
    }
# возвращаем кэш, если есть что
    public function getCache(){
        if ($this->isCache==true){
            return file_get_contents($this->path.'/'.$this->str);
        }else{
            return '';
        }
    }
# есть ли кэш
    public function isCache(){
        return $this->isCache;
    }
# записать в кэш
    public function putToCache($str){
        if ($this->isStr){
            $file = fopen ($this->path.'/'.$this->str,'w');

              if ( !$file )
              {
                $this->isCache=false;
                return false;
              }
              else
              {
                fputs ( $file, $str);
              }
              fclose ($file);
              if ($this->isFile($this->str)==true){
                  $this->isCache=true;
                  return true;
              }else{
                  return false;
              }
        }else{
            return false;
        }
    }
#очстить папку кэша
    public function clearCache(){
        if (empty($this->path) || !file_exists($this->path.'/')){return false;}
        $dir = opendir ($this->path.'/');
          while ( $file = readdir ($dir)){
            if (( $file != ".") && ($file != "..") && strlen($file)==32)
            {
                if (is_file($this->path.'/'.$file)){$this->deleteFile($this->path.'/'.$file);}
            }
          }
          closedir ($dir);
          return true;
    }
#физическое удаление файла
    private function deleteFile($file){
        //print $file;
        unlink($file);
    }
#есть ли файл кэша
    private function isFile($file){
        return (!empty($file) && file_exists($this->path.'/'.$file))?true:false;
    }
#подготавливаем имя файла из переданных параметров
    private function prepareStr($str){        
        return md5(strtolower($str));
    }
}

вроде бы все возможные косяки были учтены, и код работает.

Живой пример использования класса:

include_once('/class/Cache.php');
$cache=new Cache;
$cache->setPath('/www/cache');
#cache_code
    if(isset($cache) && $cache->setStr('GET_NEWS_'.$_GET['id']) && $cache->isCache()){
        $aNews=unserialize($cache->getCache());
    }
    else{
        $aNews=mysql_query('SELECT * FROM `news` WHERE `id`='.$_GET['id'].' LIMIT 1;');
        if (isset($cache)){$cache->putToCache(serialize($aNews));}
    }
#

SQL, удалить все данные старше 2х дней

В качестве условия в этом запросе служит параметр времени. Тип поля, по которому осуществляется выборка — DATETIME.
Вид запроса:


DELETE FROM `table` WHERE `date` < DATE_ADD( NOW( ) , INTERVAL -2 DAY ); [/sql]

SQL запрос, выборка данных в промежутках между датами

Понадобилось мне выбрать данные, что находится между 2-мя промежутками времени.
Конкретно эти промежутки были описаны так, крайняя левая — самая ранняя запись, крайняя правая — текущий момент.


SELECT *
FROM `base`.`table`
WHERE YEAR( `date` ) >= (
SELECT YEAR( MIN( `date` ) )
FROM `base`.`table`
WHERE 1
LIMIT 1 )
AND MONTH( `date` ) >= (
SELECT MONTH( MIN( `date` ) )
FROM `customer`.`table`
WHERE 1
LIMIT 1 )
AND DAYOFMONTH( `date` ) >= (
SELECT DAYOFMONTH( MIN( `date` ) )
FROM `customer`.`table`
WHERE 1
LIMIT 1 )
AND YEAR( `date` ) <= YEAR( NOW( ) ) AND MONTH( `date` ) <= MONTH( NOW( ) ) AND DAYOFMONTH( `date` ) <= DAYOFMONTH( NOW( ) ) ORDER BY `date` DESC ; [/sql] что делает запрос: он выполняет 3 подзапроса, для выборки минимального года, месяца и числа, в качестве левого условия, и с правой стороны ставит текущие значения даты. Такой вид запроса удобен если параметры как левой части запроса так и правой могут быть заданы.