So what I am trying to do is have my eccomerce site handle both Paypal and Stripe payments. However, I am not sure how this is done.
This is what I have.
class BillingProvider extends ServiceProvider
{
/**
* Bootstrap the application services.
*
* #return void
*/
public function boot()
{
//
}
/**
* Register the application services.
*
* #return void
*/
public function register()
{
$this->app->bind('App\Contracts\BillingInterface','App\Services\Billing\PaypalBilling');
}
}
This works fine if I only need Paypal. However, I need both Paypal and Stripe.
Any idea how we can go about implementing this? So when we typehint this we can either get paypal or stripe.
public function stripe(Request $request, BillingInterface $bill){// Handle stripe payment.}
public function paypal(Request $request, BillingInterface $bill){// Handle paypal payment.}
Imho, there are many ways to do it.
First of all, take a look to the contextual binding of the Service Container. If you're using different classes on your checkout procedure, you can set a resolve strategy based on who is actually using the contract.
However, I would probably choose something different.
Let's apply the strategy design pattern for this.
Basically, in two separate steps:
create a "top" class named, for example, BillingManager;
a class for each payment type (imho, make a paypal() and a stripe() method in a single class is wrong because of this) that implement your billing interface. Let's say that this interface has a 'process($orderId)` method;
Now, the BillingManager class will be called. Probably, you will call a general method pay that will take, as a parameter, something to understand what strategy it will use.
Something like
public function pay($strategy, $orderId) {
// use $strategy to understand which service must be used.
// very raw example:
$strategy = app()->make($strategy . 'Strategy');
$strategy->process($orderId);
}
Of course, you will need a safe mechanism to understand what service you will need to use. After that, you will be able to use the BillingManager class every time, with many different payment systems, without worry about anything else.
Related
I'm integrating APIs that will be connected to an E-Commerce platform. I'm supposed to add an option to track orders, how do I do that?
Also, there's no frontend yet so I'm using postman to test all the API's.
the orders go through different locations on their way to the buyer, and I'd like the customer to now at which station their item is
php artisan make:observer OrderObserver
then something in the observer like this:
/**
* Handles the correct fields when updating
* #param Order $order
* #return void
*/
public function updating(User $user)
{
if($order->isDirty('some_column_you_use_to_change_status')) {
$someStatusTable = SomeStatusTable::where('track_id', $order->track_id)->first();
$someStatusTable->status = "whateverYOUwant";
}
}
I really don't know your logic but i think this could be something.
by the wat ->isDirty() watches if columns were changed.
you can also get the $order->getOriginal(('some_column_you_use_to_change_status')
I use this for example history of a Model to register history or even set new versions in my models
As you might already know from the docs, Laravel's custom Request class (make:request using artisan) provides a possibility to keep validation rules inside of it. So, I have the following code's examples to describe the problem of the prioritization better:
api route
Route::post('/suggest', [SuggestionController::class, 'store'])
->middleware(['auth:sanctum']);
Request class
class SuggestionRequest extends FormRequest
{
public function authorize()
{
return $this->user() && $this->user()->hasRole('sales manager');
// return true;
}
public function rules() {...}
}
Here, I want to force my vendor middleware auth:sanctum (which checks if the current user has a token) to be executed before the SuggestionRequest class calls its own authorize method, because it doesn't make sense to validate the request's data and then check if the user is authenticated. Also, there's no need to have a quite similar logic in both places. Which way should I refactor my code? Are there any good approaches for it?
I'm building a Laravel app, which has a number of various features. I want to be able to enable or disable them depending on a particular domain's requirement. Currently, I have in my config a series of flags such as:
'is_feature_1_enabled' => true,
'is_feature_2_enabled' => false,
... and so on.
Then in my controllers and views, I check those config values to see whether or not I should be displaying something, allowing certain actions, etc. My app is starting to get polluted with these kinds of checks everywhere.
Is there a best practice method of managing features in a Laravel app?
This is technically called feature flags - https://martinfowler.com/articles/feature-toggles.html
depends on your requirements, flags in config/database, rollout, etc...
But it's basically if's in code and cannot be clean.
Laravel packages:
https://github.com/alfred-nutile-inc/laravel-feature-flag
https://github.com/francescomalatesta/laravel-feature
Some services:
https://launchdarkly.com/
https://bullet-train.io/
https://configcat.com/
Also look at https://marketingplatform.google.com/about/optimize/ for frontend.
I've encountered the same problem when I tried to implement multiple hotel providers.
What I did was using service container.
first you will create class for each domain With his features:
like Doman1.php ,Domain2.php
then inside each one of those you will add your logic.
then you gonna use binding in your app service provider to bind the domain with class to use.
$this->app->bind('Domain1',function (){
return new Domain1();
});
$this->app->bind('Domain2',function (){
return new Domain2();
});
Note you can use general class that holds the features goes with all domains then use that general class in your classes
Finally in your controller you can check your domain then to use the class you gonna use
app(url('/'))->methodName();
Look like you are hard coding things based on config values to enable or disable certain features. I recommend you to control things based on named routes rather than config value.
Group all the route as a whole or by feature wise.
Define name for all routes
Control the enable/disable activity by route name and record in database
Use Laravel middleware to check whether a particular feature is enabled or disabled by getting the current route name from request object and matching it with the database..
so you will not have the same conditions repeating every where and bloat your code..
here is a sample code show you how to retrieve all routes, and you can match the route group name to further process to match your situation.
Route::get('routes', function() {
$routeCollection = Route::getRoutes();
echo "<table >";
echo "<tr>";
echo "<td width='10%'><h4>HTTP Method</h4></td>";
echo "<td width='10%'><h4>Route</h4></td>";
echo "<td width='80%'><h4>Corresponding Action</h4></td>";
echo "</tr>";
foreach ($routeCollection as $value) {
echo "<tr>";
echo "<td>" . $value->getMethods()[0] . "</td>";
echo "<td>" . $value->getPath() . "</td>";
echo "<td>" . $value->getName() . "</td>";
echo "</tr>";
}
echo "</table>";
});
and here is a sample middleware handler where you can check whether a particular feature is active by matching with what you have already stored in your database..
public function handle($request, Closure $next)
{
if(Helper::isDisabled($request->route()->getName())){
abort(403,'This feature is disabled.');
}
return $next($request);
}
Assuming that those features are only needed for HTTP requests.
I would create a default Features base class with all the default flags:
Class Features {
// Defaults
protected $feature1_enabled = true;
protected $feature2_enabled = true;
public function isFeature1Enabled(): bool
{
return $this->feature1_enabled;
}
public function isFeature2Enabled(): bool
{
return $this->feature2_enabled;
}
}
Then I would extend that class for each Domain and set the overrides that are needed for that domain:
Class Domain1 extends Features {
// override
protected $feature1_enabled = false;
}
Then create a Middleware to bind the Features Class to the container:
class AssignFeatureByDomain
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
switch ($_SERVER['HTTP_HOST']) {
case 'domain1':
app()->bind(Features::class, Domain1::class);
break;
default:
abort(401, 'Domain rejected');
}
return $next($request);
}
}
Don't forget to attach this middleware to your routes: to a group or for each route.
After this you can TypeHint your Features class in your controllers:
public function index(Request $request, Features $features)
{
if ($features->isFeature1Enabled()) {
//
}
}
Laravel is great with this, you can even store your features in db, and create a relation between the domain.
I would recommend to use Gates and Policies, which will give you better control in your controllers and blade templates. This means you register the gates from your db or hard code them.
For example if you have export products feature with a button in your system and you want to make that feature available to some users you can register gates with business logic.
//Only admins can export products
Gate::define('export-products', function ($user) {
return $user->isAdmin;
});
Then you can do the following in the controllers
<?php
namespace App\Http\Controllers;
use App\Product;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
class ProductsController extends Controller
{
/**
* Export products
*
* #param Request $request
* #param Post $post
* #return Response
* #throws \Illuminate\Auth\Access\AuthorizationException
*/
public function export(Request $request)
{
$this->authorize('export-products');
// The current user can export products
}
}
Here is an example for your blade templates:
#can('export-products', $post)
<!-- The Current User Can export products -->
#endcan
#cannot('export-products')
<!-- The Current User Can't export products -->
#endcannot
more information available at https://laravel.com/docs/5.8/authorization
Interesting case you have here. It might be interesting to look into a Feature interface or abstract class that contains a few methods you generally need.
interface Feature
{
public function isEnabled(): bool;
public function render(): string;
// Not entirely sure if this would be a best practice but the idea is to be
// able to call $feature->execute(...) on any feature.
public function execute(...);
...
}
You could even devide these into ExecutableFeature and RenderableFeature.
Further on some kind of factory class could be made to make life easier.
// Call class factory.
Feature::make('some_feature')->render();
...->isEnabled();
// Make helper method.
feature('some_feature')->render();
// Make a blade directives.
#feature('some_feature')
#featureEnabled('some_feature')
What I did in my case was creating a new table on the database, you could call it Domains for instance.
Add all the specific features, those which could be shown on some domains but not in the rest, as columns for that table as bit for boolean values. Like, in my case, allow_multiple_bookings, use_company_card... whatever.
Then, consider creating a class Domain and its respective repository, and just ask these values on your code, trying to push as much as possible that logic into your domain (your model, application services, etc).
For instance, I would not do the check on the controller method for RequestBooking if the domain which is requesting a booking can request only one or more.
Instead I do it on a RequestBookingValidatorService which can check if the booking datetime has passed, the user has an enabled credit card, ... or the Domain which this action comes from is allowed to request more than one booking (and then if it already has any).
This adds the convenience of readability, as you have pushed this decision to your application services. Also, I find that whenever I need a new feature I can use Laravel (or Symfony) migrations to add that feature on the table and I could even update its rows (your domains) with the values I want on the same commit I coded.
The Model View Controller architecture tells me that all my business logic should be inside the Model, while the data flow should be handled by the Controller.
Knowing this, while I'm dealing with my logic inside the Model, I need to let the Controller know if he's supposed to redirect to another url, redirect back, what kind of message or variable to pass during the redirection, etc.
What is the best way of doing this?
I can think of some ways, like throwing exceptions on the Modeland catching them on the Controller or returning an array from the Model and treating it on the Controller, but none of them seem very nice. The easiest way would be calling the Redirect->to() (or back()) inside the Model and just returning the Model's return on the Controller, but it seem to break the architecture's separation of rules.
Is there a "right" way of doing this? What would be the pros and cons of each way?
EDIT:
The answer below is old. Laravel now includes a bunch of different ways of handling common problems.
For example, use Laravel's FormRequest's as a way of validating data easily on controller methods, and Jobs to handle business logic for creating / updating models.
OLD POST:
This is a common question, and while the 'MVC' pattern is nice for a basic starting point for a web app, I feel like the majority of developers always need another intermediate service for validation, data handling, and other problems that come up during development.
To answer your question without bias: There is no right way.
To answer your question with my own personal bias, I feel the majority of developers will use the Repositories or Services pattern to handle intermediate data handling between the controller and the model, and also have separate classes for validation as well.
In my opinion, Repositories are better for a framework and data agnostic design (due their interface driven implementation), and Services are better for handling the business logic / rules. Controllers are better used for handling responses and for passing the input data to the repository or the service.
The paths for each of these patterns are the same though:
Request -> Controller (Validation) -> Service -> Model -> Database
Request -> Controller (Validation) -> RepositoryInterface -> Model -> Database
Validation is in brackets since input isn't passed from the validator to the service / repository, the input sent to the validator, gives the 'OK', and let's the controller know it's ok to send the data to the Service / Repository to be processed.
I only use Services when I'm absolutely positive I won't be changing frameworks or data sources. Otherwise I'll use Repositories. Repositories are just a little more work to setup, since you'll need to make Laravel resolve the interface to your repository class through its IoC.
Services Example:
The Service:
namespace App\Services;
use App\Models\Post;
class PostService
{
/**
* #var Post
*/
protected $model;
/**
* Constructor.
*
* #param Post $post
*/
public function __construct(Post $post)
{
$this->model = $post;
}
/**
* Creates a new post.
*
* #param array $input
*/
public function create(array $input)
{
// Perform business rules on data
$post = $this->model->create($input);
if($post) return $post;
return false;
}
}
The Controller:
namespace App\Http\Controllers;
use App\Services\PostService;
use App\Validators\PostValidaor;
class PostController extends Controller
{
/**
* #var PostService
*/
protected $postService;
/**
* #var PostValidator
*/
protected $postValidator;
/**
* Constructor.
*
* #param PostService $postService
* #param PostValidator $postValidator
*/
public function __construct(PostService $postService, PostValidator $post Validator)
{
$this->postService = $postService;
$this->postValidator = $postValidator;
}
/**
* Processes creating a new post.
*/
public function store()
{
$input = Input::all();
if($this->postValidator->passes($input)) {
// Validation passed, lets send off the data to the service
$post = $this->postService->create($input);
if($post) {
return 'A post was successfully created!';
} else {
return 'Uh oh, looks like there was an issue creating a post.';
}
} else {
// Validation failed, return the errors
return $this->postValidator->errors();
}
}
}
Now with this pattern, you have a nice separation of all your processes, and a clear indication of what each of them do.
For a repository example, Google 'Laravel Repository Pattern'. There are tons of articles about this.
Actually - in Laravel 5 that is not the best way to do it. Business logic should not be in models. The only thing that models should do is retrieve and store data from your database.
You are better off using the CommandBus or ServiceProviders to handle application logic and business rules. There are many articles on the web about these, but personally I prefer laracasts.com as the best learning resource.
I'm kind of confused of how to use contracts. I think that's because I haven't used unit-testing so that it's not obvious for me how contracts work.
Let's have look at this code:
use Illuminate\Contracts\Auth\Guard;
...
public function __construct(Guard $auth)
{
$this->auth = $auth;
$this->middleware('guest', ['except' => 'getLogout']);
}
public function postRegister(RegisterRequest $request)
{
// Registration form is valid, create user...
$this->auth->login($user);
return redirect('/');
}
So how do I know which class implements login method of contract in this line: $this->auth->login($user) ? And how can I change the class if I want to use my own?
In laravel 4 I wrote Auth::user() as an example and I used it everywhere in any controller and it worked. Now I should inject a contract inside a controller method and use it like $auth->user?
Also, If I get it right, contracts are used for making an abstraction. Okay, so, if I want to build a new interface for my own class and then have multiple classes that implement my interface, where should I write the code? I can't think of an example but lets imagine I need to implement an interface for enabling/disabling a lamp, and I have two methods like on() and off() and I have multiple ways to do that. Do I need to create new contract for that?
I hope I can make this a bit clearer for you...
Ad.1. You can check default binding at /vendor/laravel/framework/src/Illuminate/Foundation/Application.php (method registerCoreContainerAliases around line 792). If you want to create your own class or extend existing I recommend looking at How to extend Laravel's Auth Guard class? or http://laravel.com/docs/master/extending (this one is more about Laravel 4.x but might give you an idea).
Ad.2. Actually you can still use Auth::user() but I inject a contract in constructor or a method and call it like $this->auth->user or $auth->user.
Ad.3. I have a /app/Repositories folder where I put my interfaces and implementations, so to follow your example I would create subfolder Lamp and I would create LampInterface with on() and off() methods, then I would create something like Lamp.php that implements LampInterface. Next I would create a service provider in /app/Providers, like LampServiceProvider.php with binding:
namespace Apps\Providers;
use Illuminate\Support\ServiceProvider;
class LampServiceProvider extends ServiceProvider {
/**
* Register the application services.
*
* #return void
*/
public function register()
{
$this->app->singleton(
'App\Repositories\Lamp\LampInterface',
'App\Repositories\Lamp\Lamp'
);
}
}
After that I would register new service provider in /app/config/app.php and finally I can inject my interface like:
public function switchLampOn(App\Repository\Lamp\LampInterface $lamp)
{
$lamp->on();
}