Оригинал: Copyright © 2013 Akeeba Ltd
Перевод: Copyright © 2014 Фёдоров Александр

Содержание данной документации, являюется объектом авторского права и доступно согласно лицензии Joomla! Electronic Documentation License (JEDL), если не указано иное. Вы можете обратиться к разделу частые вопросы по JEDL, чтобы уточнить как вы можете использовать данный материал. Если у вас есть какие-либо вопросы, касающиеся лицензирования данного материала, или вы хотите сообщить о возможном нарушении условий лицензии на материалы данного сайта, пожалуйста, напишите по электронной почте: Этот адрес электронной почты защищён от спам-ботов. У вас должен быть включен JavaScript для просмотра..

Введение

Таблицы используется для создания объектов отображающих единичные записи из таблиц базы данных. Чаще всего они используются для валидации записи перед сохранением в БД или для выполнения определённых действий после получения записи из базы данных (например, десереализация поля, которое содержит сериализованные данные в JSON-формате). Таблицы могут быть очень удобными для автоматического выполнения "волшебных" действий при создании / изменении / загрузке записей из базы данных.

Соглашение об именовании классов и файлов

Классы таблиц должны называться ComponentTableView, например, для представления items компонента com_todo класс таблиц будет называться TodoTableItem. Часть, отвечающая за представление должна быть в единственном числе, что в общем-то логично: класс таблиц оперирует с единичными записями, соответственно и написание представления должно быть в единственном числе.

Имя файла должно совпадать с последней частью имени класса, т.е. для класса TodoTableItem файл будет называться item.php.

Все файлы классов таблиц располагаются в папке tables административной части компонента. Если класс таблицы не загружен и FOF не может найти соответствующий файл, FOF создаёт подходяще сконфигурированный экземпляр F0FTable. При этом FOF придерживается принципа "соглашения прежде конфигурирования" (описывается ниже), чтобы определить что объект таблицы должен делать.

Соглашения об именовании таблиц базы данных

Все Модели FOF по умолчанию подключаются к таблицам баз данных. Можно, конечно, работать и с Моделью без таблицы в базе данных, но только до тех пор, пока не начнут использоваться стандартные методы работы с данными.

Все таблицы в базе данных называются по следующему шаблону: #__component_view, например, #__todo_items для компонента com_todo и Представления items.

Автоинкрементное поле называют component_view_id, например, todo_item_id для вышеуказанного примера. Если в таблице нет автоинкрементного поля, невозможно будет использовать имеющиеся стандартные методы FOF по работе с данными.

Можно переопределять значения по умолчанию без копирования кода. Это описывается в разделе Конфигурирование MVC.

"Волшебные" поля

"Волшебные" поля в FOF позволяют задействовать готовый функционал фреймворка. Доступны следующие поля:

  • title - название записи, используется далее для создания псевдонима записи;
  • slug - псевдоним записи, обычно использующийся при генерации URL-адреса компонентом. По-умолчанию псевдоним создаётся из названия с использованием простейшего алгоритма транслитерации.
  • enabled - признак опубликованности записи; аналогичен колонке published в обычных компонентах Joomla!; обычно допускаются только два значения: 0 (скрыт) и 1 (опубликован);
  • ordering - поле для сортировки записей;
  • created_by - ID пользователя, создавшего запись; FOF заполняет это поле автоматически;
  • created_on - дата создания записи; FOF заполняет это поле автоматически;
  • modified_by - ID последнего изменившего запись пользователя; FOF заполняет это поле автоматически;
  • modified_on - дата последнего редактирования записи; FOF заполняет это поле автоматически;
  • locked_by - ID пользователя, который заблокировал запись для редактирования;  FOF заполняет это поле автоматически;
  • locked_on - дата, когда запись была заблокирована для редактирования; FOF заполняет это поле автоматически;
  • hits - количество просмотров записи; FOF заполняет это поле автоматически;
  • language - язык записи в мультиязычном сайте;
  • asset_id - ID записи в таблице #__assets; FOF заполняет это поле автоматически; используется для установки прав доступа на конкретную запись;
  • access - уровень доступа к записи.

