PHP. Как грамотно создать дополнительный класс (ООП) для «конкретных» select'ов для работы с БД?

Есть в модели класс работы с бд с синглтоном (назовём его 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;
		}
  • Вопрос задан
  • 1584 просмотра
Решения вопроса 1
Adamos
@Adamos
Вариант 4. Торжественно похоронить свой велосипед и использовать SafeMySQL, который легко превращается в синглтон и не требует никакого наследования для использования каким угодно образом.
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы