1 Востаннє редагувалося Sensetivity (23.01.2015 15:15:09)

Тема: Як краще організувати підключення до БД.

Постараюсь одразу до справи.
Пишу свій перший проект використовуючи шаблон MVC та ООП. До того писав процедурний гамнокод.
Для роботи з БД я використовую обгортку safemysql.
Планую, що усі моделі підключатимуть автолоадером.

1. Зробити успадкування від safemysql в конструкторі-нащадків виконувати підключення? Грозить створенням по 1 підключенню на 1 модель.
2. Створювати підключення, записувати його у змінну і передавати в наступні класи? Але тоді не можна буде зробити виходу. Вірніше, якщо десь закрити підключення то потім його вже не відкрити.

Питання в наступному, де краще ініціювати підключення до БД?
Чим грозить не закриття підключень?
І що поганого у постійному підключенні?

2

Re: Як краще організувати підключення до БД.

Закривати підключення не потрібно, воно буде закрите автоматично після завершення роботи скрипту.
Підключення до БД краще усього створити один раз, зберегти у контейнері, а потім витягувати його у тих класах, де воно потрібне. Такий контейнер називається Dependency Injection Container або Service Locator (реалізація однакова, термінологія залежить від методу використання). Є досить багато його реалізацій, зверніть увагу на ці:

http://pimple.sensiolabs.org
http://container.thephpleague.com/

Відкривати/закривати підключення кожен раз коли воно потрібне - дуже повільно, так робити не треба.

3 Востаннє редагувалося Sensetivity (23.01.2015 15:50:41)

Re: Як краще організувати підключення до БД.

TwiStar написав:

Закривати підключення не потрібно, воно буде закрите автоматично після завершення роботи скрипту.
Підключення до БД краще усього створити один раз, зберегти у контейнері, а потім витягувати його у тих класах, де воно потрібне. Такий контейнер називається Dependency Injection Container або Service Locator (реалізація однакова, термінологія залежить від методу використання). Є досить багато його реалізацій, зверніть увагу на ці:

http://pimple.sensiolabs.org
http://container.thephpleague.com/

Відкривати/закривати підключення кожен раз коли воно потрібне - дуже повільно, так робити не треба.

Тобто грубо кажучи, такий варіант є  не хибним?

// файл bootstrap.php
// підключення файлів....
// з`єднання з БД
$pdo = new BDConnect();
// мої моделі
$users = new Users($pdo);
$cars = new Cars($pdo);
// далі виклик роутера.

4 Востаннє редагувалося TwiStar (23.01.2015 16:37:08)

Re: Як краще організувати підключення до БД.

Так, щось на зразок такого. Але мушу зауважити, що це не дуже оптимально - інстанціювати моделі до того, як ви визначили маршрут. Навпаки, правильний хід подій - це спочатку визначити маршрут, потім маршрут визиває відповідний контроллер, а вже у контроллері ми визначаємо які моделі нам потрібні.

5

Re: Як краще організувати підключення до БД.

Тоді доведеться підключати одні і ті самі файли в багатьох контролерах і у випадку зміни файлу/бібліотеки доведеться правити всі контролери.
Хіба не краще зробити єдину точку ініціалізації?

6 Востаннє редагувалося TwiStar (23.01.2015 17:51:14)

Re: Як краще організувати підключення до БД.

Ні, не краще. Сама ідея в тому, щоб ініціалізувати тільки ті класси, які потрібні для відображення сторінки і не більше.

Те, що у випадку зміни файлу/бібліотеки доведеться правити всі контроллери - це не дуже залежить від того, що і як буде ініціалізовано, це питання правильного контролю залежностей класів, і на цю тему можна писати цілу книгу. Основний принцип - Dependency Inversion, тобто, наведу простий приклад.

class DB {/* логіка роботи з базою даних */}

class UsersRepository
{
    /**
     * @var DB
     */
    private $db;

    function __construct(DB $db)
    {
        $this->db = $db;
    }

    function getAll()
    {
        return $this->db->query("SELECT * FROM `users`");
    }
}

Це - пряма залежність. Клас UsersRepository залежить від класу ДБ. Якщо ми хочемо замінити клас ДБ на інший, який не має методу query - цьому коду хана, ми не знаємо, які методи старого класу де і яким чином використовуються. Щоб уникнути цієї проблеми, ми робимо наступну річ:

interface DBInterface
{
    function query();
}

class DB implements DBInterface
{
    function query()
    {
        return 'query result';
    }
}

class UsersRepository
{
    /**
     * @var DB
     */
    private $db;

    function __construct(DBInterface $db)
    {
        $this->db = $db;
    }

    function getAll()
    {
        return $this->db->query("SELECT * FROM `users`");
    }
}

Тепер клас UsersRepository не залежить напряму від класу DB, тепер обидва ці класи залежать від інтерфейсу. Цей принцип контроля залежностей називається Dependency Inversion, і він полягає в тому, що модулі вищого рівня (наприклад, UsersRepository) не повинні залежати від модулей нижчого рівня (реалізація підключення до БД), вони обидва повинні залежати від абстракції (тобто, інтерфейсу, який не містить в собі деталей реалізації).

Взагалі, це один з п'яти принципів SOLID. Цей набір - основа будь-якої правильної ОО-Архітектури, тому я б рекомендував ознайомитися, інакше продовжите писати процедурний гамнокод, але тепер з використанням класів :)

Подякували: Анатолій1

7

Re: Як краще організувати підключення до БД.

TwiStar написав:

Якщо ми хочемо замінити клас ДБ на інший, який не має методу query

Мабуть ми все ж будемо заміняти на клас, який має метод query.

8 Востаннє редагувалося TwiStar (23.01.2015 17:42:10)

Re: Як краще організувати підключення до БД.

Я маю на увазі, що якщо в нас немає інтерфейсу, який показує, що за методи повинні бути у класа, ми не знаємо, які методи повинні бути у нового класу, і при заміні одного на інший ми будемо ловити несумісності по всьому коду. А так ми чітко бачимо, що в нього повинно бути, і навіть якщо є якісь нестиковки просто пишемо Адаптер, і заміна класу проходить швидко та безболісно.

В мене навіть є гарний приклад. Замінити в сервісі логування підключення з mysql-бази даних на mongoDB. Якщо логгер залежить напряму від методів PDO, mysqli чи враппера - маємо вагони геморрою. А якщо від інтерфейсу - задача спрощуєтся в рази.

9

Re: Як краще організувати підключення до БД.

Якщо б я оцінював "чи використовувати наведений вами клас", то я б його не використовував хоча б за цей  абсолютно безглуздий код unset($opt); // I am paranoid

    function __construct($opt = array())
    {
        $opt = array_merge($this->defaults,$opt);
        $this->emode  = $opt['errmode'];
        $this->exname = $opt['exception'];
        if ($opt['pconnect'])
        {
            $opt['host'] = "p:".$opt['host'];
        }
        @$this->conn = mysqli_connect($opt['host'], $opt['user'], $opt['pass'], $opt['db'], $opt['port'], $opt['socket']);
        if ( !$this->conn )
        {
            $this->error(mysqli_connect_errno()." ".mysqli_connect_error());
        }
        mysqli_set_charset($this->conn, $opt['charset']) or $this->error(mysqli_error($this->conn));
        unset($opt); // I am paranoid
    }

По-моєму, таке писати може лише людина, яка невпевнена в тому, що вона робить...

10

Re: Як краще організувати підключення до БД.

Ну це вже питання реалізації, які не настільки важливі. Хоча я вже два роки перестав використовувати будь-що відмінне від PDO та prepared statements.

11

Re: Як краще організувати підключення до БД.

Чи є більш-менш нормальним такий спосіб ?

<?php

class DB
{
protected $host = 'localhost'; 
protected $dbname = 'dbname'; 
protected $user = 'user'; 
protected $password = 'password'; 
protected $driver = 'mysql'; 

  function __construct() {
      $this->db = new PDO("$this->driver:host=$this->host;dbname=$this->dbname", $this->user, $this->password); 
   }
}

//наслідуємо класс бд

class Info extends DB
{
  function __construct() {
   parent::__construct(); //копіюємо його конструктор
 }

 public function data() {
   return var_dump($this->db); //тест
 }

}

//і вивід
$inf = new Info(); 
echo $inf->data();