Опубликован: 02.10.2012 | Доступ: свободный | Студентов: 1916 / 85 | Длительность: 11:48:00
Теги: joomla, mvc, php, xhtml, xml
Лекция 2:

Работа с базой данных

< Лекция 1 || Лекция 2: 123 || Лекция 3 >

Таблицы базы данных (класс JTable)

Класс JTable реализует паттерн Active Record и используется для управления таблицами базы данных.

Для каждой таблицы, которую вы будете создавать для своего компонента, необходимо создать класс, производный от JTable. Каждый такой класс помещается в отдельном файле в папке /administrator/components/com_<имя компонента>/tables. Имя класса строится по схеме Table<название таблицы>, а файла - <название таблицы>.php.

Для каждого поля таблицы необходимо создать одноименное поле класса.

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

Например, пусть имеется таблица #__mycomponent_mytable из двух столбцов: id и name. Тогда производный от JTable класс должен выглядеть так:

class TableMytable extends JTable
{
  var $id = null;
  var $name = null;

  function __construct(&$db)
  {
    parent::__construct('#__mycomponent_mytable', 'id', $db);
  }
}
    

Чтобы использовать в коде вашего компонента файл, содержащий этот класс, нужно добавить папку tables в список директорий, в которых JTable может искать классы таблиц:

JTable::addIncludePath(JPATH_ADMINISTRATOR.DS.'components'.DS.'com_mycomponent'.DS.'tables');
    

Теперь при создании экземпляра класса TableMytable Joomla будет искать файл mytable.php.

Для получения экземпляра данного класса используется метод getInstance():

object getInstance(string $type, string $prefix= 'JTable', array $config=array())
    

где

$type - вторая часть имени класса;
$prefix - первая часть имени класса;
$config - массив, содержащий настройки конфигурации.

В их числе может находиться объект-представитель базы данных, и тогда он будет использован вместо глобального объекта JDatabase. Например:

$row =& JTable::getInstance('mytable', 'Table');
    

Производный от JTable класс наследует в числе прочих методы bind(), store(), load() и delete(), позволяющие управлять записями таблицы без единой строки SQL-кода.

Создание/редактирование записи таблицы

Как правило, для создания или редактирования записи таблицы во фронтенде или бэкенде создается HTML-форма. Значения, введенные в нее пользователем, можно получить с помощью класса JRequest в виде массива. Полученный массив необходимо связать с объектом JTable. Связывание заключается в том, что каждому полю класса присваивается значение элемента массива, ключ которого совпадает с названием этого поля. Для этого используется метод

bool bind(mixed $src, mixed $ignore=array())
        

где:

$src - ассоциативный массив или объект для связывания с экземпляром класса;
$ignore - массив или разделенный пробелами список полей, которые нужно игнорировать при связывании.

Например:

if (!$row->bind(JRequest::get('post')))
  return JError::raiseWarning(500, $row->getError());
        

Обычно ошибка связывания возникает, если поля класса не соответствуют ключам массива, - например, когда в HTML-форме в название элемента input вкралась опечатка.

Для сохранения записи используется метод bool store(bool $updateNulls=false)

Если параметр $updateNulls равен false, то те поля объекта JTable, которые имеют значение null, будут игнорироваться при связывании.

Пример использования метода store():

if (!$row->store())
  JError::raiseError(500, $row->getError());
        

Метод store() на основе хранящихся в $row значений генерирует запрос UPDATE или INSERT, в зависимости от значения id. Если запись создается впервые, то она не имеет значения id и будет сконструирован запрос INSERT, в противном случае - UPDATE. Это позволяет использовать данный метод как для создания новых записей, так и для обновления существующих.

Получение записи из таблицы

Для получения записи используется метод

bool load(mixed $keys = NULL, bool $reset = true)
        

где

$keys - первичный ключ записи, которую необходимо получить, или массив полей для поиска соответствий;
$reset - задает, будут ли перед получением новой записи заново присвоены полям значения по умолчанию.

Например:

$row->load($id);
        

Удаление записи

Для удаления записи используется метод

bool delete(mixed $id=null)
        

где $id - первичный ключ записи, которую требуется удалить. Если он не задан, то используется значение соответствующего поля объекта.

Пример:

$row->delete($id);
        

Управление полями ordering, checked_out/checked_out_time, published и hits

Существуют методы класса JTable для управления часто используемыми полями ordering, checked_out/checked_out_time, published и hits.

ordering

Поле ordering позволяет пользователю упорядочить список объектов по своему желанию. Чтобы пересчитать значения в поле ordering, используется метод void reorder([string $where = ''])

При этом записи сортируются по значению ordering, а затем в это поле записываются натуральные числа, начиная с 1. Параметр $where позволяет задать условие ограничения выборки, к которой будет применена эта операция.

Пример использования метода:

$table->reorder();
            

Изменить значение ordering для одной записи, передвинув ее выше или ниже в списке, можно с помощью метода

void move(int $dirn, [string $where = ''])
            

где $dirn - величина, которая будет прибавлена к текущему значению ordering (отрицательное значение приведет к смещению записи вверх, а положительное - вниз).

Например, поднимем запись на одну строку вверх:

$table->load($id);
$table->move(-1);
            
checked_out/checked_out_time

Поля checked_out и checked_out_time используются для блокировки записей во избежание редактирования их несколькими пользователями одновременно. checked_out хранит id пользователя, работающего с записью в данный момент, а checked_out_time - время начала редактирования. Прежде чем заблокировать запись, необходимо проверить, не заблокирована ли она уже другим пользователем, с помощью метода

