Laravel change binding with Request params - php

I am using Laravel Framework 5.8.21. I am trying to bind an interface to a class based on Request params inside AppServiceProvider's register method like this.
public function register()
{
$this->app->bind('App\Contracts\SomeInterface', 'App\Logic\ClassName');
if(Request::has('notification_type') && Request::get('notification_type') == 'email') {
$this->app->bind('App\Contracts\SomeInterface', 'App\Logic\SomeotherClassName');
}
}
and later injecting the interface to Controllers __construct() method.
with tests, it always bound to ClassName. I tried to get accessed URL inside AppServiceProvider and while running unit tests, it always returning / with $this->app->request->getRequestUri(); and method as GET even though from the test I am posting to URL like this.
$this->post('/notification', [
'notification_type' => 'email',
])->assertJson(
'message-push-status' => true,
]);
While testing it with Postman, when I try to post http://localhost:8000/notification, it says, 419 | Page Expired.

This is possible. The register method of AppServiceProvider or any other service provider has access to request parameters.
This can be accessed as follows:
public function register()
{
$this->app->bind('SomeInterface', function($app) {
return new SomeClass(request()->some_parameter);
});
}

You will not be able to reliably use the current request information from within a service provider.
First, it is a general best practice to not depend on application logic within the register() method directly. You may cause a race condition where you have a dependency that hasn't been registered yet, or cause unnecessary overhead (e.g. establish a database connection even if you don't need any querying).
Second, Laravel's request lifecycle won't funnel the current request into the application until after all of the service provider registration and bootstrapping has been completed.
Without knowing exactly what business logic you're trying to accomplish, you have at least a couple options:
Use contextual binding to serve different implementations of the same interface depending on the requesting object (e.g. controller).
Use a factory or a similar facilitator-style object that you can inject in the controller, and can provide the proper dependency based on your preferred logic.

Related

Global service objects in Laravel

I have a service file that processes data and stores it in private fields. Many queries are executed, hence I'd like to instantiate the service file only once and access data from the fields when necessary in later stages of the request's lifecycle.
I've tried registering the service file in Laravel's service provider using the following snippet, but it did not seem to work as I expected.
$this->app->singleton('App\Services\UserService', function ($app) {
return new App\Services\UserService();
});
In my case, the first time I called the service methods was in my middleware class. The particular methods I called should've set many private fields in place which I could later use. Service file was loaded in using the "injection" method.
public function __construct(UserService $user_service) {
$this->user_service = $user_service;
}
However, once the request finally proceeded to the controller method, the fields in the service object had been nulled and I had to call the "heavy" methods once again.
Within the the controller constructor method, I resolved the service file using the resolve() helper method, however I don't think that would've made a difference.
Is there something I've missed or misunderstood?
Any help or pointers are appriciated!

difference between using the session via an HTTP request instance versus using the global session helper in Laravel

