DI object method - php

How do I inject dependency in some of the object methods instead of the constructor?
The example below works fine for __constructor injection
How do I inject the DateTime object in indexAction?
app.php
$app['index.controller'] = $app->share(function() use ($app) {
return new Controllers\IndexController(new \DateTime());
});
IndexController.php
namespace Moo\Controllers;
class IndexController
{
private $date;
public function __construct(\DateTime $date)
{
$this->date = $date;
}
public function indexAction()
{
return $this->date->format('y-m-d');
}
}

If your class has different dependencies depending on which method is called, than these methods should probably be defined in separate classes.
In case of controllers I think the rules are simple. Dependencies your action methods need should be passed via the constructor. Anything coming with a request should come as a method argument.
I'm not sure what kind of dependencies you're trying to inject. If they're just services, than you should split your controller to multiple classes. Big number of constructor arguments is a code smell. It's good you're concerned by it, but you're trying to solve it in a wrong way.
If the dependency is coming with a request, you should inject it into your controller method (action). A controller method should accept a request and return a response.
All route placeholders are automatically registered as a Request attribute. So if your date comes from a request:
$app->get('/my/path/{date}', 'index.controller:indexAction');
It will be available as a request attribute:
public function indexAction(Request $request)
{
$request->attributes->get('date');
}
Any request attributes can be directly injected into the controller:
public function indexAction($date)
{
}
This also means, that if you manually set a request attribute, it will be possible to inject it to your controller. It's matched by name:
// somewhere in your code (event perhaps)
$request->attributes->set('myDate', new \DateTime());
// in your controller
public function indexAction(\DateTime $myDate)
{
}
Finally you can convert simple types coming with the request, to more complex ones with route variable converters.
$callback = function ($post, Request $request) {
return new Post($request->attributes->get('slug'));
};
$app->get('/blog/{id}/{slug}', 'your.controller:indexAction')
->convert('post', $callback);
Read the docs for more.

Just for the record, as you won't probably want to do this, its is, indeed, possible to do method injection in Silex (in fact it is the Pimple container the one responsible to do it) by using the extend method:
<?php
$app['some_service'] = $app->share(function() use ($app) {
return SomeClass($app['some_dependency']);
});
$app['some_service'] = $app->extend('some_service', function($instance, $app) {
$instance->setSomeDependency($app['another_dependency']);
return $instance;
});
Having said that you should take into account what #JakubZalas is explaining as you will not want to call your controller action inside the extend method (you want to be called by the dispatcher).

Related

How to call a method service injected in another constructor service PHP Symfony

So I'm really trying to figure it out how can I do that in Symfony 5.
I have an services named PaymentRequestService which have the entire logic for requests to another application (based on microservices).
I injected PaymentRequestService in PaymentService as constructor, an service which processes data, make validation, etc.
And right now I'm trying to call from my controller an method from PaymentRequestService by using PaymentService. Something like that: $paymentService->$paymentRequestService->method.
Can someone tell me how can I do that?
Right now it looks something like that $payment = $paymentRequestService->getPaymentRequest((string)$id)
But I want to eliminate PaymentRequestService.
I dont argue the architecture you want to use... but you would do it this way:
(PHP 8 syntax)
class PaymentService
public function __construct(private PaymentRequestService $requestService)
{}
public function getRequest(): PaymentRequestService
{
return $this->requestService;
}
}
class MyController extends AbstractController
{
public function myAction(PaymentService $paymentService, Request $request): Response
{
$id = $request->get('id');
$payment = $paymentService->getRequest()->getPaymentRequest((string)$id);
return new Response('done');
}
}
you also could map specific methods to dont need the cascading - but then why you dont use the RequestService directly in the first place.
That should be doable over a getter like you have implemented currently, but why not just pass the PaymentRequestService to the controller where it is used?
e.g. $paymentRequestService->method()
Services are injectable in every class inside your application and since there is (by default) only one instance of that service running, you could just autowire it to your controller.
You have to inject service directly in controller and do data validation in PaymentService. For example:
class MyController extends AbstractController
{
private PaymentRequestService $paymentRequestService;
public function __construct(PaymentRequestService $paymentRequestService)
{
$this->paymentRequestService = $paymentRequestService;
}
public function index()
{
$this->paymentRequestService->method();
}
}

