Есть в модели класс работы с бд с синглтоном (назовём его M_MSQLI). Синтаксис используется "классический" с обёртыванием данных $this->mysqli->real_escape_string() перед вставкой в запрос.
Кроме общих стандартных функций в нём есть пачка "одноразовых" функций Select'ов, которые используются только в одном разделе сайта. Чтобы каждый раз не прогружать весь зоопарк функций, хочется выделить функции со "специальными" запросам в отдельный класс, который будет прокладкой между моделью и бд (назовём его M_MSQLI_SELECT). Вот тут и возникает вопрос как грамотно это сделать.
Итак, внимание, вопрос:
Каким образом оптимально и правильно вызвать соединение с БД в классе M_MSQLI_SELECT, чтобы использовать real_escape_string() в функциях этого класса, если учесть, что он использует некоторые функции класса M_MSQLI?
Варианты, которые посещали мои пэхэпэхнутые извилины:
- Вариант[0]. Наследование: Полистав гугл и тостер, стало понятно, что наследовать новый класс от M_MSQLI моветон и не есть гуд.
Огорчает еще то, что при этом придётся изменить область видимости свойства $mysqli (с private на protected), знаний оценить насколько это чревато не хватает, но ощущение, что не стоит этого делать есть.
- Вариант[1]. Новое подключение к БД в доп.классе: Заново вызвать в конструкторе класса M_MSQLI_SELECT подключение к БД и в функциях обращаться к нему.
Тут возникает вопрос: проблема ли, что при обращении M_MSQLI_SELECT к M_MSQLI подключение будет создаваться повторно? Или это нормально?
- Вариант[2]. Использование класса M_MSQLI как объект: В этом случае вообще придётся забыть об инкапсуляции и все требуемые свойства и функции делать публичными. Тут уже никаких "ощущений" нет - это явно лишнее.
- Вариант[3]. Копипаст: Можно просто скопировать класс M_MSQLI в M_MSQLI_SELECT, добавить в последний специальные запросы и обращаться к нему только по необходимости. Но - дублирование кода и прочие неприятности. Поэтому не хотелось бы.
Не судите строго, это мой первый вопрос поcле освоения php. Буду признателен, если укажите на ошибки в соображениях, поделитесь подходящей ссылкой или подскажите в каком направлении копать. Что смог найти по теме - прочитал, кстати, спасибо
FanatPHP за развёрнутые комментарии на тостере и терпение, были очень кстати!
Как выглядит класс M_MSQLI (кусок, далее аналогично стандартные функции для работы):
<?php
// Внутренний класс работы с БД
class M_MSQLI extends M_dbconfig
{
private static $instance;
private $mysqli;
// Получаем единственный экземпляр (Singletoon).
public static function Instance()
{
if (self::$instance == null)
self::$instance = new self();
return self::$instance;
}
// Производим подключение при создании класса.
private function __construct()
{
// Подключение к базе
$this->mysqli = new mysqli(M_dbconfig::DB_HOST,
M_dbconfig::DB_USER,
M_dbconfig::DB_PASS,
M_dbconfig::DB_NAME);
// Назначение кодировки
$this->mysqli->query('SET NAMES utf8');
// Установка внутренней кодировки в UTF-8
mb_internal_encoding("UTF-8");
}
// Далее идут общие функции.
// Выборка строк (внутренняя функция)
// $query - полный текст SQL запроса
// результат - массив выбранных объектов $arr
private function Select($query)
{
// Запрос к БД
$result = $this->mysqli->query($query);
// Проверка на успешный результат
if ($this->mysqli->error) {...}
// Создаём массив для конечного результата
$arr = array();
// Извлекаем ряд таблицы в виде ассоциативного массива. Цикл повторяется пока строки есть
while ($row = $result->fetch_assoc())
$arr[] = $row; // Помещаем извлеченные ряды в общий массив
// Возвращаем полученный общий массив
return $arr;
}
// Вставка строки
// $table - имя таблицы
// $object - ассоциативный массив с парами вида "имя столбца - значение"
// результат - идентификатор новой строки ($this->mysqli->insert_id)
public function Insert($table, $object)
{
// Создаём массив для столбцов куда будем вставлять новое значение
$columns = array();
// Создаём массив для значений которые будем вставлять
$values = array();
// Разбираем массив с данными по столбцам. array('title' => '1223', 'content' => 'dadsd')
foreach ($object as $column => $value)
{
// Экранируем специальные символы в названии столбца
$column = $this->mysqli->real_escape_string($column);
// Добавляем каждый столбец в массив столбцов
$columns[] = "`$column`";
// Записываем нулы пустых значений, чтобы длины значений и столбцов были равны
if ($value === NULL)
{
// Добавляем значение в массив значений
$values[] = "''";
}
else
{
// Экранируем специальные символы в значении
$value = $this->mysqli->real_escape_string($value);
// Добавляем каждое значение в массив значений
$values[] = "'$value'";
}
}
// Собираем столбцы в строку
$columns_s = implode(', ', $columns);
// Собираем значения в строку
$values_s = implode(', ', $values);
// Маска запроса
$sql =
"INSERT INTO `%s` (%s)
VALUES (%s)";
// Экранируем и форматируем переменные в запросе
$query = sprintf($sql,
$this->mysqli->real_escape_string($table),
$columns_s,
$values_s);
// Запрос к БД
$result = $this->mysqli->query($query);
// Проверка на успешный результат
if ($this->mysqli->error) {...}
// Возвращаем индификатор новой строки
return $this->mysqli->insert_id;
}
// Изменение строк
// $table - имя таблицы
// $object - ассоциативный массив с парами вида "имя столбца - значение"
// $where_column - имя столбца для WHERE (часть SQL запроса)
// $where_value - значение столбца для WHERE (часть SQL запроса)
// результат - число измененных строк ($this->mysqli->affected_rows)
public function Update($table, $object, $where_column, $where_sign, $where_value)
{
// аналогичный код.
}
// Удаление строк
// $table - имя таблицы
// $where_column - имя столбца для WHERE (часть SQL запроса)
// $where_value - значение столбца для WHERE (часть SQL запроса)
// результат - число удаленных строк ($this->mysqli->affected_rows)
public function Delete($table, $where_column, $where_sign, $where_value)
{
// и так далее.
}
// Количество строк в таблице
// $table - имя таблицы
// результат - число строк из массива
public function Count($table)
{
// и так далее.
}
Далее пример функции, которые пишутся под конкретный вызов для конкретного раздела. Их хотелось бы выделить в отдельный класс:
<?php
// Выборка чего-то необычного
public function SelectSomthingUnusual($var1, $var2, $id)
{
// Маска запроса
$sql =
"SELECT
`table1`.`id_table1`,
`table1`.`date`,
`table1`.`tel` AS `t1_tel`,
`table1`.`name` AS `t1_name`,
`table1`.`comment` AS `t1_comment`,
`table2`.`name` AS `t2_name`,
`table2`.`important_field`,
`table2`.`some_field`,
`another_table`.`id_another_table`,
`another_table`.`about`,
`some_table`.`some_field`,
GROUP_CONCAT(`some_table`.`some_field` SEPARATOR ', ') AS `field`
FROM `table1`
LEFT JOIN `table2` ON (`table1`.`id_table1` = `table2`.`id_table1`)
LEFT JOIN `another_table` ON (`table1`.`id_table1` = `another_table`.`id_table1`)
LEFT JOIN `another_table2some_table` ON (`another_table`.`id_table1` = `another_table2some_table`.`id_table1`)
LEFT JOIN `some_table` ON (`another_table2some_table`.`id_another_table` = `some_table`.`id_another_table`)
WHERE `some_field` >= '%s' AND `some_field` <= '%s'
AND `table1`.`id_table1` = '%s'
GROUP BY `id_another_table`
ORDER BY `id_another_table` ASC";
// Экранируем и форматируем переменные в запросе
$query = sprintf($sql,
$this->mysqli->real_escape_string($var1),
$this->mysqli->real_escape_string($var2),
$this->mysqli->real_escape_string($id));
// Подготавливаем результат запроса в метод M_MSQLI
$result = $this->Select($query);
// Возращаем массив
return $result;
}