Pull to refresh

небольшой хак для Zend_Db_Table_Row класса

Reading time 3 min
Views 1.2K
Основной любого современного web-приложения является взаимодействие с базой данных. В случае если для реализации приложения выбран Zend Framework мы неизбежно столкнемся с использованием класса Zend_Db_Table_Abstract. Этот класс успешно реализует CRUD функции, необходимые для работы с БД, но, тем не менее, эта конструкция содержит один недостаток.


Рассмотрим следующую структуру БД:

image

В данной БД представлено две сущности – новости и комментарии к новостям. Очевидно, что к одной новости может быть оставлено множество комментариев.
Ставим перед собой задачу – выбрать все новости и комментарии к ним и передать информацию в представление. Как подойти к ней с точки зрения Zend_Db_Table_Abstract? Предположим, что у нас есть соответствующие модели – News и Comments.
Выбрать все новости достаточно просто:

$News=new News();
$news=$News->fetchAll();


* This source code was highlighted with Source Code Highlighter.

Затем, следуя логике необходимо в цикле обойти все новости и выбрать для каждой новости все комментарии принадлежащие ей. Тут и возникает главный вопрос – куда записать массив объектов с комментариями?
Очень хочеться сделать так:

foreach($news as $record)
{
    $comments=$Comments->fetchAll(“news_id=”.$record->news_id);
    $record->comments=$comments;
}

$this->view->news=$news;


* This source code was highlighted with Source Code Highlighter.

Но мы получим исключение, гласящее Specified column «comments» is not in the row. Дело в том, что каждая переменная $record является объектом типа Zend_Db_Table_Row_Abstract. Пытаясь присвоить его свойству comments массив (на самом деле объект типа Zend_Db_Table_Rowset_Abstract) мы вызываем метод __set(). Осмотрим его:

  public function __set($columnName, $value)
  {
    $columnName = $this->_transformColumn($columnName);
    if (!array_key_exists($columnName, $this->_data)) {
      require_once 'Zend/Db/Table/Row/Exception.php';
      throw new Zend_Db_Table_Row_Exception("Specified column \"$columnName\" is not in the row");
    }
    $this->_data[$columnName] = $value;
    $this->_modifiedFields[$columnName] = true;
  }


* This source code was highlighted with Source Code Highlighter.

Становиться ясно, что присвоение возможно, только если данное свойство зарегистрировано в массиве $_data. Ну а в массив дата оно попадает с помощью изучения мета информации таблиц участвующих в запросе. Очевидно, что в нашем случае поля comments там никогда не будет.

Наконец-то мы добрались до решения проблемы. Самым ужасным, что можно сделать, является исправление проблемы непосредственно в коде класса Zend_Db_Table_Row_Abstract и выкинуть оттуда проверку на регистрацию в массиве $_data. Мы, естественно, так делать не будем, мы предпочтем ООП. Все что необходимо сделать – создать собственные классы Row и Rowset.
Вот код нового класса Row (положим его в файл /library/Main/Db/Table/Row.php):

<?php

class Main_Db_Table_Row extends Zend_Db_Table_Row
{
  public function newDataProperty($value)
  {
    if(!array_key_exists($value,$this->_data))
      $this->_data[$value]="";
  }
}


* This source code was highlighted with Source Code Highlighter.

Вот код нового класса Rowset (файл /library/Main/Db/Table/Rowset.php):

<?php

class Main_Db_Table_Rowset extends Zend_Db_Table_Rowset
{
  function init()
  {
    $this->_rowClass='Main_Db_Table_Row';
  }
}


* This source code was highlighted with Source Code Highlighter.

Это не все, теперь заставим наши модели использовать эти классы. На примере модели News:

<?php

class News extends Zend_Db_Table_Abstract
{
  protected $_name='news';
  protected $_primary='news_id';
  
  function init()
  {
    $this->setRowClass('Main_Db_Table_Row');
    $this->setRowsetClass('Main_Db_Table_Rowset');
  }
  
}


* This source code was highlighted with Source Code Highlighter.

Все практически готово. Последний штрих, изменим код добавления комментариев к объекту новости:

foreach($news as $record)
{
    $comments=$Comments->fetchAll(“news_id=”.$record->news_id);
    $record->newDataProperty(‘comments’);
    $record->comments=$comments;
}

$this->view->news=$news;


* This source code was highlighted with Source Code Highlighter.

Теперь работает!
Tags:
Hubs:
If this publication inspired you and you want to support the author, do not hesitate to click on the button
-9
Comments 51
Comments Comments 51

Articles