05. Модель. Адаптер БД и Zend_Db_Table. Связи между таблицами.


    Порядок работы с базой данных.


    Для того, чтобы мы могли использовать все возможности, которые предоставляет нам ZF, мы должны работать с каждой таблицей БД через отдельный объект класса Zend_Db_Table он предоставляет единый интерфейс для доступа к данным, не смотря на то, какую именно СУБД мы решили использовать. Но поэтому сначала необходимо соединится с базой данных через адаптер. Кстати для этого нам понадобится расширение pdo для php и драйвер pdo_mysql для него, это не экзотика, так что надеюсь проблем не будет. Теперь по порядку.

    Соединения с базой. Адаптер.


    Существует класс Zend_Db, он выполнен в соответствии с шаблоном проектирования "Фабричный метод" (Factory Method) и содержит всего один статичный метод factory(), призванный для инстанцирования нужного нам класса, в зависимости от используемой СУБД.

    Соединиться с базой и получить адаптер, можно следующим образом:

require_once 'Zend/Db.php';
$adapter = Zend_Db::factory(
    pdo_mysql, // драйвер
    array(
        'host' => $config->db->host,
        'username' => $config->db->user,
        'password' => $config->db->pass,
        'dbname'   => $config->db->name
    )
);

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

    Работа с таблицами. Модель.


    Начинается с создания класса для таблицы, например:

require_once 'Zend/Db/Table/Abstract.php';

class User extends Zend_Db_Table_Abstract
{
    protected $_name = 'users';
    protected $_primary = array('login');

    function init()
    {   }
}

   Затем создание объекта этого класса с передачей адаптера:

$table new Users(array('db' => $adapter));

    После чего можно легко работать с данными. Например:

$data = array(
    'login' => 'admin',
    'name' => 'Ivan',
    'email' => 'ivan@2developers.net'
);

$table->insert($data);

    И другие операции подробно расписаны в мануале. Есть другой момент, которому уделено, на мой взгляд, мало внимания это связи.

    Связи между таблицами.


    Ниже приведен пример связи между тремя таблицами: "Комментарии", "Статьи", "Пользователи". С помощью связей добиваемся следующего:
        -легко получить по id-пользователя все его статьи, а по id-статьи все комментарии к ней, не составляя SQL-запросов, а просто одним методом;
        -сделать так, чтобы при удалении пользователя удалялись все его статьи и соответственно комментарии к ним.


class User extends Zend_Db_Table_Abstract
{
    // имя таблицы в БД
    protected $_name = 'users';
    protected $_primary = array('login');
    // Зависимые таблицы, а точнее - их классы
    protected $_dependentTables = array('Post', 'Comment');

    function init()
    {    }
}

class Post extends Zend_Db_Table_Abstract
{
    protected $_name = 'posts';
    protected $_primary = array('id');
    protected $_dependentTables = array('Comment');

    // Описание связей
    protected $_referenceMap    = array(
        'User' => array(
            'columns'           =>'user_id', //поле в таблице posts
            'refTableClass'     => 'User',   //класс для таблицы users
            'refColumns'        => 'id',     //primary key в классе User
        )
    );
    
    function init()
    {    }
}

class Comment extends Zend_Db_Table_Abstract
{
    protected $_name = 'comments';
    protected $_primary = array('id');

    protected $_referenceMap    = array(
        'Post' => array(
            'columns'           => 'post_id',
            'refTableClass'     => 'Post',
            'refColumns'        => 'id',
            'onDelete'          => self::CASCADE //каскадное удаление - при удалении статьи, удалять ее каменты
        ),
        'User' => array(
            'columns'           =>'user_id',
            'refTableClass'     => 'User',
            'refColumns'        => 'id'
            // нет каскадного удаления - не удалять все каменты удаляемого пользователя
        )
    );

    function init()
    {    }
}

    Таким образом получить все статьи пользователя можно так:


$users = $user_model->find($login);
$user = $users->current();
// укаываем только класс где искать $_referenceMap с описанием связей
$posts = $user->findDependentRowset('Post');
// Получить все его комментарии
$comments = $user->findDependentRowset('Comment');

    Так же и со статьями и комментариями. А с удалением еще легче:


//просто удаляем статью и комментарии к ней автоматически удалятся
$posts = $post_model->find($id);
$posts->current()->delete();

    Вместо заключения - создание хэлпера для работы с БД


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

    lib/Divo/Helper/Action/Db.php

require_once 'Zend/Controller/Action/Helper/Abstract.php';

