You could leverage Laravel pipelines to apply some kind of checks on your order.
Imagine that you could pluck the constraints configuration correctly from the database and build up an array (or something like a ConstraintBag instance) which contains all the constraints that you need to check:
configuration
$constraints = [
DateRangeConstraint::class,
TotalAmountConstraint::class,
AllowedItemsContraint::class,
];
Each constraint may adhere to the same interface (Constraint
in this PoC) which will define a single handle
method:
use Closure;
class DateRangeConstraint implements Constraint
{
public function handle($order, Closure $next)
{
if ($order->promo->start_date >= 'date' || $order->promo->end_date <= 'date') {
throw new PromotionConstraintException($this);
}
return $next($order);
}
}
Then in your controller/service method you could use this array of rules in a pipeline and pass the order object (or an object which contains all the parts you need for validating all the constraints) though the constraints. If any of this fails, you could trigger a custom exception (maybe one per category of constraint/one per constraint) and return the resulting outcome of the validation
process:
// Do not forget to add the use statement
use Illuminate\Pipeline\Pipeline;
class PromotionValidationService
{
protected $constraints;
// Pass in the constraints array you have already built
public function __construct($constraints)
{
$this->constraints = $constraints;
}
// Start the validation process and cycle through all the constraints
// I would pass in the order object as you might need to access the total
// order amount and/or the items in the order
public function validate($order)
{
try {
app(Pipeline::class)
->send($order)
->through($this->constraints);
} catch (PromotionConstraintException $exception) {
// Handle the exception and return false or rethrow
// the exception for further handling from the caller.
return false;
}
return true;
}
}
Obviously, this is still a proof of concepts and would require more study and architectural planning to handle the various constraints you might need to check (eg: passing the whole $order
object might not be the best idea, or it might not be available yet when checking the promotion constraints). However, this could be a flexible alternative to a fixed sequence of if/elses that needs to be edited for each change.