Zend_Navigation и построение меню из базы с использованием parent_id

Как работать с Zend_Navigation написано в мануале, а вот как быть, если меню строится не из xml файла? Т.е. — динамически из базы?
Начнем подготовку.

основной bootstrap.php

    protected function _initNavigation() {
        $this->bootstrap('FrontController');
        if ($this->_navigation === null) {
            $front = $this->getResource('frontController');
            $navigation = new Plugin_UserMenu();
            $front->registerPlugin($navigation);
            $this->_navigation = $navigation;
        }
        return $this->_navigation;
    }

Plugin_UserMenu — наш плагин, который, собственно, и будет генерировать данные для навигации.

plugins/UserMenu.php

<?php

final class Plugin_UserMenu extends Zend_Controller_Plugin_Abstract {

    protected $_view;
    public function preDispatch(Zend_Controller_Request_Abstract $request) {
        $mapper = new Admin_Model_PagesMapper();
        $oData = $mapper->fetchAllAsArray();
        $data = Default_View_Helper_SelectParentIdCategories::Tree($oData);
        $layout = Zend_Layout::getMvcInstance();
        $this->_view = $layout->getView();
        $container = new Zend_Navigation();
        $container->setPages($data);
        $this->_view->navigation($container);
    }
}

Хитрость в fetchAllAsArray():

    public function fetchAllAsArray() {
        $where = $this->getDbTable()->select()
                ->where('lang_id = ?', Zend_Registry::get('uds_lang_id'))
                ->order('pr ASC');
        $resultSet = $this->getDbTable()->fetchAll($where);
        $entries = array();
        foreach ($resultSet as $row) {
            $entry['id'] = $row->id;
            $entry['parent_id'] = $row->parent_id;
            $entry['title'] = $row->title;
            $entry['label'] = $row->title;
            $entry['txt'] = $row->txt;
            $entry['kw'] = $row->kw;
            $entry['pic'] = $row->pic;
            $entry['visible'] = $row->visible;
            $entry['external_url'] = $row->external_url;
            $entry['uri'] = $row->external_url;
            $entries[] = $entry;
        }
        return $entries;
    }

$entry['uri'] и $entry['label'] = $row->title используются именно для построения Zend_Navigation. Можно, конечно, при проектировании бд назвать поля таблицы сразу, как хочет zend, но не всегда удобно. По этому так.

Не забываем, что у нас меню использует древовидную структуру по parent_id. Из мануала Zend_Navigation можно посмотреть пример массива, в каком виде нужно его передавать для Zend_Navigation. Собственно, нам осталось лишь подготовить сам массив. На одном форуме, нашел прекрасную функцию, которая сортирует дерево типа parent_id с помощью всего одного запроса и без рекурсии. Эта функция и вызывывается Default_View_Helper_SelectParentIdCategories::Tree($oData);

Default_View_Helper_SelectParentIdCategories::Tree

<?

/* * *************************************************
 * Выкидывает селект под парент id
 */

class Default_View_Helper_SelectParentIdCategories {

    public function Tree($rows) {
        $children = array(); // children of each ID
        $ids = array();
        $idName = 'id';
        $pidName = 'parent_id';
        // Collect who are children of whom.
        foreach ($rows as $i => $r) {
            $row = & $rows[$i];
            //$d['label'] = $rows[$i]['title'];
            //$row[] = $d;
            $id = $row[$idName];
            if ($id === null) {
                continue;
            }
            $pid = $row[$pidName];
            if ($id == $pid)
                $pid = null;
            $children[$pid][$id] = & $row;

            if (!isset($children[$id]))
                $children[$id] = array();
            $row['pages'] = & $children[$id];
            $ids[$id] = true;
        }
        // Root elements are elements with non-found PIDs.
        $forest = array();
        foreach ($rows as $i => $r) {
            $row = & $rows[$i];
            $id = $row[$idName];
            $pid = $row[$pidName];
            if ($pid == $id)
                $pid = null;
            if (!isset($ids[$pid])) {
                $forest[$row[$idName]] = & $row;
            }
            //unset($row[$idName]);
            //unset($row[$pidName]);
        }
        //echo '<pre>';
        //print_r($forest);
         return $forest;
    }

