How $this->app->singleton() works in laravel? - php

In my laravel project I have following interface, repository and controller.
This is Interface
interface TrainingClassTypeInterfaces
{
public function updateTrainingClassType($id, $request);
}
This is Repository
use App\Models\Trainings\AppTrainingClassType;
class TrainingClassTypeEloquent implements TrainingClassTypeInterfaces
{
protected $model;
public function __construct(AppTrainingClassType $appTrainingClassType)
{
$this->model = $appTrainingClassType;
}
public function updateTrainingClassType($id, $request)
{
$response = false;
$isUpdated = $this->model::where('training_class_id',$id)->update([
'app_id' => $request->app_id
]);
....
}
}
This is controller
class TrainingClassTypesController extends \TCG\Voyager\Http\Controllers\VoyagerBaseController
{
protected $trainingService;
public function __construct(TrainingClassTypeEloquent $trainingClassTypeInterfaces) {
$this->trainingService = $trainingClassTypeInterfaces;
}
public function insertOrUpdate()
{
...
$this->trainingService->updateTrainingClassType($id, $request);
..
}
}
Everything working fine till here
As you can see I am using TrainingClassTypeEloquent's method inside TrainingClassTypesController. But it was returning error something like
Argument 1 passed to ...::__construct() must be an instance of
Basically it was asking me to put instance of Model into TrainingClassTypeEloquent class. Then I did as following
$TCTypes = new AppTrainingClassType();
$TCT = new TrainingClassTypeEloquent($TCTypes);
$TCT->updateTrainingClassType($id, $request);
which was working fine but I was confused that this approach is not proper, there should be some proper way.
After googling I found another solution which is singleton binding, and then I tried following in AppServiceProvider
$this->app->singleton(
\App\Services\Voyager\Eloquent\TrainingClassType\TrainingClassTypeInterfaces::class,
\App\Services\Voyager\Eloquent\TrainingClassType\TrainingClassTypeEloquent::class
);
After adding this singleton binding, I notice script was working without providing model instance into TrainingClassTypeEloquent class.
I would like to know how $this->app->singleton() is working, so in this way my concept would be clear about it. If someone knows then kindly guide me about it.
Thank you so much

It is all about BINDING a service to the service container.
What does $this->app->singleton(); method do?
The singleton method binds a class or interface into the service container so that Laravel can maintain dependency (when using an interface as the constructor parameter).
(Actually Singleton is a design pattern. Singleton implementation always returns the same object on subsequent calls instead of a new instance). So $this->app->singleton(); method returns the same object again and again.
Point to be noted that Laravel doc says:
There is no need to bind classes into the container if they do not
depend on any interfaces. The container does not need to be instructed
on how to build these objects, since it can automatically resolve
these objects using reflection.
But your controller class depends on an interface, so the container needs to be informed and to do this, you need to use this $this->app->singleton(); method but there are other ways around.
Again, at the same time, this TrainingClassTypeEloquent::class has a dependency of AppTrainingClassType::class. But in this case, we do not need to worry about that because Laravel uses Reflection API to maintain its dependency as this class does not use interface as like TrainingClassTypesController::class class.
Once you are done with binding the service to the container, Laravel will then automagically put the service onto the constructor method as an argument where the interface is used.
I hope this would help you. You may find more help from this answer.

You need to register TrainingClassTypeEloquent
$this->app->singleton(TrainingClassTypeInterfaces::class, static function ($app) {
return new TrainingClassTypeEloquent(new AppTrainingClassType());
});
Then you can inject it in your Controller
public function insertOrUpdate(TrainingClassTypeInterfaces $trainingService, $id)
{
$trainingService->updateTrainingClassType($id, request());
}

Related

Laravel 5 IoC type-hinting not working