Используя метод setColumnAlias в классе таблицы можно настроить названия "волшебных" полей по своему вкусу. Например, если есть желание назвать колонку published вместо enabled, это можно сделать так:

function __construct($table, $key, $db, $config)
{
    $this->setColumnAlias('enabled', 'published');
    parent::__construct($table, $key, $db, $config);
}

Теперь FOF будет знать, что колонка published содержит данные о статусе публикации записи. Такие ссылки бывают особенно удобны, если осуществляется переход с компонента, разработанного без использования FOF.

Важно. Настоятельно рекомендуется включать объявление ссылок внутрь конструктора таблицы, более того, делать это до вызова конструктора родительского конструктора. В таком случае FOF будет знать про ссылку и сможет подключить все автоматические функции.

Использование связанных таблиц

Зачастую бывает необходимо использовать связанные таблицы. Их можно связать в Представлении, перехватив событие buildQuery, но иногда необходимо получить связанные таблицы при работе с объектом таблицы. Последнее можно реализовать создав join-запрос и передав его в конструктор F0FTable. FOF сделает всё остальное.

Подробнее это можно рассмотреть на примере. Пусть имеется таблица foo, связанная с таблицей bar:

foo_idtitle
Таблица foo
1 Первая строка
2 Вторая строка
Таблица bar
bar_idtitlebarfieldfoo_id
1 Название 1 в таблице Bar Отдельное поле 1 1
2 Название 2 в таблице Bar Отдельное поле 2 2

В обычном SQL это обрабатывалось бы следующим запросом:

SELECT jos_foo.*, jos_bar.title as bar_title, barfield
FROM jos_foo
INNER JOIN jos_bar ON jos_bar.foo_id = jos_foo.foo_id
WHERE jos_foo.foo_id = 1

Когда создаётся таблица в FOF, выполняется запрос следующего типа:

SELECT jos_foo.*
FROM jos_foo
WHERE jos_foo.foo_id = 1

Всё, что необходимо сделать - это добавить часть с указанием join. Это возможно сделать расширяя F0FTable, создав запрос с использованием синтаксиса Joomla! и назначив этот запрос таблице:

class FoobarTableFoo extends F0FTable
{
  public function __construct($table, $key, &$db)
  {
      $query = $db->getQuery(true)
          ->select(array($db->qn('#__bar').'.'.$db->qn('title').' as '.$db->qn('bar_title')))
          ->select('barfield')
          ->innerJoin('#__bar ON #__bar.foo_id = #__foo.foo_id');

      $this->setQueryJoin($query);

      parent::__construct($table, $key, $db);
  }
}

Как можно заметить, всё достаточно просто: необходимо создать запрос без строки FROM (FOF будет использовать текущую таблицу). Можно указывать названия колонок, имена таблиц, псевдонимы колонок и таблиц и т.д. - как в обычном запросе. В результате такого запроса FOF будет знать обо всех полях из связанных таблиц.

Настройка специальными классами

Таблицы FOF могут быть настроены с помощью методов onBeforeSomething / onAfterSomething. Something - это имя метода, к которому относится событие. Например, onBeforeBind выполняется перед вызовом метода bind(), а onAfterBind сразу после. Замечания к использованию того или иного действия можно найти в комментариях к каждому методу в исходном коде FOF.

Настройка с использованием плагинов

Можно настроить действия таблиц с использованием стандартных плагинов system. F0FTable автоматически создаёт события для плагинов с использованием определённых префиксов и добавлением в конце названия таблицы. Например, если есть таблица TodoTableItem FOF создаст для плагинов событие под названием onBeforeBindItem. В дальнейшем в документации будет использоваться суффикс TABLENAME.