    /*     * ******************************** */
}

Ну и вывод меню в layout:

<?php print( $this->navigation()->menu()->renderMenu() ); ?>

PS: Кто заметил Zend_Registry::get(‘uds_lang_id’)? Позже будет статья, как сделать мультиязычный сайт с неограниченным количеством языков.

MySql проблемы сортировки utf-8

Столкнулся с проблемой: на локальной машине, все сортируется нормально. А вот как только тестирование начинается на сервере — сортировка русских слов и фраз идёт не верным путем.
Начал разбираться. Хоть таблицы и в utf8_general_ci, но сервер настроен на latin1.
В принципе легко посмотреть с какой стороны подходить, достаточно зайти в базу и посмотреть в читабельном виде наши данные или нет. Если нет — нужно прописывать перекодировку на utf-8 в скрипте, после подключения к бд.

        $this->getAdapter()->query("SET NAMES utf8");
        $this->getAdapter()->query("SET collation_connection=utf8_general_ci");
        $this->getAdapter()->query("SET collation_server=utf8_general_ci");
        $this->getAdapter()->query("SET character_set_client=utf8");
        $this->getAdapter()->query("SET character_set_connection=utf8");
        $this->getAdapter()->query("SET character_set_results=utf8");
        $this->getAdapter()->query("SET character_set_server=utf8");

PS: $this->getAdapter()->query = mysql_query.

Для Zend Franework: application.ini

resources.db.params.charset = "utf8"
resources.db.params.driver_options.1002 = "SET NAMES utf8"

Но, после этого, на сайте у нас произойдет ужас с кодировкой. И для этого данные в базе нужно из latin1 перекодировать в UTF-8.
Так, а что же делать с данными, которыми у нас наполнен сайт? Не вбивать же всё вручную. Нашел очень удобную панель управления базой — Sypex Dumper. Очень приятно, что разработали это украинские ребята.
Так вот, делаем экспорт с помощью Sypex Dumper, установив в кодировке latin1 и жмем выполнить.

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

Построение дерва с помощью рекурсии

Бывает часто, что нужно вывести дерево с неограниченной глубиной.

Структура базы:

Перейдём к коду. Первым делом создадим базу данных и наполним её деревом.
Дамп базы данных:

CREATE TABLE catalogue
(id INT UNSIGNED AUTO_INCREMENT,
title VARCHAR (50),
pid INT UNSIGNED,
PRIMARY KEY(id));

INSERT INTO catalogue (title, pid) VALUES ('Раздел 1', 0);
INSERT INTO catalogue (title, pid) VALUES ('Раздел 2', 0);
INSERT INTO catalogue (title, pid) VALUES ('Раздел 3', 0);
INSERT INTO catalogue (title, pid) VALUES ('Раздел 4', 0);
INSERT INTO catalogue (title, pid) VALUES ('Подраздел 1.1', 1);
INSERT INTO catalogue (title, pid) VALUES ('Подраздел 1.2', 1);
INSERT INTO catalogue (title, pid) VALUES ('Подраздел 1.3', 1);
INSERT INTO catalogue (title, pid) VALUES ('Подраздел 2.1', 2);
INSERT INTO catalogue (title, pid) VALUES ('Подраздел 2.2', 2);
INSERT INTO catalogue (title, pid) VALUES ('Подраздел 1.1.1', 5);
INSERT INTO catalogue (title, pid) VALUES ('Подраздел 1.1.2', 5);
INSERT INTO catalogue (title, pid) VALUES ('Подраздел 1.1.2.1', 11);
INSERT INTO catalogue (title, pid) VALUES ('Подраздел 3.1', 3);
INSERT INTO catalogue (title, pid) VALUES ('Подраздел 4.1', 4);
INSERT INTO catalogue (title, pid) VALUES ('Подраздел 4.2', 4);
INSERT INTO catalogue (title, pid) VALUES ('Подраздел 4.2.1', 15);

