Binding to Laravel IoC instead of instantiating again and again - php

In my app I have a service called "LogService" to log events and other items. I basically need to use this on every controller to log events by users. Instead of having to instantiate this service in each controller, I had two thoughts for accomplishing this.
Option 1: Bind the service into the IoC and then resolve it that way
Option 2: Make a master class with the service in it and then extend it for other classes so they come with the service already bound
I have questions for each of these methods:
Option 1: Is this even possible? If so, would it just be with "App::make()" that it would be called? That way doesn't seem to play too well with IDE's
Option 2: I have done this kind of thing in the past but PHPStorm does not seem to recognize the service from the parent object because it is instantiated by "App::make()" and not through the regular dependency injection.
What would be the best course of action?
Thanks!

You can have it both ways, I think the neatest way would be:
1) Have an interface that describes your class, let's call it LogServiceInterface
2) Create a Service Provider that instantiates your class, like so:
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
class LoggerServiceProvider extends ServiceProvider
{
/**
* Register bindings in the container.
*
* #return void
*/
public function register()
{
$this->app->bind(LogServiceInterface::class, function($app)
{
return new LogService();
});
}
}
3) Register this service provider in config/app.ph file:
'providers' => [
// Other Service Providers
App\Providers\LoggerServiceProvider::class,
],
4) Now, in controller you can request the instance of something that implements LoggerServiceInterface straight in the constructor:
(Some controller):
<?php namespace App\Http\Controllers;
use Illuminate\Routing\Controller;
use App\Repositories\OrderRepository;
class OrdersController extends Controller {
/**
* The logger service.
* #var LoggerServiceInterface $loggerService
*/
protected $loggerService;
/**
* Create a controller instance.
*
* #param OrderRepository $orders
* #return void
*/
public function __construct(LoggerServiceInterface $loggerService)
{
$this->loggerService = $loggerService;
}
/**
* Show all of the orders.
*
* #return Response
*/
public function index()
{
// $this->loggerService will be an instance of your LoggerService class that
// is instantiated in your service provider
}
}
This way, you have got an easy way to quickly change the implementation of your service, moreover, Phpstorm can handle this very easily.
You will still be able to use app()->make() to obtain an instance of your service.
This, however, will not be automatically picked up by Phpstorm. But you can help it to understand that, all you need to do is to use #var annotation, see:
/**
* #var LoggerServiceInterface $logger
*/
$logger = app()->make(LoggerServiceInterface::class);
That way, Phpstorm will know what to expect from that $logger object.

Related

inject model in laravel controllers constructor

I want to know if this is a good practice to use my model class in controllers in this way :
public function __construct(Rule $rules)
{
$this->rules = $rules;
}
I do not want to repeat myself in my controllers so I want to know what is the best approach for that
You use Dependency Injection - it is very good practice.
According to documentation:
Dependency injection is a fancy phrase that essentially means this: class dependencies are "injected" into the class via the constructor or, in some cases, "setter" methods.
namespace App\Http\Controllers;
use App\User;
use App\Repositories\UserRepository;
use App\Http\Controllers\Controller;
class UserController extends Controller
{
/**
* The user repository implementation.
*
* #var UserRepository
*/
protected $users;
/**
* Create a new controller instance.
*
* #param UserRepository $users
* #return void
*/
public function __construct(UserRepository $users)
{
$this->users = $users;
}
/**
* Show the profile for the given user.
*
* #param int $id
* #return Response
*/
public function show($id)
{
$user = $this->users->find($id);
return view('user.profile', ['user' => $user]);
}
}
In this example, the UserController needs to retrieve users from a data source. So, we will inject a service that is able to retrieve users. In this context, our UserRepository most likely uses Eloquent to retrieve user information from the database. However, since the repository is injected, we are able to easily swap it out with another implementation. We are also able to easily "mock", or create a dummy implementation of the UserRepository when testing our application.
Read also about Service Container - it is powerful tool:
https://laravel.com/docs/5.6/container
It is a good practice for injecting models in controllers, however, the recommended approach is:
Have a use statement at the top of your controller file
Implement it in the functions that requires access to the model, i would not recommend you do it in your controller
If you have a look at the documentation, you will be able to bind the model directly to your route and eliminate some hassle of Model::find(id) https://laravel.com/docs/5.6/routing#route-model-binding
The constructor approach you presented is recommended in using other classes like repositories, singletons, or whatever functionality you wish to inject, see the docs for more info: https://laravel.com/docs/5.6/container
Hope this helps