Очевидным недостатком этого механизма является возможный конфликт имён. Например для двух таблиц TodoTableItem и ContactusTableItem событие, вызываемое перед привязкой записи к таблице, будет называться одинаково onBeforeBindItem. Для того, чтобы решить этот конфликт первый параметр, передаваемый в обработчик события в плагине, это ссылка на объект таблицы, по соглашениям называемый $table. Имя таблицы, с которой сейчас работает модель можно получить запросом $table->getTableName(), который вернёт что-то из разряда #__todo_items. Далее можно проверить та ли эта таблица, с которой должна вестись работа в рамках метода, и, если нет, вернуть true, чтобы FOF продолжил работу без дальнейшей обработки события.

Полный список обрабатываемых событий такой:

  • onBeforeBindTABLENAME - вызывается перед сопоставлением данных из массива / объекта с объектом таблицы
  • onAfterLoadTABLENAME - вызывается после загрузки записи
  • onBeforeStoreTABLENAME - вызывается перед сохранением записи в таблицу
  • onAfterStoreTABLENAME - вызывается после сохранения записи в таблицу
  • onBeforeMoveTABLENAME - вызывается перед перемещением (переупорядочиванием) единичной записи
  • onAfterMoveTABLENAME - вызывается после перемещением (переупорядочиванием) единичной записи
  • onBeforeReorderTABLENAME - вызывается перед упорядочиванием нескольких записей в таблице
  • onAfterReorderTABLENAME - вызывается после упорядочивания нескольких записей в таблице
  • onBeforeDeleteTABLENAME - вызывается перед удалением записи
  • onAfterDeleteTABLENAME - вызывается после удаления записи
  • onBeforeHitTABLENAME - вызывается перед увеличение счётчика посещений
  • onAfterHitTABLENAME - вызывается после увеличение счётчика посещений
  • onBeforeCopyTABLENAME - вызывается перед копированием (дублированием) записи
  • onAfterCopyTABLENAME - вызывается после  копирования (дублирования) записи
  • onBeforePublishTABLENAME - вызывается перед публикацией записи
  • onBeforeResetTABLENAME - вызывается перед сбросом состояния объекта таблицы
  • onAfterResetTABLENAME - вызывается после сброса состояния объекта таблицы

Если вернуть логическое false из какого-либо onBefore события, операция будет отменена.

Этот механизм даёт возможность внешним разработчикам или интеграторам расширять возможности компонента, разработанного на базе FOF, с большой лёгкостью.

Поведение таблицы

Введение

Объект Таблица может реализовывать сложную многократно используемую функциональность с помощью поведений. Поведения таблицы - это практичный способ изменять стандартные действия F0FModel. Таблица работает с использованием стандартной системы событий Joomla.

До и После определённых действий (onBefore* / onAfter*) таблица вызывает событие, которое размножается на каждое зарегистрированное поведение данной модели. Поведения в свою очередь, регистрируясь на эти события, могут изменять содержание той или иной операции.

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

В FOF уже предусмотрено некоторое количество стандартных поведений, которые могут быть использованы любым объектом F0FTable. Связанные с поведениями классы в настоящее время находятся в папке table/behavior фреймворка FOF. По умолчанию к таблице подключены только поведения assets и tags.

Стандартные поведения таблицы FOF

  • assets - это поведение управляет наборами ресурсов, если в записи есть поле (или псевдоним) asset_id
  • contenthistory - это поведение управляет историей записи
    Важно. Это поведение требует параметра save_history в файле параметров компонента config.xml.
  • tags - Это поведение работает с тегами, с которыми связана текущая запись. Оно срабатывает только, если значение параметра hasTags равно true.

Добавление готового поведения в таблицу

Есть 5 способов добавить поведение в объект таблицы:

  1. Использовать свойство модели default_behaviors. Это массив с именами поведений, которые должны быть загружены при создании таблицы. По умолчанию массив содержит поведения tags и assets. Можно переопределить это свойство в декларации класса таблицы:
    class FoobarTableTodo extends FOFtable {
        protected $default_behaviors = array('tags');
    }
  2. Передать список поведений в параметре $config конструктора класса таблицы:
    class FoobarTableTodo extends FOFtable {
    
        public function __construct($config = array()) {
            if (!isset($config['behaviors'])) {
                $config['behaviors'] = array('tags');
            }
    
            parent::__construct($config);
        }
    }
  3. Передать список поведений в параметре config при создании экземпляра таблицы:
    $model = F0FTable::getTmpInstance('Todo', 'FoobarTable', array('behaviors' => array('tags')));
  4. Динамически добавить нужные поведения используя метод addBehavior
    class FoobarTableTodo extends FOFtable {
    
        public function __construct($config = array()) {
            parent::__construct($config);
    
            if ($iWantToAddIt) {
                $this->addBehavior('tags');
            }
        }
    }
  5. Добавить поведения в представление, используя fof.xml
    <?xml version="1.0" encoding="utf-8"?>
    <fof>
        <common>
            <table name="todo">
                <behaviors>tags</behaviors>
            </table>
        </common>
    </fof>

