Magento 2 WYSIWYG editor is one of the most used Magento features in almost every project. It powers CMS block and CMS page in general but it is also used in other parts like category as well.
When using the WYSIWYG editor we use Magento inbuilt directives to replace a part of the static content with dynamic content. For example, the media directive is used to load the media URL of a static asset, and the store directive is used to get the store URL.
ON THIS PAGE
Steps To Create Custom Magento 2 CMS Directives
Let’s see how to create a simple custom CMS directive to load blog post content from third-party API using post ID. This allows us to fetch the content when needed as it supports customer segmentation, caching, and other CMS page features.
Step 1. Create a Directive Class
We create a directive class that processes the parameters passed to the directive and replaces the information with dynamic data.
Create a file
app/code/Klizer/BlogPost/Filter/DirectiveProcessor/BlogPostDirective.php
<?php
/**
* Klizer_BlogPost extension
*
* @copyright Copyright © 2024 Klizer. All rights reserved.
* @author Vuchuru Sai Mohan - mohanvs@dckap.com
*/
declare(strict_types=1);
namespace Klizer\BlogPost\Filter\DirectiveProcessor;
use Magento\Framework\Filter\DirectiveProcessorInterface;
use Magento\Framework\Filter\Template;
use Magento\Framework\Filter\Template\Tokenizer\Parameter;
use Magento\Framework\Filter\Template\Tokenizer\ParameterFactory;
use Magento\Framework\Filter\VariableResolverInterface;
use Magento\Framework\App\State;
use Magento\Framework\HTTP\Client\Curl;
use Psr\Log\LoggerInterface;
class BlogPostDirective implements DirectiveProcessorInterface
{
/**
* @var ParameterFactory
*/
private $parameterTokenizerFactory;
/**
* @var VariableResolverInterface
*/
private $variableResolver;
/**
* @var LoggerInterface
*/
private $logger;
/**
* @var State
*/
private $appState;
/**
* @var Curl
*/
private $curl;
/**
* @param ParameterFactory $parameterTokenizerFactory
* @param VariableResolverInterface $variableResolver
* @param LoggerInterface $logger
* @param State $appState
* @param Curl $curl
*/
public function __construct(
ParameterFactory $parameterTokenizerFactory,
VariableResolverInterface $variableResolver,
LoggerInterface $logger,
State $appState,
Curl $curl
) {
$this->parameterTokenizerFactory = $parameterTokenizerFactory;
$this->variableResolver = $variableResolver;
$this->logger = $logger;
$this->appState = $appState;
$this->curl = $curl;
}
/**
* @inheritdoc
*/
public function process(array $construction, Template $filter, array $templateVariables): string
{
$content = "";
if(!isset($construction[1]))){
return $content;
}
$construction['parameters'] = $construction[1];
$params = $this->extractParameters($construction, $filter, $templateVariables);
try{
$blogPostId = $params['id'];
$this->curl->get('https://jsonplaceholder.typicode.com/posts/'.$id.'/');
$httpStatusCode = $this->curl->getStatus();
$response = $this->curl->getBody();
if ((string)$httpStatusCode === 200) {
$data = $this->json->unserialize($response);
$content = isset($data['body']) ? $data['body'] : "";
}
}catch(\LogicException $e){
$this->logger->critical($e);
}
return $content;
}
/**
* @inheritdoc
*/
public function getRegularExpression(): string
{
return '/{{(blogPost)(.*?)}}/si';
}
/**
* Extract and parse parameters from the construction
*
* @param array $construction
* @param Template $filter
* @param array $templateVariables
* @return array
*/
private function extractParameters(array $construction, Template $filter, array $templateVariables): array
{
if (empty($construction['parameters'])) {
return [];
}
/** @var Parameter $tokenizer */
$tokenizer = $this->parameterTokenizerFactory->create();
$tokenizer->setString($construction['parameters']);
$parameters = $tokenizer->tokenize();
foreach ($parameters as $key => $value) {
if (substr($value, 0, 1) === '$') {
$parameters[$key] = $this->variableResolver->resolve(
substr($value, 1),
$filter,
$templateVariables
);
}
}
return $parameters;
}
}
Step 2. Add the Directive Processor to the Magento 2 Filtering List.
Create a di.xml file and add the following code to add the code to add directive to the list.
<?xml version="1.0"?>
<!--
/**
* Klizer_BlogPost extension
*
* @copyright Copyright © 2024 Klizer. All rights reserved.
* @author Vuchuru Sai Mohan - mohanvs@dckap.com
*/
-->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
<type name="Magento\Framework\Filter\Template">
<arguments>
<argument name="directiveProcessors" xsi:type="array">
<item name="blogpost" sortOrder="300" xsi:type="object">Klizer\BlogPost\Filter\DirectiveProcessor\BlogPostDirective</item>
</argument>
</arguments>
</type>
</config>
Step 3: Run Deployment Commands
php bin/magento setup:upgrade
php bin/magento setup:di:compile
php bin/magento cache:flush
Usage
- Open any CMS page or CMS block and add the following code to load the dynamic data using the blogpost directive.
<h1>Blog Post Content :</h1><br>
<p>{{blogpost id="1"}}</p>
- Save the CMS page or CMS block and open the front end to see the results.
ScreenShot
Recommended Read: Latest on Magento 2.4.7 Release
Case Study
Problem
Recently one of our clients wanted to use a separate content management system known as Contentful for all their content management needs. Contentful is a content management system that provides data modeling and other features.
In contentful you can link multiple resources in a single resource and use that information across different platforms like mobile, web, wear os, and other gadgets. They provide an API to expose the data for each page or resource through which we can get all the information like Title, Author, Content, assets URLs, etc.
The problem is that the contentful data model can be changed at any time from contentful and whatever we had put in the phtml to read the schema is no longer valid and we had to do a deployment whenever there is a change. This requires frequent deployment in production and customers have to face downtime.
Solution
- The solution was to provide a feature where we could read the data from the API and still be able to do caching, customer segmentation, and scheduling which are part of Magento 2 CMS pages and CMS Blocks.
- We achieved this by creating a new CMS directive to fetch the information from the contentful API and parse the response to get the title, author, and other information.
- Based on this changed response schema we update the dynamic template which can be edited from the WYSIWYG editor.
- When the page is loaded for the first time it gets the information from the API and renders the dynamic template with the response from the API.
- The Magento CMS system then caches this information and would only be cleared when the block is updated or cached from the admin.
Outcome
- This solution’s outcome is a feature that extends the Magento 2 CMS Page and CMS block feature with content that supports caching, customer segmentation, and scheduling.
- Using this CMS directive developers can always modify the template from admin without the need for code deployment and save hours in development and deployment time.
- The client can also monitor and make changes to the content layout css, and how the content is displayed within a few minutes from the admin. Using this we can achieve high page load times with effective caching.
Implement Custom Directives in Magento 2 with Klizer
The custom directives implementation in Magento 2 expands its CMS capabilities by enabling seamless content integration from external sources, such as Contentful. This approach streamlines content management processes, allowing for efficient adaptation to schema changes and optimized performance through effective caching. It encourages clients with greater flexibility and agility in managing content, enhancing their overall experience with the platform.
Get in touch with us to get further inquiries and explore how our solutions can benefit your business.