How to create aliases in laravel?

I want to create aliases in laravel like Auth so that i can use it in view just like
Auth::user().
For example, i want to return data from my setting table and want to use it like
Setting::method()->value. in view and use Setting in controllers.
What should i create for this Facades Or Service Providers? Provide me some procedures.
I tried using Service Providers but i am confused how to call database there.
This will be a long answer, using Laravel 5.4.
You need a service class (in this case, it's a concrete class behind the facade). I made it inside app/Services folder:
namespace App\Services;
use Illuminate\Database\DatabaseManager;
class Setting
{
protected $db;
public function __construct(DatabaseManager $db)
{
$this->db = $db;
}
public function method()
{
return;
}
}
As you see, I inject the database inside that concrete class. This is the magic of Laravel IoC. It will automatically resolve any dependency into your concrete class.
Next, create a facade. I made it inside app/Facades folder:
namespace App\Facades;
use Illuminate\Support\Facades\Facade;
class Setting extends Facade
{
/**
* Get the registered name of the component.
*
* #return string
*/
protected static function getFacadeAccessor()
{
return 'setting';
}
}
Notice the setting string returned from getFacadeAccessor. You need to register this service name later to the Container (IoC). The magic behind this facade is, it's automatically call (proxy) any static method to the instance method of concrete class, in this case you can call:
Service::method()
Register the service to the container. Inside your AppServiceProvider
namespace App\Providers;
use App\Services\Setting;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
// ...
/**
* Register any application services.
*
* #return void
*/
public function register()
{
// ...
$this->app->singleton(Setting::class);
$this->app->alias(Setting::class, 'setting');
}
}
In this Service Provider, I declare that Setting service is a singleton service. Which means, you can use Setting service in another place without re-initialize class, in another words you are using the same instance across file. Last, I tell the container that Setting service has another alias, name setting, so Container can figure out which Concrete Class behind App/Facade/Setting.
For aliasing, as mentioned by apokryfos. Register your facade alias inside config/app.php, find the section aliases and add this line:
'aliases' => [
// ...
'Setting' => App\Facades\Setting::class,
],
After this you can call your facade like this:
use Setting;
Setting::method();
Hope this helps.

Laravel Contracts Vs Service providers

How can i implement laravel contracts and service providers ?
I have a class Cart and its method purchase , the class will of course contain my program structure to which most of my controllers will rely upon.
How can effectively manage dependency injection of this class among other controllers ?
You can simply reference the class as dependency in your controller. It will be instantiated even if you don't use an interface.
<?php
namespace App\Http\Controllers;
use App\Users\Repository as UserRepository;
class UserController extends Controller
{
/**
* The user repository instance.
*/
protected $users;
/**
* Create a new controller instance.
*
* #param UserRepository $users
* #return void
*/
public function __construct(UserRepository $users)
{
$this->users = $users;
}
}
If you were to use an interface just make sure your class implements it and simply call use the bind method from the app instance in any of your service providers.
$this->app->bind(UserRepository::class, EloquentUserRepository::class);
https://laravel.com/docs/5.2/container
https://laravel.com/docs/5.2/providers

Which provider do I use to bind my database repositories in laravel 5?

I am working with laravel 5 and I am novice developer so I just learnt how to use the Repository pattern. Now as novices go, I first use the routes.php file to bind my UserRepositoryInterface to DbUserRepository like so:
App::bind('UserRepositoryInterface', 'DbUserRepository');
This is just psuedo, image the namepspaces with the above code too.
So after this I realized that that there is something called a Service Provider which is supposed to contain code like so. Now I refactored this in the AppServiceProvider in my Providers folder and it works fine still.
But since I will be having so many more repositories, is this is a good idea to place them into the AppServiceProvider or should I go ahead and make a dedicated provider for my repositories and bind them there? What is the best way to do this? Is there a standard for this?
So later I got to understand that this all about preference so I coded a RepositoryServiceProvider in which I bind all the repository contracts to the desired implementations like so:
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use Archive\Repositories\TenantRepository;
use Archive\Repositories\TenantRepositoryEloquent;
use Archive\Repositories\UserRepository;
use Archive\Repositories\UserRepositoryEloquent;
use Archive\Repositories\OrderRepository;
use Archive\Repositories\OrderRepositoryEloquent;
class RepositoryServiceProvider extends ServiceProvider
{
/**
* Bootstrap the application services.
*
* #return void
*/
public function boot()
{
//
}
/**
* Register the application services.
*
* #return void
*/
public function register()
{
$this->app->bind(TenantRepository::class, TenantRepositoryEloquent::class);
$this->app->bind(UserRepository::class, UserRepositoryEloquent::class);
$this->app->bind(OrderRepository::class, OrderRepositoryEloquent::class);
}
}

