Magento – How to create a custom Filter directive (e.g. for emails)

email-templatesmagento-2.1

Depending on a custom order's attribute, we would like to slightly adapt the emails for the new order confirmations.

Therefor, we try to implement a new equals directive in Magento\Framework\Filter\Template (we extend this class in a custom module, of course). This directive should take two parameters and evaluate whether these values are equal. Magento's build in if directive only checks, if the given parameter is not equal an empty string.

For example:

{{equals value1 value2}}The two parameters are equal.{{else}}The two parameters are NOT equal.{{/equals}}

We added the following constant to our custom Template.php:

const CONSTRUCTION_EQUALS_PATTERN = '/{{equals\s*(.*?)}}(.*?)({{else}}(.*?))?{{\\/equals\s*}}/si';

The value of this constant is the same as the value of the CONSTRUCTION_IF_PATTERN constant, except we replaced both if with equals.

Furthermore, we override the filter($value) function of the original Template and teach this function to execute a new callback named equalsDirective, if one of the given values matches to above defined pattern.

foreach ([
  self::CONSTRUCTION_DEPEND_PATTERN => 'dependDirective',
  self::CONSTRUCTION_IF_PATTERN => 'ifDirective',
  self::CONSTRUCTION_TEMPLATE_PATTERN => 'templateDirective',
  self::CONSTRUCTION_EQUALS_PATTERN => 'equalsDirective' // this is interesting line
] as $pattern => $directive)

The function equalsDirective just should write a debug message, when it's got called.

Additionally, we adapt the template for the new orders confirmation emails (Magento\Sales\view\email\order_new.html) by adding the following beneath the greeting:

{{equals order.checkout_type request}}This is a request.{{else}}This is an order.{{/equals}}

The problem is, the equalsDirective function never gets called. The given values of the filter function do not contain the above equals directive. Thus, a pattern — as defined above — never is found.

We also verified that {{var order.checkout_type}} returns the correct value. Surprisingly, this is not part of the given values of the filter function, as well. But its value is correctly rendered in the email that we received.

Has someone an idea what we missed? Do we have to declare the directives that are parsed from the email templates somewhere else?

We appreciate any help 🙂

FYI: Couple of years ago we did the same in Magento 1 and it was working fine.

Best Answer

Seems like in 2.3.4 this logic was modified and now you can use di.xml to inject your directives by passing it into the directiveProcessors property of the constructor, see gitlab link.

Taken from Magento's original article for v2.3.4

Create a class that implements Magento\Framework\Filter\SimpleDirective\ProcessorInterface:

declare(strict_types=1);
namespace GadgetCorp\CustomEmailDirective\Model;
use Magento\Framework\Filter\SimpleDirective\ProcessorInterface;
use Magento\Framework\Pricing\PriceCurrencyInterface;
/**
* Calculates the lifetime spend of all customers
*/
class LifetimeSpendDirective implements ProcessorInterface
{
 /**
 * @var PriceCurrencyInterface
 */
 private $priceCurrency;

 /**
 * @param PriceCurrencyInterface $priceCurrency
 */
 public function __construct(PriceCurrencyInterface $priceCurrency)
 {
     $this->priceCurrency = $priceCurrency;
 }

 /**
 * @inheritDoc
 */
 public function getName(): string
 {
     return 'lifetime_spend';
 }

 /**
 * @inheritDoc
 */
 public function process($value, array $parameters, ?string $html): string
 {
     $shouldBold = !empty($parameters['should_bold']);
     $amount = $this->priceCurrency->getCurrencySymbol() . $this->calculateLifetimeSpend();
     return ($shouldBold ? '<strong>' . $amount . '</strong>' : $amount);
 }

 /**
 * @inheritDoc
 */
 public function getDefaultFilters(): ?array
 {
     // Make sure newlines are converted to <br /> tags by default
     return ['nl2br'];
 }

 /**
 * Calculate the total amount of money spent by all customers for all time
 *
 * @return float
 */
 private function calculateLifetimeSpend(): float
 {
     // Add code here to calculate the lifetime spend
     return 123.45;
 }

}

Add the new directive to the pool by adding this block to di.xml.

<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
 <type name="Magento\Framework\Filter\SimpleDirective\ProcessorPool">
 <arguments>
     <argument name="processors" xsi:type="array">
         <item name="lifetime_spend" xsi:type="object">GadgetCorp\CustomEmailDirective\Model\LifetimeSpendDirective</item>
     </argument>
 </arguments>