B
B
blnk2015-11-13 15:00:59
PHP
blnk, 2015-11-13 15:00:59

PHP. How to competently create an additional class (OOP) for "concrete" selects to work with the database?

There is a class for working with a database with a singleton in the model (let's call it M_MSQLI). The syntax is "classic", wrapping the data in $this->mysqli->real_escape_string() before inserting it into the query.
In addition to common standard functions, it has a bunch of "one-time" Select functions that are used only in one section of the site. In order not to load the entire zoo of functions each time, I would like to separate functions with "special" queries into a separate class, which will be a gasket between the model and the database (let's call it M_MSQLI_SELECT). This is where the question arises of how to do it correctly.
So, pay attention, the question is:
What is the optimal and correct way to call a database connection in the M_MSQLI_SELECT class in order to use real_escape_string() in the functions of this class, given that it uses some of the functions of the M_MSQLI class?
The options that visited my floppy convolutions:

  • Option[0]. Inheritance: Looking through Google and the toaster, it became clear that inheriting a new class from M_MSQLI is bad manners.
    It is also upsetting that in this case you will have to change the scope of the $mysqli property (from private to protected), there is not enough knowledge to assess how fraught this is, but there is a feeling that you should not do this.
  • Option 1]. New database connection in additional class: Recall the database connection in the M_MSQLI_SELECT class constructor and access it in functions.
    Here the question arises: is it a problem that when M_MSQLI_SELECT is accessed by M_MSQLI, the connection will be re-created? Or is it ok?
  • Option[2]. Using the M_MSQLI class as an object: In this case, you will have to forget about encapsulation altogether and make all the required properties and functions public. There are no "feelings" here - this is clearly superfluous.
  • Option[3]. Copy-paste: You can simply copy the M_MSQLI class to M_MSQLI_SELECT, add special queries to the latter and access it only when necessary. But - code duplication and other troubles. Therefore, I would not like to.

Do not judge strictly, this is my first question after mastering php. I would be grateful if you point out errors in the considerations, share a suitable link or tell me in which direction to dig. What I could find on the topic - I read it, by the way, thanks to FanatPHP for detailed comments on the toaster and patience, they were very helpful!
What the M_MSQLI class looks like (a piece, further similarly to the standard functions for work):
<?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)
    {
      // и так далее. 
    }

The following is an example of a function that is written for a specific call for a specific section. I would like to separate them into a separate class:
<?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;
    }

Answer the question

In order to leave comments, you need to log in

2 answer(s)
A
Adamos, 2015-11-13
@blnk

Option 4. Solemnly bury your bike and use SafeMySQL , which easily turns into a singleton and does not require any inheritance to be used in any way.

O
OnYourLips, 2015-11-13
@OnYourLips

Doctrine DBAL, Illuminate database.

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question