class Divo_Helper_Action_Db extends Zend_Controller_Action_Helper_Abstract
{
    protected $db;
    public $config;

    // Выполняется соединение. Только один раз, при первом вызове помощника
    public function __construct()
    {
        // предварительно нужно добавить в config.ini доступы к БД
        $this->config = Divo_Controller::divoLoadConfig('config.ini', 'DB');
        require_once 'Zend/Db.php';
        $this->db = Zend_Db::factory($this->config->type,
                array('host'     => $this->config->host,
                      'username' => $this->config->user,
                      'password' => $this->config->pass,
                      'dbname'   => $this->config->name
                )
              );
        $this->db->query('SET NAMES UTF8');
        // для отладки (отдельная тема на потом)
        $this->db->getProfiler()->setEnabled($this->config->debugging);
    }

    /** @return object Zend_Db_Adapter_Pdo_Mysql 
        возвращает адаптер */
    public function direct()
    {
        return $this->db;
    }

    /** @param str Имя класса модели например Post или User
        @return object
        Возвращает объект класса, созданного для работы с таблицей */
    public function getModel($name)
    {
        require_once(APPPATH."/models/$name.php");
        $cache = Zend_Controller_Action_HelperBroker::getStaticHelper('Cache');
        return new $name(array('metadataCache' => $cache->getCoreCache(), 'db' => $this->db));
    }

}

    Выполнить запрос к базе можно следующим образом, в любом контроллере:

$users = $this->_helper->db()->fetchAll("SELECT * FROM users");

    Или получить модель и выполнить запрос:

$user_model = $this->_helper->db->getModel('User');
$users = $user_model->fetchAll();



Прикрепленные файлы:

ZF - DB (Загрузок: 223)

Добавлена: 23-02-2009 | Пользователем: djaarf | Просмотров: 10448

Комментарии

Stepan Tanasiychuk 25-03-2009 03:06
Отличная статья! Действительно связи в мануале обделили вниманием.
djaarf 25-03-2009 14:12
Спасибо за отзыв.
unit 12-12-2009 10:05
Спасибо вам огромное за статью, долго искал подобное решение, головой об стенку бился, а тут на тебе почти готовое решение. Еще раз спасибо.
Sweefy 15-08-2010 02:59
Спасибо большое, побольше бы статей с примерами. За исходники в архиве отдельное спасибо.
Elvis 15-11-2010 18:28
доброе время суток , у меня вопрос если я использую отношение один ко многим имею две таблицы Users и Transfers выбираю данные из Transfer и хочу присоединить логины из Users но по Transfer ещё и группирую
примерно так :
$res = $this->fetchAll( $this->select( )-> where('type_th = 2')-> group('id_user_th') );
     
      foreach( $res  as $val ){
 #3     $r = $val-> findDependentRowset('Accaunt_Users' ) ;
          foreach( $r as $row ){
              $this->_listTopUsers[ $row-> id_user ] = $row-> nick_name ;
          }
      }

почему я не могу обратиться к столбцам таблицы Transfer, после #3  ведь они же выбираются тут :
$res = $this->fetchAll( $this->select( )-> where('type_th = 2')-> group('id_user_th') );
большое спасибо заранее

djaarf 15-11-2010 18:57
Elvis, вот озадачили... пример частный, что такое "type_th = 2" не понял :)
Я бы посмотрел Zend_Debug::dump($res->toArray()), если там нет того что вам надо, то это не скрипт виноват, а вероятно группировка.
Хотя я просто недопонял суть задачи наверно...
Может там имена столбцов одинаковые?.. а может еще что-то.. Не получилось разобраться, извините.
Elvis 18-11-2010 13:17
Спасибо, :)  уже разобрался грубо конечно, просто выбрал данные из массива,  но это не выход, в общем если у кого то получится пожалуйста объясните , суть в том что нужно получать данные и из родительской таблицы функцией $row->findParentRow($table, [$rule]); ....  и вот это не получается :( , т.е мы берём зависимые данные $r = $val-> findDependentRowset('Accaunt_Users' ) ; все отлично, а вот данные (столбцы) из главной таблицы Acc_Users где лежат логины и т.д тут как то сложно . все равно спасибо :)
Сергей 26-08-2011 18:15
Спасибо!


Оставить комментарий



Капча *

Captcha

Комментарий будет опубликован после проверки модератором

Для подсветки синтаксиса исходный код следует обрамлять следующими тэгами
<pre><code class="синтаксис" >код</code></pre>
Подерживаются следующие: cpp php javascript sql html-xml css ini