0

I want to connect to different database according to URL. I try to set request attribute and get that attribute in *Factory.php.
I edit autoload/pipeline.php:

<?php
$app->pipe(UrlHelperMiddleware::class);
$app->pipe(\App\Action\Choose::class);
$app->pipeDispatchMiddleware();

in Choose.php I implement process() like this:

<?php
public function process(ServerRequestInterface $request, DelegateInterface $delegate)
{
    /** @var RouteResult $route */
    $route = $request->getAttribute(RouteResult::class);
    if ($route->getMatchedRouteName() == 'FirstRoute' or $route->getMatchedRouteName() == 'SecondRoute') {
        $request = $request->withAttribute('DB_NAME', $route->getMatchedParams()['param']);
    }
    return $delegate->process($request);
}

The main problem is in *Factory.php I don't access to request.
Any try to access to Interop\Http\ServerMiddleware\MiddlewareInterface or Psr\Http\Message\ServerRequestInterface in *Factory.php raises same error.

Is there any way pass parameter from pipeline middleware to factory class?

ooghry
  • 11
  • 1
  • 2

1 Answers1

0

If you use zend-servicemanager you can try this (just a theory, not tested):

Create 2 database factories in your config: 'db.connection.a' => DbFactoryA::class, 'db.connection.b' => DbFactoryB::class,

Then depending on the route, in Choose you load the connection you need and pass it to the container as the default connection. $db = $container->get('db.connection.a'); $container->setService('db.connection.default', $db);

And now in all following middleware you can grab the default connection.

UPDATE:

As mentioned in the comments, this requires the container to be injected which is considered bad practice. How about you wrap the two in a common connection class and set the required from Choose:

class Connection
{
    private $connection;

    /**
     * @var ConnectionInterface
     */
    private $db_a;

    /**
     * @var ConnectionInterface
     */
    private $db_b;

    public function __construct(ConnectionInterface $a, ConnectionInterface $b)
    {
        $this->db_a = $a;
        $this->db_b = $b;
    }

    public function setConnection($connection)
    {
        if ($connection === 'a') {
            $this->connection = $this->db_a;
            return;
        }

        $this->connection = $this->db_b;
    }

    public function getConnection()
    {
        return $this->connection;
    }
}

Or you can inject only the config and create the database connection that's really needed. Store it in a property for caching (like the container does).

xtreamwayz
  • 1,285
  • 8
  • 10
  • Thank you,But I think pipeline middleware(in this case:`Choose` class) `implements Interop\Http\ServerMiddleware\MiddlewareInterface` and in `process(ServerRequestInterface $request, DelegateInterface $delegate) `we don't access to `$container`. – ooghry Apr 07 '17 at 14:17
  • You are right. It's bad practice to inject the container. But I think exceptions can be made for special use cases. After all, it is a sort of a factory since it needs to interact with the container. I don't see any other way how to do this. – xtreamwayz Apr 07 '17 at 17:20
  • @ooghry I've added another solution so you don't need to inject the entire container. – xtreamwayz Apr 07 '17 at 17:30
  • I appreciate your helping me, but if I have more than 5 connection or add new server to system, maintenance this code is hard. – ooghry Apr 07 '17 at 17:47