Should I create separate provider for service in Laravel? - laravel

I have custom service in Laravel:
<?php
namespace App\Library\Services;
class RegisterCustomerService
{
public function do()
{
return 'Output from DemoOne';
}
}
In which cases I should create provider for this service, when not?
Could I use RegisterCustomerService as composition in specific class like:
$c = new RegisterCustomerService();
Or am i obligated to create Provider?

Service providers are only necessary if the service needs custom configuration. You can typehint any class in the constructor and Laravel will try to resolve it to an instance.
An example service provider that configures a service with a config value is shown below:
class MyServiceProvider extends ServiceProvider
{
public function register()
{
$this->app->singleton(MyCustomService::class, function ($app) {
return new MyCustomService(config('api_token'));
});
}
}
usage:
class ProjectController {
// Receives the service configured by the service provider above.
public function __construct(MyCustomService $service){
$this->service = $service;
}
}
More details about service providers: https://laravel.com/docs/5.8/providers

Related

How do I do the type-hint 'automatic injection' custom class laravel

Below is the EmailNotifier Class
class EmailNotifier
{
public function notify()
{
echo 'Sending payment notification via email' ;
}
}
Below is my AppServiceProvider
class AppServiceProvider extends ServiceProvider
{
public function boot()
{
}
public function register()
{
$this->app->make(App\EmailNotifier::class); // resolve the EmailNotifier Class
}
}
Below is billing class
class Billing
{
protected $notifier;
public function __construct(EmailNotifier $notifier)
{
$this->notifier = $notifier;
}
public function pay()
{
// Process the bill payment
$this->notifier->notify();
}
}
and in my controller I did
$data = new Billing(1);
As you can see I already resolve the EmailNotifier Class at the AppServiceProvider Class but when I call that like the code above, it throws an error said 'must be an instance of EmailNotifier'
and based on the laravel documentation, it's stated that :
you may "type-hint" the dependency in the constructor of a class that
is resolved by the container (for the automatic injection)
how do I achieve automatic injection for the type-hint in laravel ?
Use $data = resolve(Billing::class); instead of $data = new Billing(1); and you can remove $this->app->make(App\EmailNotifier::class); // resolve the EmailNotifier Class from service provider's register method.

Injecting Interface on class (Laravel Package)

I'm developing my own L5 package for handling payments. To be able to change the payment gateway in the future, I'm using interfaces.
My interface looks like this:
interface BillerInterface
{
public function payCash();
public function payCreditCard();
}
I also have a concrete implementation, which is the desired payment gateway.
class Paypal implements BillerInterface
{
public function payCash()
{
// Logic
}
public function payCreditCard()
{
// Logic
}
}
The Biller class is the main class, and the constructor method expects the above interface, like so:
class Biller {
protected $gateway;
public function __construct(BillerInterface $gateway)
{
$this->gateway = $gateway;
}
// Logic
}
Last, I created the service provider, to bind the interface to the gateway class.
public function register()
{
$this->app->bind(BillerInterface::class, 'Vendor\Biller\Gateways\Paypal');
}
Seems to be working, but I'm getting an error when trying to instantiate the Biller class...
Biller::__construct() must be an instance of Vendor\Biller\Contracts\BillerInterface, none given
I tried the following code but doesn't seem to work...
public function register()
{
$this->app->bind(BillerInterface::class, 'Vendor\Biller\Gateways\Paypal');
$this->app->bind(Biller::class, function ($app) {
return new Biller($app->make(BillerInterface::class));
});
}
Any clues?
You’re binding interfaces to an implementation fine in your service provider. But dependencies will only be resolved by the service container, i.e.
class SomeClass
{
public function __construct(Billing $billing)
{
$this->billing = $billing;
}
}
Laravel’s service container will read the type-hint of the constructor method’s parameters, and resolve that instance (and also any of its dependencies).
You won’t be able to “new up” the Billing instance directly (i.e. $billing = new Billing) because the constructor is expecting something implementing BillingInterface, which you’re not providing.
When you're binding interface to actual class try replacing the BillerInterface::class with a string '\Your\Namespace\BillerInterface'
This is how I've done it in my app and it seems to be working:
public function register()
{
$this->app->bind('DesiredInterface', function ($app) {
return new DesiredImplementationClass(
$app['em'],
new ClassMetaData(DesiredClass::class)
);
});
}
Talking about #MaGnetas answer
I prefer to bind class with interface using this way.
public function register()
{
$this->app->bind(AppointmentInterface::class, AppointmentService::class);
}
This helps IDEs to find the path of the class and we can jump to that class by just clicking on it.
If we pass class path as string path like shown below:
public function register()
{
$this->app->bind('App\Interfaces\AppointmentInterface', 'App\Services\AppointmentService');
}
Then IDE can not find the location of class when we click on this string.