Сам исполняющий файл index.php


<?php
include( "dbopen.php" );

 function ShowTree($ParentID, $lvl) {

 global $link;
 global $lvl;

 $lvl++;

 $sSQL = "SELECT id, title, pid FROM catalogue WHERE pid = " . $ParentID . " ORDER BY title";

 $result = mysql_query($sSQL, $link);

 if (mysql_num_rows($result) > 0) {
 echo("<UL>\n");
 while ( $row = mysql_fetch_array($result) ) {
 $ID1 = $row["id"];
 echo("<LI>\n");
 echo("<A HREF=\"" . "?ID=" . $ID1 . "\">" . $row["title"] . "</A>" . "&nbsp;&nbsp;\n");
 ShowTree($ID1, $lvl);
 $lvl--;
 }
 echo("</UL>\n");
 }

 }

ShowTree(0, 0);

mysql_close($link);

?>

dbopen.php — просто подключается к БД


<?php

 $hostName = "localhost";
 $userName = "root";
 $password = "";
 $databaseName = "4";

 if (!($link=mysql_connect($hostName,$userName,$password))) {
 printf("Ошибка при соединении с MySQL !\n");
 exit();
 }
 if (!mysql_select_db($databaseName, $link)) {
 printf("Ошибка базы данных !");
 exit();
 }

?>

И результат работы: Отмечу, что если это будет использоваться для меню — желательно применить кэширование! Скачать пример:

http://vazelin.org.ua/wp-content/plugins/downloads-manager/img/icons/default.gif download: phptree (1.09KB)
added: 05/10/2010
clicks: 110
description:
Автор: http://www.codenet.ru/webmast/php/tree.php

Начинаем работу с PDO

PHP Data Objects (PDO) — расширение для PHP, предоставляющее разработчику простой и универсальный интерфейс для доступа к различным базам данных.

PDO предлагает единые методы для работы с различными базами данных, хотя текст запросов может немного отличаться. Так как многие СУБД реализуют свой диалект SQL, который в той или иной мере поддерживает стандарты ANSI и ISO, то при использовании простых запросов можно добиться совместимости между различными языками. На практике это означает, что можно достаточно легко перейти на другую СУБД, при этом не меняя или частично изменяя код программы.

PDO не использует абстрактных слоёв для подключения к БД, наподобие ODBC, а использует для разных БД их «родные» драйверы, что позволяет добиться высокой производительности. В настоящее время для PDO существуют драйверы практически ко всем общеизвестным СУБД и интерфейсам. Впрочем, есть и драйвер для подключения к ODBC.

(C) Викпидея

Для работы PHP c PDO нужны следующие библиотеки: (WIN)

extension=php_pdo.dll
extension=php_pdo.dll
extension=php_pdo_firebird.dll
extension=php_pdo_informix.dll
extension=php_pdo_mssql.dll
extension=php_pdo_mysql.dll
extension=php_pdo_oci.dll
extension=php_pdo_oci8.dll
extension=php_pdo_odbc.dll
extension=php_pdo_pgsql.dll
extension=php_pdo_sqlite.dll 

Подключение к БД
$bd = new PDO('mysql:host=хост;dbname=база', 'логин', 'пароль');

Запросы на изменение и добавление данных


<strong>
</strong><code>$insert = $bd->exec('INSERT INTO `table` (`имя столбца`) VALUES("значение")');
</code><code>$update = $bd->exec('UPDATE `table` SET `имя столбца`="значение"');</code>




Закрытие соединения и освобождение ресурсов


<strong>
</strong><code>$bd = NULL;</code>



Экранирование символов

<code>$bd->quote($_GET['myvar']);</code>


Выборка данных

<code>$id = $bd->lastInsertId(); // последний auto_increment
$sel = $bd->query('SELECT * FROM `table`;'); // значение FALSE в случае ошибки
 $res = $bd->query('SELECT * FROM `table`;');
 while ($row = $res->fetch(PDO::(FETCH_BOTH или FETCH_ASSOC или FETCH_NUM или FETCH_OBJ или FETCH_LAZY))) {
...     цикл...
 }</code>

