How to use Expressive when I want a value in the Session to be available at object creation time?

expressive

#1

I have a use case where I need to have access to a quote_id for a fairly large number of Middleware actions.

I have a set of entities such as Quote, Order, and others, that interact with the database via a repository layer, and repositories needs to have quote_id available to them in order to access data pertaining to the entities. For example, user opens a Quote, which becomes active, and most user actions on that Quote require access to the quote_id.

Current Design choices

Since quote_id is so ubiquitous and frequently-used in my application, I put it into the session, so it is available most all the time, even when it is not explicitly needed.

When it is needed, I pull it out of the session, and inject it into constructor of a repository class. This effectively “locks in” the repository to that quote_id and stays that way throughout the run-time of the request. And that is okay as so far I have not identified any use-cases where I need to have two or more different quote_id numbers existing simultaneously.

Having $quoteId being available as part of the repository itself also simplifies handling a bit, as I could refer to it as $this->quoteId inside the repository. If I didn’t have $quoteId be a part of the repository, I’d have to pass it as a parameter to every single method call of the repository, which would require me to maintain the value of $quoteId elsewhere, such as a service or a middleware.

Problem

So, here is problem #1: In my factory, I use the session to get the quote_id ($_SESSION['quote_id']) and inject its value into a repository of choice. The problem is that I do this before I have access to a ServerRequestInterface object. I do this at “Factory” time, before request and session from request are available to me.

Also, it is not just the retrieval of the quote_id that I do, I have a mechanism that first checks the $_GET superglobal, because there is a use case where I may wish to override the $_SESSION with the $_GET, and so my code below takes care of that, as well as taking care of any validation checks on the id:

class QuoteIdFactory
{
    public function __invoke(ContainerInterface $container)
    {
        if (isset($_GET['quote_id']))
            $quoteId = filter_var($_GET['quote_id'], FILTER_VALIDATE_INT);
        else if (isset($_SESSION['quote_id']))
            $quoteId = filter_var($_SESSION['quote_id'], FILTER_VALIDATE_INT);
        else
            throw new \RuntimeException("Quote has not been initialized");

        Assert::that($quoteId)->integer("Quote Id must be an integer")
                              ->greaterThan(0, "Quote Id must be a positive integer");

        return $quoteId;
    }
}

I invoke this via container:

return [
    'dependencies' => [
        'factories' => [
            'quoteId' => QuoteIdFactory::class
        ],
    ]
];

And when I need this id, I pull it out of the container, and I may inject it into a repository like so:

//in a Factory:
$quoteId = $container->get('quoteId');
$repository = new Repository($quoteId, ... );

//inside middleware/service:
$this->repository->retrieveQuote(); //uses `$this->quoteId` inside

Is there a way to redesign this so that it follows Expressive principles?
I assume the main one is to ensure that I pull GET and SESSION data from Request object, when I have access to the Request object, and not to use superglobal GET and SESSION variables, like I am doing now.


#2

The session is not a service, so it cannot be used at service instantiation
time.

You Basically need to convert $this->repository->retrieveQuote() to
$this->repository->retrieveQuote($quoteId) :slight_smile:

Marco Pivetta

http://twitter.com/Ocramius

http://ocramius.github.com/


#3

If you have an object / data that you need in multiple middleware, you can also pass data with the request. Basically you grab the data once, add it to the request and access it in all following middleware when needed: https://docs.zendframework.com/zend-expressive/cookbook/passing-data-between-middleware/