Плюшка CMS#29.08.2018

/* Создан для быстрой разработки приложений */
Скачать Демо

20. Универсальная модель базы данных (model)

03.10.2015

Класс model является упрощенным вариантом привычного ActiveRecord, реализуя возможность сохранять данные в таблицу БД, а также предварительно проводить фильтрацию и валидацию. Реализацию класса можно найти в файле /core/model.php. Предусмотрены два варианта использования этого класса: динамический, когда перечень полей и их тип задаются непосредственно в коде и статический, когда класс наследуется от базового model и описывает параметры полей таблицы базы данных.

Общий алгоритм работы с классом такой:

  1. получить экземпляр класса;

  2. передать данные HTML-формы вызвав метод set() или поочерёдно установить все поля сеттерами;

  3. вызвать метод validate() для проверки валидности и фильтрации данных;

  4. записать данные в базу данных вызвав метод save().

Вот типичный пример динамического использования:

public function actionTestSubmit($data) { //submit-действие контролле
	$m=core::model('myTable'); //получение экземпляра класса, myTable — имя таблицы БД
	$m->set($data); //передача _POST-данных
	$m->myField='some text';
	if(!$m->save(array( //сохранить данные, проведя предварительную фильтрацию и валидацию
		'id'=>array('primary'),
		'name'=>array('string','имя',true,'min'=>2,'max'=>40),
		'myField'=>array('string')
	))) return false;

	core::redirect('myController/myAction','Изменения сохранены');
}

Этот код выполнит запрос INSERT или UPDATE в зависимости от того, задан ли $data['id'] (это поле помечено как первичный ключ).

Вот пример статического класса, содержащего минимум необходимой информации:

class myTable extends model {
	protected $fields='id,alias,name'; //список полей, участвующих в SELECT, INSERT и UPDATE, если они не указаны явно

	function __construct($db='db') {
		parent::__construct('myTable',$db);
	}

	//Должен возвращать массив, содержащий правила валидации
	protected function validateRule() {
		'id'=>array('primary'),
		'name'=>array('string','имя',true,'min'=>2,'max'=>40),
		'myField'=>array('string')
	}

	//public function beforeInsertUpdate($id,$fields) //выполняется перед INSERT или UPDATE
	//public function afterUpdate($id) //выполняется после UPDATE
	//public function afterInsert($id) //выполняется после INSERT
}

Использование этого класса:

public function actionTestSubmit($data) { //submit-действие контроллера
	core::import('model/myTable');
	$m=new myTable();
	$m->set($data);
	if(!$m->save()) return false;
	core::redirect('myController/myAction','Изменения сохранены');
}

Для того, чтобы передать данные полей в модель, необходимо воспользоваться методом set(), принимающем в единственном параметре ассоциативный массив, где ключ - имя, а значение - значение этого поля. Другой путь - установить поля поотдельности при помощи сеттеров. Таким же образом задаётся значение первичного ключа. Если таблица содержит первичный ключ и его значение было передано (не ноль, не null, не false и не пустая строка) в класс model, то при вызове метода save() будет выполнен SQL-запрос «UPDATE». Если же значение первичного ключа не было передано, или оно ложно, то будет выполнен SQL-запрос «INSERT». После выполнения операции INSERT знать значение первичного ключа можно через геттер:

$id=$m->id;

Операция валидации данных проводит также и фильтрацию этих данных в зависимости от указанного типа.

Статический вариант может реализовывать функции beforeInsertUpdate(int id, array fields), afterInsert(int id) и afterUpdate(int id). Эти функции вызываются до или после выполнения SQL-запросов INSERT или UPDATE. Также бывает полезным перегрузить методы load(), validate() и save().

Если функция beforeInsertUpdate возвратит false, то SQL-запрос INSERT или UPDATE выполнен не будет (считается, что валидация не была пройдена). Обычно при этом также требуется установить сообщение об ошибке в глобальную переменную core::$error.

Параметры этих функций следующие: id – значение первичного ключа, fields – список полей, затронутых в SQL-запросе INSERT или UPDATE.

Методы класса

object model::__construct(string namespace[,string db='db']);
тут namespace — имя таблицы базы данных, db — используемая СУБД (может принимать следующие значения: db — указанная в общих настройках (по умолчанию), mysql или sqlite).

null model::set(array data);
Передаёт модели данные полей таблицы, параметр data должен быть ассоциативным массивом, где ключ — имя поля, а значение — содержимое поля.

array model::get();
Возвращает ассоциативный массив, содержащий все поля записи БД.

