Итак, я устал от PHP, но большую часть времени я использовал процедурный стиль. Я всегда старался следовать шаблону MVC, хотя я делал вещи процедурно. Кроме того, я привык к некоторым фреймворкам, таким как CakePHP и CodeIgniter. Тем не менее, я еще ничего не сделал с нуля, используя ООП. Сегодня я делаю это для работы в колледже, и у меня есть несколько вопросов.
Обычно в рамках мы просто выполняем $this->database->query()
, в любом классе контроллера, и он работает. В процедуре $link
с базой данных имеет глобальный масштаб, поэтому вам не нужно беспокоиться. Я искал способ сделать это с каждым классом в своем коде, но безуспешно. Лучшее, что я мог получить, это использовать класс Singleton для создания базы данных, однако мне нужно было получить ссылку на базу данных в каждом отдельном классе. Так, например, в классе Users я должен был бы получить его в конструкторе, и в классе Main, и в любом другом.
Мои вопросы довольно просты. Как использовать ссылку на соединение с базой данных через несколько классов, без необходимости создавать экземпляр db во всех них. И если это самый лучший способ, есть ли другой способ сделать это, чем метод Singleton?
Mysqli
, я использую Mysqli
lib.
Здесь класс, который я использовал.
class DB extends Mysqli {
protected static $instance;
private function __construct() {
global $config;
parent::__construct($config['db']['host'], $config['db']['user'], $config['db']['password'], $config['db']['database']);
if($this->connect_error)
die('Connect Error ('. $mysqli->connect_errno .') '. $mysqli->connect_error);
}
public static function getInstance() {
if( !self::$instance ) {
self::$instance = new self();
}
return self::$instance;
}
}
Вот пример использования класса.
class Main {
protected $db;
public function __construct() {
$this->db = DB::getInstance(); // <-- having to do that in every class is the problem
}
protected function start() {
$this->db->query(something...); // works
}
}
Если вы создаете суперкласс с помощью магического метода __get, вы можете проверить, является ли имя запрашиваемого свойства db, и если это так, то автоматически получите экземпляр из вашего класса DB, и если не ошибка, как обычно:
<?php
class Super
{
public function __get($name)
{
if($name=="db") {
return DB::getInstance();
} else {
trigger_error("Undefined property", E_USER_NOTICE);
}
}
}
?>
Лучше всего использовать инъекцию зависимостей вместо инициирования соединения в каждом классе:
$db = DB::getInstance(); // or any other way to initiate it
$obj1 = new MyClass1($db);
$obj2 = new MyClass2($db);
Таким образом, вы всегда решаете, какую БД вы хотите передать каждому классу. Кроме того, это дает некоторые преимущества, если вы также проводите модульное тестирование - тогда вы можете протестировать каждый класс, предоставив ссылку на тестовую БД или даже издеваясь над ней.
$this->db = $db
чтобы получить доступ к ссылке во всем классе, в каждом классе. Все сводилось к тому же, что и к синглтон-классу.
Итак, я придумал свой собственный ответ. Я не уверен, что это оптимальный способ, но вот он. Используя тот же класс DB, что и вопрос, я создал класс "Супер", чтобы "удерживать" ссылку на базу данных при построении. Таким образом, пока я не использую конструктор для других классов, я могу расширить класс "Супер" и получить доступ к $this->db
без необходимости определять его в каждом классе. Если конструктор действительно нужен, я могу вызвать parent::__construct()
из него, и он также будет работать.
class Super {
protected $db;
public function __construct() {
$this->db = DB::getInstance();
}
}
class Main extends Super {
/* No constructor,
otherwise it will overwrite the Super
constructor and lack what we've done there. */
public function start() {
$this->db->query(something...); // works
}
}
Я также ввел некоторые другие переменные и функции, которые были необходимы в нескольких классах. Оттуда я могу выбрать, где продлить или нет класс "Супер", когда это необходимо.