Ответ 1
Правильный способ решения этого вопроса - ввести объект базы данных в другой класс (инъекция зависимостей):
$db = new DB_MySQL("localhost", "root", "", "test"); // connect to the database
include_once("pagi.php");
$pagination = new Paginator($db);
$records = $pagination->get_records("SELECT the, fields, you, want, to retrieve FROM `table`");
class Paginator
{
protected $db;
// Might be better to use some generic db interface as typehint when available
public function __construct(DB_MySQL $db)
{
$this->db = $db;
}
public function get_records($q) {
$x = $this->db->query($q);
return $this->db->fetch($x);
}
}
Другой способ, которым вы могли бы решить это, - ввести экземпляр класса базы данных в метод, который его использует:
$db = new DB_MySQL("localhost", "root", "", "test"); // connect to the database
include_once("pagi.php");
$pagination = new Paginator();
$records = $pagination->get_records("SELECT the, fields, you, want, to retrieve FROM `table`", $db);
class Paginator
{
public function get_records($q, DB_MySQL $db) {
$x = $db->query($q);
return $db->fetch($x);
}
}
Какой бы метод вы ни выбрали, зависит от ситуации. Если только одному методу нужен экземпляр базы данных, вы можете просто ввести его в метод, иначе я бы ввел его в конструктор класса.
Также обратите внимание, что я переименовал ваш класс из pagi
в Paginator
. Paginator - лучшее имя IMHO для класса, потому что для других людей (re) для вашего кода ясно. Также обратите внимание, что я сделал первую букву в верхнем регистре.
Еще одна вещь, которую я сделал, - это изменить запрос, чтобы выбрать поля, которые вы используете, вместо того, чтобы использовать "подстановочный знак" *
. Это по той же причине, что я изменил имя класса: People (re), просматривающий ваш код, точно узнает, какие поля будут извлечены без проверки базы данных и/или результата.
Обновление
Поскольку ответ привел к обсуждению вопроса о том, почему я отправился на путь внедрения зависимостей вместо объявления объекта global
, я хотел бы пояснить, почему я бы использовал инъекцию зависимостей над ключевым словом global
: когда у вас есть метод вроде:
function get_records($q) {
global $db;
$x = $db->query($q);
return $db->fetch($x);
}
Когда вы используете метод выше, неясно, зависит ли класс или метод от $db
. Следовательно, это скрытая зависимость. Другая причина, почему выше сказанное плохо, заключается в том, что вы тесно связали класс $db
(таким образом, DB_MySQL
) с этим методом/классом. Что делать, если вам нужно использовать 2 базы данных в какой-то момент. Теперь вам нужно пройти весь код, чтобы изменить global $db
на global $db2
. Вам не нужно менять код, чтобы переключиться на другую базу данных. По этой причине вы не должны делать:
function get_records($q) {
$db = new DB_MySQL("localhost", "root", "", "test");
$x = $db->query($q);
return $db->fetch($x);
}
Опять же, это скрытая зависимость и плотно соединяет класс DB_MySQL
с методом/классом. Из-за этого также невозможно правильно unit test класс Paginator
. Вместо тестирования только единицы (класс Paginator
) вы также тестируете класс DB_MySQL
в одно и то же время. А что, если у вас несколько тесно связанных зависимостей? Теперь вы неожиданно тестируете несколько классов с помощью так называемых модульных тестов. Поэтому при использовании инъекции зависимостей вы можете легко переключиться на другой класс базы данных или даже на насмешку для тестирования. Помимо преимуществ тестирования только одного устройства (вам не нужно беспокоиться о неправильных результатах из-за зависимостей), он также гарантирует, что ваши тесты будут быстро завершены.
Некоторые люди могут подумать, что шаблон Singleton - это правильный способ получить доступ к объекту базы данных, но должно быть ясно, что, прочитав все вышеизложенное, singleton - это просто другой способ сделать вещи global
. Он может выглядеть по-другому, но он имеет те же самые характеристики и, следовательно, те же проблемы, что и global
.