As far as I've understood, I should be able to type-hint my class instance arguments in the constructor of my class that I only instantiate with help of a Service Provider. Unfortunately I'm getting an error about missing arguments.
Tracker.php
function __construct($query, Tracker\Shipment $shipment) {
$this->query = $query;
$this->shipment = $shipment;
}
TrackerServiceProvider.php
class TrackerServiceProvider extends \Illuminate\Support\ServiceProvider {
public function register() {
$this->app->bind('TrackerAPI', function($app, $shipment_number) {
return new Tracker\API($shipment_number); // omitting Shipment argument should resolve on its own?
});
}
}
Class API is extending Tracker, thus using its constructor. Why isn't it resolving without the implied class type hint?
You've confused the PHP new functionality with Laravel's dependency injection via type hinting functionality.
In your TrackerAPI binding, when you specify return new Tracker\API($shipment_number);, there is nothing built into PHP to try to flesh out any missing parameters. To do this, you would need to return $app->make('Tracker\API', [$shipment_number]);
When you use the App::make() method, Laravel will do its magic to create a new object. It first checks for an explicitly defined binding. If it doesn't find an explicit binding, it will fall back to just using a combination of the supplied parameters plus type hints to create objects for any missing parameters.
Take the following statement: App::make('Tracker\API', [$shipment_number]). Assume there is no explicitly defined binding for Tracker\API. The Tracker\API constructor requires two parameters. $shipment_number is passed in in the parameter array, so it will be used as the first parameter. Since only one parameter was supplied, Laravel will App::make() the rest of the parameters using the type hints.
However, when you specifically instantiate the object yourself using the new keyword, none of this happens. It is a plain PHP object instantiation.
Nope. Laravel can't resolve partials like that. You have to be explicit:
use Tracker\API;
use Tracker\Shipment;
use Illuminate\Support\ServiceProvider;
class TrackerServiceProvider extends ServiceProvider {
public function register()
{
$this->app->bind('TrackerAPI', function($app, $shipment_number)
{
return new API($shipment_number, $app[Shipment::class]);
});
}
}

Why use static method in PHP's laravel model class?

In PHP laravel, we have codes like
$user = User::find(1);
var_dump($user->name);
I am not concerning how to use the find method, I am concerning why laravel use a static method? Shouldn't the use of static method make the method hard to test?
Will it be better if they designed using singleton?
e.g.
$user = User::getInstance()->find(1);
var_dump($user->name);
In fact, your example is very similar to what Laravel does behind the scene. When you do User::find(), you are actually asking for a new instance, either an instance of Collection or a QueryBuilder.
Illuminate\Database\Eloquent\Model (reference):
public static function find($id, $columns = array('*'))
{
if (is_array($id) && empty($id)) return new Collection;
$instance = new static;
return $instance->newQuery()->find($id, $columns);
}
As a side note, you'll also see another way of using static methods in Laravel e.g. Input::get(). These are called Facades.
Facades provide a "static" interface to classes that are available in the application's IoC container ... Laravel "facades" serve as "static proxies" to underlying classes in the IoC container, providing the benefit of a terse, expressive syntax while maintaining more testability and flexibility than traditional static methods.
When a user references any static method on the ... facade, Laravel resolves the cache binding from the IoC container and runs the requested method (in this case, get) against that object.
You can read more about Larave's Facades at: http://laravel.com/docs/facades
Unnawut has a very good answer, however I felt it necessary to add in further explanation.
In your example
$user = User::find(1);
var_dump($user->name);
Laravel isn't using a static method, you are. Another way to do this which you are probably looking for is to use dependency injection, which Laravel makes very easy because it can be done automatically. So in whatever class you are using your User model in, you should be setting up something like this in the constructor...
public function __construct(User $user)
{
$this->user = $user;
}
And then you can modify your code to not use the static bindings.
$user = $this->user->find(1);
var_dump($user->name);
This would restrict the system from only having one User. Whilst the find method may be static, the User class will have other methods and properties that aren't, a likely example is in your example: $user->name
A method that does not rely upon any instance variables, I.e variables who's value is specific to the particular object instance, but instead provides generic functionality that applies to all instances, can, and probably should, be static. This is why the $this operator is illegal within static methods, as it can make no reference to a particular object instance.
#unnawut reference link to the source code of Model.php is no longer using static find function as the class has been refactored. The function that get called eventually is in Builder.php (source code),
The logic still same
- magic function `__callStatic` of Model class get called
- a new instance of Builder is created and return
- call the find method of the Builder instance
Model.php
public function newEloquentBuilder($query)
{
return new Builder($query);
}
Builder.php
public function find($id, $columns = ['*'])
{
if (is_array($id) || $id instanceof Arrayable) {
return $this->findMany($id, $columns);
}
return $this->whereKey($id)->first($columns);
}

How can I override a DI container dependency with a specific instance?