Laravel DI: call controller method without passing injected variable, is it possible?

class SomeController extends Controller {
public function doALot(Request $request) {
$this -> doOne($someOtherVariable);
// Type error: Argument 1 passed to App\Http\Controllers\SomeController::doOne() must be an instance of Illuminate\Http\Request
$this -> doOne($request, $someOtherVariable);
// Bad practice?
...
}
public function doOne(Request $request, $someOtherVariable) {}
...
}
So how do one call doOne() from doALot() without passing injected resource, yet having Request in doOne()? It feels like bad practice to pass $request all over the place.
Solution tl;dr not possible, but there are other ways – read short answer from Alexey Mezenin
Long version (probably not the best yet suffice).
$ php artisan make:provider SomeServiceProvider
Then in created provider edit register() call to something along the lines:
public
function register() {
$this -> app -> bind('App\Services\SomeService', function ($app) {
return new SomeService();
});
}
Then proceed to create service class which will have injected resource as attribute.
<?php
namespace App\Services;
use \Illuminate\Support\Facades\Request;
class SomeService {
private $request;
/**
* SomeService constructor.
*/
public
function __construct(Request $request) {
$this -> request = $request;
}
public function doOne($someOtherVariable) {}
}
Then move your methods from controller to service and inject service into controller instead.
Tradeoffs: (-) two useless files to perform basic functionality, (+) detaches logic implementation from controller, (~) probably cleaner code.
It's not a good idea to call controller actions manually. The business logic should be in the service class. You can see an example of that in my Laravel best practices repo. If you don't want to pass $request object every time, you can inject Request class in service class' or controller constructor.
Another way to use Request data is to use request() helper:
request('key')
request()->has('key')
Or Request facade:
Request::has('key')
Or you can manually inject it inside the method:
$request = app('Illuminate\Http\Request');

Using Request, Response in constructor in router class Slim Framework 3

I am using Slim Framework for my application. I am using routes. All is working fine. But now I want to do some pre-process working under my constructor on Request and Response.
So that I should not rework on every function of the class. Like getting host and token in every function. I am using middle-ware for many pre-process. But I also want to do some work in class constructor. When I am trying to access request and response interface in constructor, It is showing the error, Please show me the right way of using Request and Response in a class constructor. Will I have to append $app, or will need to work with container.
If it can be done without help of middleware, It will be great for me.
use \Psr\Http\Message\ServerRequestInterface as Request;
use \Psr\Http\Message\ResponseInterface as Response;
$app->group('/products', function() {
new \Products($this);
});
And I have a class called Products.
class Products
{
public function __construct($app)
{
$app->map(['GET','POST'], '/createupdate', array($this, 'createupdate'));
//I want to use Request and Response here in constructor. But it is showing error.
$this->req_data['request_token'] = $request->getAttribute('request_token');
}
public function createupdate($request, $response, $args) {
//This is working fine.
$this->req_data['request_token'] = $request->getAttribute('request_token');
}
}
When you really want to do this, then you could get the request/response object from the container.
class Products
{
public function __construct($app)
{
$app->map(['GET','POST'], '/createupdate', array($this, 'createupdate'));
$request = $app->getContainer()->get('request');
$this->req_data['request_token'] = $request->getAttribute('request_token');
}
// [..]
}
But, also this will not make much difference $this->req_data['request_token'] is nearly as long as $request->getAttribute('request_token'); so you should use this inside the code.
Note: I expect you to set this attribute already inside middleware, so it may not be available here, because first the container will create a new request object and second cause the middleware is not run when php executes your constructor code.
When you now still want to use $this->req_data['request_token'] inside your class then you should do this:
$products = new \Products();
$app->group('/products', function() use ($products) {
$products->addRoutes($this);
})->add($products); // add the class as middleware as well to set there the class attributes (__invoke function)
class Products
{
public function addRoutes($app)
{
$app->map(['GET','POST'], '/createupdate', array($this, 'createupdate'));
}
public function __invoke($request, $response, $next) // this is middleware function
{
$this->req_data['request_token'] = $request->getAttribute('request_token');
return $next($request, $response); // next in this example would be your route function like createupdate()
}
}
Ofcourse you will get an error, $request is not defined.
public function __construct($app)
{
$app->map(['GET','POST'], '/createupdate', array($this, 'createupdate'));
// Where does $request comes from?!
$this->req_data['request_token'] = $request->getAttribute('request_token');
}
Slim way of doing pre-processing is by using middlewares.
When a route is called, it is automatically injected with the Request and Response objects (and the request route params if any), but when a class is created for the route, it is not automatically injects those instances to the constructor, so they are not available "out of the blue".
If you have pre-processing, I would stick to middlewares, it is much cleaner code (although this is my opinion).

