Controller Constructors in Laravel - php

I'm a novice developer working on an existing Laravel app. I'm looking through the project and I don't see any controllers constructed anywhere but the controllers all have constructor functions.
Is this a Laravel thing or am I just missing something? Is controller instantiation handled in the routes or something? If so, is it bad practice to call a controller constructor manually?(although I can't think of a case offhand where you would want to do this)

From the docs: "The Laravel service container is used to resolve all Laravel controllers."
And: I often create a base controller in my apps, therefore I also have constructor in my extended controllers. It could also be useful to inject dependencies "properly" like shown in this example:
https://laravel.com/docs/5.2/controllers#dependency-injection-and-controllers

Related

Laravel load and initialize custom helper class

There are many questions regarding loading custom helper classes in Laravel. However, none of them focus on the loading them with proper initialization.
As of Laravel version 5.3 we can use psr-4 autoloading which is autoloading the entire app/ directory. However, classes are loading but never initialized.
I have my helper class inside the app/helpers/support.php. This class has a constructor, where I want to load some important configuration in order to make the helper usable.
So how can I load my helper but ALSO initialize it properly in Laravel? Right now I am simply working-around the problem by using new \App\Helper\Support(); inside AppServiceProvider.php.
Edit: I'm using the following approach to maintain my helper class:
Best practices for custom helpers on Laravel 5
It seems like what you have is a service. Rather than creating an instance, you can declare it in your app service provider and inject it as a dependency when you need it.
In your register method:
$this->app->bind(\App\Helper\Support::class);
You can now use dependency injection to get an instance of your class. You can also make an instance like this:
app()->make(\App\Helper\Support::class);
If you only want one instance to exist at any given time, use singleton rather than bind.
I recommend reading the service container documentation:
https://laravel.com/docs/5.5/container

Migration of zf2 to zf3

I have some issues regarding the zf2 to zf3 migration of my application. I've gone through the migration guides and started the migration process as describe there.
According to the migration guide, there is no serviceLocator available into controllers anymore. And I used to use it within each and every controller action to inject the config variable array (located in module.config.php), Doctrine MongoDB DocumentManager and the Doctrine EntityManager into the Models where they are needed. Now I'm getting so many deprecated warnings messages as below;
PHP Deprecated: Usage of
Zend\ServiceManager\ServiceManager::getServiceLocator is deprecated
since v3.0.0; please use the container passed to the factory instead
in
/var/www/html/LeapX/vendor/zendframework/zend-servicemanager/src/ServiceManager.php
on line 169
Since I need to access config variables and inject the Doctrine DocumentManager and Doctrine EntityManager into my Models, I had to call $this->getServiceLocator() within my controllers. Let me know how to fix this issue. Is there any possibility to directly inject these dependencies into my Model classes? Should I need to use factories for Models?
And the other question is when it comes to factories regarding the controllers, Should I need to create individual factory for each and every controller of my application? There are quite a lot of number of controllers spread within few Modules in the application. If I add dedicated factory for each and every controller there will be double the number. Let me know the best way to do this.
From what i have read myself, it is no longer possible to call getServiceLocator() from controllers, they removed it since it promotes antipattern.
Here's a blog post from Matthew himself on this issue:
https://mwop.net/blog/2016-04-26-on-locators.html
Another explanation from the guy himself here (shorter, might be easier to digest):
https://github.com/zendframework/zend-mvc/issues/89
I am currently also in the process of trying to migrate a ZF2 (2.4.10) project to ZF3 and face the same issue.
I personally agree with the recommendation to explicitly define dependencies of your classes and controllers instead of (over)using getServiceLocator() method, though depending on your code, refactoring lots of code for this purpose might seem to be a PITA.
Alternatively, this can probably help (though, i can't say for sure since i haven't tried this myself):
http://circlical.com/blog/2016/3/9/preparing-for-zend-f

Symfony 2.8 Autowiring for method injection

Update 2020 (this should have been much sooner). There can be a new class for each controller action. Each controller can be named by the action it is going to do, with an Invoke() method. Consider "Action Domain Responder" (ADR).
Why do I want to do this? Controllers do not necessarily adhere to SRP, and I'm not about to go creating a new class for each of, what is effectively, a controller 'action'. Therefore, a controller should not have everything injected via constructor, but the relevant method being called should be able to state explicitly "I require these objects" and another method "these other objects".
As of Symfony 2.8, the Dependency Injection component now provides auto-wiring for your services if you provide autowire: true in services.yml.
I'm defining my controller as a service, like so:
test_controller:
class: AppBundle\Controller\TestController
autowire: true
My controller looks as follows:
class TestController
{
public function indexAction(TestClass1 $tc1, $id)
{
return new Response('The slug is: ' . $id);
}
}
You will notice I'm typehinting for TestClass, which is just an empty class. However, the following error appears when I refresh the page:
What do I need to change in my services.yml file to have auto wiring dependency injection in my controller method? Just a note, the issue isn't because I have an $id 'slug' afterwards. Removing it does nothing.
Edit: I've created a bundle allowing to do more or less what you want called DunglasActionBundle.
Disclaimer: I'm the author of the Symfony autowiring system.
The Symfony Dependency Injection Component (and the autowiring system is part of it) doesn't work that way. It only allows to automatically inject dependencies in the class constructor of services and knows nothing about controller classes, actions and other parts of the HttpKernel Component.
It's not currently possible to do what you want to do. Initially, the autowiring system has been designed for domain services, not for controllers.
It should be possible to bridge the autowiring system with controller parameters using a custom param converter. However, I'll suggest you take another way I've first described here:
There is another approach I want to discuss since several time but I
did not have the time to blog about it.
It's a derivate of/similar to the ADR pattern, applied to Symfony.
An action is a single class with a __invoke() method containing all it's
logic (should be only some lines of glue code to call the domain and
the responder).
Actions are registered as a service (it can be
automated using a compiler pass and some conventions, similar to how
we discover controllers right now, with the ability to override the
definition if needed)
On such action services, autowiring is enabled.
It means that almost as easy as current controllers to use for the
developper, but with an explicit dependency graph and a better
reusability. Only things that are really necessary are injected. In
fact, it's already doable to use such system with Symfony. We do it in
API Platform master. Here is one action for instance:
https://github.com/dunglas/DunglasApiBundle/blob/master/Action/PostCollectionAction.php
In this implementation I also rely a lof on kernel events to be able
to plug and play some sort of logic (persistence, validation...) in a
decoupled manner but it's out of scope.

Laravel 4 Auth Scope Resolution

I like to spend time studying how frameworks are coded in attempt to better my code.
As far as I know the Scope Resolution Operator in PHP calls a function in a class as a static function, meaning that you do not have access to '$this' because the class hasn't been instantiated.
But when I started reading up how the Laravel 4 Auth works I noticed that the documentation tells you to use Auth::check() or Auth::user() to retrieve information, yet the code in those functions is using many '$this' statements.
Can anyone explain to me how they are able to retrieve the instance properties if the functions are being called as static methods?
Here is the link to the github raw file for Laravel Auth
https://raw.githubusercontent.com/illuminate/auth/master/Guard.php
Laravel uses a development pattern known as Facades and Inversion of Control (IoC) in order to take static calls to some objects (the 'Facade') and retrieve an actual instance of an object (from the IoC container) to call the method on.
Put another way, when you do Auth::check() and Auth::user() those seemingly static calls get replaced with actual object instances from within the IoC container, so Auth::check() becomes $auth->check() with $auth being derived from within the container.
This allows you to 1) Write Auth::check() instead of $auth = new Auth; $auth->check() and 2) let the IoC container do all of the dirty work around actually creating the instance you want, so if implementation details change later you don't have to rewrite any code that uses the Auth class.
See this documentation page about facades and this question about IoC in general for more information.
As a side note, the Auth facade referred to in Auth::check() et al is actually this class: https://github.com/laravel/framework/blob/master/src/Illuminate/Support/Facades/Auth.php
It's a Fcade.
When you're calling Auth::{anything}, you're actually calling Illuminate\Support\Facades\Auth. The static method is only used as an entry point.
This is a simplified version of the Facade design pattern:
http://sourcemaking.com/design_patterns/facade/php