Laravel dependency injection into custom non-controller class fails in PHPUnit

all.
For a Laravel project I'm working on, I've started to use Dependency Injection in order to mock classes in my tests. However, I've found that if I try to inject into a custom class with no explicit parent, two things are true:
The dependency injection works correctly when running the application
The injection fails when running tests in PHPUnit
Here is some sample code similar to what I'm using:
DemoController
// The controller we're testing
class DemoController extends Controller
{
// The injection and constructor
private $helpLayer1;
public function __construct(HelpLayer1 $helpLayer1)
{
$this->helpLayer1 = $helpLayer1;
}
...
// The test function I call
public function testDeps()
{
$this->helpLayer1->testLayer1();
}
}
HelperLayer1
// Our first helper class
class HelperLayer1
{
private $helpLayer2;
public function __construct(HelpLayer2 $helpLayer2)
{
$this->helpLayer2 = $helpLayer2;
}
...
// The testing function
public function testLayer1()
{
// When called via route, this dumps the actual object
// When called via test, this returns null
dd($this->helperLayer2);
}
}
Helper1ServiceProvider
class Helper1ServiceProvider extends ServiceProvider
{
public function register()
{
$this->app->bind('HelperLayer1', function()
{
return new HelperLayer1(App::make('HelperLayer2'));
});
}
[OR]
public function register()
{
$this->app->bind('HelperLayer1', 'HelperLayer1');
}
}
Helper2ServiceProvider
class Helper2ServiceProvider extends ServiceProvider
{
public function register()
{
$this->app->bind('HelperLayer2', 'HelperLayer2');
}
}
I'm relatively new to using DI, so I'm not entirely sure that this set-up is correct, but I'm at a loss.
Any help would be greatly appreciated! Thank you!

Grouping event registrations on a service provider

I want to define a service provider that registers events. So have done the following (taken from a book)
<?php
namespace MyApp\Providers;
use Illuminate\Support\ServiceProvider;
class EventsProvider extends ServiceProvider
{
public function boot()
{
Event::listen('some.event', function($parameter)
{
// Handle the event..
});
}
}
Then I added it to the provider array.
But when I execute the code I get the following error
implement the remaining methods
(Illuminate\Support\ServiceProvider::register)
It compels me to declare a register() method.
When I add to the EventsProvider class a method named register() (without implementation, and just make it return null) I get the following error
Class 'MyApp/Providers/EventProvider' not found
Why is that, and how can I solve it?
You just need to declare it, because it's declared in the Interface:
<?php namespace MyApp\Providers;
use Illuminate\Support\ServiceProvider;
class EventsProvider extends ServiceProvider {
public function boot()
{
Event::listen('some.event', function($parameter)
{
// Handle the event..
});
}
public function register()
{
}
}
Then execute
composer dump-autoload --optimize
But you might also have a typo somewhere because it says Class 'MyApp/Providers/EventProviders' not found, but it should be MyApp/Providers/EventProvider.

Symfony2 Use Doctrine in Service Container

