Работа с базами данных
- Работа с БД осуществляется через класс 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 пикселя).