I could not find any information related two types of session access methods.
$request->session() from HTTP request instance and session() from session helper in Laravel 5.3 .
Is there any difference or which one to use when ?
How to send a get request to below controller method when using P.H.P unit
public function testMyMethod(Request $request){
$userExist = $request->session()->exists('user_id');
}
The Service Container is the core of the Laravel architecture. All services, components, and dependencies are registered there, and you can ask for any of them whenever you need it.
But Laravel provides more than one way to "ask for it". You have the global helper functions, you have facades, you have the "proper", "purer" injection of the component instance in the method signature. I think a big part of the philosophy of Laravel is a clean, easy, intuitive API. And in this case, it can be left up to your personal preference to define what "clean and easy" is, and whatever matches your style will be, by definition, intuitive to you.
I know there have been heated debates in the PHP community as to which method is "best", facades have been controversial, traditional dependency injection OOP purists say the only right way may be injecting the object with the controller method signature...
In the end, any of this various methods just grab the object from the same service container bag. It makes no difference performance-wise (I bet two function calls of indirection won't hit your performance) or otherwise. So, use whatever suits your style better. I personally do the "proper" injection anyway if I'm in a typical controller, but maybe I use the global helper if it makes for a cleaner, more readable code in its context.
<?php
namespace App\Http\Controllers;
use Illuminate\Contracts\Session\Session;
use Facades\Illuminate\Contracts\Session\Session as SessionRealTimeFacade;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Input;
use Illuminate\Support\Facades\Request as RequestFacade;
use Illuminate\Support\Facades\Session as SessionFacade;
use PHPUnit\Framework\Assert;
class TestController extends Controller
{
public function sessionTest(Request $request, Session $session)
{
$options = [
$request->session()->get('_token'),
session()->get('_token'),
session('_token'),
$session->get('_token'),
SessionFacade::get('_token'),
SessionRealTimeFacade::get('_token'),
app('session')->get('_token'),
];
array_reduce(
$options,
function ($one, $other) {
Assert::assertEquals($one, $other);
return $other;
},
array_shift($options)
);
return 'All tests passed!';
}
}
And by the way, as you can see you have more than just 2 options :)
Of course, this doesn't apply to sessions only, the same goes for getting the request data, getting DB connection, and so much more.
(Also, I think you don't have the real-time facades in 5.3 yet)
Laravel Documentation
According to the Laravel Docs - https://laravel.com/docs/5.3/session - there is 'little practical difference between using the session via an HTTP request instance versus using the global session helper' and that both are testable.
->assertSessionHas($key, $value = null);
Laravel Up & Running - Matt Stauffer
In his book (page 320-), Matt talks through the different ways available to access session information:
Using the Session facade
session()->get('user_id');
Using the session() method on any given Illuminate Request object
Route::get('dashboard', function(Request $request) {
$request->session()->get('user_id');
});
Inject an instance of Illuminate\Session\Store
Route::get('dashboard', function(Illuminate\Session\Store $session) {
return $session->get('user_id');
});
Using the global session() helper
$value = session()->get('key);
$value = session('key);
His final comment is: If you're new to Laravel and not sure which to use, I'd recommend using the global helper.
So, I wouldn't worry about the differences and just go with what 'feels right' for you. If you have no preference either way, go with Matt Stauffer's recommendation.
Ultimately, the way that you normally use the two options, there is no practical difference between $request->session() and session(). However, they do not technically return the same objects.
session() will return the SessionManager. $request->session() will return the session Store.
The Store is actually the object that contains the session data.
The SessionManager is the object that manages the available session Stores. Most applications will only ever use one session store (the default one).
The reason why they seem to act as the same object is that when you call a method on the SessionManager that doesn't exist, it forwards that method call to the default session Store.
So, in your example, you use the exists() method. This method does not exist on the SessionManager. So, if you were to call session()->exists('user_id'), it would forward that call down to the default session Store, and it would be equivalent to calling $request->session()->exists('user_id').
One could argue that directly using $request->session() is more appropriate when attempting to access the session data, since that is what is returned, and doesn't require the additional forwarding function call. However, it is up to you to weigh the pros and cons to determine which method to use.
$request->session() and session() both are same thing.
There are two primary ways of working with session data in Laravel: the global function in session() helper and via a $request instance.
you can use it like this
public function testMyMethod(Request $request){
//$userExist = $request->session()->exists('user_id');
$userExist = $request->session()->has('user_id');
}
Actually native PHP session is a ready solution versus a better implementation from Laravel and Symfony that had put more thought into it. Read the explanation at Symfony Session configuration manual entry and you will understand i think. So there's a difference, but in the way it is thought and implemented. From the point of view from performance i think there isn't much. I will let you take your conclusions. Another entry that helps is at PHP manual

Zend Expressive - Best way to inject middleware

I am currently learning zend expressive and I can see a couple of options as to how to access middleware within routes/actions.
In the expressive skeleton application, there is a HomePageFactory where the container is injected, then the router, template engine etc are pulled from the container and a new HomePageAction class is constructed using these and returned.
For example:
class HomePageFactory
{
public function __invoke(ContainerInterface $container)
{
$router = $container->get(RouterInterface::class);
$template = ($container->has(TemplateRendererInterface::class))
? $container->get(TemplateRendererInterface::class)
: null;
return new HomePageAction($router, $template);
}
I then needed a flash messenger and came across the following:
class SlimFlashMiddlewareFactory
{
public function __invoke($container)
{
return function ($request, $response, $next) {
// Start the session whenever we use this!
session_start();
return $next(
$request->withAttribute('flash', new Messages()),
$response
);
};
}
}
So this is slightly different and is adding the middleware to the request via an attribute. Which can then be retrieved where its needed by something like:
$flashMessenger = $request->getAttribute('flash');
So really my question is what are the advantages/disadvantages of these two methods of getting the FlashMessenger into the action?
If I have a UserService which deals with retrieving users from a database and therefore may be needed in multiple actions/routes, would I be better off modifying the HomePageAction & Factory (and any others that need it) to accept the UserService?
i.e.
class HomePageFactory
{
public function __invoke(ContainerInterface $container)
{
$router = $container->get(RouterInterface::class);
$template = ($container->has(TemplateRendererInterface::class))
? $container->get(TemplateRendererInterface::class)
: null;
$userService = $container->get(App\UserService::class);
return new HomePageAction($router, $template, $userService);
}
Or would I be better to go with how the FlashMessenger works (which seems a bit easier to manage) and add it to the request via an attribute to access it that way where needed?
i.e.
$userService = $request->getAttribute('UserService');
I'm wondering if there are any performance issues with the latter option, although I do understand that it could be done in this way only for specific routes rather than the UserServer being application wide.
My gut feeling (after writing out this question) is that really, this is a Service and not true middleware so I should really be Modifying the HomePageAction & Factory and adding the UserService that way rather than doing what seems easier and using the attribute ala FlashMessenger. But it would be very handy if a guru could help clarify this.
Many thanks in advance.
I don't know about performance differences since I haven't tested this. But I guess the question you need to ask yourself is what does the class do? Is it needed by other middleware before or after invoking the Action class or do you need it only in the Action class or somewhere else in the application.
In your case the UserService might take care of registering or updating an user. That stuff would be injected with the ActionFactory as your gut feeling tells you. However for authentication it would be different if that is done with middleware. First you need that class in your authentication middleware which you can pass on to your authorisation middleware with the request (or maybe only pass the authenticated user object, that's what I do).
So I guess the rule of thumb is that if something is needed to change the incoming Request or outgoing Response before / after the Action, you pass it on with the Request, otherwise inject it in the Action with a Factory.
If you start passing everything around with a Request, it will be very hard to keep track of. I find it much easier to inject as much as possible into the Action itself because then I can easily see what is needed inside that class and I can test it easier.
Things like a flash messenger, session and the authenticated user I would inject in the request.

Using dependency injection over laravel facades

I have read a number of sources that hint that laravel facade's ultimately exist for convenience and that these classes should instead be injected to allow loose coupling. Even Taylor Otwell has a post explaining how to do this. It seems I am not the only one to wonder this.
use Redirect;
class Example class
{
public function example()
{
return Redirect::route("route.name");
}
}
would become
use Illuminate\Routing\Redirector as Redirect;
class Example class
{
protected $redirect;
public function __constructor(Redirect $redirect)
{
$this->redirect = $redirect
}
public function example()
{
return $this->redirect->route("route.name");
}
}
This is fine except that I am starting to find that some constructors and methods are beginning to take four+ parameters.
Since the Laravel IoC seems to only inject into class constructors and certain methods (controllers), even when I have fairly lean functions and classes, I am finding that constructors of the classes are becoming packed out with the needed classes that then get injected into the needed methods.
Now I am finding that if I continue down this approach that I will need my own IoC container, which feels like reinventing the wheel if I am using a framework like laravel?
For example I use services to control the business / view logic rather than controllers dealing with them - they simply route the views. So a controller will first take its corresponding service, then then the parameter in its url. One service function also needs to check the values from a form, so then I need Request and Validator. Just like that, I have four parameters.
// MyServiceInterface is binded using the laravel container
use Interfaces\MyServiceInterface;
use Illuminate\Http\Request;
use Illuminate\Validation\Factory as Validator;
...
public function exampleController(MyServiceInterface $my_service, Request $request, Validator $validator, $user_id)
{
// Call some method in the service to do complex validation
$validation = $my_service->doValidation($request, $validator);
// Also return the view information
$viewinfo = $my_service->getViewInfo($user_id);
if ($validation === 'ok') {
return view("some_view", ['view_info'=>$viewinfo]);
} else {
return view("another_view", ['view_info'=>$viewinfo]);
}
}
This is a single example. In reality, many of my constructors already have multiple classes being injected (Models, Services, Parameters, Facades). I have started to 'offload' the constructor injection (when applicable) to method injection, and have the classes calling those methods use their constructors to inject dependencies instead.
I have been told that more than four parameters for a method or class constructor as a rule of thumb is bad practice / code smell. However I cannot see how you can really avoid this if you choose the path of injecting laravel facades.
Have I got this idea wrong? Are my classes / functions not lean enough? Am I missing the point of laravels container or do I really need to think of creating my own IoC container? Some others answers seems to hint at the laravel container being able to eliminate my issue?
That said, there doesn't seem to be a definitive consensus on the issue...
This is one of the benefits of constructor injection - it becomes obvious when you class is doing to much, because the constructor parameters grow too large.
1st thing to do is split up controllers that have too many responsibilities.
Say you have a page controller:
Class PageController
{
public function __construct(
Request $request,
ClientRepositoryInterface $clientrepo,
StaffRepositortInterface $staffRepo
)
{
$this->clientRepository = $clientRepo;
//etc etc
}
public function aboutAction()
{
$teamMembers = $this->staffRepository->getAll();
//render view
}
public function allClientsAction()
{
$clients = $this->clientRepository->getAll();
//render view
}
public function addClientAction(Request $request, Validator $validator)
{
$this->clientRepository->createFromArray($request->all() $validator);
//do stuff
}
}
This is a prime candidate for splitting into two controllers, ClientController and AboutController.
Once you have done that, if you still have too many* dependencies, its time to look for what i will call indirect dependancies (because i cant think of the proper name for them!) - dependencies that are not directly used by the dependant class, but instead passed on to another dependency.
An example of this is addClientAction - it requires a request and a validator, just to pass them to the clientRepostory.
We can re factor by creating a new class specifically for creating clients from requests, thus reducing our dependencies, and simplifying both the controller and the repository:
//think of a better name!
Class ClientCreator
{
public function __construct(Request $request, validator $validator){}
public function getClient(){}
public function isValid(){}
public function getErrors(){}
}
Our method now becomes:
public function addClientAction(ClientCreator $creator)
{
if($creator->isValid()){
$this->clientRepository->add($creator->getClient());
}else{
//handle errors
}
}
There is no hard and fast rule as to what number of dependencies are too many.
The good news is if you have built your app using loose-coupling, re-factoring is relatively simple.
I would much much rather see a constructor with 6 or 7 dependencies than a parameterless one and a bunch of static calls hidden throughout the methods
One issue with facades is that additional code has to be written to support them when doing automated unit testing.
As for solutions:
1. Resolving dependencies manually
One way of resolving dependencies, if you do not wish to do it via. constructors or methods injection, is to call app() directly:
/* #var $email_services App\Contracts\EmailServicesContract
$email_services = app('App\Contracts\EmailServicesContract');
2. Refactoring
Sometimes when I find myself passing too many services, or dependencies into a class, maybe I have violated the Single Responsibility Principe. In those cases, maybe a re-design is needed, by breaking the service or dependency into smaller classes. I would use another service to wrap up a related group of classes to serve something as a facade. In essence, it'll be a hierarchy of services/logic classes.
Example: I have a service that generate recommended products and send it out to users via email. I call the service WeeklyRecommendationServices, and it takes in 2 other services as dependency - a Recommendation services which is a black-box for generating the recommendations (and it has its own dependencies -- perhaps a repo for products, a helper or two), and an EmailService which maybe has Mailchimp as a dependency). Some lower-level dependencies, such as redirects, validators, etc. will be in those child services instead of the service that acts as the entry point.
3. Use Laravel global functions
Some of the Facades are available as function calls in Laravel 5. For instance, you can use redirect()->back() instead of Redirect::back(), as well as view('some_blade) instead of View::make('some_blade'). I believe it's the same for dispatch and some other commonly used facades.
(Edited to Add) 4. Using traits
As I was working on queued jobs today, I also observe that another way to inject dependencies is by using traits. For instance, the DispathcesJobs trait in Laravel has the following lines:
protected function dispatch($job)
{
return app('Illuminate\Contracts\Bus\Dispatcher')->dispatch($job);
}
Any class that uses the traits will have access to the protected method, and access to the dependency. It's neater than having many dependencies in the constructor or method signatures, is clearer (about what dependencies are involved) than globals and easier to customize than manual DI container calls. The drawback is that each time you invoke the function you have to retrieve the dependency from the DI container,
Class methods that form a part of the routing mechanism in Laravel (middleware, controllers, etc.) also have their type-hints used to inject dependencies - they don't all need to be injected in the constructor. This may help to keep your constructor slim, even though I'm not familiar with any four parameter limit rule of thumb; PSR-2 allows for the method definition to be stretched over multiple lines presumably because it's not uncommon to require more than four parameters.
In your example you could inject the Request and Validator services in the constructor as a compromise, since they're often used by more than one method.
As for establishing a consensus - Laravel would have to be more opinionated for applications to be similar enough to utilise a one-size-fits-all approach. An easier call though is that I think facades will go the way of the dodo in a future version.
Not so much an answer but some food for thought after talking to my colleagues who have made some very valid points;
If the internal structure of laravel is changed between versions (which has happened in the past apparently), injecting the resolved facade class paths would break everything on an upgrade - while using the default facades and helper methods mostly (if not completely) avoids this issue.
Although decoupling code is generally a good thing, the overhead of injecting these resolved facade class paths makes classes cluttered - For developers taking over the project, more time is spent trying to follow the code which could be spent better on fixing bugs or testing. New developers have to remember which injected classes are a developers and which are laravels. Developers unfamiliar with laravel under the hood have to spend time looking up the API. Ultimately the likelihood of introducing bugs or missing key functionality increases.
Development is slowed and testability isn't really improved since facades are already testable. Rapid development is a strong-point of using laravel in the first place. Time is always a constraint.
Most of the other projects use laravel facades. Most people with experience using laravel use facades. Creating a project that doesn't follow the existing trends of previous projects slows things down in general. Future inexperienced (or lazy!) developers may ignore facade injection and the project may end up with a mixed format. (Even code reviewers are human)
Well your thoughts and concerns and correct and I had them as well.
There are some benefits of Facades ( I generally dont use them ), but if you do use just I would suggest using them only in the controllers, as the controllers are just entry and exit points for me at least.
For the example you gave I'll show how I generally handle it:
// MyServiceInterface is binded using the laravel container
use Interfaces\MyServiceInterface;
use Illuminate\Http\Request;
use Illuminate\Validation\Factory as Validator;
...
class ExampleController {
protected $request;
public function __constructor(Request $request) {
// Do this if all/most your methods need the Request
$this->request = $request;
}
public function exampleController(MyServiceInterface $my_service, Validator $validator, $user_id)
{
// I do my validation inside the service I use,
// the controller for me is just a funnel for sending the data
// and returning response
//now I call the service, that handle the "business"
//he makes validation and fails if data is not valid
//or continues to return the result
try {
$viewinfo = $my_service->getViewInfo($user_id);
return view("some_view", ['view_info'=>$viewinfo]);
} catch (ValidationException $ex) {
return view("another_view", ['view_info'=>$viewinfo]);
}
}
}
class MyService implements MyServiceInterface {
protected $validator;
public function __constructor(Validator $validator) {
$this->validator = $validator;
}
public function getViewInfo($user_id, $data)
{
$this->validator->validate($data, $rules);
if ($this->validator->fails()) {
//this is not the exact syntax, but the idea is to throw an exception
//with the errors inside
throw new ValidationException($this->validator);
}
echo "doing stuff here with $data";
return "magic";
}
}
Just remember to break your code to small individual pieces that each one handles his own responsibility.
When you properly break your code, in most cases you will not have so many constructor parameters, and code will be easily testable and mocked.
Just one last note, if you are building a small application or even a page in a huge application for example a "contact page" and "contact page submit", you can surely do everything in the controller with facades, it simply depends on the complexity of the project.
I love the laravel due to its beautiful architecture.Now as from my approach i wouldnt inject all the facades in to the controller method only why? Injecting Redirect facades only in controller wrong practices as it might need in other. And mainly the things that are mostly used should be declared for all while for those who uses some or only then its best practice to inject them via method as when you declare at top it will hamper in your memory optimization as well as the speed of your code. Hope this would help

Symfony - Changing how controllers are instantiated and executed

Note: as of version 2.8, Symfony provided autowire: true for service configuration, and as of version 3.3, Symfony provided alias (instead of autowire_types) to alias a concrete object to an interface for automatic dependency injection into 'controllers as services'. There's also a bundle to allow autowiring for controller 'action' methods, although I've moved away from this and have focussed more on a variation of the ADR pattern (which is, basically, a single 'action' class with an interface method and not shoving a load of actions methods within a single class which eventually makes for an architectural nightmare). This is, effectively, what I've been looking for all these years and now no longer need to 'hook-in' a decent recursive dependency injector (auryn) as the framework now handles what it should have four years previous. I'll leave this answer here in case anyone wants to trace the steps that I did to see how the kernel works and some of the options available at this level.
Note: Although this question primarily targets Symfony 3, it should also be relevant to users of Symfony 2 as the kernel logic doesn't seem to have changed much.
I want to change how controllers are instantiated in Symfony. The logic for their instantiation currently resides in HttpKernel::handle and, more specifically, HttpKernel::handleRaw. I want to replace call_user_func_array($controller, $arguments) with my own injector performing that specific line instead.
The options I have tried thus far:
Extending HttpKernel::handle with my own method and then having this called by symfony
http_kernel:
class: AppBundle\HttpKernel
arguments: ['#event_dispatcher', '#controller_resolver', '#request_stack']
The downside of this is that, because handleRaw is private, I can't extend it without hacky reflection and so I would have to copy and paste a tonne of code.
Creating and registering a new controller resolver
controller_resolver:
class: AppBundle\ControllerResolver
arguments: []
This was a fundamental misunderstanding I had so I thought I'd document it here. The resolver's job is to resolve where to find the controller as a callable. It hasn't actually been called yet. I am more than happy with how Symfony takes the routes from routes.yml and figures out the class and method to call for the controller as a callable.
Adding an event listener on kernel.request
kernel.request:
class: MyCustomRequestListener
tags:
- { name: kernel.event_listener, event: kernel.request, method: onKernelRequest, priority: 33 /** Important, we'll get to why in a minute **/ }
Taking a look at the Http Kernel Component Documentation, we can see that it has the following typical purpose:
To add more information to the Request, initialise parts of the system, or return a Response if possible (e.g. a security layer that denies access).
I figured that by creating a new listener, using my custom injector to create my controller, and then return a response in that listener, would bypass the rest of the code that instantiates the controller. This is what I want! But there's a major flaw with this:
The Symfony Profiler doesn't show up or any of that stuff, it's just my response and that's it. Dead. I found that I can switch the priority from 31 to 33 and have it switch between my code and Symfonys, and I believe this is because of the router listener priority. I feel I'm going down the wrong path here.
Listening on the kernel.controller event.
No, this allows me to change the callable that will be called by call_user_func_array(), not how the controller is actually instantiated, which is my goal.
I've documented my ideas but I'm out. How can I achieve the following?
Change how the controllers are instantiated and then executed, specifically call_user_func_array() which is in a bloody private method (thanks Symfony)
Fall back to the default controller instantiation if mine doesn't work
Allow everything else to work as expected, like the profiler loading
Be able to bundle this up with an extension for other users
Why do I want to do this?
Controllers can have many different methods for different circumstances and each method should be able to typehint for what it individually requires rather than having a constructor take all the things, some of which may not even be used depending on the controller method being executed. Controllers don't really adhere to the Single Responsibility Principle, and they're an 'object edge case'. But they are what they are.
I want to replace how controllers are created with my own recursively autowiring injector, and also how they are executed, again with recursive introspection via my injector, as the default Symfony package does not seem to have this functionality. Even with the latest "autowire" service option in Symfony 2.8+.
The controller resolver actually does two things. The first is to get the controller. The second is to get a list of arguments for a given action.
$arguments = $this->resolver->getArguments($request, $controller);
$response = call_user_func_array($controller, $arguments);
It is the getArguments method that you could override to implement your special "action method injection" functionality. You just need to determine what arguments the action method needs and return an array of them.
Based on a different question, I also think you might be misunderstanding the autowire functionality. Autowire really only applies to constructor injection. It's not going to help with action method injection.
If the getArguments does not solve your requirement then overriding the handle method is really your only option. Yes there is quite a bit of code to copy/paste from handleRaw but that is because there is quite a bit to do in there. And even if handleRaw was protected you would still have to copy/paste the code just to get at the one line you want to replace.
Why don't you return your own callable from custom ControllerResolverInterface that would instantiate Controller in a way you want and call it?
It would be basically a decorator.
You can extend Symfony\Component\HttpKernel\Controller\ControllerResolver with your own implementation of instantiateController() method, or you can implement ControllerResolverInterface from the scratch.
UPD:
When Symfony makes a call_user_func_array($controller, $arguments); call in handleRaw(), the $controller variable is what you've returned from your custom ControllerResolver. That means you can return any callable from your resolver (it can be [$this, "callController"] f.e.) and inside this callable you would create a new Controller with Auryn and call it.
UPD2:
If you're still struggling with this, I'll add an example because you might miss what I meant here.
use Symfony\Bundle\FrameworkBundle\Controller\ControllerResolver;
class AutowiringControllerResolver extends ControllerResolver
{
// ... constructor stuff; assume $injector is a part of this class
protected function createController($controller)
{
$controller = parent::createController($controller);
return function (...$arguments) use ($controller) {
// you can do with resolved $arguments whatever you want
// or you can override getArguments() method and return
// empty array to discard getArguments() functionality completely
return $this->injector->execute($controller);
};
}
protected function instantiateController($classname)
{
return $this->injector->make($classname);
}
}
Listener is too late to solve your needs, since it excludes Dependency Injection container, which is crucial to create a valid object (~= service).
You are probably looking for Controller Autowiring feature.
If that's so, you might find solution or at least inspiration in this bundle: http://www.tomasvotruba.cz/blog/2016/03/10/autowired-controllers-as-services-for-lazy-people/
It meets your needs in these points:
autowire injector
fallback to default (FrameworkBundle's) controller resolver, if not found
it also should keep all the flow working, since there are no hacks during controller resolving process

Categories