Out of Stock feature in Drupal Commerce 2.x

Out of Stock feature in Drupal Commerce 2.x

Out of Stock feature in Drupal Commerce 2.x

Implementing Out of Stock feature in Drupal Commerce 2.x is easy. Unfortunately, you can't just enable this feature, because Commerce doesn't support it out of box. You have to get your hands dirty and write some code.

As you may know, there is a contrib stock module for Drupal 8, but it's still in the development phase, so I wouldn't recommend you to use it in production.

Let's see how in three simple steps you can add Out of Stock feature to your web shop.

Step 1

Go to your Product variation fields configuration (/admin/commerce/config/product-variation-types/default/edit/fields) and add a new integer field. Call it Stock or something like that. The value of this field will dictate if the product is in stock or not.

Step 2

Implement the AvailabilityChecker service. Inside your MODULE_NAME.services.yml file add this:

MODULE_NAME.variation_availability_checker:
  class: Drupal\MODULE_NAME\VariationAvailabilityChecker
  tags:
     - { name: commerce.availability_checker, priority: 100 }

You can now create a class file inside the src directory. The name of this file should be VariationAvailabilityChecker.php

<?php

namespace Drupal\MODULE_NAME;

use Drupal\commerce\AvailabilityCheckerInterface;
use Drupal\commerce\Context;
use Drupal\commerce\PurchasableEntityInterface;

/**
 * Class VariationAvailabilityChecker.
 */
class VariationAvailabilityChecker implements AvailabilityCheckerInterface {

  /**
   * {@inheritdoc}
   */
  public function applies(PurchasableEntityInterface $entity) {
    return TRUE;
  }

  /**
   * {@inheritdoc}
   */
  public function check(PurchasableEntityInterface $entity, $quantity, Context $context) {
    if ($entity->field_stock->value <= 0) {
      return FALSE;
    }
    return TRUE;
  }

}

As you can see, this is a super simple class. We have only two methods. applies() is used to determine whether the checker applies to the given purchasable entity. Maybe you have several variation types and you want to apply the checker to only to one of them. In our case we just return TRUE. That means that the checker will be always applied.

The second method is check(). This method checks the availability of the given purchasable entity. If we return TRUE that means that the entity is available, and FALSE means that it is unavailable. Logic in our example is quite simple. If the value of Stock field is equal or less than zero then return FALSE and make the variation unavailable.

Step 3

The last step is to alter the Add to cart form and use the AvailabilityChecker service to enable/disable Add to cart button. Inside your MODULE_NAME.module file add this:

use Drupal\commerce\Context;
use Drupal\commerce_product\Entity\ProductVariation;

/**
 * Implements hook_form_alter().
 */
function MODULE_NAME_form_alter(&$form, FormStateInterface $form_state, $form_id) {
  if (strpos($form_id, 'commerce_order_item_add_to_cart_form') !== FALSE) {
    $selected_variation_id = $form_state->get('selected_variation');
    $product_variation = ProductVariation::load($selected_variation_id);

    /** @var \Drupal\MODULE_NAME\VariationAvailabilityChecker $variation_availability_checker */
    $variation_availability_checker = \Drupal::service('MODULE_NAME.variation_availability_checker');

    $current_user = \Drupal::currentUser();
    $current_store = \Drupal::service('commerce_store.current_store')->getStore();
    $context = new Context($current_user, $current_store);

    if (!$variation_availability_checker->check($product_variation, 1, $context)) {
      $form['actions']['submit']['#value'] = t('Out of stock');
      $form['actions']['submit']['#disabled'] = TRUE;
    }
  }
}

In the form alter we are calling the check() method to see if the currently selected variation is available. We are passing three arguments: $product_variation, 1, $context. Since we didn't implement any logic regarding the quantity, we can pass any value, but here we are passing 1.

You don't have to implement the step 3. It's optional but it greatly improves the user experience. If you don't disable the Add to cart button, users will be able to click on it and they will get the message that says that product has been added to the cart, but that won't happen. Product won't be added to the cart and in my opinion that is really confusing.

You can vastly improve upon this example and create even better stock management features. You can for example automatically decrease the Stock field value each time someone buys an item. To do this you can subscribe for example to the ORDER_PAID event and add some logic to decrease the stock value for all ordered products.

Please note that Drupal 8 adheres to PSR-4. This means that files must be named in certain ways and placed in specific folders. Here is what the module structure should look like:

my_module
├── my_module.info.yml
├── my_module.module
├── my_module.services.yml
└── src
    └── VariationAvailabilityChecker.php

 

About author

Goran Nikolovski is a highly experienced Drupal developer with an extensive skill set that includes PHP, MySQL, HTML, CSS and Javascript. He has experience with large Drupal sites and Drupal Commerce 2.x integration, and he is author of several Drupal modules.