I am developing a web application in which I will be using third party API integrations (Payment gateways, SMS Vendors, Emailing services like mailchimp, other external APIs). My application will be having the repository pattern which will have repositories associated with each of my models and my controllers will be utilising the repository functions.
Now where does all my API integrations comes in? Should I be keeping all my API integrations in a single repository?
Example :
If I have a SMS provider Valuefirst, how does my Laravel repository design look like?
My SMS interface
interface ISmsProvider {
public function sendSms($mobileNo,$msg);
}
My ValueFirst (SMS Provider) repository
class ValueFirstRepository implements ISmsProvider {
public function sendSMS($mobieNo,$msg)
{
//API Call to ValueFirst SMS provider
// Any database insertions/updates to SMS model
// Any file logs
}
}
Assuming I have a service provider class where I have done the binding of the interface and repository my controller will look like
SMS Controller
class SmsController extends BaseController {
// ISmsProvider is the interface
public function __construct(ISmsProvider $sms)
{
$this->sms = $sms;
}
public function sendSMS()
{
$mobileNo = '9999999999';
$msg = 'Hello World!';
$users = $this->sms->sendSMS($mobileNo,$msg);
}
}
My questions
Is this the right approach to an API integration using repository pattern?
Does anything related to any database activities happens in the ValueFirst repository? Eg : If I want to update a record after the API response from ValueFirst, do I do it in the repository?
If it's right , can someone show me how the model interactions happens from the repository?
Now if I need to choose between ValueFirst or any other vendor how does that happens? How can I do it from the controller?
I will be using this project as a skeleton for my application
l5-Repository
Please help me with this design, I am fairly new to Laravel / repository pattern. Trying to learn stuff. TIA :)
Your approach seems pretty good to me but there is something i would improve
First of all I would keep the ValueFirstRepository class with the only resposability of managing your SMS API, and inject a specific repository class ( SmsRepository ) to interact with the DB through eloquent ( or only a model if you don't need a repository )
The important point here is to keep the resposability of managing your API in one class and the responsability of interacting with the DB in another:
ValueFirstRepository
class ValueFirstRepository implements ISmsProvider
{
//INJECT REPOSITORY CLASS TO INTERACT WITH DB
public function __construct(SmsRepository $smsRepo )
{
$this->smsRepo = $smsRepo;
}
//this method will be called from your controller
public function sendSMS($mobieNo,$msg)
{
//API CALL
$info = this->sendSMSAPI($mobieNo,$msg);
//DB CALL
$this->smsRepo->save( $info );
// Any file logs
}
//this will actually interact with the API
protected function sendSMSAPI($mobieNo,$msg)
{
//API Call to ValueFirst SMS provider
}
}
A litte variant to this solution could be using events, and fire events in your ValueFirstRepository class when a sms is sent, and respond to that event implementing some listeners that will do other operations related to the event
Another alternative solution could be to handle the steps directly in your controller:
SMS Controller
//INJECT THE DEPENDECIES IN YOUR CONTROLLER
public function __construct(ISmsProvider $sms, SmsRepository $smsRepo )
{
$this->sms = $sms;
$this->smsRepo = $smsRepo;
}
public function sendSMS()
{
//send SMS
$mobileNo = '9999999999';
$msg = 'Hello World!';
$info= $this->sms->sendSMS($mobileNo,$msg);
//use the model to save data
this->$smsRepo->save($info);
}
This time the dependency of SmsRepository would be injected in the controller, instead of the ValueFirstRepository class, and the controller's methods would be a litte more big, but it's up to you to decide the best way for you
For the last question: if you want to change your vendor provider, you could use Laravel's capability to bind interfaces to implementation through the bind method:
App::bind( App\ISmsProvider::class , App\ValueFirstRepository::class );
This will tell laravel wich class inject when requesting a specific interface. So, in this case, when a ISmsProvider interface is requested, Laravel will automatically inject a ValueFirstRepository concrete instance.
If you want to change the vendor you should only change the line to:
App::bind( App\ISmsProvider::class , App\AnotherSMSVendorRepository::class );
and the AnotherSMSVendorRepository class will be injected instead of ValueFirstRepository
Related
In my project I have decided to use the Service Pattern (Possibly with the Repository Pattern) to deal with the business logic in my application. I have for example a Client model which represents a customer and a corresponding ClientService that is responsible for client-specific business logic.
class ClientService extends Service implements ClientServiceContract
{
public function create(array $attributes)
{
// Create a new client...
}
public function doSomethingElse(Client $client)
{
// Do something else
}
}
Say for example I have another service UserService, which is similar to the ClientService above in that it has methods to create and do other things to User models.
Now on my site, imagine that I have a form that someone can fill in to register their interest in becoming a client. In my back end system I would like to create a button that takes a client's interest record ClientInterest and creates a Client, a User, associates the two and finally sends an e-mail to the new user with the details.
Where, when using the service pattern would it be best to put this logic?
I have considered:
Create a service and method ClientInterestService::createClientAndUser(...) which would use the ClientService and UserService classes to create the Client and User instances and then carry out the association before triggering an event which sends the email. This approach means that I'm not duplicating code, however I'm coupling classes together and I'm breaking some SOLID principles. I'm not sure but I have a feeling this wouldn't be great for testing either.
As described above, create a service class and method to carry out the logic, but instead of using the other two services I would write the logic to create the Client and User instances, carry out the association and trigger the event to send the email. This approach feels nicer, my code is more loosely coupled and I'm not breaking any SOLID principles, however, I'm potentially duplicating code.
Simply put the logic that I would have had in ClientInterestService::createClientAndUser(...) in my controller. Doing this would mean that I have business logic in my controller which kind of defeats the point of having services.
I think if you break this down into smaller steps you can achieve DRY architecture. The steps I'm seeing are:
Create Client
Create User
Associate (via pivot table, junction table etc)
Email
To avoid having the dreaded duplicate code you'd create a method around each of these in your service class or classes. You'd then create an action encapsulating all of the steps involved based around these methods.
Don't be scared to implement things outside of your service class - this doesn't mean it is outside of your service layer.
I see registering client interest as an action. You follow synchronous steps to achieve your desired action. So based on methods like creating a user, client etc we can build an action to register client interest, like so:
<?php
class ClientService {
public function addAction(IAction $action)
{
return $action->process();
}
public function createUser() {} // business logic for creating a user.
public function createClient() {} // business logic for creating a client.
public function createAssociation() {} // business logic for creating an association.
}
interface IAction {
public function process();
}
class RegisterClientInterestAction implements IAction {
protected $client;
public function __construct(ClientService $client)
{
$this->client = $client;
}
public function process()
{
$this->createUser()->createClient()->createAssociation();
}
private function createUser() {} // interact with your client service to call the method $client->createUser()
private function createClient() {} // interact with your client service to call the method $client->createClient()
private function createAssociation() {} // interact with your client service to call the method $client->createAssociation()
}
//USAGE
$service = new ClientService;
$results = $service->addAction(new RegisterClientInterestAction($service));
?>
By doing it this way you are able to utilise the createUser etc methods in a new action but without duplicating the code. By having the addAction on the service class you are still executing the business logic inside of your service layer.
If two or more services are required, I'd take a slightly different approach by moving where I would execute the action.
In terms of handling more than one service you can use DI within the constructor of your action.
Like this:
<?php
class Service {
public function addAction(IAction $action)
{
return $action->process();
}
// Other stuff for a base service...
}
class UserService extends Service {
public function createUser() {} // business logic for creating a user.
}
class ClientService extends Service {
public function createClient() {} // business logic for creating a client.
public function createAssociation() {} // business logic for creating an association.
}
interface IAction {
public function process();
}
class RegisterClientInterestAction implements IAction {
protected $client;
protected $service;
public function __construct(ClientService $client, UserService $user)
{
$this->user = $user;
$this->client = $client;
}
public function process()
{
$this->createUser()->createClient()->createAssociation();
}
private function createUser() {} // interact with your user service to call the method $client->createUser()
private function createClient() {} // interact with your client service to call the method $client->createClient()
private function createAssociation() {} // interact with your client service to call the method $client->createAssociation()
}
//USAGE
$service = new Service;
$results = $service->addAction(new RegisterClientInterestAction(new ClientService, new UserService));
?>
What feels best for me is your proposed solution of #2.
What I like to do is build out the two service classes and see what the duplication there is, then refactor/extract any duplication to another class. This way all classes are very testable and you have the least chance of breaking any SOLID principles.
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 ......
}
}
...
I read in the documentation how to use a controller as a service. But I am not sure what would be the purpose of it. Why not then simply use a service (a class define as a service)?
If anyone could give me some good examples of transforming a controller in a service that would be great.
The classical Symfony controller uses a Service Locater pattern to pull in it's dependencies:
class PersonController
{
public function showAction()
{
$personRepository =
$this->getDoctrine()->getEntityManager()->getRepository('Entity\Person');
$person = $personRepository->find(1);
return new JsonResponse($person);
Getting the person repository requires the action to have quite a bit of knowledge about how to locate things. Somewhat magical in fact . The controller is tied directly to doctrine and the framework infrastructure.
It also makes the action hard to test. You have to make a container then define the necessary services before running the action.
Contrast that with a controller defined as a service with it's dependencies injected:
class PersonController
{
protected $personRepository;
public function __construct($personRepository)
{
$this->personRepository = $personRepository;
}
public function showAction()
{
$person = $this->personRepository->find(1);
The action no longer needs know about how to locate the repository. It's just there. For testing, just need to make a repository and inject it. Clean and simple.
I have a question! I have a library, whenever I need to call, I'll include it into & new Class() like the link below.
Now, I want to include it to use with Lumen framework & call usually inton Controller, then how to register service, class in Lumen to make it comfortable so that when needing, just call new FileMaker();
http://laravel.io/bin/E3d9x
Thank you so much!
What you're looking for is a Service Provider. Instead of including files in your Controllers, and then new'ing up instances of a class it is better to register the class within a service provider and then resolve the object out of the IoC container.
An example of how you can register a provider:
public function register()
{
$this->app->singleton('Full\Vendor\Namespace\FileMaker', function($app) {
return new FileMaker('someparameters');
});
}
Doing it this way means that you can inject dependencies into your Controllers and Laravel, or Lumen in this case will automatically resolve the object without you needing to instantiate the object.
For example, in your controller:
public function someControllerMethod(FileMaker $filemaker)
{
// The $filemaker instance is available to use
}
I'm currently working on my first PHP/Laravel 4 project, I'm developing on it a storage class to add Eloquent support to a 3rd party library.
My EloquentStorage class extends the AbstractStorage class from the library, and I make usage of most of AbstractStorage methods. Now that I want to add Eloquent support to my new EloquentStorage class, I faced the fact that PHP does not support multiple inheritance.
Is there a proper way to define an Eloquent model without extending it as:
class MyClass extends Eloquent {}
And, if not, how to deal with such situation when I need to extend 3rd party class and also extend Eloquent? Maybe using Laravel's IoC?
I think your model should extend from Eloquent, and instead be accessed through a repository. Your repository can have a $storage property, and would be responsible for calling the appropriate methods on your AbstractStorage implementation. Below is more pseudo- than actual code, but illustrates where you can plug in your implementation for an update operation.
class MyClass extends Eloquent
{
/* Normal Eloquent model implementation */
}
class MyRepository
{
protected $storage;
protected $myClass;
public function __construct(MyClass $myClass, AbstractStorage $storage)
{
$this->myClass = $myClass;
$this->storage = $storage;
}
public function update($id, $data)
{
// This is just an example operation, basically here's your chance to call
// the 3rd-party implementation. Here is pre-eloquent update, but can be
// after
$this->storage->update($id, $data);
// Use the empty Eloquent class property instance to obtain an instance of
// the requested model
$instance = $this->myClass->find($id);
// set instance properties
$instance->save();
// Example post-eloquent update
$this->storage->update($id, $data);
}
}
class MyStorage extends AbstractStorage { /* Your Storage Implementation */ }
$repo = new MyRepository(new MyClass, new MyStorage);
// Update item id 42's foo property
$repo->update(42, [ 'foo' => 'bar' ]);
A benefit to this approach is that the construction of the repository itself can be offloaded to the IoC through a Service Provider, and be injected inside of a controller / form validator etc, which means the execution will become automatic and hide the underlying complexity of the 3rd party library from the rest of the system (the repository helps keep your 3rd party abstraction from leaking).
Another benefit is that you don't need any special code in your eloquent models having anything to do with your completely unrelated 3rd party code. All of the logic is encapsulated in a single spot, and can even be shared amongst multiple models. Want to change 3rd party providers? Write a new implementation of AbstractStorage, update the service provider and you're done.
One other benefit is improved testability. Instead of statically utilizing an eloquent model directly (a la $user = User::find($id)) you would be manipulating your repository object instead ($user = $this->repo->find($id)). Since your repository can be trivially mocked and itself be tested (without also testing Eloquent or hitting the database), you can write integration tests on all of your controller routes and know the moment that changes to your codebase break your business rules.