Attempting to bind Guzzle Curl Client to Laravel's Service Container -- then Type Hint the Client Fails when attempting to __construct()

So I figured I'd try to actually use this fancy IoC container in Laravel. I'm starting with Guzzle but I cannot get it to work. Perhaps there is a gap in my understanding. I really appreciate any help here.
so I've got a class for connecting to a RESTful Api. Here is a sample from it:
use GuzzleHttp\Exception\BadResponseException;
use GuzzleHttp\Client;
use GuzzleHttp\Subscriber\Oauth\Oauth1;
class EtApi {
//you can pass in the model if you wanna
//protected $model;
//client Id
protected $clientId;
//client secret
protected $clientSecret;
//base_uri
protected $getTokenUri;
protected $client;
//build
function __construct(Client $client)
{
$this->client = $client;
$this->clientId = 's0m3R4nd0mStr1nG';
$this->clientSecret = 's0m3R4nd0mStr1nG';
$this->getTokenUri = 'https://rest.api/requestToken';
$this->accessToken = $this->getToken($this->clientId, $this->clientSecret, $this->getTokenUri);
}
}
I've successfully installed and used Guzzle by manually newing it up inside of methods like $client = new Client(); but that's not very DRY and it's not the right way of doing things. So I created a ServiceProvider at app\Providers\GuzzleProvider.php. I made sure this was registered in app/config/app.php under $providers = ['App\Providers\GuzzleProvider']. Here is the Provider Code:
<?php namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use GuzzleHttp\Client;
use GuzzleHttp\Subscriber\Oauth\Oauth1;
class GuzzleProvider extends ServiceProvider {
/**
* Bootstrap the application services.
*
* #return void
*/
public function boot()
{
//
}
/**
* Register the application services.
*
* #return void
*/
public function register()
{
//
$this->app->bind('Client', function () {
return new Client;
});
}
}
So when I try to access my EtApi methods that load fails during the instantiation (__construct) with the following error.
ErrorException in EtApi.php line 23:
Argument 1 passed to App\EtApi::__construct() must be an instance of GuzzleHttp\Client, none given, called in /home/vagrant/webdocs/et_restful_test/app/Http/Controllers/EtConnectController.php on line 23 and defined
Do any of you Laravel Masters have any idea why I can't bind Guzzle using this code and Laravel's magic will just inject the obj into the constructor? The [docs1 say I should be able to do this. I must be missing something. Thank You!
It's a little hard to say for certain based on the information in your question, but based on this
Argument 1 passed to App\EtApi::__construct() must be an instance of GuzzleHttp\Client, none given, called in /home/vagrant/webdocs/et_restful_test/app/Http/Controllers/EtConnectController.php on line 23 and defined
It sounds like you're directly instantiating your App\Eti class on line 23 of EtConnectController.php with code that looks something like this
$api = new App\EtApi;
If that's the case, there's a key piece of Laravel's dependency injection you're missing. Laravel can't change the behavior of standard PHP -- i.e. if you create a new class with PHP's built-in new keyword, then Laravel never has the change to inject any dependencies in __construct.
If you want to take advantage of dependency injection, you also need to instantiate your object via Laravel's app container. There's many different way to do that -- here's two them
//$api = new App\EtApi;
\App::make('App\EtApi'); //probably "the right" way
$api = app()['App\EtApi']
If you do that, Laravel will read the type hints in __construct and try to inject dependencies for your object.
Just change your register function to
/**
* Register the application services.
*
* #return void
*/
public function register()
{
//
$this->app->bind('GuzzleHttp\Client\Client', function () {
return new Client;
});
}
That should do the trick => the IOC resolves the fqcn and not the short one, so exposing it in your container you'll need to bind it to the fqcn too!
Hope it helps!

Categories