bool isCheckedOut(int $with=0, int $against=null)
            

где:

$with - id пользователя, с которым нужно сравнить значение поля checked_out. Если запись заблокирована как раз этим пользователем, то функция вернет false, как и в том случае, если она не заблокирована вообще. В обоих этих случаях текущий пользователь имеет право работать с ней;
$against - id пользователя, использующийся, если функция вызвана как статическая.

Для блокировки записей используется метод

bool checkOut(int $userId, mixed $pk=null)
            

где:

$userId - id пользователя, блокирующего запись;
$pk - первичный ключ записи, которую необходимо заблокировать. Если он не задан, используется значения соответствующего поля класса.

При этом в поле checked_out_time будет записано текущее время.

Для разблокировки записей используется метод bool checkIn(mixed $pk=null)

Рассмотрим пример использования этих методов:

$table->load($id);
$user =& JFactory::getUser();
if ($table->isCheckedOut($user->get('id')))
  die('Запись уже заблокирована другим пользователем');
echo 'Запись не заблокирована';
if (!$table->checkout($user->get('id')))
  die('Не удалось заблокировать запись с id текущего пользователя');
echo 'Заблокировали запись';
// работа с записью...
if (!$table->checkin($user->get('id')))
  die('Не удалось разблокировать запись');
echo 'Разблокировали запись';
            
published

Значение поля published показывает, опубликована ли запись. Чтобы изменить значение этого поля для одной или нескольких записей, используется метод

bool publish(mixed $pks=null, int $state=1, int $userId=0)
            

где:

$pks - массив ключей записей, к которым необходимо применить операцию;
$state - новое значение поля published (0 или 1);
$userId - используется только когда в таблице существует также поле checked_out. При наличии в таблице этого поля метод publish() может быть применен только к тем записям, для которых checked_out равно 0 или заданному $userId.

Метод вернет true и в том случае, если какие-либо из записей были заблокированы и для них не удалось изменить значение published.

Пример использования этого метода:

$id_list = array($id);
$user =& JFactory::getUser();
if (!$table->publish($id_list, 1, $user->get('id')))
  die($table->getError());
            
hits

В поле hits хранится количество просмотров записи. Для увеличения этого значения на 1 используется метод

bool hit(mixed $pk=null)
            

где $pk - первичный ключ записи.

Например:

$table->hit();
            

Практика

Создание таблицы базы данных

Создайте таблицу для хранения вопросов, выполнив следующий SQL-запрос:

CREATE TABLE `jos_myquestions`
(
`id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
`name` VARCHAR(255) NOT NULL,
`date` DATETIME NOT NULL,
`question` TEXT NOT NULL,
`city` VARCHAR(50) NULL,
`email` VARCHAR(50) NOT NULL,
`IP` VARCHAR(15) NOT NULL,
`id_cat` INT NOT NULL,
`published` TINYINT(1) NULL DEFAULT '1',
`expiration_date` DATETIME NULL DEFAULT '0000-00-00 00:00:00',
`senttoexpert` TINYINT(1) NULL DEFAULT '0',
`answer` TEXT NULL DEFAULT '',
`senttoauthor` TINYINT(1) NULL DEFAULT '0'
);
        

По умолчанию дата снятия вопроса с публикации имеет значение '0000-00-00 00:00:00'. Будем считать, что такая дата означает, что вопрос опубликован навсегда.

Для тестирования системы добавьте запись в таблицу, выполнив SQL-запрос:

INSERT INTO `jos_myquestions`(`id`, `name`, `date`, `question`, `city`, `email`, `IP`, `id_cat`)
 VALUES(NULL, 'Аноним', '2012-01-01 09:00:00', 
'Есть ли жизнь на Марсе', 'Москва', 'somebody@mail.ru', '12.345.67.890', '1')
        

Создание класса таблицы

Создайте в папке /administrator/components/com_myquestions папку tables. В этой папке создайте файл question.php:

<?php
defined('_JEXEC') or die('Restricted access');
class TableQuestion extends JTable
{
  var $id = null;
  var $name = null;
var $date = null;
  var $question = null;
  var $city = null;
  var $email = null;
  var $IP = null;
  var $id_cat = null;
  var $published = null;
  var $expiration_date = null;
  var $senttoexpert = null;
  var $answer = null;
  var $senttoauthor = null;

  function __construct(&$db)
  {
    parent::__construct('#__myquestions', 'id', $db);
  }
}
?>
        

Как видите, класс TableQuestion расширяет класс JTable. Каждому полю таблицы #__myquestions соответствует поле этого класса. Также перегружен конструктор __construct(), принимающий в качестве параметра объект-представитель базы данных и вызывающий родительский конструктор, используя название таблицы базы данных, первичный ключ и объект-представитель базы данных.

< Лекция 1 || Лекция 2: 123 || Лекция 3 >
Кирилл Гусаров
Кирилл Гусаров

В разделе "Первые папки и файлы. Добавление пунктов меню"

предлагается создать две файла:

- myquestions.php;

- admin.myquestions.php

с соответствуюшими адресами:

/components/com_myquestions/myquestions.php;

- /administrator/components/com_myquestions/admin.myquestions.php;

Так вот, при создании файла "admin.myquestions.php" В админке выдает ошибку - "Компонент не найден", а при переименовании его на  "myquestions.php" в последующем шаге, в админке не выводятся кнопки редактирования. 
Проверил кодировку, проверил правильность пути к файлам, пересохранил указанный код. Скажите что я делаю не так или в чем может быть причина?