DI Container and controllers

I just followed the http://fabien.potencier.org/article/50/create-your-own-framework-on-top-of-the-symfony2-components-part-1 articles, and have some questions about the DI container.
Let's say I want to fire an event inside my controller, how would i get the dispatcher inside my controller?
I'm starting my test framework through
$c->get('app')->handle($request);
where 'app' is the Symfony\HttpKernel. How can i set the dependencies to the container? Let's say I have a view engine, defined in the container
$c->register('view.engine', 'Core\ViewEngine');
and I want to give that object, or resolve that object, inside my Controller to render some views. It's the same problem with the event fire, I don't have access to those values inside my controller ... How is a DI container supposed to work in situations like this?
Thanks!
There are different approaches. You might want to read through the silex documentation as a next step. In silex, the application itself is a DI container. You might also read through the introduction to Symfony 2 documentation.
The most straight forward approach (and the one used by S2 as a default) is to inject the DI container itself into your controller. The controller can then pull out services such as the dispatcher as needed.
A "better" approach is to inject the dispatcher along with whatever else the controller needs directly into the controller. It's "better" because the controller itself does not need access to the container. But it's more difficult since a controller often needs a number of services just to it's job.
==============================================
How would I inject the container in the controller though?
That is where looking at existing frameworks starts to come in handy. Remember that HTTPKernel is a component and not a framework. How you use it is up to you.
In Symfony 2 the app object is actually derived from Kernel and not HTTPKernel. The Kernel in turn contains an instance of HTTPKernel as well as an instance of the container.
There are several approaches you might take. There is no single "correct" one.
If you look into HTTPKernel::handleRaw you will find:
$controller = $this->resolver->getController($request))
You might make your own controller resolver object which would inject the container after creating the controller. Just one possibility.

Categories