Last active
November 14, 2018 10:59
-
-
Save samsch/9dd2cec7f21a3e879fd5 to your computer and use it in GitHub Desktop.
Symfony dynamic DB connection
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace AppBundle\Services\Factories; | |
//Inject the EM and Connection for the configured em that needs to be dynamic. | |
//I think you can actually just inject the EM, and call ->getConnection() it. | |
use Doctrine\ORM\EntityManager; | |
use Doctrine\DBAL\Connection; | |
//This service gets the dynamic DB creds from the request/user/whatever. | |
use AppBundle\Services\RequestToAccount; | |
class EmFactory | |
{ | |
protected $reqToAcct; | |
protected $conn; | |
protected $rawEm; | |
protected $em = null; | |
public function __construct(RequestToAccount $reqToAcct, Connection $connection, EntityManager $em) | |
{ | |
$this->reqToAcct = $reqToAcct; | |
$this->conn = $connection; | |
$this->rawEm = $em; | |
} | |
public function getEm() | |
{ | |
//This bit of code effectively caches the connection. | |
//It assumes the connection isn't going to change once set during a request, | |
//so it just returns the connected EM. | |
if($this->em !== null) { | |
return $this->em; | |
} | |
//getDbCredArray() returns the DB creds as an array. | |
//You can see the structure used below. This can be modified to suit. | |
$dbCreds = $this->reqToAcct->getDbCredArray(); | |
$dbParams = $this->conn->getParams(); | |
//This is an extra check to make sure the EM isn't already connected | |
//to the correct DB. In my case, the user is always different. | |
//You might check based on host, dbname, whatever. | |
if($dbParams["user"] !== $dbCreds["user"]) { | |
$dbParams["user"] = $dbCreds["user"]; | |
$dbParams["host"] = $dbCreds["host"]; | |
$dbParams["dbname"] = $dbCreds["dbname"]; | |
$dbParams["password"] = $dbCreds["pass"]; | |
if($this->conn->isConnected()) { | |
$this->conn->close(); | |
} | |
$this->conn->__construct( | |
$dbParams, $this->conn->getDriver(), $this->conn->getConfiguration(), $this->conn->getEventManager() | |
); | |
try { | |
$this->conn->connect(); | |
} | |
catch(Exception $ex) { | |
//Might want to log this error somewhere. | |
throw new Exception("Failed to connect to DB"); | |
} | |
} | |
$this->em = $this->rawEm; | |
return $this->em; | |
} | |
//These are your factory methods. | |
//They can be used to create services for repositories, so you can just inject | |
//what you need directly into most services. Since it's just getting injected | |
//in, those services don't know and don't care that the connection is dynamic. | |
//Generic respository method. | |
public function getRepository($repName) | |
{ | |
return $this->getEm()->getRepository($repName); | |
} | |
//Specific repo method. I usually make one of these per repo that I plan to use. | |
public function createThingRepository() | |
{ | |
return $this->getEm()->getRepository('AppBundle:Thing'); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
... | |
#Obviously, the service names themselves aren't important. I'm just using what's here as | |
#examples. | |
#If you plan to use the repositories in security (user provider, or whatever), these | |
#services need to be declared "lazy". For lazy services, you need proxymanager setup, | |
#which you can find details about here: | |
#http://symfony.com/doc/current/components/dependency_injection/lazy_services.html | |
#This is made with the assumption that your dynamic connection and em is called "dynamic". | |
#Swap out the service names according to your actual DB configuration. | |
connection_factory: | |
class: AppBundle\Services\Factories\EmFactory | |
arguments: ["@app.request_to_account", "@doctrine.dbal.dynamic_connection", "@doctrine.orm.dynamic_entity_manager"] | |
lazy: true | |
#If you need the EM in a service (or controller/containerAware!), use a service like this. | |
entity_manger: | |
class: Doctrine\ORM\EntityManager | |
factory: ["@connection_factory", getEm] | |
lazy: true | |
#Use services like this for injecting repositories. | |
thing_repo: | |
class: AppBundle\Model\ThingRepository | |
factory: ["@connection_factory", createThingRepository] | |
lazy: true |
The create repository methods make sense to be split off into a new services.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@samusenkoiv; I'm not sure about overriding the internal Doctrine service, but you can use this service as a factory to inject the EM or Connection objects as dependencies of your other services. If your app is entirely service-driven, this should cover nearly all usage. If you are using
$this->get("doctrine.dbal.default_connection")
or similar in your controllers (or other container aware classes), then you would have to change these out.