Cleaner code in Laravel for factory pattern

I have an application where I have to deal with multiple vendors - each having their different implementation. For example, let's say Payment Systems, there are many vendors and banks.
There are a few things common, like implementation process. In each case, I have to give a callback URL to them to give me a response.
For now, I have two vendors - VendorPay and VendorA.
I will have two routes:
payment/callback/vendorpay
payment/callback/vendora
each of them call two methods in controller.
processVendorpay and processVendora
Now, if I want to add, lets say, 15 more vendors like this, will I have to create methods everytime I add new vendor? Is there any cleaner solution for this?
My controller right now looks like this:
class PaymentController extends BaseController
{
protected $vendorpay_validator, $vendora_validator, $transaction, $transaction_log, $vendor_product, $vendor_transaction;
public function __construct(VendorpayValidator $vendorpay_validator, VendorAValidator $vendora_validator, Transaction $transaction, TransactionLog $transaction_log, VendorProduct $vendor_product, VendorTransaction $vendor_transaction)
{
$this->vendorpay_validator = $vendorpay_validator;
$this->vendora_validator = $vendora_validator;
$this->transaction = $transaction;
$this->transaction_log = $transaction_log;
$this->vendor_product = $vendor_product;
$this->vendor_transaction = $vendor_transaction;
}
}
These four are the Model Repository Objects: $transaction, $transaction_log, $vendor_product, $vendor_transaction
If I have to add more venodrs, it keeps on adding validator object here. What would be a much cleaner way to do it?
One of the solutions that I thought was - for multiple routes, I just create one method. Now I'll check the route in this method and call the Factory Object basis on that.
You should have just one route...
payment/callback/{vendor}
Then if you want to go the factory route (which I think would be a good idea in this case)...
class VendorValidatorFactory
{
private function __construct() {}
public static function getValidator($vendor)
{
switch ($vendor) {
case 'vendorpay':
return new VendorPayValidator;
case 'vendora':
return new VendarAValidator;
}
}
}
Remove the now unnecessary injections from your constructor and in the method which responds to your route, use the factory to grab the correct validator...
class SomeController extends Controller
{
public function __construct(Transaction $transaction, TransactionLog $transaction_log, VendorProduct $vendor_product, VendorTransaction $vendor_transaction)
{
$this->transaction = $transaction;
$this->transaction_log = $transaction_log;
$this->vendor_product = $vendor_product;
$this->vendor_transaction = $vendor_transaction;
}
public function processVendorResponse($vendor)
{
$validator = VendorValidatorFactory::getValidator($vendor);
}
}
And just a suggestion, every time you need to add a new method to your validator classes which your controller uses, add that to a ValidatorInterface and make sure all your validators implement that ValidatorInterface. This way when you need to add more, all you need to do is implement that interface and that should tell you exactly what functions you need to write. Then just update your factory method to include the new one and you are done. No more changing your controller, adding routes, or adding dependencies to your controller.

(Laravel) Dynamic dependency injection for interface, based on user input

