Skip to main content
Home
Drupal life hacks

Main navigation

  • Drupal
  • React
  • WP
  • Contact
  • About
User account menu
  • Log in

Breadcrumb

  1. Home

Implementing a Real-World Feature Across Drupal, Symfony, and Laravel

By admin, 4 January, 2026

Implementing a Real-World Feature Across Drupal, Symfony, and Laravel

Feature: E-Commerce “Discount Code Validator”

Requirement:

  • When a user applies a discount code during checkout:
    • Validate the code
    • Check expiration and usage limits
    • Calculate discount
    • Return an updated order total
  • Must be extensible for future promotion types

This feature is small but touches services, events, plugins, and DI—perfect for comparing modular approaches.


1. Drupal: Module + Plugin + Service

1.1 Module Structure

discount_code/
├── discount_code.info.yml
├── discount_code.services.yml
└── src/
    ├── Plugin/
    │   └── DiscountValidator/
    │       └── BaseValidator.php
    │       └── PercentageValidator.php
    └── Service/
        └── DiscountService.php

1.2 Base Plugin (Interface)

namespace Drupal\discount_code\Plugin\DiscountValidator;

use Drupal\commerce_order\Entity\OrderInterface;

abstract class BaseValidator {
  abstract public function validate(OrderInterface $order, string $code): bool;
}

1.3 Custom Plugin

namespace Drupal\discount_code\Plugin\DiscountValidator;

use Drupal\commerce_order\Entity\OrderInterface;

class PercentageValidator extends BaseValidator {
  public function validate(OrderInterface $order, string $code): bool {
    // Example: 10% off if code = 'NEWYEAR10'
    return $code === 'NEWYEAR10';
  }
}

1.4 Service

namespace Drupal\discount_code\Service;

use Drupal\discount_code\Plugin\DiscountValidator\BaseValidator;

class DiscountService {

  protected array $validators;

  public function __construct(iterable $validators) {
    $this->validators = $validators;
  }

  public function applyDiscount($order, $code) {
    foreach ($this->validators as $validator) {
      if ($validator->validate($order, $code)) {
        // apply discount logic
        return true;
      }
    }
    return false;
  }
}

Key Takeaways (Drupal)

  • Plugin API allows adding new validators easily
  • Service centralizes logic
  • Runtime discovery + DI makes it flexible
  • UI can expose configurable discount rules

2. Symfony: Service + Event

Symfony doesn’t have a plugin system, so we use services + events.

2.1 Validator Interface

namespace App\Service\Discount;

use App\Entity\Order;

interface DiscountValidatorInterface {
    public function validate(Order $order, string $code): bool;
}

2.2 Concrete Validator

namespace App\Service\Discount;

use App\Entity\Order;

class PercentageValidator implements DiscountValidatorInterface {
    public function validate(Order $order, string $code): bool {
        return $code === 'NEWYEAR10';
    }
}

2.3 Discount Service

class DiscountService {
    public function __construct(private iterable $validators) {}

    public function applyDiscount(Order $order, string $code): bool {
        foreach ($this->validators as $validator) {
            if ($validator->validate($order, $code)) {
                return true;
            }
        }
        return false;
    }
}

2.4 Event Integration

namespace App\EventListener;

use App\Event\OrderApplyDiscountEvent;

class DiscountListener {
    public function __construct(private DiscountService $service) {}

    public function onDiscount(OrderApplyDiscountEvent $event) {
        $this->service->applyDiscount($event->getOrder(), $event->getCode());
    }
}

Key Takeaways (Symfony)

  • Services and interfaces handle modularity
  • Adding a new validator requires manual registration
  • DI + events = decoupled architecture

3. Laravel: Package + Service Provider + Events

Laravel uses service providers and packages.

3.1 Validator Interface

namespace App\Services\Discount;

interface DiscountValidator {
    public function validate($order, string $code): bool;
}

3.2 Concrete Validator

class PercentageValidator implements DiscountValidator {
    public function validate($order, string $code): bool {
        return $code === 'NEWYEAR10';
    }
}

3.3 Service

class DiscountService {
    public function __construct(private array $validators) {}

    public function applyDiscount($order, $code) {
        foreach ($this->validators as $validator) {
            if ($validator->validate($order, $code)) {
                return true;
            }
        }
        return false;
    }
}

3.4 Service Provider

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use App\Services\Discount\DiscountService;
use App\Services\Discount\PercentageValidator;

class DiscountServiceProvider extends ServiceProvider {
    public function register() {
        $this->app->singleton(DiscountService::class, function ($app) {
            return new DiscountService([
                new PercentageValidator(),
            ]);
        });
    }

    public function boot() {
        // Optional: register events
    }
}

Key Takeaways (Laravel)

  • Service Provider bootstraps the service and validators
  • Easy to extend with new validators
  • DI + automatic resolution = minimal boilerplate
  • Manual registration, unlike Drupal’s runtime discovery

4. Side-by-Side Summary

AspectDrupalSymfonyLaravel
Validator extensibilityRuntime via Plugin APICompile-time service registrationManual via ServiceProvider
DIYesYesYes
Runtime discoverable✅ Yes❌ No⚠ Only via provider
UI integration✅ Optional❌❌
Adding a new validatorDrop new pluginAdd new service + configureAdd validator + update provider
BoilerplateHigherMediumLow

5. Architectural Lessons

  1. Drupal: Best for pluggable, CMS-integrated, runtime discoverable features.
  2. Symfony: Best for clean architecture, explicit contracts, and decoupled services.
  3. Laravel: Best for developer speed, packages, and simple DI.

Conclusion:

This single feature shows why Drupal feels “heavier” but more flexible, Symfony is strict but robust, and Laravel is fast but relies on conventions.

 

Tags

  • Drupal
  • Symfony
  • Laravel
  • PHP
  • modularity
  • Dependency Injection
  • DI
  • plugin API
  • service provider
  • Services
  • bundles
  • CMS
  • web development
  • PHP frameworks
  • software architecture
  • extensibility
  • Drupal module
  • Symfony Components
  • Laravel packages
  • event system
  • Service Container
  • Autowiring
  • software design
  • code reusability
  • feature implementation

Comments

About text formats

Restricted HTML

  • Allowed HTML tags: <a href hreflang> <em> <strong> <cite> <blockquote cite> <code> <ul type> <ol start type> <li> <dl> <dt> <dd> <h2 id> <h3 id> <h4 id> <h5 id> <h6 id>
  • Lines and paragraphs break automatically.
  • Web page addresses and email addresses turn into links automatically.
Powered by Drupal