FETCH_NUM — массив с ключами чисел
FETCH_ASSOC — ассоциативный массив с названием столбцов
FETCH_BOTH — числовой и ассоциативный массив
FETCH_OBJ — выборка объекта (если актуально данным выборки)
FETCH_LAZY — выборка объекта, которая подгружает данные только во время обращения к ним

<code>$res = $var(переменная запроса)->fetchColumn(); // выборка одной записи с запроса, если актуально
 $sel = 'SELECT * FROM `table`;';
 $var = $bd->query($sel)->fetchAll(PDO::FETCH_ASSOC); // выборка всех данных с таблицы</code>

Выборка массива

<pre>$res = $db->query(“SELECT * FROM users”);
while ($row = $res->fetch(PDO::FETCH_NUM)){
// $row - массив с числовыми ключами
}

$res = $db->query(“SELECT * FROM users”);
while ($row = $res->fetch(PDO::FETCH_ASSOC)){
// $row - ассоциативный массив значений
}

$res = $db->query(“SELECT * FROM users”);
while ($row = $res->fetch(PDO::FETCH_BOTH)){
// $row - числовой и ассоиативный массив
}

Выборка одного элемента


<pre>$column = $db->query(“SELECT id FROM users WHERE login=‘login’ AND password=‘password’”);
$user = $column->fetchColumn())</pre>

Read more

Передача данных из MySQL в checkbox


<?php
 $string = "1/2/3/4/5/6/7/8"; // Строка из базы
 $ar=explode("/",$string);
 print_r($ar);
 ?>

Inner Join, Outer (left, right, full) Join в SQL

Ключевое слово join в SQL используется при построении select выражений. Инструкция Join позволяет объединить колонки из нескольких таблиц в одну. Объединение происходит временное и целостность таблиц не нарушается. Существует три типа join-выражений:

  • inner join;
  • outer join;
  • cross join;

В свою очередь, outer join может быть left, right и full (слово outer обычно опускается).
В качестве примера (DBMS Oracle) создадим две простые таблицы и сконструируем для них SQL-выражения с использованием join.
Read more

Получение значени из MySQL с помощью PHP

Структура таблицы curs:
———
| curs |
———
|id |rub|
———
|1 |7.45|
———
Нам нужно получить значение поля rub

  $sql=" SELECT `rub` FROM `curs` WHERE `id` =1 LIMIT 1";
  $result = @mysql_query($sql);
  $data = @mysql_fetch_array($result);
  @mysql_free_result($result);
   $curs = $data['rub'];
echo $curs;
 

Это первый вариант, если нужно получить только одну запись.
Данный кусок кода был написан для получения курса валют.

Paginator (постраничный вывод) данных из базы MySQL

$page — номер страницы.
В данном примере на страницу выводиться десять новостей.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
if ((!$page)||($page==1)){
        $query="Select * from `news` limit 0,20";
        $result = @mysql_query($query);
       
        while ($row = mysql_fetch_object($result)){
            echo htmlspecialchars($row->id)."-";
            echo htmlspecialchars($row->date)."-";
            echo htmlspecialchars($row->text)."-";
            }
       
        }
   
        else{
            $fr = $page."0";
            $to = $page+10;
            $query = "Select * from `news` limit $fr,$to";
            $result = @mysql_query($query);
            while ($row = mysql_fetch_object($result)){
            echo htmlspecialchars($row->id)."-";
            echo htmlspecialchars($row->date)."-";
            echo htmlspecialchars($row->text)."-";
            }
            }

Ошибка в fetchObject()

Error fetchObject() — возникает, если конвертирование данных в массив возвращает пустое значение

Что нужно знать при написании скрипта

Подведу черту над своими знаниям в области построения защищенных сайтов. Некоторые методы давно всем известны, но они занимают большую нишу в построении защиты.

Read more