问题
TL;DR;
How do I make a PDO connection available to the CardOnFile\Controller
class so that it can make database queries? (I think I have to use the container, but not sure how).
Background
I want to avoid having multiple connections to the database each time I instantiate an object that needs database access. So, I think I need to instantiate ONE copy of a database connection (PDO) and then inject it into the classes where it will be needed using League\Route and League\Container.
I don't want to inject the container into the classes, because that's service location. But, I am completely unsure how to get the PDO object into the class that is going to need it so it can do work.
How to I make it so that the mapped callable has database access?
Code:
/* 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);
回答1:
I figured it out.
The docs don't make this as obvious as they probably could, but the Container object requires that you have an interface defined AND that you use the ->add()->withArgument()
methods.
So, putting it all together, we have this:
Instantiate your initial objects
The code below, uses a closure in order to add a PDO object with the correct database connection to the container. This is the most performant way of adding objects to your container because they are not instantiated until used.
//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;
});
Add your class to the container and specify the constructor*
$container
->add('controller', 'CardOnFile\Controller')
->withArgument($container->get('pdo'));
At this point, the Container object is going to try to use type hints to figure out if it should add something to the constructor, so make sure your class has a type hint in its contstructor. In this case, we are using the PDO class, but we're also in the namespace of CardOnFile, so we need to add the use
statement as well as the type hint:
<?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;
}
// ...
}
Create an interface so the Container can find / recognize that a PDO object should be injected into the instantiated object
<?php
namespace CardOnFile;
interface CardControllerInterface
{
}
Yep. It's blank. It is just used to help out the Container system.
Now, you have successfully injected a PDO dependecy object into your app and its classes.
来源:https://stackoverflow.com/questions/51486449/league-route-container-and-pdo