Due to the way I use certain classes I'm not able to do service injection, but I need to have access to the service container in them.
namespace DocumentsUploadSystem;
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\DependencyInjection\ContainerAwareTrait;
class DocumentsUpload implements ContainerAwareInterface
{
use ContainerAwareTrait;
public function getData()
{
var_dump($this->container);
}
}
If I call this method from controller like so.
use DocumentsUploadSystem\DocumentsUpload
...
$x = new DocumentsUpload($request)->getData();
and I get no errors but the container comes back as null. Is this a wrong way to implementing ContainerAwareInterface? I'm using Symfony 3.1
But in this example [https://symfony.com/doc/current/bundles/KnpMenuBundle/index.html] they use the same technique and access container fine, it works there, is the service called else where for that?
// src/AppBundle/Menu/Builder.php
namespace AppBundle\Menu;
use Knp\Menu\FactoryInterface;
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\DependencyInjection\ContainerAwareTrait;
class Builder implements ContainerAwareInterface
{
use ContainerAwareTrait;
public function mainMenu(FactoryInterface $factory, array $options)
{
$em = $this->container->get('doctrine')->getManager();
}
}
Implementing an Interface doesn't do anything. An Interface is a contract between the builder of the class and the user of the class guaranteeing the existence of certain methods - that is all. In this case, the ContainerAwareInterface guarantees that the setContainer() method is available. You then must USE it in order to set your container.
// MyController extends Symfony\Bundle\FrameworkBundle\Controller\Controller
use DocumentsUploadSystem\DocumentsUpload
...
$x = new DocumentsUpload($request);
$x->setContainer($this->container);
$xData = $x->getData();
For the record, I don't recommend injecting the entire container - only the actual services you require to accomplish your task.
Libraries like KnpMenuBundle appear to be using this but in reality, they are automating the creation of the menu and injects the container for you. see https://github.com/KnpLabs/KnpMenuBundle/blob/master/Provider/BuilderAliasProvider.php#L122-L124
best wishes!
Related
So I have the following class that's a facade:
namespace App\Helpers;
use App\Http\Requests\HomepageRequest;
class Params {
public function __construct(HomepageRequest $request) {
}
Then I have the ParamsServiceProvider class which instantiates the facade class on script startup:
public function register()
{
//
App::bind('params', function() {
return new Params();
});
}
edit: here is the actual facade for the Params class
use Illuminate\Support\Facades\Facade;
class Params extends Facade {
protected static function getFacadeAccessor() {
return 'params';
}
}
This all works fine, the class is instantiated properly, however, it doesn't seem to inject the request object in the constructor like it would in a controller class. Is there a way to inject the request into a facade class like you would in a controller? With the current code, I get the following error:
Too few arguments to function App\Helpers\Params::__construct(), 0
passed in /var/www/v4api/html/app/Providers/ParamsServiceProvider.php
on line 21 and exactly 1 expected
I want to avoid having to manually pass the request input into the class and just have it automatically be injected in the constructor. Any help that you guys can give would be appreciated!
Looks like this worked out:
In the ParamsServiceProvider, instead of using App::bind to instantiate the Params class, do this instead:
public function register()
{
App::alias(Params::class, 'params');
}
then the request object will be injected properly into the facade.
The class you've posted isn't actually a Facade - it's just a regular class.
Because you've type-hinted it's dependencies you don't need to tell Laravel how to create an instance of it - it can work it out all by itself.
You can either inject that class into a controller method (where Laravel will new it up for you), or you can call app(App\Helpers\Params::class) and it will return a new instance of the class for you.
Read more on creating facade classes if you want to create an actual facade. Alternatively you can create a realtime facade - where you instead reference Facades\App\Helpers\Params::foo() and Laravel will let you use the method as if you had an instance of that class.
You have a number options here - point the facade straight to the underlying class and let Laravel work out how to build it, explicitly bind it to the container, or use a realtime facade. Let's go through each.
class Params extends Facade
{
protected static function getFacadeAccessor()
{
return \App\Helpers\Params::class;
}
}
This option points the facade straight to the class you intend it to be a facade for and Laravel will work out the rest.
Alternatively, you can keep it as params and instead fix the binding in the container.
The first example use's Laravel's container to make an instance of the class and return it. Laravel can automatically reflect the class and inject it's dependencies.
App::bind('params', function ($app) {
return $app->make(Params::class);
});
The second example explicitly builds the instance the way you desire, which is just additional code for you to maintain.
App::bind('params', function() {
return new Params(new HomepageRequest);
});
The final option - as mentioned in the earlier answer - is to use a realtime facade and skip the manual binding entirely. You can learn more about realtime facades in the docs.
I have made a repository pattern app, having a repo and interface:
class UserRepository extends EloquentRepository implements UserRepositoryInterface
{
public function __construct()
{
$this->model = new User();
}
...
}
The repository and interfaces as well as extended class and its interface is registered in service provider and is called on app boot.
The questions I have are:
Is there a need to watch out for the order of registering? For example, should EloquentRepository class be loaded before the
repo, or does Laravel handle that on its own?
In case I inject UserRepositoryInterface in a controller, is the constructor method called automatically even though I didn't really new-up a class?
How long does the DI injection "live"? If I inject it in a page controller which calls some other controller and needs the same dependency, does the constructor call twice then, and operate separately in each controller?
Is there a difference if I call it like App::make() instead of DI?
Is there a need to watch out for the order of registering? For example, should EloquentRepository class be loaded before the repo, or does Laravel handle that on its own?
I don't quite understand where you would load EloquentRepository as (from the code posted) it seems you're only extending it. Which shouldn't be a problem.
In case I inject UserRepositoryInterface in a controller, is the constructor method called automatically even though I didn't really new-up a class?
Yes. Most of Laravel's main classes (controllers included) are loaded with DI in mind and the dependencies will be resolved automatically.
That being said, since you are injecting an interface and an interface by default cannot be initialized as a class, since it has no implementation - you need to bind an implementation to the interface first in order to use it.
How long does the DI injection "live"? If I inject it in a page controller which calls some other controller and needs the same dependency, does the constructor call twice then, and operate separately in each controller?
My understanding is that a new instance of the class will be created when the next controller is initialized. Unless you bind a class as a singleton.
Is there a difference if I call it like App::make() instead of DI?
App::make(some::class) will automatically resolve the dependencies of class some.
For example:
namespace App;
use App\Dependancy;
class Test
{
protected $d;
public function __construct(Dependancy $d)
{
$this->d = $d;
}
}
If you call this in the controller: $a = new \App\Test() you will get an error that \App\Test constructor expects class Dependency as first parameter.
But if you initialize it like this: $a = \App::make(\App\Test::class) the Dependency will be automatically resolved.
try to make the repositories abstract in the controllers and inject these through constructor.
like this here:
public function __construct(EloquentRepository $repository)
{
$this->repository = $repository;
}
And in the AppServiceProvider you can inject repositories you will need.
public function boot()
{
// provides any Repository in SomeController
$this->app->when(SomeController::class)
->needs(EloquentRepository::class)
->give(function (Application $app) {
return $app->make(SomeRepositoryInterface::class)
});
}
I encountered this issue using the repository pattern. Currently I use an interface, and a custom class to achieve it, then type-hint it into the controller's construct and because of Laravel, it will solve the repositories' dependencies automatically and recursively.
I also do this in a service provider:
$this->app->bind(path/to/repoInterface,path/to/implementationClass)
However, because of the way I coded these repositories, in order to avoid code duplication, I created an abstract class that has a common method to all these repositories. This class is as follows:
abstract class CommonRepo{
public function __construct(SomeModelClass model){}
public function commonMethod(){//Code here}
And my repositories have the following structure:
public class ExampleRepository extends CommonRepo implements ExampleRepositoryI{
public function __construct(){
parent::__construct();
}
}
Laravel doesn't like this, so its giving this error:
Argument 1 passed to path/to/repo/CommonRepo::__construct() must be an instance of path/to/model/SomeModelClass, none given, called in...
So, obviously is not resolving the dependency of the class CommonRepo, but it does resolve the dependencies on the normal repositories.
I'd like, if it's possible, to use type-hinting (the Laravel way) without having to do anything related to the new operator
How can I, then, resolve that class's dependencies ?
PD: Using Laravel 5.2
Parent constructor is called like normal function without touching dependency resolver so you should do one of two possibilities:
public class ExampleRepository extends CommonRepo implements ExampleRepositoryI
{
public function __construct(SomeModelClass $model){
parent::__construct($model);
}
}
or
public class ExampleRepository extends CommonRepo implements ExampleRepositoryI
{
public function __construct(){
parent::__construct(App::make(SomeModelClass::class));
}
}
nice question. I did some tinkering, though I don't know if this is what you're looking for. But you can dynamically create an instance of Eloquent model required by your repository class.
Let's say you have your User model class stored in app\Models\User.php:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
//
}
You then create a base abstract class for all of your repository classes: app\Repositories\BaseRepository.php. This is where you place all common functionalities for your repository classes. But rather than injecting the Eloquent instance through the constructor, you may add a method named getModel() to dynamically create an instance of Eloquent model for your repository.
<?php
namespace App\Repositories;
use ReflectionClass;
use RuntimeException;
use Illuminate\Support\Str;
abstract class BaseRepository
{
protected $modelNamespace = 'App\\Models\\';
public function getById($id)
{
return $this->getModel()->find($id);
}
public function getModel()
{
$repositoryClassName = (new ReflectionClass($this))->getShortName();
$modelRepositoryClassName = $this->modelNamespace . Str::replaceLast('Repository', '', $repositoryClassName);
if (! class_exists($modelRepositoryClassName)) {
throw new RuntimeException("Class {$modelRepositoryClassName} does not exists.");
}
return new $modelRepositoryClassName;
}
}
Now let's say you want to create a repository for your User model, and this user's repository must implement the following interface: app\Repositories\UserRepositoryInterface.php
<?php
namespace App\Repositories;
interface UserRepositoryInterface
{
public function getByEmail($email);
}
You create app\Repositories\UserRepository.php class and simply extend it from the BaseRepository class. Also don't forget to implement all specific implementations defined on UserRepositoryInterface.
<?php
namespace App\Repositories;
use App\Repositories\BaseRepository;
use App\Repositories\UserRepositoryInterface;
class UserRepository extends BaseRepository implements UserRepositoryInterface
{
public function getByEmail($email)
{
return $this->getModel()->where('email', $email)->firstOrFail();
}
}
This way you can bind the UserRepositoryInterface to it's implementation like so:
$this->app->bind(\App\Repositories\UserRepositoryInterface::class, \App\Repositories\UserRepository::class);
Finally you can freely inject the UserRepositoryInterface to a controller's constructor or methods. You can also resolve it via service container like this:
$userRepository = App::make(App\Repositories\UserRepositoryInterface::class);
$userRepository->getByEmail('john#example.com');
Of course there's a catch to this approach. The repository class should be started with the associated model, so the InvoiceRepository.php is dedicated for Invoice.php model class.
Hope this help!
This might help. You can listen in for when an object resolves and set attributes.
$this->app->resolving(CommonRepo::class, function ($object, $app) {
// Called when container resolves object of any type...
$object->commonObject = app(CommonObject::class);
});
Docs: https://laravel.com/docs/5.4/container#container-events
I'm trying to inject the service manager into a controller.
Actual Error:
\vendor\zendframework\zend-servicemanager\src\Exception\ServiceLocatorUsageException.php:34
Service "Project\Service\ProjectServiceInterface" has been requested to plugin manager of type "Zend\Mvc\Controller\ControllerManager", but couldn't be retrieved.
A previous exception of type "Zend\ServiceManager\Exception\ServiceNotFoundException" has been raised in the process.
By the way, a service with the name "Project\Service\ProjectServiceInterface" has been found in the parent service locator "Zend\ServiceManager\ServiceManager": did you forget to use $parentLocator = $serviceLocator->getServiceLocator() in your factory code?
The process goes:
class BaseController extends AbstractActionController implements ServiceLocatorAwareInterface
{
public function __construct(\Zend\ServiceManager\ServiceLocatorInterface $sl)
{
$this->serviceLocator = $sl;
}
}
Create controller and use constructor method
Extend this BaseController to AdminController
Setup Routes to AdminController => /admin
use Module.php
public function getControllerConfig()
Use closer as factory to create controller object injecting the serviceLocator
'Project\Controller\Project' => function($sm) {
$serviceLocator = $sm->getServiceLocator();
return new \Project\Controller\ProjectController($serviceLocator);
},
try to use $this->getServiceLocator()->get('service_name')
Exception found for missing service.....
Now the problem is this:
/**
*
* #param ServiceLocatorInterface $sl
*/
public function __construct(\Zend\ServiceManager\ServiceLocatorInterface $sl)
{
$rtn = $sl->has('Project\Service\ProjectServiceInterface');
echo '<br />in Constructor: '.__FILE__;var_dump($rtn);
$this->serviceLocator = $sl;
}
public function getServiceLocator()
{
$rtn = $this->serviceLocator->has('Project\Service\ProjectServiceInterface');
echo '<br />in getServiceLocator: '.__FILE__;var_dump($rtn);
return $this->serviceLocator;
}
Within the __constructor() the service IS FOUND. Within the getServiceLocator() method the service with the same name IS NOT FOUND....
in Constructor: Project\Controller\BaseController.php
bool(true)
in getServiceLocator: Project\Controller\BaseController.php
bool(false)
Am I missing something? Is the SharedServiceManager doing something here?
The entire purpose of this exercise was due to this message:
Deprecated: ServiceLocatorAwareInterface is deprecated and will be removed in version 3.0, along with the ServiceLocatorAwareInitializer. ...
If you really need the ServiceLocator, you have to inject it with a factory
Something like this
Controller:
<?php
namespace Application\Controller;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\ServiceManager\ServiceLocatorInterface;
class BaseController extends AbstractActionController
{
protected $serviceLocator = null;
public function __construct(ServiceLocatorInterface $serviceLocator)
{
$this->setServiceLocator($serviceLocator);
}
public function setServiceLocator(ServiceLocatorInterface $serviceLocator)
{
$this->serviceLocator = $serviceLocator;
return $this;
}
public function getServiceLocator()
{
return $this->serviceLocator;
}
}
Factory:
<?php
namespace Application\Controller\Factory;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
use Application\Controller\BaseController;
class BaseControllerFactory implements FactoryInterface
{
public function createService(ServiceLocatorInterface $serviceLocator);
{
$controller = new BaseController($serviceLocator->getServicelocator());
return $controller;
}
}
?>
in module.config.php
<?php
// ...
'controllers' => [
'factories' => [
'Application\Controller\BaseController' => 'Application\Controller\Factory\BaseControllerFactory',
// ...
],
// ...
In Zend Framework 2 there are multiple service locators (docs here), one general (mainly used for your own services), one for controllers, one for view helpers, one for validators, ... The specific ones are also called plugin managers.
The error message you are receiving is just telling you that you are using the wrong service locator, the ones that retrieves controllers and not the general one. It is also suggesting you how to solve your problem:
did you forget to use $parentLocator = $serviceLocator->getServiceLocator() in your factory code
What is probably happening (not 100% sure about this) is that in the constructor you are passing in an instance of the general service manager, and everything works fine with it. Then, since the controller implements the ServiceLocatorAwareInterface, the controller service locator is injected into your controller, overriding the one that you defided before.
Moreover, I think that the idea beyound the decision of removing ServiceLocatorAwareInterface in version 3 is that you don't inject the service locator inside your controller, but instead you inject directly the controller dependencies.
You should try to prevent injecting the service manager or service locator in the controller. It would be much better to inject the actual dependencies (in your case 'Project\Service\ProjectServiceInterface') directly into the __construct method of your class. Constructor injection (the dependencies are provided through a class constructor) is considered best practice in ZF2.
This pattern prevents the controller from ever being instantiated without your dependencies (it will throw an error).
If you inject a ServiceLocator or ServiceManager from which you will resolve the actual dependencies in the class, then it is not clear what the class actually needs. You can end up in a class instance with missing dependencies that should never have been created in the first place. You need to do custom checking inside the class to see if the actual dependency is available and throw an error if it is missing. You can prevent writing all this custom code by using the constructor dependency pattern.
Another issue is that it is harder to unit-test your class since you cannot set mocks for your individual dependencies so easily.
Read more on how to inject your dependencies in my answer to a similar question.
UPDATE
About the issue you encountered. Controller classes implement a ServiceLocatorAwareInterface and during construction of your controller classes the ControllerManager injects a ServiceLocator inside the class. This happens here in the injectServiceLocator method at line 208 in ControllerManager.php. Like #marcosh already mentioned in his answer, this might be a different service locator then you injected. In this injectServiceLocator method you also find the deprecation notice you mentioned in your question.
Yours is available in the __construct method because at that time (just after constructing the class) the variable is not yet overwritten. Later when you try to access it in your getServiceLocator method it is overwritten.
I am creating a Laravel app that needs to communicate with a remote (in-house) service via API.
This API needs to be authenticated at least once per session, and after that other calls can work fine.
I think the best way is to use Laravel's service providers to do this, but I'm open to other solutions.
What I would like:
What I would like is a way to have this Service available for use whenever. I don't want to have to put the service in the parameters of a controller's method if I can avoid it. Something like this:
use MyServiceProvider;
class SomeController extends Controller
{
public function someMethod ()
{
MyServiceProvider::method();
}
}
I can post what I've started doing thus far, if needed - but I'd rather focus on doing what I want rather than fixing what I did wrong.
inb4: I did read the docs.
What you're trying to do is create a Facade. Facades are very similar to using dependency injection, except that they can be used globally without specific injection. Docs: https://laravel.com/docs/5.0/facades#creating-facades
In your service provider:
App::bind('foo', function()
{
return new \MyServices\Foo; //returns a concrete class
});
Foo.php
use Illuminate\Support\Facades\Facade;
class Foo extends Facade {
protected static function getFacadeAccessor() { return 'foo'; } //matches binding in SP
}
Now your service provider is available as Foo anywhere, even without explicitly injecting it:
use Foo;
class SomeController extends Controller
{
public function someMethod ()
{
Foo::method(); //creates a Foo object according to App::bind, then calls method();
}
}