I have a custom class App/Http/Responder, which had a few methods to build a specific JSON response back in my application. I want to test my controller in isolation, so I'm trying to inject my dependencies via the constructor.
My plan was to simply create a service provider, attach bind it to the $app and then, as per the docs, let it be automatically resolved:
public function register()
{
$this->app->bind('responder', function()
{
return new App\Http\Responder($this->app['cache'], $this->app['app'], new JsonResponse, $this->app['config']);
});
}
I then add this to my config/app.php.
Okay, so now my Responder and it's dependancies are bound to the app, as responder.
Now I thought I'd be able to inject Responder into my controller constructor, and Laravel would be able to automatically resolve this from the IoC container:
class AreasController extends BaseController {
protected $responder;
public function __construct(Responder $responder)
{
$this->responder = $responder;
}
However I get Class Responser does not exist.
The only way I can get it working, without using the App::make() Facade, is to inject the app into my controller:
use Illuminate\Foundation\Application as App;
class AreasController extends BaseController {
protected $app;
public function __construct(App $app)
{
$this->app = $app;
}
I can then do $this->app['responder']->method().
Obviously I'm missing something, but I want to keep away from using Facades in my app so I can test.
If you want to type hint classes to be resolved in the IOC container, you should bind the actual class name with namespace:
$this->app->bind('App\Http\Responder', function()
{
return new App\Http\Responder($this->app['cache'], $this->app['app'], new JsonResponse, $this->app['config']);
});
Technically the container would still resolve this class, because it's a concrete class that can be found, but the way you're doing allows to inject other IOC-bound resources, which is a good practice.
Then, when you wish to have this class injected for you, type hint the full path to the class as you normally would:
use App\Http\Responder;
class AreasController extends BaseController {
protected $responder;
public function __construct(Responder $responder)
{
$this->responder = $responder;
}
}
Also, for what it's worth, your error indicates that you misspelled "Responder" as "Responser".
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'm still in the process of learning about Laravel and Dependency Injection. I understand the concept, but I don't know how to mock a dependency in this specific case:
MyController.php
use Illuminate\Routing\Controller;
use MyPackage\Services\ServiceInterface;
class MyController extends Controller{
protected $service;
public function __construct(ServiceInterface $service)
{
$this->service = $service;
}
}
MyServiceProvider.php
use Illuminate\Support\ServiceProvider;
class MyServiceProvider extends ServiceProvider{
public function register()
{
$this->app->bind('MyPackage\Services\ServiceInterface', function ($app) {
return new MyPackage\Services\ConcreteService(['key'=>'123'], $app->make('GuzzleHttp\Client'));
});
}
}
So, as you can see, I have a controller that requires an instance of ServiceInterface. That instance is being resolved in the ServiceProvider. The constructor of ConcreteService requires a client to perform Http request to an API. This Http is being resolved by the Service container (It will be an instance of Guzzle).
Now, how can I mock this instance of Guzzle on my tests?
The ideal result is doing something like this:
MyTest.php
...
$this->post(route('routeToActionInMyController'), $params);
So, in my tests I just need to hit the route that will be using an specific method of MyController.php but I don't need a "real" Guzzle instance. I just need to mock the response to test if MyController behaves in the expected way (and stores things in the database properly).
How can I instruct the Service Container to inject a Mocked object during tests only? Or am I doing this in the completely wrong way?
Any help will be appreciated.
Thanks in advance
In your test class:
class TestingSomething extends TestCase {
protected function setUp() {
parent::setUp();
$mockServiceInterface = $this->getMockBuilder(ServiceInterface::class)->getMock();
$this->app->instance(ServiceInterface::class,$mockServiceInterface);
}
public function testPostToRoute() {
$this->post(route('routeToActionInMyController'), $params);
}
}
This should replace what's already bound in the service container with that mock instance.
Refer to the PHPUnit manual on chapter 9. Test doubles for what you can do with the mock builder and resulting mocks.
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();
}
}
I have a service provider that I want to use to bind an instance of a class to the service container:
namespace App\Providers;
use Eluceo\iCal\Component\Calendar;
use Illuminate\Support\ServiceProvider;
class IcalProvider extends ServiceProvider
{
public function register()
{
$this->app->instance('iCal', function () {
return new Calendar(config('calendar.name'));
});
}
}
As I understand the documentation on binding an instance, this allows me to bind the key iCal to the service container so that later in my controller or service class I can type hint iCal and the instance created in the service provider will be used.
So I created a controller and tried to type hint my instance:
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
class CalendarInviteController extends Controller
{
public function download(iCal $ical, $sessionId)
{
dd($ical);
}
}
But when I do I get the error:
Class App\Http\Controllers\iCal does not exist
Makes sense, as it applies it's looking for a class named iCal in the controller namespace which doesn't exist. There's not a use statement for the instance since iCal is just a text key, so I tried telling it to look at the root namespace thinking that may fix it:
public function download(\iCal $ical, $sessionId)
and I get the error:
Class iCal does not exist
When I read the section of the documentation on resolving from the service container it looks like the only thing I need to do in the controller is type hint to get the instance.
Am I misunderstanding the docs?
Update
I should also mention that I did add my service provider to my config/app.php file.
Also, when I create an interface, bind it to the service container instead, edit the vendor code to implement said interface, and inject that interface instead it works, but that requires that I edit the vendor code which I don't want.
As you see in the docs the method instance takes a key and an object instance to register in the container. So, if you want to register a specific instance in the container, the registration should be:
namespace App\Providers;
use Eluceo\iCal\Component\Calendar;
use Illuminate\Support\ServiceProvider;
class IcalProvider extends ServiceProvider
{
public function register()
{
//register a specific instance of the Calendar class in the container
$this->app->instance('iCal', new Calendar(config('calendar.name') );
}
}
This way you could get back the instance with:
$cal = \App::make('iCal');
If your purpose is to type-hint the class in the controller method, and you want to resolve the previous registered instance from the service container, you could do like this:
namespace App\Providers;
use Eluceo\iCal\Component\Calendar;
use Illuminate\Support\ServiceProvider;
class IcalProvider extends ServiceProvider
{
public function register()
{
//the key will be 'Eluceo\iCal\Component\Calendar'
$this->app->instance( Calendar::class, new Calendar(config('calendar.name') );
}
}
Now, in your controller:
namespace App\Http\Controllers;
//important: specify the Calendar namespace
use Eluceo\iCal\Component\Calendar;
class CalendarInviteController extends Controller
{
public function download(Calendar $ical, $sessionId)
{
dd($ical);
}
}
This way Laravel will see that you want a Calendar object and it will try to get it from the service container looking if exists a binding for this key: (because this is the namespace of the class you have specified in the controller)
Eluceo\iCal\Component\Calendar
and the binding exists! As you have bound this key to your service container in your service provider, so Laravel will return your registered instance.
In the code you provided, you tipe-hinted the class iCal, but the class didn't exist anywhere so Laravel wasn't able to instantiate the class
If you’re wanting to inject dependencies into your controller (which is good, so kudos!) then you need an interface name to type-hint on.
Usually you would have a generic interface, and then bind that interface to a concrete implementation. So you may have a calendar service interface, that’s bound to your iCal implementation. Something like this:
use Eluceo\iCal\Component\Calendar;
class CalendarServiceProvider extends ServiceProvider
{
public function register()
{
$this->app->bind('App\Services\Calendar', function ($app) {
return new Calendar(config('calendar.name'));
});
}
public function provides()
{
return ['App\Services\Calendar'];
}
}
So long as you register your service provider in your config/app.php file, you can now type-hint your calendar dependency in classes:
use App\Services\Calendar;
class InvitationController extends Controller
{
protected $calendar;
public function __construct(Calendar $calendar)
{
$this->calendar = $calendar;
}
}
I have the following problem in my laravel 5 project. I have a service provider for form macros named MacroServiceProvider.php. Some macros should receive data from the database, I'm currently using the model and getting the results with eloquent but I want to use repositories instead, so I created my repository but I can't inject this directly to my service provider.
I want something like this:
...
public function register(MyRepoInterface $repo)
{
$registers = $repo->findAll();
Form::macro...
}
...
How can I do this?
Thanks.
I don't think you can do what are you asking, and I think you are misunderstanding the way providers work and what they are intended for.
In providers, you usually say what are the bindings among interfaces and implementations, so that when you do dependency injection in your application code, it works. I'm pretty sure they are not intended for doing real stuff.
For what you say about your code, I imagine something like this:
a repository interface (MyRepoInterface) with a real implementation using Eloquent (say EloquentMyRepo)
a facade, say Macro, so that you can do Macro::myMacro1(), Macro::myMacro2(), etc.
the methods myMacro1(), myMacro2(), etc, use the repository to get some data from the db and then call some methods from the Form facade
If I'm right, then I suggest something like this.
Repository
Define the interface in the file MyRepoInterface.php with
interface MyRepoInterface
{
public function findAll();
// ... your other repo methods
}
and an implementation EloquentMyRepo.php with
class EloquentMyRepo implements MyRepoInterface
{
public function findAll()
{
// ... do what you need
}
}
Facade
Define a facade file MacroFacade.php with this
use Illuminate\Support\Facades\Facade;
class MacroFacade extends Facade
{
protected static function getFacadeAccessor()
{
return 'macro';
}
}
Service class
Define your macro service class in a file MacroService.php, where you can use dependency injection and access your repository. In this class you define your myMacro1()... methods.
class MacroService
{
protected $myRepo;
public function __construct(MyRepoInterface $myRepo)
{
$this->myRepo = $myRepo;
}
public function myMacro1()
{
// access the repo
$items = $this->myRepo->findAll();
// ... do something with $items and finally return a string
return Form::macro(...);
}
public function myMacro2($arg1, $arg2)
{
// ... use the parameters to do something else
}
}
Bindings
In your Providers/AppServiceProvider.php file, go to the register() method and add
public function register()
{
// ...
$this->app->bind('App\MyRepoInterface', 'App\EloquentMyRepo');
// ...
}
so that when you use MyRepoInterface in dependency injection, Laravel knows it has to use an instance of EloquentMyRepo.
Now, let's create a service provider for your macro service. Create a file Providers/MacroServiceProvider.php and put in it
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
class MacroServiceProvider extends ServiceProvider
{
public function register()
{
$this->app->bind('macro', 'App\MacroService');
}
}
Now, when we need the facade that is registered as macro, an instance of MacroService is used.
Configuration
We finally need some changes to the configuration. Open the config/app.php file, add the new provider
...
'providers' => [
...
'App\Providers\AppServiceProvider',
...
'App\Providers\MacroServiceProvider',
],
(note that the MacroServiceProvider is declared after the AppServiceProvider.)
Add the alias for the facade:
'aliases' => [
...
'Macro' => 'App\MacroFacade',
],
Done!
What happens
Let's suppose you call
...
Macro::myMacro1();
...
in your code. How the right method is called?
Macro is an alias handled by the MacroFacade class
The facade is registered in the IoC with the macro name by the getFacadeAccessor() method of MacroFacade
The MacroServiceProvider registered the MacroService class as an implementation for macro
An instance of MacroService must be created, but it has MyRepoInterface as dependency
The AppServiceProvider said Laravel to use EloquentMyRepo when MyRepoInterfice is required
So an instance of EloquentMyRepo is created and it is used to create an instance of MacroService
Macro has been resolved to an instance of MacroService
Laravel calls the myMacro1() method of that instance
I hope this can clarify a bit what happens.