How do I use Doctrine in a service container?
The Code just causes an error message "Fatal error: Call to undefined method ...::get()".
<?php
namespace ...\Service;
use Doctrine\ORM\EntityManager;
use ...\Entity\Header;
class dsdsf
{
protected $em;
public function __construct(EntityManager $em)
{
$this->em = $em;
}
public function create()
{
$id = 10;
$em = $this->get('doctrine')->getEntityManager();
$em->getRepository('...')->find($id);
}
}
services.yml
service:
site:
class: ...\Service\Site
According to your code, you already have an EntityManager injected. You don't need to call $em = $this->get('doctrine')->getEntityManager() — just use $this->em.
If you don't inject an EntityManager already, read this.
UPDATE:
You need to make the container inject an EntityManager into your service. Here's an example of doing it in config.yml:
services:
your.service:
class: YourVendor\YourBundle\Service\YourService
arguments: [ #doctrine.orm.entity_manager ]
I prefer to define bundles' services in their own services.yml files, but that's a bit more advanced, so using config.yml is good enough to get started.
For easily accessing the Entitymanager use the following one:
//services.yml
your service here:
class: yourclasshere
arguments: [#doctrine.orm.entity_manager]
And in the class itself:
class foo
{
protected $em;
public function __construct(\Doctrine\ORM\EntityManager $em)
{
$this->em = $em;
}
public function bar()
{
//Do the Database stuff
$query = $this->em->createQueryBuilder();
//Your Query goes here
$result = $query->getResult();
}
}
This is my first answer so any comments are appreciated :)
Please try this code:
$em=$this->container->get('doctrine')->getEntityManager();
$rolescheduels=$em->getRepository('OCSOCSBundle:RoleScheduel')->findByUser($user->getId());
For Symfony 3.x
The most easy-peasy solution for me was to just turn on autowiring/autoconfiguring, and then injecting the service I needed via the constructor. Note that I have also allowed any controller to be injected as a service by setting resource: '../../src/AppBundle/*'
#services.yml or config.yml
services:
_defaults:
autowire: true
autoconfigure: true
public: false
# Allow any controller to be used as a service
AppBundle\:
resource: '../../src/AppBundle/*'
# you can exclude directories or files
# but if a service is unused, it's removed anyway
exclude: '../../src/AppBundle/{Entity,Repository,Tests,DataFixtures,Form}'
Then in any service, you can inject & use the entity manager $em (or any other service/controller) via the constructor like this:
// class xyz
private $em;
// constructor
public function __construct(\Doctrine\ORM\EntityManager $em) {
$this->em = $em;
}
public function bar() {
//Do the Database stuff
$query = $this->em->createQueryBuilder();
//Your Query goes here
$result = $query->getResult();
}
for anyone who works with symfony3: u need to do the following inside config/services.yml in order to use doctrine in Service Container:
servicename_manager:
class: AppBundle\Services\MyServiceClass
arguments: [ "#doctrine.orm.entity_manager" ]
in the Symfony 3.4. If you want to use Doctrine in a service you can do it:
Only this method worked for me
services.yml:
YourBundle\PatchService\YourService:
public: true
arguments: [ '#doctrine.orm.entity_manager' ]
Service:
class YourService
{
private $em;
public function __construct($em) {
$this->em = $em;
}
Controller:
use YourBundle\PatchService\YourService;
/**
* #Route("/YourController/",name="YourController")
*/
public function indexAction()
{
$em = $this->getDoctrine()->getManager();
$Notification = new YourService($em);
I am using Symfony 3.4. If you want to create a service in a bundle this works for me:
services:
Vendor\YourBundle\Service\YourService:
arguments:
$em: '#doctrine.orm.entity_manager'
In your Service.php
<?php
namespace Hannoma\ElternsprechtagBundle\Service;
use Doctrine\ORM\EntityManager;
use Hannoma\ElternsprechtagBundle\Entity\Time;
class TimeManager
{
protected $em;
public function __construct(EntityManager $em)
{
$this->em = $em;
}
}
Since 2017 and Symfony 3.3 you can register Repository as service, with all its advantages it has.
Your code would change like this.
1. Service configuration
# app/config/services.yml
services:
_defaults:
autowire: true
...\Service\:
resource: ...\Service
2. Create new class - custom repository:
use Doctrine\ORM\EntityManagerInterface;
class YourRepository
{
private $repository;
public function __construct(EntityManagerInterface $entityManager)
{
$this->repository = $entityManager->getRepository(YourEntity::class);
}
public function find($id)
{
return $this->repository->find($id);
}
}
3. Use in any Controller or Service like this
class dsdsf
{
private $yourRepository;
public function __construct(YourRepository $yourRepository)
{
$this->yourRepository = $yourRepository;
}
public function create()
{
$id = 10;
$this->yourRepository->find($id);
}
}
Do you want to see more code and pros/cons lists?
Check my post How to use Repository with Doctrine as Service in Symfony.

Resources