Создание нового Поведения

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

Если есть желание добавить поведение bar в компонент foo, FOF будет искать это поведение в следующем порядке:

  1. Сначала будет попытка найти ComponentnameTableViewnameBehaviorname (например, FooTableTodosBar)
    Внимание, Viewname должно быть в единственном числе.
  2. Затем будет попытка найти ComponentnameTableBehaviorname (например, FooTableBar)
  3. Затем F0FTableBehaviorBehaviorname (например, F0FTableBehaviorBar)

Это позволяет создавать поведения как для конкретной таблицы, так и для компонента в целом.

Кроме того возможно переопределить существующее поведение. Например, для того, чтобы переопределить поведение tags достаточно создать класс под названием FooTableBehaviorTags).

Важно. Поведения автоматически не загружаются, поэтому необходимо предусмотреть либо свой загрузчик, либо загружать классы поведений в конструкторе Таблицы перед вызовом метода parent::__construct(…).

Доступные события для Поведения Таблицы

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

/**
 * Это событие вызывается перед сопоставлением данных с таблицей
 *
 * @param   F0FTable  &$table  Таблица, вызывающая данное событие
 * @param   array     &$data   Данные для сопоставления
 *
 * @return  boolean  True в случае успеха
 */
public function onBeforeBind(&$table, &$data)
{
    return true;
}

/**
 * Это событие вызывается после сопоставления данных с таблицей
 *
 * @param   F0FTable  &$table  Таблица, вызывающая данное событие
 * @param   object|array  &$src  Данные для сопоставления
 *
 * @return  boolean  True в случае успеха
 */
public function onAfterBind(&$table, &$src)
{
    return true;
}

/**
 * Это событие вызывается после загрузки записи из базы данных
 *
 * @param   F0FTable  &$table  Таблица, вызывающая данное событие
 * @param   boolean  &$result  True в случае успеха
 *
 * @return  void
 */
public function onAfterLoad(&$table, &$result)
{

}

/**
 * Это событие вызывается перед сохранением данных в базу данных
 *
 * @param   F0FTable  &$table  Таблица, вызывающая данное событие
 * @param   boolean  $updateNulls  Обнулять ли значения полей в базе данных, если передаваемые данные нулевые (true), или пропускать их (false)?
 *
 * @return  boolean  True разрешает сохранение записи
 */
public function onBeforeStore(&$table, $updateNulls)
{
    return true;
}

/**
 * Это событие вызывается после сохранения данных в базу данных
 *
 * @param   F0FTable  &$table  Таблица, вызывающая данное событие
 *
 * @return  boolean  True в случае сохранения без ошибок
 */
public function onAfterStore(&$table)
{
    return true;
}

/**
 * Это событие вызывается перед перемещением записи в базе данных
 *
 * @param   F0FTable  &$table  Таблица, вызывающая данное событие
 * @param   boolean  $updateNulls  Обнулять ли значения полей в базе данных, если передаваемые данные нулевые (true), или пропускать их (false)?
 *
 * @return  boolean  True разрешает перемещение записи
 */
public function onBeforeMove(&$table, $updateNulls)
{
    return true;
}

/**
 * Это событие вызывается после перемещения записи в базе данных
 *
 * @param   F0FTable  &$table  Таблица, вызывающая данное событие
 *
 * @return  boolean  True в случае перемещения без ошибок
 */
