This article is a step by step tutorial for building a Magento 2 module to manage catalogue release by a pre-specified date.
This module was built as part of the selection process for the Magento 2 developer profile, but I decided to write my complete thought process and development to help my fellow learners!
-
Gather Requirements
- Product should have a
Release Date Time
attribute. The attribute should be editable in the Admin -
If the
Release Date Time
attribute is defined for the product consider two cases:-
The value of the
Release Date Time
attribute is in the future:- Do not allow the product to be purchased.
- Inform the customer about the release date and time on product details and product listing page.
-
The value of the
Release Date Time
attribute is now or in the past:- Fallback to standard Magento 2 behaviour on product details and product listing pages.
-
- Product should have a
-
Research & Analysis
- We should do some analysis and search for a solution that can be implemented in Magento 2.
-
Entry points for the catalogue release date validation:
-
API:
-
Add New Product
/rest/:storeId/V1/carts/mine/items
/rest/:storeId/V1/guest-carts/:cartId/items
-
Update Product Quantity
/rest/:storeId/V1/carts/mine/items
/rest/:storeId/V1/guest-carts/:cartId/items
-
-
Legacy Checkout:
- Add New Product from Product Listing Page
- Add New Product from Product View Page
- Update Quantity from Mini Cart
-
- Timer display (using
knockout.js
component) in Product Listing Page and Product View Page - Add the
Release Date Time
attribute in the product (website scope) and display it in the Product Description
-
Implimentation
- Add a new attribute
release_date_time
using data patch.
<?php
namespace Adapttive\Catalog\Setup\Patch\Data;
use Adapttive\Catalog\Api\Data\ProductInterface;
use Magento\Catalog\Model\Product;
use Magento\Catalog\Setup\Patch\Data\UpdateProductAttributes;
use Magento\Eav\Model\Entity\Attribute\Backend\Datetime;
use Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface;
use Magento\Eav\Setup\EavSetup;
use Magento\Framework\Setup\ModuleDataSetupInterface;
use Magento\Framework\Setup\Patch\DataPatchInterface;
use Magento\Eav\Setup\EavSetupFactory;
class AddReleaseDateTimeAttribute implements DataPatchInterface
{
/**
* @var ModuleDataSetupInterface
*/
private $moduleDataSetup;
/**
* @var EavSetupFactory
*/
private $eavSetupFactory;
/**
* AddReleaseAttribute constructor.
* @param ModuleDataSetupInterface $moduleDataSetup
* @param EavSetupFactory $eavSetupFactory
*/
public function __construct(
ModuleDataSetupInterface $moduleDataSetup,
EavSetupFactory $eavSetupFactory
) {
$this->moduleDataSetup = $moduleDataSetup;
$this->eavSetupFactory = $eavSetupFactory;
}
public static function getDependencies()
{
return [
UpdateProductAttributes::class
];
}
public function getAliases()
{
return [];
}
public function apply()
{
/** @var EavSetup $eavSetup */
$eavSetup = $this->eavSetupFactory->create(['setup' => $this->moduleDataSetup]);
$eavSetup->addAttribute(
Product::ENTITY,
ProductInterface::RELEASE_DATE_TIME,
[
'type' => 'datetime',
'label' => 'Release Date Time',
'input' => 'date',
'required' => false,
'sort_order' => 50,
'backend' => Datetime::class,
'global' => ScopedAttributeInterface::SCOPE_WEBSITE,
'visible_on_front' => true,
'used_in_product_listing' => true,
'is_used_in_grid' => true,
'is_visible_in_grid' => false,
'is_filterable_in_grid' => false,
]
);
}
}
- Create a common validator for the release date of the product.
<?php
namespace Adapttive\Catalog\Model;
use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\Stdlib\DateTime;
use Magento\Framework\Stdlib\DateTime\TimezoneInterface;
class ReleaseValidator
{
/**
* @var TimezoneInterface
*/
private $timezone;
public function __construct(
TimezoneInterface $timezone
) {
$this->timezone = $timezone;
}
/**
* Validate if product is released
* @param $product
* @return bool
* @throws LocalizedException
*/
public function validate($product)
{
if ($product && $product->getReleaseDateTime()) {
// if product not released throw exception.
$releaseDateTime = $this->timezone->date((string)$product->getReleaseDateTime());
$current = $this->timezone->date();
if ($releaseDateTime && $releaseDateTime > $current) {
throw new LocalizedException(
__(
"The product is not available for purchase. Please retry after %1.",
$releaseDateTime->format(DateTime::DATETIME_PHP_FORMAT
)
)
);
}
}
return true;
}
}
- Create an
Observer
to trigger validation and throw an error.
<?php
namespace Adapttive\Catalog\Observer;
use Adapttive\Catalog\Helper\Config;
use Adapttive\Catalog\Model\ReleaseValidator;
use Magento\Framework\Event\Observer;
use Magento\Framework\Exception\LocalizedException;
use Magento\Quote\Model\Quote\Item;
use Magento\Framework\Event\ObserverInterface;
/**
* Class ReleaseObserver
* Trigger the release date validation..
* Reference: catalog inventory validation
*/
class ReleaseObserver implements ObserverInterface
{
/**
* @var ReleaseValidator
*/
private $validator;
/**
* @var Config
*/
private $config;
public function __construct(
Config $config,
ReleaseValidator $validator
) {
$this->validator = $validator;
$this->config = $config;
}
/**
* @param Observer $observer
* @return void
* @throws LocalizedException
*/
public function execute(Observer $observer)
{
/* @var $quoteItem Item */
$quoteItem = $observer->getEvent()->getItem();
if (!$quoteItem ||
!$quoteItem->getProductId() ||
!$quoteItem->getQuote() ||
!$this->config->isEnabled()
) {
return;
}
$product = $quoteItem->getProduct();
$this->validator->validate($product);
}
}
- Find the complete module with documentation here: https://github.com/adapttive/catalog-release