Итак, я стар для PHP, но большую часть времени я использовал процедурный стиль. Я всегда пытался следовать шаблону MVC, хотя я делал что-то процедурно. Кроме того, я привык к некоторым фреймворкам, таким как CakePHP и CodeIgniter. Тем не менее, я еще ничего не делал с нуля, используя ООП. Сегодня я делал это для работы в колледже, и я получил несколько вопросов.
Обычно в рамках мы просто делаем $this->database->query()
, на любом контроллере класса, и это работает. По процедурному $link
В базу данных входит глобальная область, так что вам не о чем беспокоиться. Я искал способ сделать это с каждым классом в моем коде, но безуспешно. Лучшее, что я мог получить, это использовать класс Singleton для создания экземпляра базы данных, однако мне нужно было получить ссылку на базу данных в каждом отдельном классе. Так, например, в классе Users я должен был бы получить его в конструкторе, и в классе Main, и в любом другом.
Мои вопросы довольно просты. Как использовать ссылку на соединение с базой данных через несколько классов, без необходимости создания экземпляров БД во всех них.
И, если это лучший способ проще, есть ли другой способ сделать это, кроме метода Singleton?
я использую 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);
Таким образом, вы всегда решаете, какую БД вы хотите передать каждому классу. Кроме того, это дает некоторые преимущества, если вы также проводите модульное тестирование — тогда вы можете протестировать каждый класс, предоставив ссылку на тестовую БД или даже посмеявшись над ней.
Итак, я придумал свой ответ. Я не уверен, что это оптимальный путь, но вот он.
Используя тот же класс БД, что и в вопросе, я создал «Супер» класс для «удержания» ссылки на базу данных при конструировании. Таким образом, до тех пор, пока я не использую конструктор в других классах, я могу расширить класс «Super» и получить доступ к $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
}
}
Я также ввел некоторые другие переменные и функции, которые были необходимы в нескольких классах. Оттуда я могу выбрать, где расширить или нет класс «Супер», когда это необходимо.