Ordering
Когда мы хотим расположить элементы в определенном порядке, JTable прsetQueryедоставляет нам для этого набор методов. Первый из рассмотренных нами методов будет reorder(). Этот метод исправляет ошибки в порядке расположения записей в таблице.
$table->reorder();
В более сложных таблицах записи обычно разбиты по группам, и для этого в метод reorder() нужно дописать дополнительный параметр. Представим, что в нашей таблице есть поле group. В этом примере мы упорядочим записи в группе 1.
$db =& $table::getDBO(); $where = $db->nameQuote('group').' = 1'; $table->reorder($where)
Заметьте, что мы получаем объект базы данных не из JFactory, а из таблицы! Ранее мы уже использовали метод getNextOrder(). Метод выдает нам следующую позицию в порядке упорядочивания. Как и в случае с reorder(), мы имеем возможность определения груп. Например, получим следующий номер порядка для группы 1.
$db =& $table::getDBO(); $where = $db->nameQuote('group').' = 1'; $nextPosition = $table->getNextOrder($where);
И последний метод – это move(). Он нужен для перемещения записи на одну позицию вверх или вниз. Переместим на примере запись вверх.$table->load($id); $table->move(-1); Опять у нас есть возможность для указания групп. Покажем это на примере:
$db =& $table::getDBO(); $where = $db->nameQuote('group').' = 1'; $table->load($id); $table->move(1, $where)
Написание запросов
Вот несколько правил при написании запросов к базе данных.
- Используйте префикс #__ вначале всех имен таблиц.
- Используйте метод nameQuote() для формирования названных элементов в запросе.
- Используйте метод Quote() для формирования значений.
nameQuote() – устанавливает правильность разделителей в имени поля, а Quote() – устанавливает правильность разделителей в передаваемом значении. Например:
$db = JFactory::getDBO(); $query = 'SELECT * FROM ' .$db->nameQuote('#__test') .' WHERE ' .$db->nameQuote('name') .' = ' .$db->Quote('Some Name');
Если мы использовали MySQL или MySQLi драйвер, то запрос в итоге будет выглядеть следующим образом:
SELECT * FROM 'jos_test' WHERE 'name' = "Some Name";
Пример схемы
Таблица нарисована для расширения названного ‘My Extension’ и записей названных обобщенно foobar. Имя таблицы будет #__myextension_foobars.
Поле | Тип | NOT NULL | AUTO INC | UNSIGNED | По-уомлчанию |
id | INTEGER | + | + | + | NULL |
content | TEXT | + | |||
checked_out | INTEGER | + | + | ||
checked_out_time | DATETIME | + | 0000-00-00 00:00:00 | ||
params | TEXT | + | |||
ordering | INTEGER | + | + | ||
hits | INTEGER | + | + | ||
published | TINYINT(1) | + | + |
Эта таблица использует все зарезервированные поля и одно автоинкрементное ключевое поле ID. SQL запрос который создаст таблицу описанную в схеме выше:
CREATE TABLE '#__myextension_foobars' ( 'id' INTEGER UNSIGNED NOT NULL DEFAULT NULL AUTO_INCREMENT, 'content' TEXT NOT NULL DEFAULT '', 'checked_out' INTEGER UNSIGNED NOT NULL DEFAULT , 'checked_out_time' DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00', 'params' TEXT NOT NULL DEFAULT '', 'ordering' INTEGER UNSIGNED NOT NULL DEFAULT , 'hits' INTEGER UNSIGNED NOT NULL DEFAULT , 'published' INTEGER UNSIGNED NOT NULL DEFAULT , PRIMARY KEY('id') ) CHARACTER SET 'utf8' COLLATE 'utf8_general_ci';
Использование ADOdb
ADOdb – это абстрактный слой на php по управлению базами данных, работающих под BSD. ADOdb поддерживает ряд ведущих баз данных. Сама Joomla не поддерживает ADOdb, а лишь эмулирует некоторую функциональность в своих базах данных. Мы должны использовать ADOdb методы, если мы портируем существующее приложение, использующее ADOdb, или создаем расширение, работающее standalone с использованием ADOdb. Joomla! использует JRecordSet класс для эмуляции класса ADORecordSet. JRecordSet класс еще не закончен, и обладает далеко не всеми методами класса ADORecordSet. В этом примере мы покажем простейшее использование класса JRecordSet ($row – это массив).
$db =& JFactory::getDBO(); $rs = $db->Execute('SELECT * FROM #__test'); while ($row = $rs->FetchRow()) { // обрабатываем $row }
JTableJoomla! предоставляет нам мощный абстрактный класс JTable; при этом мы можем выполнять все основные функции по работе с таблицей. Для каждой таблицы, которую мы хотим использовать в классе JTable, мы должны создать новый подкласс. При создании подкласса JTable, мы должны придерживаться некоторых правил. Эти правила позволят нам интегрировать наше расширение в фрэймворк Joomla. Итак, каждый подкласс JTable должен быть распложен в отдельном файле в каталоге tables (в административной части компонента). Имя создаваемого класса должно иметь префикс table. Имя файла обязательно должно быть в единственном числе. Используем описанную выше схему таблицы, чтобы показать на примере как работать с классом JTable. Класс должен называться TableFoobar и расположен в каталоге JPATH_COMPONENT_ADMINISTRATOR.DS.’tables’.DS.’foobar.php’. При первом использовании нашего класса мы должны определить глобальные свойства. Глобальные свойства соответствуют полям таблицы и должны иметь такие же названия. Мы используем эти свойства как буфер для хранения отдельных записей. Во-вторых. В целях использования метода JTable::getInstance() мы должны определить конструктор. В-третьих нам нужно переопределить метод check(). Этот метод проверяет содержимое буфера и возвращает булев результат. Если метод вернул значение false, то используем метод setError() для пояснения ошибки.
/** * обработчик таблицы #__myextenstion_foobars * */ class TableFoobar extends JTable { /** @var int Primary key */ var $id = null; /** @var string Content */ var $content = null; /** @var int Checked-out owner */ var $checked_out = null; /** @var string Checked-out time */ var $checked_out_time = null; /** @var string Parameters */ var $params = null; /** @var int Order position */ var $ordering = null; /** @var int Number of views */ var $hits = null; /** * Constructor * * @param database Database object */ function __construct( &$db ) { parent::__construct('#__myextension_foobars', 'id', $db); } /** * Проверка * * @return boolean True if buffer is valid */ function check() { if(!$this->content) { $this->setError(JText::_('Ваш Foobar должен содержать контент')); return false; } return true; } }
Теперь, когда мы создали TableFoobar класс нужно инстанцировать объект с помощью статического метода JTable:: getInstance().
JTable::addIncludePath(JPATH_COMPONENT_ADMINISTRATOR.DS.'tables'); $table = JTable::getInstance('foobar', 'Table');
Заметьте, что мы подключаем не foobar.php а только каталог с таблицами. Когда JTable начинает инстанцировать TableFoobar на объект, автоматически подключается foobar.php.
В качестве заключения
Если вы ищите компонент для создания карты сайта на Joomla 3 или Joomla 4, то первым делом обратите внимание на JL Sitemap; если на вашем сайте используется компонент, для которого еще нет плагина к JL Sitemap, то обратите внимание на OSMap и список существующих к нему плагинов; компоненты Xmap и mapX морально устарели и не обновляются, поэтому не рекомендуем тратить на них время; компонент OSMap является актуальным наследником Xmap; если сайт использует только базовые компоненты Joomla, то для генерации карты сайта будет предостаточно бесплатной версии OSMap; если сайт использует только базовые компоненты Joomla, то для генерации карты сайта будет предостаточно бесплатной версии OSMap; если на сайте используются компоненты сторонних разработчиков, то не спешите покупать OSMap Pro — сперва протестируйте совместимость OSMap Free с соответствующими плагинами для Xmap; если карта сайта будет генерироваться некорректно в связке OSMap + плагин для Xmap, то проверьте, включается ли нужный плагин в платную версию OSMap: возможно, придется приобретать плагин отдельно; если вы ищите генератор карты сайта Sitemap для Joomla, то не рассматривайте jSitemap, т. к
это «тяжелое» во многих отношениях расширение, требующее время на освоение.
CRUD
CRUD (Create Read Update Delete) – это общее название основных задач по управлению таблицей. Все CRUD примеры $table ссылаются на класс TableFoobar и $id ссылается на идентификатор записи которую мы в данный момент обрабатываем. В этом примере мы создаем новую запись; $table – экземпляр класса TableFoobar.
$table->reset(); $table->set('content', "Наш контент"); $table->set('ordering', $table->getNextOrder()); if ($table->check()) { if (!$table->store()) { // обработчик ошибок записи // используем $table->getError() } } else { // обработчик ошибки проверки буфера // тоже используем $table->getError() }
Метод reset() очищает наш буфер и приводит значения всех свойств к значениям по-умолчанию. Метод getNextOrder() определяет следующий по порядку вложенности элемент. Если запись не существующая, то он ставит значение 1. Давайте рассмотрим наш пример подробнее. Некоторые из полей имели значение по-умолчанию, и после записи, значение даты будет пустым. После выполнения предыдущего примера буфер $table выглядит так:
=> 1 => Наш контент => => => => 1 => 0
После выполнения метода store() (сохранения записи), мы можем загрузить его:
$table->load($table->id);
Теперь наш буфер выглядит так:
=> 1 => Наш контент => 0 => 0000-00-00 00:00:00 => => 1 => 0
Вместо загрузки нашей сохраненной записи, мы могли бы изначально верно установить значения по-умолчанию, и нам бы не пришлось перезагружать запись.Однако некоторые значения по-умолчанию зависят от типа данных. Поэтому нам нужно переопределить метод reset(). Для примера значение checked_out_time будет равно $db->getNullDate(). Итак для загрузки конкретной записи используем метод:
if (!$table->load($id)) { // обработчик загрузки // используем $table->getError() для ловли ошибок }
Для обновления записи в буфере мы можем использовать два метода: первый – это загрузить запись из БД, второй – это установить конкретные значения для свойств буфера.В этом примере покажем как обновить запись:
// установка значений $table->reset(); $table->setVar('id', $id); $table->setVar('content', JRequest::getString('content')); if ($table->check()) { if (!$table->store()) { // обрабатываем ошибки записи с помощью $table->getError() } } else { // обрабатываем ошибки ввода в буфер $table->getError() }
Последнее что мы расмотрим – это удаление записи:
if (!$table->delete($id)) { // обрабатываем ошибки }
Если мы не указываем в методе delete() номер id, то id будет браться из буфера. Если наша запись в таблице имеет родственные записи с другими таблицами, то мы должны сначала выполнить проверку методом canDelete(). Этот метод имеет всего один параметр в виде двумерного массива. Внутри массива должно быть несколько ключей – idfield, name, joinfield, и label. idfield – это имя первичного ключа в соответствующей таблице. name – это название самой таблицы. joinfield – это имя внешнего ключа соответствующей таблицы. label – это описание отношения между таблицами, для вывода сообщения об ошибке, если родственных связей не найдено.Представьте что есть еще одна таблица #__myextension_children. Эта таблица имеет первичный ключ childid и внешний ключ primary, который ссылается на запись в таблице #__myextension_foobars. В этом примере мы проверим нет ли зависимости между записями таблицами #__myextension_children и #__myextension_foobars, перед удалением записи из таблицы #__myextension_foobars.
$join1 = array('idfield' => 'childid', 'name' => '#__myextension_children', 'joinfield' => 'parent', 'label' => 'Children'); $joins = array($join1); if ($table->canDelete($id, $joins)) { if (!$table->delete($id)) { // обрабатываем ошибки удаления } } else { // обрабатываем в случае нахождения зависимостей }
Мы можем определить более одной межтабличной связи. Допустим еще существует таблица #__myextension_illegitimate_children:
$join1 = array('idfield' => 'childid', 'name' => '#__myextension_children', 'joinfield' => 'parent', 'label' => 'Children'); $join2 = array('idfield' => 'ichildid', 'name' => '#__myextension_illegitimate_children', 'joinfield' => 'parent', 'label' => 'illegitimate Children'); $joins = array($join1, $join2);