&array model::load(string where[, string field]);
Загружает данные из базы данных, выбирая запись (одну первую) по указанному в параметре where условию (часть SQL-запроса, после «WHERE». В параметре field указывается перечень (через запятую) полей, которые необходимо выбрать. Если параметр field не указан, то список полей будет взят из атрибута model::$field. Если этот атрибут не был задан, то будут загружены все поля.

Функция возвращает ссылку на ассоциативный массив, поэтому можно менять значения полей «на лету» и записывать данные обратно в базу данных.

&array model::loadById(int id[,string fields]);
Тоже, что model::load(), но загружает данные не по условию, а по уникальному идентификатору.

bool model::validate([array validate]);
Выполняет фильтрацию и валидацию данных. Возвращает true, если проверка прошла успешно. Параметр validate должен быть ассоциативным массивом, содержащим правила валидации (описано далее). В случае статического использования класса этот параметр может быть опущен. Этот метод также устанавливает текст сообщения об ошибке при помощи core::$error.

null model::multiLanguage();
Устанавливает мультиязычный режим: при добавлении новых строк (INSERT) мультиязычные поля копируются на все другие языки (так сказать "значение по умолчанию"). Для мультиязычных таблиц (копии таблиц для каждого языка) выполняется несколько запросов INSERT.

bool model::save([mixed validate],[string fields],[string id]);
Сохраняет данные записи в базе данных. Если в таблице содержится первичный ключ и он не false, то выполняется SQL-запрос «UPDATE», если в таблице нет первичного ключа или он не был задан или он false, то выполняется SQL-запрос «INSERT».

Если параметр validate является массивом, то он воспринимается как правила валидации и перед сохранением будет проведена фильтрация и валидация согласно этим правилам. Если параметр равен true, то валидация будет проведена согласна правилам, заданным в атрибуде model::$validate (только для статического использования). Если параметр validate равен false, то валидация проводиться не будет.

Если задан параметр fields, то в запросе INSERT или UPDATE будут учавствовать только поля, перечисленные в этом параметре. Этот параметр следует использовать только тогда, когда не задан первый параметр (validate), иначе информацию о перечне полей движок сможет взять из правил валидации.

Параметр id может содержать имя первичного ключа — параметр следует использовать в том случае, если не проводится валидация (в противном случае движок найдёт имя первичного ключа в правилах валидации).

bool model::delete([int id],[bool affected]);
Удаляет запись с идентификатором id. Если идентификатор не задан, то будет удалена запись, загруженная ранее. Если параметр affected равен true, то метод возвращает true только в том случае, если запись существовала и действительно была удалена, если её не существовало, то вернёт false. Если включён режим мультиязычности (model::multiLanguage()) и таблица является мультиязычной (отдельные копии таблицы для каждого языка), то будут удалены записи из всех таблиц.

Мультиязычность

Модель способна автоматически распознавать мультиязычные таблицы (копии таблиц для каждого языка) и мультиязычные поля таблиц (копии полей в одной таблице). Мультиязычные поля следует указывать без суффикса языка, тоесть "title", а не "title_ru", "title_en" и т.д. Это справедливо и для SELECT и для INSERT/UPDATE. Модель будет автоматически использовать имя поля, которое соответствует текущему языку (определён в константе _LANG).
Откуда скрипт знает о мультиязычных полях таблиц? Он анализирует таблицы ВСЕХ установленных модулей (секция "table" всех файлов из директория /admin/module). Эта информация кешируется в файле /cache/language-database.php, поэтому накладные расходы небольшие.
Для включения режима поддержки мультиязычности необходимо вызвать метод model::mulitLanguage().
При добавлении новых строк (INSERT) модель может заполнять все мультиязычные поля по умолчанию. Для этого нужно перед вызовом model::save() вызвать model::multiLanguage(). К примеру, если таблица содержит поля "title_ru", "title_en" и "title_de", а текущий язык - "ru", то содержимое "title_ru" будет также скопировано в "title_en" и "title_de". Предполагается, что в последствии эти данные будут переведены. Такой подход позволяет избежать "пустых" мультиязычных ячеек базы данных.
Для мультиязычных таблиц (они должны иметь названия с окончанием языка, например article_ru, article_en, article_de и т.д.) поведение model::multiLanguage() иное: модель выполняет несколько SQL-запросов INSERT для каждого языка.
Внимание! Это может быть опасно для таблиц, содержащих первичный ключ: модель сгенерирует его только для первой таблицы и попытается использовать его же для всех остальных, чтобы обеспечить один первичный ключ для всех языков. Вы должны быть уверены, что во всех других таблицах нет записей с таким первичным ключом.
Если метод afterInsert() определён, то для мультиязычных таблиц он будет вызван один раз после выполнения всех SQL-запросов INSERT.
Для операций UPDATE будут обновлены данные только для соответствующего языка.
В мультиязычном режиме операция DELETE (model::delete()) будет выполнена для каждой таблицы.

Валидация и фильтрация

Параметры валидации (проверки корректности данных) могут быть непосредственно переданы методам validate() или save(), а также заданы статически в методе validateRule(). Эти параметры представляют из себя ассоциативный массив, где ключ - это имя поля таблицы базы данных, а значение - массив, содержащий параметры валидации. Первый параметр этого массива - тип поля, второй и последующие зависят от первого. Ниже представлены все доступные типы полей и перечень параметров:
Тут $title - это название поля (должно быть в именительном падеже), оно нужно чтобы сформировать текст об ошибке. Можно не указывать, если неверное значение для поля невозможно.
$null - допустимо или нет пустое значение (null). В данном случае, если параметр имеет значение NULL и $null не равен true, то валидация будет неудачной, в противном случае поле не будет участвовать в SQL-операции INSERT/UPDATE.