Imagine we have a Request object and a Controller object. The Controller object is constructed with a Request object, like so:
abstract class Controller {
public $request;
public function __construct(Request $request)
{
$this->request = $request;
}
}
As you can see, this is an abstract class, so in reality a subclass of Controller will be constructed. Let's imagine the code is something like this:
// Instantiate the request.
$request = new Request($params);
// Instantiate the registration controller.
$controller = new RegistrationController($request);
Now let's say that we add a dependency to our RegistrationController, like so:
class RegistrationController extends Controller {
private $user_repo;
public function __construct(Request $request, UserRepo $user_repo)
{
parent::__construct($request);
$this->user_repo = $user_repo;
}
}
At this point, what I'd like to do is introduce a dependency injection container to automatically inject the dependencies via the constructor. For this, I've been using PHP-DI. Usually, this would go something like so:
// Instantiate the registration controller.
$controller = $container->get('RegistrationController');
This would then instantiate RegistrationController with an instance of Request and an instance of UserRepo. It'd know to construct with those objects thanks to reflection, but if I wanted I could also override this via a configuration file.
The problem is that the Request object takes a parameter which is dynamic. I need the Request object passed to RegistrationController to be a specific instance, one I've just created.
I essentially want to be able to say: "Give me an instance of this class with all of its dependencies injected, but for a particular parameter, pass in this specific instance".
I've looked to see if PHP-DI (and a hand-full of other DI containers) support this kind of "override" for specific parameters, but so far I can't find anything.
What I want to know is:
Is there a DI container out there that can do this?
Is there an alternative approach which would leave the classes clean (I don't want to use annotations or anything else that'll add the container I use as a dependency)?
PHP-DI author here.
So there are two things, first I'll answer your question:
PHP-DI's container provides a make method that you can use like that:
$request = new Request($myParameters);
$controller = $container->make('RegistrationController', array(
'request' => $request
));
This make method, as you can see, is the same as get except it will always create a new instance (which is what you want here since you probably don't want to reuse an existing instance of the controller) and it will take the extra parameters you give it. That's the behavior of a factory, with the benefits of the container that will find the rest of the parameters you didn't provide.
So that's what I would use here. You could also do this:
$request = new Request($myParameters);
$container->set('Request', $request);
$controller = $container->get('RegistrationController');
But that's less clean because it will set the request in the container, which is bad (explained below).
Now the second thing is that a request object is not really a service, it's a "value object". A container should generally only contain service objects, i.e. objects that are stateless.
The reason for this is imagine you have several request in the same process (e.g. you do "sub-requests", or you have a worker process that handles several requests, etc...): your services would be all messed up because they would have the request injected and the request object might change.
Symfony did just that and realized it was a mistake. Since Symfony 2.4, they have deprecated having the Request in the container: http://symfony.com/blog/new-in-symfony-2-4-the-request-stack
Anyway, so what I suggest you to do is not to have the Request object in the container, but instead use the make method I showed you.
Or, even better, I would do that:
class RegistrationController extends Controller {
private $user_repo;
public function __construct(UserRepo $user_repo)
{
$this->user_repo = $user_repo;
}
public function userListAction(Request $request)
{
// ...
}
}
// in the front controller
$controller = $container->make('RegistrationController');
// This is what the router should do:
$action = ... // e.g. 'userListAction'
$controller->$action(new Request($myParameters));
(this is what Symfony and other frameworks do by the way)

Mocking Laravel's Request::segment method

This is related to this question, but following that solution did not fix my issue. I also realize that Laravel's own documentation states that you should not mock the Request object, but I'm not sure how else to go about writing this test.
Here's a semblance of the code I want to test:
public function getThirdSegment()
{
return Request::segment(3);
}
Here's a test I currently have:
/**
* #test
*/
public function indexReturnsOk()
{
$this->prepareForTests();
$this->call('GET', '/api/v1/courses');
$this->assertResponseOk();
$this->assertResponseStatus(200);
}
This is failing because Request::segment(3) is returning null when running PHPUnit. I first tried to mock the Request object like this:
Request::shouldReceive('segment')->andReturn('courses');
But it still returns null. Then I tried this:
$request = m::mock('Illuminate\Http\Request');
$request->shouldReceive('segment')->andReturn('courses');
Input::swap($request);
And the segment method is still returning null. Is there any way to mock the return value of this method?
Update
This code is within a service provider's register method, but I don't think that's the cause of the issue. Hitting the site from a browser does what I would expect it to do, yet running PHPUnit doesn't seem to flesh out either the route or the URL, or anything having to do with the request itself.
Best answer here so far is I was doing it wrong. Service Providers run way before a controller is even loaded, and, when unit testing, Laravel's Illuminate\Foundation\Testing\TestCase loads the application (calling all service providers' both boot and register methods) during execution of the setUp method, way before any calls can be made out during the execution of any individual test.
I tried finding a solution by moving the logic down and got something to work, something along the lines of:
class MyTestClass extends TestCase
{
public function setUp()
{
// No call to parent::setUp()
// From: Illuminate\Foundation\Testing\TestCase
$this->app = $this->createApplication();
$this->client = $this->createClient();
// Not this one!
//$this->app->setRequestForConsoleEnvironment();
$this->app->boot();
// ...
}
public function testWhatever()
{
// Calls to this will now actually have a request object
$this->call('GET', '/api/v1/courses');
}
}
But that just can't be right, at least it doesn't feel so.
Instead, I figure it's probably best not to rely on anything in the Request object from within Service Providers. Instead, why not just inject an object that can do what I need it to do in the controller I want it to? No Service Provider necessary, and I can easily mock any object other than Request in Unit Tests. I should've believed the docs.
Update
Taking this answer to my own question a bit further, I believe my original mistake was that I was utilizing the Request object within a Service Provider. I think, on retrospection, that you should probably never use the Request object at all within a service provider, because providers get loaded for everything related to laravel (including artisan commands, which of course should have no request). My original code worked in the browser, but I probably would have noticed issues had I tried to run any artisan commands.
I came across this looking for a good way to handle a view composer that uses the request, and ultimately ended up going with constructor injection - because Laravel uses the Symfony Request class, it was really easy to "mock" a request with Request::create('http://myurl.com'), and pass it into an instance of my class for testing. Much better than trying to mess with stubbing methods on the request class, and doesn't rely on building up the whole Laravel application.
For clarity's sake, the view composer looks generally like this:
class SiteLayoutComposer extends BaseComposer {
function __construct(Request $request) {
$this->request = $request;
}
public function compose($view)
{
$templateName = preg_match('/siteurlexample/', $this->request->getHost()) ? 'site1' : 'site2';
$view->with('siteLayout', "layouts.sites.{$templateName}");
}
}
And the test:
class SiteLayoutComposerTest extends TestCase {
public function testWillSetTheSiteLayoutToSite1()
{
$request = Request::create('http://www.siteurlexample.com');
$view = View::make('home');
$composer = new SiteLayoutComposer($request);
$composer->compose($view);
$this->assertEquals('layouts.sites.site1', $view->siteLayout);
}
}

