Работа с базами данных
- Работа с БД осуществляется через класс DbSimple от DkLab
- В микроне можно работать с несколькими БД сразу
- Так же предусмотренно как текстовое логирование запросов в файл, так и вывод в нижную панель при Debug-режиме работы сайта.
Настройка подключения(-ий) к БД
Настройка подключения и работы с БД находятся в файле /core/config/db.php, рассмотрим параметры этого файла подбробнее:
-
$g_config['dbSimple']['databases'] - массив в котором содержатся все подключения к базам данных в формате DSN.
DSN (Data Source Name) - это универсальный формат описывающий подключение к источнику данных, по своей сути обычная строка где через спец. разделители указаны все необходимые параметры подключения.
В нашем примере этот формат выглядит следущим образом:
mysql://ИМЯ_ПОЛЬЗОВАТЕЛЯ:ПАРОЛЬ@ХОСТ/ИМЯ_БАЗЫ_ДАННЫХ?charset=КОДИРОВКА_БАЗЫ_ДАННЫХРассмотрим пример:
$g_config['dbSimple']['databases'] = array ( 'db' => array('dsn' => 'mysql://root:@localhost/test_db?charset=UTF8') );
Здесь есть
- db - имя создаваемого подключения к БД, т.е. после к объекту DbSimple работающему с этим подключением можно будет обращаться так: $g_databases->db
- mysql://root:@localhost/test_db?charset=UTF8 - само подключение в формате DSN
Стоит отметить, что часто при разработке сайта, программист работает с локальной копией БД, конечно он мог бы каждый раз править подключение перед заливкой файлов на сервер, однако куда удобнее сделать вот так:
$g_config['dbSimple']['databases'] = array ( 'db' => array ( // DEBUG_MODE - будет true только на локальной копии 'dsn' => DEBUG_MODE ? 'mysql://root:@localhost/my_local_db?charset=UTF8' : 'mysql://user:pwd@localhost/real_db?charset=UTF8' ) );
Что бы работать с разными БД сразу, Вам нужно лишь перечислить их в массиве подключений и дать имя будущему объекту для обращения
$g_config['dbSimple']['databases'] = array ( 'db' => array('dsn' => 'mysql://root:@localhost/main_db?charset=UTF8'), 'dbPayments' => array('dsn' => 'mysql://root:@localhost/payments_db?charset=UTF8'), 'dbLogger' => array('dsn' => 'mysql://root:@localhost/logger_db?charset=UTF8'), );
В приведённом выше примере, я создал 3 подключения к разным БД, обращаться к ним мне нужно будет $g_databases->db, $g_databases->dbPayments, $g_databases->dbLogger соответственно.
- $g_config['dbSimple']['dbLogFile'] - путь к файлу для записи ошибочных запросов к БД Измените это имя с дефолтного на любое другое для повышения безопастности сайта
- $g_config['dbSimple']['logDbError'] - Выставьте в false если логирования не требуется В реальности, нет необходимости отключать логгирование даже на продакшен сервере
Работа с моделями
Модель - класс предназначенный для работы с данными объектов системы. Примерами моделей могут служить: класс работы с пользователем, класс работы с новостями, класс работы с партнёрами и пр.
Модели в микроне хранятся в папке /model.
Модели, как и библиотеки, подключаются автоматически если вызываемый класс и называются одинаково, т.е.
если у Вас в коде встречается строчка:
$u = new UserModel();и при этом подключение файла где описана модель UserModel еще не производилось, то будет произведена попытка подключить файл /model/UserModel.php.
По своей сути модель - является отображением информационных объектов системы.
В 99 случаях из 100 модель работает с данными хранящимися в БД (хотя это и не правило)
Это означает, что вот такой код:
$user = new UserModel(1001); // 1001 - это user_id, уникальный индентификатор пользователя в системе $user->pwd_hash = md5($newPassword); // Конечно простой md5 некуда не годится как хеш на пароль, но это всего лишь пример :-)По результату работы был бы индентичен:
$g_databases->db->select("UPDATE `users` SET `pwd_hash` = ? WHERE `user_id` = 1001", $newPassword);Но если во втором случае мы напрямую работаем с данными, то в первом мы работаем с логической сущностью "пользователь" что идеологически приятнее, делает код менее зависимым, более читаемым зачастую сокращает его, да и кроме того открывает огромное кол-во дополнительных возможностей, как то, создание виртуальных (вычисляемых) полей, прозрачный ввод логирования, кеширования и пр.
Стоит заметить, что зачастую работа с моделями разных объектов носит весьма схожий характер, и складывается из
- Извлечение данных записи по ключевому полю (например извлечение конкретной новости или статьи)
- Обновление данных конретной записи (той же новости, статьи, пользователя)
- Добавление новогй записи
Чтобы упростить создание моделей с подобным функционалом, нами была разработана модель Model описанная в файле /model/Model.php.
Посмотрим как работать с ней:
Пусть у нас уже есть созданное подключение к БД и мы можем обращаться к нему через $g_databases->db, перед нами стоит задача сделать модель при помощи которой мы могли бы извлекать данные для конретного пользователя, изменять их, а так же удалять этого пользователя при необходимости.
Тогда мы создаем файле /model/UserModel.php в котором пишем код:
class UserModel extends Model { public function __construct($user_id = NULL, $onlyShow = false) { global $g_databases; parent::__construct($g_databases->db, 'user', 'user_id', $user_id, $onlyShow); } public function CreateTable() { $this->db->query("CREATE TABLE IF NOT EXISTS ?# ( `user_id` int(11) NOT NULL AUTO_INCREMENT, `login` varchar(128) CHARACTER SET utf8 NOT NULL, `pwd_hash` varchar(32) CHARACTER SET utf8 NOT NULL, PRIMARY KEY (`user_id`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1", $this->table); } };
Теперь мы можем:
-
Извлекать данные о конкретном пользователе:
$u = new UserModel(123); // 123 - это user_id echo "Логин пользователя: " . $u->login;
-
Изменять данные пользователя:
$u = new UserModel(123); $u->login = "NewLogin";
-
Добавлять новых пользователей:
$u = new UserModel(); $u->login = "AnyLogin"; $u->pwd_hash = md5("123"); echo "USER_ID нового пользователя: " . $u->Flush(); // Если Flush() не вызывать то он автоматом вызовится в деструкторе
-
Удалять пользоваля:
$u = new UserModel(123); $u->Delete();
Как видите наша модель построенна и полностью функциональна.
Если Вам нужны еще поля то просто создайте их в таблице пользователя:
class UserModel extends Model { public function __construct($user_id = NULL, $onlyShow = false) { global $g_databases; parent::__construct($g_databases->db, 'user', 'user_id', $user_id, $onlyShow); } public function CreateTable() { // Если Ваша таблица уже создана то запрос 2-ой раз не пройдёт, и их нужно либо добавит ручками либо пересоздать таблицу $this->db->query("CREATE TABLE IF NOT EXISTS ?# ( `user_id` int(11) NOT NULL AUTO_INCREMENT, `login` varchar(128) CHARACTER SET utf8 NOT NULL, `pwd_hash` varchar(32) CHARACTER SET utf8 NOT NULL, `name` varchar(128) CHARACTER SET utf8 NOT NULL, `second_name` varchar(32) CHARACTER SET utf8 NOT NULL, `create_time` int(11) NOT NULL, PRIMARY KEY (`user_id`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1", $this->table); } };
Виртуальные поля в моделе
Предположим перед нами встала задача знать время последнего визита пользователя.
В быстром решении мы можем просто добавить поле last_activity типа INT в нашу таблицу пользоватлей, и выставлять в неё значение time() при каждой процедуре авторизации.
В будущем же, когда потребуется еще хранить историю авторизаций, мы можем поступить так:
1) Создадим таблицу user_activities с полями user_id и time
2) Каждый раз при авторизации пользователя мы будет класть новую запись в эту таблицу с текущим time()
3) А для того что бы не менять нигде исходники уже написанно $user->last_activity мы сделаем виртуальное поле при магического метода __get который перехватывает вызов любой
незадекларированной переменной в классе:
class UserModel extends Model { public function __construct($user_id = NULL, $onlyShow = false) { global $g_databases; parent::__construct($g_databases->db, 'user', 'user_id', $user_id, $onlyShow); } public function CreateTable() { // Если Ваша таблица уже создана то запрос 2-ой раз не пройдёт, и их нужно либо добавит ручками либо пересоздать таблицу $this->db->query("CREATE TABLE IF NOT EXISTS ?# ( `user_id` int(11) NOT NULL AUTO_INCREMENT, `login` varchar(128) CHARACTER SET utf8 NOT NULL, `pwd_hash` varchar(32) CHARACTER SET utf8 NOT NULL, `name` varchar(128) CHARACTER SET utf8 NOT NULL, `second_name` varchar(32) CHARACTER SET utf8 NOT NULL, `create_time` int(11) NOT NULL, PRIMARY KEY (`user_id`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1", $this->table); } // Фунция перехватывающая вызов несуществующих методов public function __get($key) { swith ($key) { // Если запрашивается поле last_activity то мы перехватываем это и делаем запрос в таблицу user_activities извлекая последнее (максимальное) время когда юзер посещал нас case 'last_activity': $ret = $this->db->selectCell("SELECT MAX(`time`) FROM `user_activities` WHERE `user_id` = ?d", $this->user_id); break; // Иначе подключить стандартный ход работы для модели Model default: $ret = parent::__get($key); }; return $ret; } };Интересным моментом также является возможность перехвата записи в поле по средствам метода __set($key, $value).
Вот например при записи в поле $user->last_activity Вы можете перехватить это сабытие через __set() и положить новую запись в таблицу user_activities.
Так же стоит отметить, что имена это обычные строки, потому Вы можете производить поиск в них или вырезать параметры, вот например можно организовать работу так что при вызове атрибута $u->avatar32x32 происходит перехват методом __get, поиск миниатуры и генерация налету если её нет (размером 32х32 пикселя).