Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Symfony2 DI service container priority/flow

The story

Let's take this request https://nike.awesomedomainname.com/ as an example. I need a service that can fetch the nike.


What I currently have

I've a service that fetches a router key from the @request_stack. Let's use company as router key.

My main route as followed;

<import host="{company}.domain.{_tld}" prefix="/" schemes="https" resource="@ExampleBundle/Resources/config/routing.xml">
    <default key="_tld">%app_tld%</default>
    <requirement key="_tld">com|dev</requirement>
    <requirement key="company">[a-z0-9]+</requirement>
</import>

So I wrote this service to fetch the key and search for the company;

...

use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;

class CompanyContextRequest {

    protected $request;
    protected $companyRepository;

    public function __construct(RequestStack $requestStack, CompanyRepository $companyRepository) {
        $this->request = $requestStack->getMasterRequest();
        $this->companyRepository = $companyRepository;
    }

    public function getCompany()
    {
        if (null !== $this->request)
        {
            $company = $this->request->attributes->get('company');
            if (null !== $company)
            {
                return $this->companyRepository->findOneBy([
                    'key' => $company
                ]);
            }
        }
    }

    ....

}

And the service xml definition;

<service id="app.company_context_request" class="AppBundle\Request\CompanyContextRequest">
    <argument type="service" id="request_stack"/>
    <argument type="service" id="orm_entity.company_repository"/>
</service>
<service id="app.current_company" class="AppBundle\Entity\Company">
    <factory service="app.company_context_request" method="getCompany"/>
</service>

Now my problem is that in some cases the service app.current_company doesn't return a Company object instead a null is given.

For example I have a custom UserProvider that has the CompanyContextRequest as a dependency to check if the user belongs to the company but I can't do so because it returns null.

But it works perfectly in controllers and most other DI places.

Any suggestions? Is there something like priority for services like for event subscribers?


What I've tried

I've set the scope app.company_context_request into request and tried to fetch it from the container. With false results. According to the Symfony docs this should work.


I run the latest stable version of Symfony v2.7.3


Edit: My goal is to have a service at all times that can tell me the current company that is based ofcourse on the subdomain.

Edit 2: This example is almost what I need except that in this example they use the users company and I need the company(key) from the subdomain.

like image 705
Katch Avatar asked Jul 27 '15 18:07

Katch


2 Answers

The problem that your service app.company_context_request is synthetic by nature - you cannot compile cause there is no request data at this moment.

How to Inject Instances into the Container

Why does it work in some places?

It works for cases when you call the services in the scope: after synthetic service Request was inserted into Service Container and scope request was activated. request scope setup and RequestStack filling happen into \Symfony\Component\HttpKernel\DependencyInjection\ContainerAwareHttpKernel::handle method and its parent one.

UserProvider doesn't work cause its initialization happens out of request scope: before Request service is activated or after the app left the scope.

How to prevent it?

  1. Save RequestStack service to the service property and get current request not in the constructor but in the getCompany method - at the moment of calling constructor Request could be undefined;

    public function __construct(RequestStack $requestStack, CompanyRepository $companyRepository) 
    {
        // You cannot use request at this point cause at initialization of the service there could be no request yet
        $this->requestStack = $requestStack;
        $this->companyRepository = $companyRepository;
    }
    ...
    public function getCompany()
    {
        $request = $this->requestStack->getMasterRequest();
        if (null !== $request)
    ...
    
  2. Add to service definition scope request and get it with via container inside another services: it will autogenerate exceptions when any services will try to get the one out of scope; There some constraints you should be aware: How to Work with Scopes This way is used in FOS bundles, for instance, FOSOauthServer.

If you will change code as in 1 then you can left service definition as it is

<service id="app.company_context_request" class="AppBundle\Request\CompanyContextRequest">
    <argument type="service" id="request_stack"/>
    <argument type="service" id="orm_entity.company_repository"/>
</service>

But current_company should have scope. request to throw exception if Request is not defined at the moment of initialization of the current_company. Or you can use scope=prototype (it will return new instance on every call) if you want to get current_company in the moments when it is set and get null in the moments when Request doens't exist (out of scope, cli calls).

<service id="app.current_company" class="AppBundle\Entity\Company" scope="request">
    <factory service="app.company_context_request" method="getCompany"/>
</service>

UPD It is not related to service priorities at all. It is related with container compilation mechanism. Compilation of the certain service happens on first call of this service: on explicit call or on compilation of dependent service.

With UserProvider routes compilation of your service (so calling of the __construct) happens at the moment when Request object is not yet in RequestStack and in the Container.

like image 126
origaminal Avatar answered Nov 16 '22 01:11

origaminal


I don't really understand you question but if you want to set priority in YML is done like this

app.locale_listener:
    class: Erik\AppBundle\Service\LocateListener
    arguments: ["%kernel.default_locale%"]
    tags:
        - { name: kernel.event_subscriber, priority: 17 }

--- Edit ---

As first fix your ssl setup "This Connection is Untrusted" under linux firefox there are online tools to check if ssl is configured correctly.

As second the answer to your question you can set up some service or event listener that will read subdomain name, in case of service just use $this->get("serviceName")->getSubdomain()

how to get subdomain PHP function to get the subdomain of a URL

like image 35
Erik Kubica NetLime Avatar answered Nov 16 '22 01:11

Erik Kubica NetLime