TL; DR;
Как сделать соединение PDO доступным для класса CardOnFile\Controller, чтобы он мог выполнять запросы к базе данных? (Я думаю, мне нужно использовать контейнер, но не знаю, как это сделать).
Задний план
Я хочу избежать множественных подключений к базе данных каждый раз, когда я создаю экземпляр объекта, которому требуется доступ к базе данных. Итак, я считать Мне нужно создать ОДНУ копию соединения с базой данных (PDO), а затем внедрить ее в классы, где она понадобится, используя League \ Route и League \ Container.
Я не хочу вставлять контейнер в классы, потому что это местоположение службы. Но я совершенно не уверен, как поместить объект PDO в класс, которому он понадобится, чтобы он мог работать.
Как сделать так, чтобы отображаемый вызываемый объект имел доступ к базе данных?
Код:
/* Connect to a MySQL database using driver invocation */
$dsn = 'mysql:dbname=foo;host=localhost';
$user = 'bar';
$password = 'supersecret';
try {
$pdo = new PDO($dsn, $user, $password);
} catch (PDOException $e) {
echo 'Connection failed: ' . $e->getMessage();
die(__FILE__ . ":" . __LINE__);
}
$container = new League\Container\Container;
$container->share('response', Zend\Diactoros\Response::class);
$container->share('request', function () {
return Zend\Diactoros\ServerRequestFactory::fromGlobals(
$_SERVER, $_GET, $_POST, $_COOKIE, $_FILES
);
});
$container->share('emitter', Zend\Diactoros\Response\SapiEmitter::class);
//DI PDO instance and connection.
$container->share('PDO', $pdo);
$route = new League\Route\RouteCollection($container);
$route->map('GET', '/cards/', 'CardOnFile\Controller::requestEmail' );
$route->map('GET', '/cards/emails/confirm', 'CardOnFile\Controller::getUserController' );
$route->map('GET', '/cards/emails/', 'CardOnFile\Controller::getCardController' );
$response = $route->dispatch($container->get('request'), $container->get('response'));
$container->get('emitter')->emit($response);
Это полезно, и я согласен с этим, но я не вижу, как я могу проникнуть в область действия $route->map(), чтобы предоставить экземпляр базы данных ИЛИ передать переменную $pdo самому классу.
Отредактировано, чтобы переформулировать вопрос, чтобы прояснить то, что мне нужно.
Получая соединение в Controller, я, вероятно, просто получил бы соединение по умолчанию из статического фабричного класса. Не уверен, что в вашем приложении есть концепция «помощника контроллера», как в некоторых фреймворках. $db = $this->_helper->dbManager() ... А фреймворк пользуетесь? У вас есть какой-то подход к базе данных / модели?
Я не использую фреймворк. Подход, который вы здесь перечисляете, действителен, но я старался не использовать глобальное пространство имен. Не уверен, что смогу провести модульное тестирование таким образом.
Я чувствую, что ответ может быть спрятан здесь, но я не могу его найти. stackoverflow.com/questions/44387343/…
может быть неплохим для обсуждения в IRC ... у каждого будет свое мнение. Хорошо, наверное, поговорить и резиновую уточку.
Позвольте нам продолжить обсуждение в чате.






Я понял.
документы не делает это настолько очевидным, насколько возможно, но объект-контейнер требует, чтобы у вас был определенный интерфейс И чтобы вы использовали методы ->add()->withArgument().
Итак, сложив все вместе, мы получаем следующее:
Создайте экземпляры ваших исходных объектов
В приведенном ниже коде используется закрытие для добавления объекта PDO с правильным подключением к базе данных в контейнер. Это наиболее эффективный способ добавления объектов в ваш контейнер, поскольку они не создаются до тех пор, пока не будут использованы.
//DI PDO instance and connection.
$container->add('pdo', function() {
/* Connect to a MySQL database using driver invocation */
$dsn = 'mysql:dbname=foo;host=localhost';
$user = 'bar';
$password = 'supersecret';
try {
$pdo = new PDO($dsn, $user, $password);
} catch (PDOException $e) {
echo 'Connection failed: ' . $e->getMessage();
die(__FILE__ . ":" . __LINE__);
}
return $pdo;
});
Добавьте свой класс в контейнер и укажите конструктор *
$container
->add('controller', 'CardOnFile\Controller')
->withArgument($container->get('pdo'));
На этом этапе объект-контейнер будет пытаться использовать подсказки типа, чтобы выяснить, следует ли добавлять что-то в конструктор, поэтому убедитесь, что ваш класс имеет подсказку типа в своем конструкторе. В этом случае мы используем класс PDO, но мы также находимся в пространстве имен CardOnFile, поэтому нам нужно добавить оператор use, а также подсказку типа:
<?php
namespace CardOnFile;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
**use \PDO;**
class Controller implements CardControllerInterface
{
public $content = '';
protected $pdo = null;
public function __construct(**PDO** $pdo) {
$this->pdo = $pdo;
}
// ...
}
Создайте интерфейс, чтобы Контейнер мог найти / распознать, что объект PDO должен быть вставлен в созданный объект.
<?php
namespace CardOnFile;
interface CardControllerInterface
{
}
Ага. Пусто. Он просто используется, чтобы помочь системе контейнеров.
Теперь вы успешно внедрили объект зависимости PDO в свое приложение и его классы.
FWIW: Шаблоны проектирования: как создать объект / соединение с базой данных только при необходимости?. Думаю, мой типичный подход - синглтон (с фабрикой / реестром?). Я думаю о том, как Doctrine используется в ZF и Symfony.