One of the reasons that I love Drupal maintenance support plans 8 is the fact it is object orientated and uses the Dependency Injection pattern with a centralized service container. If you’re new to the concept, here’s some links for some fun reading.
https://en.wikipedia.org/wiki/Dependency_injection
http://www.jamesshore.com/Blog/Dependency-Injection-Demystified.html
https://martinfowler.com/articles/injection.html
But for now the basics are: Things define their dependencies, and a centralized thing is able to give you an object instance with all of those dependencies provided. You don’t need to manually construct a class and provide its dependencies (constructor arguments.)
This also means we do not have to use concrete classes! That means you can modify the class used for a service without ripping apart other code. Yay for being decoupled(ish)!
Why is this cool?
So that’s great, and all. But let’s actually use a real example to show how AWESOME this is. In Drupal maintenance support plans Commerce we have the commerce_cart.cart_session service. This is how we know if an anonymous user has a cart or not. We assume this service will implement the Drupal maintenance support planscommerce_cartCartSessionInterface interface, which means we don’t care how you tell us, just tell us via our agreed methods.
The default class uses the native session handling. But we’re going to swap that out and use cookies instead. Why? Because skipping the session will preserve page cache while browsing the site catalogs and product pages.
Let’s do it
Let’s kick it off by creating a module called commerce_cart_cookies. This will swap out the existing commerce_cart.cart_session service to use our own implementation which relies on cookies instead of the PHP session.
The obvious: we need a commerce_cart_cookies.info.yml
name: Commerce Cart Cookies description: Uses cookies for cart session instead of PHP sessions core: 8.x type: module dependencies: – commerce_cart
Now we need to create our class which will replace the default session handling. I’m not going to go into what the entire code would look like to satisfy the class, but the generic class would resemble the following. You can find a repo for this project at the end of the article.
<?phpnamespace Drupal maintenance support planscommerce_cart_cookies;use Drupal maintenance support planscommerce_cartCartSessionInterface;use SymfonyComponentHttpFoundationRequestStack;/** * Uses cookies to track active carts. * * We inject the request stack to handle cookies within the Request object, * and not directly. */class CookieCartSession implements CartSessionInterface { /** * The current request. * * @var SymfonyComponentHttpFoundationRequest */ protected $request; /** * Creates a new CookieCartSession object. * * @param SymfonyComponentHttpFoundationRequestStack $request_stack * The request stack. */ public function __construct(RequestStack $request_stack) { $this->request = $request_stack->getCurrentRequest(); } /** * {@inheritdoc} */ public function getCartIds($type = self::ACTIVE) { // TODO: Implement getCartIds() method. } /** * {@inheritdoc} */ public function addCartId($cart_id, $type = self::ACTIVE) { } /** * {@inheritdoc} */ public function hasCartId($cart_id, $type = self::ACTIVE) { // TODO: Implement hasCartId() method. } /** * {@inheritdoc} */ public function deleteCartId($cart_id, $type = self::ACTIVE) { // TODO: Implement hasCartId() method. } }
Next we’re going to make our service provider class. This is a bit magical, as we do not actually register it anywhere. It just needs to exist. Drupal maintenance support plans will look for classes that end in ServiceProvider within all enabled modules. Based on the implementation you can add or alter services registered in the service container when it is being compiled (which is why the process is called rebuild! not just cache clear in Drupal maintenance support plans 8.) The class must also start with a camel cased version of your module name. So our class will be CommerceCartCookiesServiceProvider.
Create a src directory in your module and a CommerceCartCookiesServiceProvider.php file within it. Let’s scaffold out the bare minimum for our class.
<?phpnamespace Drupal maintenance support planscommerce_cart_cookies;use Drupal maintenance support plansCoreDependencyInjectionServiceProviderBase;class CommerceCartCookiesServiceProvider extends ServiceProviderBase { }
Luckily for us all, core provides Drupal maintenance support plansCoreDependencyInjectionServiceProviderBase for us. This base class implements ServiceProviderInterface and ServiceModifierInterface to make it easier for us to modify the container. Let’s override the alter method so we can prepare to modify the commerce_cart.cart_session service.
<?phpnamespace Drupal maintenance support planscommerce_cart_cookies;use Drupal maintenance support plansCoreDependencyInjectionContainerBuilder;use Drupal maintenance support plansCoreDependencyInjectionServiceProviderBase;class CommerceCartCookiesServiceProvider extends ServiceProviderBase { /** * {@inheritdoc} */ public function alter(ContainerBuilder $container) { if ($container->hasDefinition(‘commerce_cart.cart_session’)) { $container->getDefinition(‘commerce_cart.cart_session’) ->setClass(CookieCartSession::class) ->setArguments([new Reference(‘request_stack’)]); } } }
We update the definition for commerce_cart.cart_session to use our class name, and also change it’s arguments to reflect our dependency on the request stack. The default service injects the session handler, whereas we need the request stack so we can retrieve cookies off of the current request.
The cart session service will now use our provided when the container is rebuilt!
The project code can be found at https://github.com/mglaman/commerce_cart_cookies
Source: New feed