Getting Instance Of Silex\Application

I am creating an application with Silex and was wondering if it is possible to somehow get the instance of the Silex\Application in a place where I can't do method_name(Application $application) in the method parameters?
For example, say I have a private method on a controller that is not an action. If I put Application $application as the parameter, it throws an error saying I need to pass it in.
I would rather not have to manually pass that method in if I don't have to.
There's really only two ways to do it.
a) Pass Silex\Application as an argument to the constructor of your class and assign it as an instance variable.
b) Pass the Silex\Application to your private method as an argument by hand.
Are you sure you need the full app in your class? The point of dependency injection is to inject dependencies directly, instead of injecting the container (yes, Silex\Application extends \Pimple, which is a Dependency Injection Container.
From your comment on the other answer, your goal of getting at the Silex/Application is to get at the Twig service there. The way that I've solved getting at the current application in another function for my Silex projects is:
use \Silex\Application;
use \Symfony\Component\HttpFoundation\Request;
class myController {
private $a;
// Route defined as:
// $app->get('/foo', 'myController::showPage');
public function showPage(Application $a) {
$this->a = $a;
return $this->doAwesome();
}
private function doAwesome() {
$twig = $this->a['twig'];
return $twig->render('awesomePage.twig');
}
}
Every function that is an endpoint for a route would then save the Application passed to it as a class property, for other functions to get at. It means you have to remember to do $this->a = $a; in every function that uses doAwesome() (before calling doAwesome()), but that's the cleanest way I've come up with to tackle that.

Categories