Работа с базой реализует слой «модель» в терминологии MVC. В состав фреймворка входит абстрактный класс waModel
для описания модели. Классы описания
сущностей предметной области приложения наследуются от
waModel
.
Файлы классов модели размещаются в файловой структуре приложения в подкаталоге приложения lib/models/
.
В описании модели есть только одно обязательное поле — название таблицы в базе данных в переменной $table
.
Пример класса описания модели:
<?php class blogModel extends waModel { protected $table = 'blog'; }
Модель инициализируется в коде приложения следующим образом:
$model = new blogModel();
В этом примере blog
— это пример идентификатора приложения. После инициализации модель получает от базы данных описание
указанной таблицы (с помощью запроса
DESCRIBE $table
) и кеширует его. На этом этапе
определяется главный ключ (primary key) таблицы, определяются поля и их типы.
В коде контроллеров и экшенов все взаимодействие с базой данных происходит исключительно через классы моделей приложения:
// Создаем экземпляр модели для получения данных из БД $model = new guestbookModel(); // Получаем записи гостевой книги из БД $records = $model->order('datetime DESC')->fetchAll();
Класс waModel
содержит ряд сервисных методов для автоматического построения SQL-запросов, что позволяет
достичь независимости кода приложения от конкретного вида используемой СУБД.
Методы автоматического построения SQL-запросов
Запросы SELECT
// Получить запись по значению первичного ключа $model->getById($id); // Получить запись по значению одного из полей $model->getByField('name', $name); // возвращает первую найденную запись // Если необходимо вернуть все записи в предыдущем примере, // то нужно добавить в вызов метода третий параметр - true $model->getByField('name', $name, true); // Можно задать условия отбора по значению нескольких полей // (в запросе они будут объединены через AND) $model->getByField(array('name' => $name, 'contact_id' => $contact_id)); // Если необходимо провести отбор по нескольким возможным значениям одного поля, // то вместо одной переменной задается массив, в этом случае при построении запроса // будет использоваться оператор IN $model->getByField('name' , array($name1, $name2)); // Агрегатные запросы типа SELECT COUNT(*) ... WHERE ... создаются методом countByField // Параметры вызова метода полностью аналогичны getByField $model->countByField($field, $value); $model->countByField(array('name' => $name, 'contact_id' => $contact_id)); $model->countByField('name' , array($name1, $name2));
Запросы UPDATE
// Обновить запись по значению первичного ключа $model->updateById($id, $data); // $data - ассоциативный массив значений, которые нужно обновить $model->updateById($id, array('field_1' => $field_1, 'field_2' => $field_2)); // По аналогии с методом getByField также доступен метод updateByField, // которому в качестве третьего параметра передается массив обновляемых значений: $model->updateByField('contact_id', $contact_id, array('published' => true));
Запросы DELETE
// По аналогии с запросами UPDATE доступны следующие два метода: $model->deleteById($id); $model->deleteByField($field, $value);
Запросы INSERT и REPLACE
// Для вставки/замены записи доступны методы: // Оба эти метода вставляют только одну запись $model->insert($data); // в случае auto_increment метод вернет id вставленной записи $model->replace($data);
Выполнение SQL-запросов, записанных в явном виде
Во фреймворке доступны два основных метода модели для выполнения SQL-запросов — query
и exec
.
Первым параметром они принимают SQL-запрос, а вторым — необязательный массив значений для
плейсхолдеров.
Пример:
$model->query("SELECT * FROM ".$this->table." WHERE id = i:id", array('id' => $id));
Пояснения к примеру:
i:id
— это именованный плейсхолдер, в которомi
указывает на тип Integer (то есть значение, полученное из массива, будет приведено к целочисленному типу);id
— ключ в массиве значений;array('id' => $id)
— массив значений.
Доступные для использования типы значений плейсхолдеров:
's'
— String'i'
— Integer'b'
— Boolean'f'
— Float/Double
Если тип плейсхолдера явно не указан (например, в запросе просто используется :id
,
то по умолчанию используется тип
String
). Использование плейсхолдеров упрощает построение запросов, а главное исключает возможность
написания небезопасных SQL-запросов, подверженных SQL-инъекциям.
Использование плейсхолдеров не является обязательным, однако в случае отказа от них разработчик должен самостоятельно позаботиться о подстановке данных в запрос.
Здесь может быть полезным метод модели
escape
, который экранирует строки (при использовании СУБД MySQL его работа аналогична результату выполнения
функции
mysql_real_escape_string
):
$model->query("SELECT * FROM ".$this->table." WHERE id = ".(int)$id); $model->query("SELECT * FROM ".$this->table." WHERE name LIKE '".$this->escape($name)."'");
Единственное отличие между методами query
и exec
заключается в
результате, который они возвращают.
Метод query($sql, $params = null)
Метод выполняет SQL-запрос и возвращает объект результата. Тип возвращаемого объекта зависит от типа запроса:
- для SELECT-запросов возвращается объект типа
waDbResultSelect
, - для INSERT-запросов —
waDbResultInsert
- для UPDATE-запросов —
waDbResultUpdate
- для DELETE-запросов —
waDbResultDelete
Запросы SELECT
Возвращается объект результата типа waDbResultSelect
, который можно использовать, например, в цикле foreach
.
Примеры работы с
waDbResultSelect
:
$result = $model->query("SELECT * FROM ".$this->table." WHERE text LIKE '%тест%'"); // Получаем количество записей в результате (аналогично функции mysql_num_rows) $result->count(); // Проходим по всем записям результата в цикле: foreach ($result as $row) { ... } // Получить все записи в массив $data = $result->fetchAll(); // Если в методе fetchAll указать первым параметром название поля, то результатом будет // ассоциативный массив с ключами, равными значению этого поля $data = $result->fetchAll('id'); // Если известно, что в результате только одна запись, либо если нужно получить данные // только одной записи, то будут полезны следующие методы: // аналог mysql_fetch_assoc $result->fetch(); // выполняет fetch и возвращает значение по переданному ключу, либо значение первого // элемента массива, если ключ не указан $result->fetchField('id'); // например, так можно получить результат запроса COUNT $n = $model->query("SELECT count(*) FROM ".$this->table." WHERE ...")->fetchField();
Запросы DELETE и UPDATE
Возвращается объект результата типа waDbResultDelete
и waDbResultUpdate
.
$result = $model->query("UPDATE ".$this->table." SET name = 'no name' WHERE name = ''"); // Получить количество затронутых записей (функция mysql_affected_rows) $result->affectedRows()
Запросы INSERT
Возвращается объект типа waDbResultInsert
.
// Получить значение первичного ключа созданной записи в случае // auto_increment(функция mysql_insert_id) $result->lastInsertId()
Метод exec($sql, $params = null)
Метод выполняет SQL-запрос и возвращает результат, полученный от адаптера работы с БД (в случае с MySQL возвращается
результат выполнения функции
mysql_query
). Этот метод стоит использовать в тех случаях, когда результат
выполнения запроса не нужен или достаточно проверить, успешно ли выполнился запрос.
Конструктор SELECT-запросов
SELECT-запросы можно строить с помощью простого конструктора запросов, например:
// Выбрать все записи отсортированные по полю datetime: $records = $model->order('datetime DESC')->fetchAll(); // Получить ассоциативный массив id => name $data = $model->select('id,name')->fetchAll('id', true); // В текущей версии доступны методы select, where, order $model->select('id, name, datetime') ->where('contact_id = '.(int)$contact_id) ->order('datetime DESC') ->fetchAll('id'); // Предыдущий пример выполняет то же самое, что и следующий: $model->query("SELECT id, name, datetime WHERE contact_id = ".(int)$contact_id." ORDER BY datetime DESC")->fetchAll('id');
Рекомендации по реализации дополнительных методов модели в приложении
Чтобы во всех местах приложения не писать один и тот же код (напрямую используя базовые методы модели), можно в модели вашего приложения объявить методы для реализации часто используемых операций.
Пример:
// метод, возвращающий все записи контакта public function getByContact($contact_id) { return $this->getByField('contact_id', $contact_id, true); }
Все методы для работы с одной таблицей реализуются в соответствующем классе модели. В дальнейшем вы можете усовершенствовать эти методы модели (например добавлять кеширование).
Рекомендуется большинство операций с конкретной таблицей БД реализовывать в собственных методах модели, не привязанных к названию полей. Это позволит в случае рефакторинга данной таблицы внести изменения лишь в один файл — в код класса модели.
Подключение к другим базам данных
Класс waModel
можно использовать для подключения к другим базам кроме той, в которой хранятся данные Вебасиста. Это может быть полезно, например, для импорта данных из стороннего программного обеспечения.
Для подключения к другой базе данных необходимо при создании экземпляра модели в качестве аргумента конструктора указать параметры подключения. Сделать это можно несколькими способами.
Создание постоянной конфигурации подключения
Если вы планируете многократно использовать в вашем приложении подключение к другой (какой-то определенной) базе данных, добавьте параметры подключения в конфигурационный файл фреймворка wa-config/db.php
в виде дополнительного массива, как показано в примере:
<?php return array ( 'default' => array ( 'host' => 'localhost', 'user' => 'user1', 'password' => '12345678', 'database' => 'webasyst', 'type' => 'mysqli', ), 'extra' => array ( 'host' => 'localhost', 'user' => 'user2, 'password' => '87654321', 'database' => 'wordpress', 'type' => 'mysqli', ),
);
В приведенном выше примере добавлена новая запись с параметрами подключения, условно обозначенная как extra
. Для подключения к базе данных wordpress
(в данном примере) необходимо в PHP-коде приложения создать экземпляр модели следующим образом:
$extra_model = new waModel('extra');
При использовании методов созданной таким образом модели обращение будет выполняться только к базе данных, имя которой указано в соответствующей конфигурации.
Динамическое указание параметров подключения
Если использовать постоянное подключение к какой-то заранее известной базе данных не планируется, то при создании экземпляра модели параметры подключения можно указать явно — в виде массива данных. Пример создания модели в PHP-коде приложения таким способом показан ниже:
$extra_connection = array ( 'host' => 'localhost', 'user' => 'user2', 'password' => '87654321', 'database' => 'somedb', 'type' => 'mysqli', ); $extra_model = new waModel($extra_connection);
Оба приведенных выше способа подключения к другой базе данных применимы не только к экземпляру базового класса waModel
, но и к экземплярам классов моделей вашего приложения (например, myappSomeModel
):
$extra_model = new myappSomeModel('extra');
либо
//массив $extra_connection должен быть заполнен данными подключения к БД $extra_model = new myappSomeModel($extra_connection);
При использовании таких подключений в моделях вашего приложения дополнительные (созданные вами) методы этих моделей должны учитывать структуру таблиц указываемой базы данных.
Для подключения к базам данных с различной структурой рекомендуется создавать отдельные классы моделей. Это позволит упростить структуру вашего приложения и облегчит отладку программного кода.