public function onAfterMove(&$table)
{
    return true;
}

/**
 * Это событие вызывается перед переупорядочиванием записей в базе данных
 *
 * @param   F0FTable  &$table  Таблица, вызывающая данное событие
 * @param   string  $where  Условие WHERE в SQL-запросе для выполнения переупорядочивания (фильтр записей)
 *
 * @return  boolean  True разрешает переупорядочивание
 */
public function onBeforeReorder(&$table, $where = '')
{
    return true;
}

/**
 * Это событие вызывается после переупорядочивания записей в базе данных
 *
 * @param   F0FTable  &$table  Таблица, вызывающая данное событие
 *
 * @return  boolean  True в случае переупорядочивания без ошибок
 */
public function onAfterReorder(&$table)
{
    return true;
}

/**
 * Это событие вызывается перед удалением записи из базы данных
 *
 * @param   F0FTable &$table  Таблица, вызывающая данное событие
 * @param   integer  $oid  Первичный ключ записи для удаления
 *
 * @return  boolean  True разрешает удаление
 */
public function onBeforeDelete(&$table, $oid)
{
    return true;
}

/**
 * Это событие вызывается после удаления записи из базы данных
 *
 * @param   F0FTable &$table  Таблица, вызывающая данное событие
 * @param   integer  $oid  Первичный ключ удалённой записи
 *
 * @return  boolean  True в случае удаления без ошибок
 */
public function onAfterDelete(&$table, $oid)
{
    return true;
}

/**
 * Это событие вызывается перед увеличением счётчика просмотров записи
 *
 * @param   F0FTable &$table  Таблица, вызывающая данное событие
 * @param   integer  $oid  Первичный ключ записи для увеличения счётчика
 * @param   boolean  $log  Надо ли логировать счётчик
 *
 * @return  boolean  True разрешает увеличение счётчика
 */
public function onBeforeHit(&$table, $oid, $log)
{
    return true;
}

/**
 * Это событие вызывается после увеличения счётчика просмотров записи
 *
 * @param   F0FTable &$table  Таблица, вызывающая данное событие
 * @param   integer  $oid  Первичный ключ записи, у которой был увеличен счётчик
 *
 * @return  boolean  True в случае увеличения счётчика без ошибок
 */
public function onAfterHit(&$table, $oid)
{
    return true;
}

/**
 * Это событие вызывается перед копированием записи
 *
 * @param   F0FTable &$table  Таблица, вызывающая данное событие
 * @param   integer  $oid  Первичный ключ записи для копирования
 *
 * @return  boolean  True разрешает копирование
 */
public function onBeforeCopy(&$table, $oid)
{
    return true;
}

/**
 * Это событие вызывается после копирования записи
 *
 * @param   F0FTable &$table  Таблица, вызывающая данное событие
 * @param   integer  $oid  Первичный ключ скопированной записи (не новой)
 *
 * @return  boolean  True в случае копирования без ошибок
 */
public function onAfterCopy(&$table, $oid)
{
    return true;
}

/**
 * Это событие вызывается перед публикацией (снятия с публикации) записи
 *
 * @param   F0FTable &$table    Таблица, вызывающая данное событие
 * @param   integer|array  &$cid    Первичный ключ записи(-ей) для публикации (снятия с публикации)
 * @param   integer        $publish  1 - опубликовать, 0 - снять с публикации
 *
 * @return  boolean  True разрешает публикацию (снятие с публикации)
 */
public function onBeforePublish(&$table, &$cid, $publish)
{
    return true;
}

/**
 * Это событие вызывается перед сбросом записи к значению по умолчанию
 *
 * @param   F0FTable &$table  Таблица, вызывающая данное событие
 *
 * @return  boolean  True разрешает сброс
 */
public function onBeforeReset(&$table)
{
    return true;
}


/**
 * Это событие вызывается после сброса записи к значению по умолчанию
 *
 * @param   F0FTable &$table  Таблица, вызывающая данное событие
 *
 * @return  boolean  True в случае сброса без ошибок
 */
public function onAfterReset(&$table)
{
    return true;
}