I am currently facing a very interesting dilemma with my architecture and implementation.
I have an interface called ServiceInterface which have a method called execute()
Then I have two different implementations for this interface: Service1 and Service2, which implements the execute method properly.
I have a controller called MainController and this controller has a "type-hint" for the ServiceInterface (dependency injection), it means that both, Service1 and Service2, can be called as resolution for that dependency injection.
Now the fun part:
I do not know which of those implementations to use (Service1 or Service2) because I just know if I can use one or other based on a user input from a previous step.
It means the user choose a service and based on that value I know if a can use Service1 or Service2.
I am currently solving the dependency injection using a session value, so depending of the value I return an instance or other, BUT I really think that it is not a good way to do it.
Please, let me know if you faced something similar and, how do you solve it, or what can I do to achieve this in the right way.
Thanks in advance. Please let me know if further information is required.
Finally, after some days of researching and thinking a lot about the best approach for this, using Laravel, I finally solved it.
I have to say that this was especially difficult in Laravel 5.2 because, in this version, the Session middleware only is executed in the controllers used in a route, it means that if for some reason I used a controller (not linked for a rote) and try to get access to the session it is not going to be possible.
So, because I cannot use the session, I decided to use URL parameters. Here you have the solution approach; I hope some of you found it useful.
so, you have an interface:
interface Service
{
public function execute();
}
Then a couple of implementations for the interface:
Service one:
class ServiceOne implements Service
{
public function execute()
{
.......
}
}
Service two.
class ServiceTwo implements Service
{
public function execute()
{
.......
}
}
The interesting part is that I have a controller with a function with a dependency with the Service interface. Still, I need to resolve it dynamically to ServiceOne or ServiceTwo based on user input. So:
The controller
class MyController extends Controller
{
public function index(Service $service, ServiceRequest $request)
{
$service->execute();
.......
}
}
Please note that ServiceRequest, validated that the request already have the parameter that we need to resolve the dependency (call it 'service_name')
Now, in the AppServiceProvider we can resolve the dependency in this way:
class AppServiceProvider extends ServiceProvider
{
public function boot()
{
}
public function register()
{
//This specific dependency is going to be resolved only if
//the request has the service_name field stablished
if(Request::has('service_name'))
{
//Obtaining the name of the service to be used (class name)
$className = $this->resolveClassName(Request::get('service_name')));
$this->app->bind('Including\The\Namespace\For\Service', $className);
}
}
protected function resolveClassName($className)
{
$resolver = new Resolver($className);
$className = $resolver->resolveDependencyName();
return $className;
}
}
So now all the responsibility is for the Resolver class. This class basically use the parameter passed to the constructor to return the full name (with namespace) of the class that is going to be used as an implementation of the Service interface:
class Resolver
{
protected $name;
public function __construct($className)
{
$this->name = $className;
}
public function resolveDependencyName()
{
//This is just an example, you can use whatever as 'service_one'
if($this->name === 'service_one')
{
return Full\Namespace\For\Class\Implementation\ServiceOne::class;
}
if($this->name === 'service_two')
{
return Full\Namespace\For\Class\Implementation\ServiceTwo::class;
}
//If none, so throw an exception because the dependency can not be resolved
throw new ResolverException;
}
}
Well, I really hope it helps some of you.
Best wishes!
---------- EDIT -----------
I just realize that it is not a good idea to use the request data directly inside the container of Laravel. It really is going to cause some trouble in the long term.
The best way is to directly register all the possible instances supported (serviceone and servicetwo) and then resolve one of them directly from a controller or a middleware, so then is the controller "who decides" what service to use (from all the available) based on the input from the request.
In the end, it works at the same, but it is going to allow you to work more naturally.
I have to say thanks to rizqi, a user from the questions channel of the slack chat of Laravel.
He personally created a golden article about this. Please read it because it solves this issue completely and in a very right way.
laravel registry pattern
The fact that you define that your controller works with ServiceInterface is ok
If you have to choose the concrete implementation of the service basing on a previous step (that, as i've understood, happens in a previous request) storing the value in session or in database is right too, as you have no alternative: to choose the implementation you have to know the value of the input
The important point is to 'isolate' the resolution of the concrete implementation from the input value in one place: for example create a method that takes this value as a parameter and returns the concrete implementation of the service from the value:
public function getServiceImplementation($input_val)
{
switch($input_val)
{
case 1 : return new Service1();
case 2 : return new Service2();
}
}
and in your controller:
public function controllerMethod()
{
//create and assign the service implementation
$this->service = ( new ServiceChooser() )->getServiceImplementation( Session::get('input_val') );
}
In this example i've used a different class to store the method, but you can place the method in the controller or use a Simple Factory pattern, depending on where the service should be resolved in your application
It's an interesting problem. I'm currently using Laravel 5.5 and have been mulling it over. I also want my service provider to return a specific class (implementing an interface) based upon user input. I think it's better to manually pass the input from the controller so it's easier to see what's going on. I would also store the possible values of the class names in the config.
So based upon the Service classes and interface you've defined above i came up with this:
/config/services.php
return [
'classes': [
'service1' => 'Service1',
'service2' => 'Service2',
]
]
/app/Http/Controllers/MainController.php
public function index(ServiceRequest $request)
{
$service = app()->makeWith(ServiceInterface::class, ['service'=>$request->get('service)]);
// ... do something with your service
}
/app/Http/Requests/ServiceRequest.php
public function rules(): array
$availableServices = array_keys(config('services.classes'));
return [
'service' => [
'required',
Rule::in($availableServices)
]
];
}
/app/Providers/CustomServiceProvider.php
class CustomServiceProvider extends ServiceProvider
{
public function boot() {}
public function register()
{
// Parameters are passed from the controller action
$this->app->bind(
ServiceInterface::class,
function($app, $parameters) {
$serviceConfigKey = $parameters['service'];
$className = '\\App\\Services\\' . config('services.classes.' . $serviceConfigKey);
return new $className;
}
);
}
}
This way we can validate the input to ensure we are passing a valid service, then the controller handles passing the input from the Request object into the ServiceProvider. I just think when it comes to maintaining this code it will be clear what is going on as opposed to using the request object directly in the ServiceProvider.
PS Remember to register the CustomServiceProvider!
I find the best way to deal with this is using a factory pattern. You can create a class say ServiceFactory and it has a single method create() it can accept an argument which is used to dynamically choose which concrete class to instantiate.
It has a case statement based on the argument.
It will use App::make(ServiceOne::class) or App::make(ServiceTwo::class).depending on which one is required.
You are then able to inject this into your controller (or service which depends on the factory).
You can then mock it in a service unit test.
Recently, I had to implement a similar logic where I was to implement a method to perform mobile top-ups for multiple networks in our application. So, I decided to implement the logic using Factory and Bridge pattern. Factory to create an instance of the concrete Service class based on the user input, and then, the Bridge pattern to set closely related classes into separate hierarchies and route the request to the respective class.
In the controller's method, both Factory and Service classes are injected. The TopUpServiceFactory's create method creates an object of the concrete class. The TopUpService class then routes the request to that concrete class method.
class TopUpController extends Controller
{
public function topUp(Request $request, TopUpServiceFactoryInterface $serviceFactory, TopUpServiceInterface $topUpService)
{
$serviceFactory->create($request->networkCode);
$topUpService->TopUp($request->all());
}
}
The TopUpServiceFactoryInterface and TopUpServiceInterface are bound to TopUpServiceFactory and TopUpService concrete Classes respectively in Service Container.
class AppServiceProvider extends ServiceProvider
{
public function register()
{
$this->app->bind(TopUpServiceFactoryInterface::class, TopUpServiceFactory::class);
$this->app->bind(TopUpServiceInterface::class, TopUpService::class);
}
}
The create method accepts user input and creates an object of the respective class based on the user input.
class TopUpServiceFactory implements TopUpServiceFactoryInterface
{
public function create(string $networkCode)
{
switch ($networkCode) {
case 'network1':
app()->bind(NetworkServiceInterface::class, Network1Service::class);
break;
case 'network2':
app()->bind(NetworkServiceInterface::class, Network2Service::class);
break;
default:
app()->bind(NetworkServiceInterface::class, DefaultNetworkService::class);
break;
}
}
}
The Service Class then picks the object of NetworkService Class and forwards the request.
class TopUpService implements TopUpServiceInterface
{
public function topUp(array $requestParams)
{
$networkService = app()->get(NetworkServiceInterface::class);
$networkService->topUp($requestParams);
}
}
All network's concrete classes implement a common interface NetworkServiceInterface, which is used to inject dependency dynamically, implementing Liskov Substitution Principle
class Network1Service implements NetworkServiceInterface
{
public function topUp(array $requestParam)
{
Process Topup ......
}
}
class Network2Service implements NetworkServiceInterface
{
public function topUp(array $requestParam)
{
Process Topup ......
}
}
...

Categories