<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Graham Crocker</title>
    <description>The latest articles on Forem by Graham Crocker (@grahamcrocker).</description>
    <link>https://forem.com/grahamcrocker</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F707600%2F6f1c1174-2f55-442e-a8f3-45e209647020.jpg</url>
      <title>Forem: Graham Crocker</title>
      <link>https://forem.com/grahamcrocker</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/grahamcrocker"/>
    <language>en</language>
    <item>
      <title>Send emails with php on mac quickly with mailhog and mhsendmail</title>
      <dc:creator>Graham Crocker</dc:creator>
      <pubDate>Wed, 15 Mar 2023 17:04:04 +0000</pubDate>
      <link>https://forem.com/grahamcrocker/send-emails-with-php-on-mac-quickly-with-mailhog-and-mhsendmail-48od</link>
      <guid>https://forem.com/grahamcrocker/send-emails-with-php-on-mac-quickly-with-mailhog-and-mhsendmail-48od</guid>
      <description>&lt;p&gt;Install Mailhog&lt;br&gt;
&lt;code&gt;brew update &amp;amp;&amp;amp; brew install mailhog &amp;amp;&amp;amp; brew services start mailhog&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Check whether PHP can send mail&lt;/p&gt;

&lt;p&gt;&lt;code&gt;php -a&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mail('test@test.com', 'subject', 'test');
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check &lt;a href="http://0.0.0.0:8025/" rel="noopener noreferrer"&gt;http://0.0.0.0:8025/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If nothing appears the easiest way to fix this is to install MHSendmail&lt;/p&gt;

&lt;p&gt;&lt;code&gt;brew install go&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;go install github.com/mailhog/mhsendmail@latest&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Get your php version and create a new file with respect to your version&lt;/p&gt;

&lt;p&gt;&lt;code&gt;php --ini&lt;/code&gt;&lt;br&gt;
/usr/local/etc/php/X.X/conf.d/80-sendmail.ini&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sendmail_path = /Users/xxxx/go/bin/mhsendmail
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Restart PHP and try send mail again, it should work!&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Investigating which events are being dispatched in Magento2</title>
      <dc:creator>Graham Crocker</dc:creator>
      <pubDate>Thu, 17 Nov 2022 15:46:50 +0000</pubDate>
      <link>https://forem.com/grahamcrocker/investigating-which-events-are-being-dispatched-in-magento2-4d5h</link>
      <guid>https://forem.com/grahamcrocker/investigating-which-events-are-being-dispatched-in-magento2-4d5h</guid>
      <description>&lt;p&gt;There are cases when certain events expected to be dispatched aren't, such as with product MassUpdate and individual product edit. Individual product edit dispatches &lt;code&gt;catalog_product_save_after&lt;/code&gt;, but this does not apply to MassUpdate so we must investigate which events can be hooked in.&lt;/p&gt;

&lt;p&gt;Go to &lt;code&gt;vendor/magento/module-staging/Model/Event/Manager.php&lt;/code&gt; and in function &lt;code&gt;dispatch&lt;/code&gt; below $eventName variable declaration add the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$eventName = mb_strtolower($eventName);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// devmod start
$filename = BP . '/var/log/events.log';

$writer = new \Zend\Log\Writer\Stream($filename);
$logger = new \Zend\Log\Logger();
#Fix for 2.4.x below
$logger-&amp;gt;addWriter($writer);
$logger-&amp;gt;info($eventName);
// devmod end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Magento 2.4.x Fix&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$writer = new \Zend_Log_Writer_Stream($filename);
$logger = new \Zend_Log();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Before running your Magento process or journey&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;&lt;code&gt;tail -f /tmp/events.log&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;E.g. Search and investigate events&lt;br&gt;
&lt;code&gt;cat /tmp/events.log | grep catalog_product&lt;/code&gt;&lt;br&gt;
&lt;code&gt;cat /tmp/events.log | grep save_after&lt;/code&gt;&lt;br&gt;
&lt;code&gt;cat /tmp/events.log | grep update_before&lt;/code&gt;&lt;br&gt;
&lt;code&gt;cat var/log/events.log| grep save_after | grep -v model | grep -v theme&lt;/code&gt;&lt;/p&gt;

</description>
      <category>watercooler</category>
    </item>
    <item>
      <title>Understanding Magento2 Versioning Specification</title>
      <dc:creator>Graham Crocker</dc:creator>
      <pubDate>Mon, 22 Nov 2021 09:16:03 +0000</pubDate>
      <link>https://forem.com/grahamcrocker/understanding-magento2-versioning-specification-190n</link>
      <guid>https://forem.com/grahamcrocker/understanding-magento2-versioning-specification-190n</guid>
      <description>&lt;h3&gt;
  
  
  Introduction
&lt;/h3&gt;

&lt;p&gt;The Magento version specification is quite complex. One need only read the release notes. Take the example of 2.3.0 and 2.3.1. A big release and another big release is my interpretation. You can spend a whole day reading them and trying to understand the difference. One may interpret that 2.3.0 brings about a lot of changes in terms of the way the system is run and in 2.3.1 a lot of improvements on it. There are further complications which I tackle later in this post.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://devdocs.magento.com/guides/v2.3/release-notes/ReleaseNotes2.3.0Commerce.html"&gt;2.3.0 release notes&lt;/a&gt;&lt;br&gt;
&lt;a href="https://devdocs.magento.com/guides/v2.3/release-notes/ReleaseNotes2.3.1OpenSource.html"&gt;2.3.1 release notes&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Or maybe join me in my deep dive into understanding their versioning system by analysing the potential backwards compatibility breaks they introduce when upgrading your Magento instance.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Please note this is not a criticism or a bashing exercise, but simply a deep dive to help developers upgrade their systems by understanding the context of it. A lot of Magento is open-source and I encourage anyone to submit their pull requests if they think they handle backwards compatibility better.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Versioning Specification
&lt;/h3&gt;

&lt;p&gt;Magento2 uses their own versioning specification, which may be interpreted as:&lt;br&gt;
X - Different Application&lt;br&gt;
Y - Major backward compatibility break extremely likely&lt;br&gt;
Z - Major backward compatibility break possible&lt;br&gt;
p - Patch Version where minor backward compatibility break is possible, but unlikely&lt;/p&gt;

&lt;p&gt;For example 2.3.7-p2 may be interpreted as Magento2 version 3.7 with security patch version 2.&lt;/p&gt;

&lt;p&gt;More info: &lt;a href="https://devdocs.magento.com/guides/v2.3/release-notes/backward-incompatible-changes/reference.html"&gt;https://devdocs.magento.com/guides/v2.3/release-notes/backward-incompatible-changes/reference.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This differs from semver which is interpreted as:&lt;br&gt;
X - Major version: Major backward compatibility break&lt;br&gt;
Y - Minor version: No backward compatibility break&lt;br&gt;
Z - Patch Version: No backward compatibility break.&lt;/p&gt;

&lt;p&gt;More Info: &lt;a href="https://semver.org/"&gt;https://semver.org/&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Backward compatibility
&lt;/h3&gt;

&lt;p&gt;Backward compatibility breaks means that something was changed to the effect that it can break functionality. A major one means major components stop working to the extent that the website fails to load (P0), because its no longer compatible with a service, or add to cart is not working (P1), because certain features now require enforcement like catalog permissions and so on. This may include some P2 issues. A minor one would break visual or functional website functionality (P3), but still allows the basic functions of the website to continue. This may also include P2 issues. &lt;/p&gt;

&lt;p&gt;The relevance of Backward compatibility breaks are that you may have services installed that are no longer supported, code changes such as classes/methods or database changes using tables/columns that are depricated.&lt;/p&gt;

&lt;h3&gt;
  
  
  Case for or against backwards compatibility
&lt;/h3&gt;

&lt;p&gt;There are several opinions about this. &lt;/p&gt;

&lt;p&gt;Increasing backwards compatibility may make the application more convoluted and perhaps harder to improve the code base long term. Conversely companies will be more willing to upgrade to more secure and later version. &lt;/p&gt;

&lt;p&gt;Decreasing backwards compatibility ensures that the application is trimmed off its extra code and enforces developers to use the more modern features and readapt their customizations such as writing more compatible code or using Interfaces rather than concrete classes in the DI. It also however makes upgrades more costly and time consuming especially for smaller businesses.&lt;/p&gt;

&lt;h3&gt;
  
  
  How Magento does it
&lt;/h3&gt;

&lt;p&gt;Relative to semver, it might sound strange that a x.y.Z-p incompatibility could introduce a major break, but realistically if you're running a server using a php-fpm of version 7.2 and upgrade Magento from 2.3.5 to 2.3.6 may break your website (P0) due to incompatibility with 7.3 and from 2.3.5 to 2.3.7 is extremely likely as there are are significant backward incompatibilities in php 7.4 with commonly used methods.&lt;br&gt;
If you spent 1 minute looking at each of the release notes of 2.3.0 to 2.3.1, one may read&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;We are pleased to present Magento Open Source 2.3.1. This release includes over 200 functional fixes to the core product, over 500 pull requests contributed by the community, and over 30 security enhancements.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This means that a x.y.Z-p release has nothing to do with size or backwards compatibility and rather to do with improving whatever x.Y.z-p release intended. Where matters are complicated is when Y versions are upgraded in tandem and one starts seeing features brought in by Y+1 in Y version. &lt;/p&gt;

&lt;p&gt;What Magento do is generally release a x.Y.z-p version and run it in tandem with a x.(Y-1).z-p version, which is upgraded seperately. There are surely reasons for this, it is likely to introduce several significant changes in one area while allowing some vendors with heavy customisations to get the upgrades necessary in smaller iterations. This is how you end up with versions like Magento 2.4.0 being behind Magento 2.3.7 especially when you look at version compatibilty of services like composer or Redis that support the instance running.  This makes sense when you look at release dates. Magento 2.4.0 was released 28 Jul 2020, but Magento 2.3.7 was released at 11 May 2021. See &lt;a href="https://github.com/magento/magento2/releases"&gt;https://github.com/magento/magento2/releases&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--NczmyDsy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.imgur.com/nzA7Mos.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--NczmyDsy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.imgur.com/nzA7Mos.png" alt="Magento vs Service version requirements" width="880" height="554"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;See systems requirements: &lt;a href="https://devdocs.magento.com/guides/v2.4/install-gde/system-requirements.html"&gt;https://devdocs.magento.com/guides/v2.4/install-gde/system-requirements.html&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Summary
&lt;/h3&gt;

&lt;p&gt;A X.y.z-p version is wholly incompatible. It requires special purpose tools to migrate data and code or porting to a new instance.&lt;br&gt;
A x.Y.z-p version upgrade is a very large release and usually a severe backward compatibility break, like a service incompatibility or removal/enforcement of services that support Magento, e.g. php version upgrade, or enforcement of elasticsearch for catalog indexing over mysql&lt;br&gt;
A x.y.Z-p version, is a big release, but may still introduce severe backward compatibility break through incompatibility of services, code and database changes.&lt;br&gt;
A x.y.z-P will include security fixes, which were orignally released on a patch list page on devdocs and installed via git patching. Explained in depth here. &lt;a href="https://community.magento.com/t5/Magento-DevBlog/Introducing-the-New-Security-Patch-Release/ba-p/141287"&gt;https://community.magento.com/t5/Magento-DevBlog/Introducing-the-New-Security-Patch-Release/ba-p/141287&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Implement a strategy pattern in Magento2</title>
      <dc:creator>Graham Crocker</dc:creator>
      <pubDate>Thu, 11 Nov 2021 19:31:25 +0000</pubDate>
      <link>https://forem.com/grahamcrocker/implement-a-strategy-pattern-in-magento2-2mc5</link>
      <guid>https://forem.com/grahamcrocker/implement-a-strategy-pattern-in-magento2-2mc5</guid>
      <description>&lt;h2&gt;
  
  
  The problem
&lt;/h2&gt;

&lt;p&gt;Do you ever end up creating massive switch statements in functions in order to handle different behavior resulting in lots of logic in one place thats difficult to test or understand? &lt;/p&gt;

&lt;p&gt;Take for example a type handler for an entity comprising of $X and $Y with these initial requirements:&lt;/p&gt;

&lt;p&gt;If its of Type A then return &lt;code&gt;[$X=&amp;gt;$Y]&lt;/code&gt;, if its of Type B then return &lt;code&gt;[ $X =&amp;gt; [ $Y-&amp;gt;getId(), $Y-&amp;gt;getContent() ] ]&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;One could use a simple if statement:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if ($type instanceof A) {
    return [
        $X =&amp;gt; $Y
    ]; 
} elseif ($type instanceof B) {
    return [
        $X =&amp;gt; [
            $Y-&amp;gt;getId(), $Y-&amp;gt;getContent()
    ];
} else {
    throw new \InvalidArgumentException(__('Type: %1 is not supported', $type);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above is perfectly acceptable, but what if the requirements change and you had to add several cases, handle errors with 3rd party modules for specific types or handle edge cases? You'd end up with something unmanagable and difficult to read and maintain. Potentially hundreds of lines of If statements, but then again you could improve and use a switch statement like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$typeClassName = get_class($type);
switch($typeClassName) {
    case A::class: 
        return [
            $X =&amp;gt; $Y
        ]; 
    case B::class:
        return [
            $X =&amp;gt; [
                $Y-&amp;gt;getId(), $Y-&amp;gt;getContent()
        ];
    case C::class:
        if ($X instanceof int) {
            // Do something
        } 
        if (is_array($Y)) {
             //Convert $Y into DataObject
        } 
        return [
            $X =&amp;gt; [
                $Y-&amp;gt;getId(), $Y-&amp;gt;getContent()
        ];

    default:
        throw new \InvalidArgumentException(__('Type: %1 is not supported', $type);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Neater, but new requests keep coming in for further logic to add and eventually you'll spend time looking for the type to modify and worse still increase chances of making a mistake and adding a bug.  A long convoluted switch statement will dramatically increase that chance, and as such this is a code smell and evidence of poor quality code.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution
&lt;/h2&gt;

&lt;p&gt;We know that data wise, its the same function (give us an entry based on the type of it), but the $type variable requires a different behavior. We can extend these behaviorial differences out into isolated classes limiting the scope of bugs on modification and increasing testability. This is a common design pattern known as the Strategy pattern, but how do we wire it up in something like Magento? &lt;/p&gt;

&lt;h3&gt;
  
  
  1. Create the Interface
&lt;/h3&gt;

&lt;p&gt;These types all have something in common, a get() function, but we also need a way to discern from other types and one way to do it is test if it is applicable&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;?php
namespace \Your\Vendor\Model\TypeMapper;

interface TypeInterface 
{
    public function get($X, $Y): array;

    public function is($type): bool
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Create the Type Mapper
&lt;/h3&gt;

&lt;p&gt;This class sets the context of the behavior, by looping over classes that implements TypeInterface until it gets the one which matches the type and implements the required behavior&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;namespace \Your\Vendor\Model;

use \Your\Vendor\Model\TypeMapper\TypeInterface;

class TypeMapper
{
    private $arrayOfTypeMappers;

    public function __construct(
        array $arrayOfTypeMappers
    )
    {
        $this-&amp;gt;arrayOfTypeMappers = $arrayOfTypeMappers;
    }

    public function get($type, $X, $Y): array
    {
        $type = array_filter($this-&amp;gt;arrayOfTypeMappers,
            function ($mapper) use ($fieldType) {
                if ($mapper instanceof TypeInterface) {
                    return $mapper-&amp;gt;is($fieldType);
                }
         });
        if (count($type) !== 1) {
            throw new \InvalidArgumentException(__("Type %1 is not supported by %2", $type, self::class));
        }
        return current($type)-&amp;gt;get($X, $Y);
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Wouldn't it be great if you could avoid this and get the classes implementing the Interface like you can do in .NET? &lt;code&gt;get_declared_classes()&lt;/code&gt; unfortunetely won't work as PHP lazy loads classes. There is a way to preload specific folders in composer, but its rarely worth doing unless you're building something specific (like a microservice outside of Magento). We'll get to solving this in step 4.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Extend the Interface to create a concrete class/s
&lt;/h3&gt;

&lt;p&gt;To keep this short, we're just going to implement one concrete class and add the declared classes in the di component&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;namespace \Your\Vendor\Model\TypeMapper;
use \Your\Vendor\Model\Types\A as TypeA;
class A implements TypeInterface
{
    public function get($X, $Y): array
    {
        return [
            $X =&amp;gt; $Y
        ]; 
    }

    public function is(string $type): bool
    {
       return $type instanceof TypeA;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Add the types to your etc/di.xml
&lt;/h3&gt;

&lt;p&gt;See &lt;a href="https://devdocs.magento.com/guides/v2.4/extension-dev-guide/build/di-xml-file.html#constructor-arguments"&gt;https://devdocs.magento.com/guides/v2.4/extension-dev-guide/build/di-xml-file.html#constructor-arguments&lt;/a&gt; for further info&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"&amp;gt;
    &amp;lt;type name="Your\Vendor\Model\TypeMapper"&amp;gt;
        &amp;lt;arguments&amp;gt;
&amp;lt;argument name="arrayOfTypeMappers" xsi:type="array"&amp;gt;
                &amp;lt;item name="a" xsi:type="object"&amp;gt;
                    Your\Vendor\Model\TypeMapper\A
                &amp;lt;/item&amp;gt;
                &amp;lt;item name="b" xsi:type="object"&amp;gt;
                    Your\Vendor\Model\TypeMapper\B
                &amp;lt;/item&amp;gt;
                &amp;lt;item name="c" xsi:type="object"&amp;gt;
                    Your\Vendor\Model\TypeMapper\C
                &amp;lt;/item&amp;gt;
                &amp;lt;item name="d" xsi:type="object"&amp;gt;
                    Your\Vendor\Model\TypeMapper\D
                &amp;lt;/item&amp;gt;
            &amp;lt;/argument&amp;gt;
        &amp;lt;/arguments&amp;gt;
    &amp;lt;/type&amp;gt;
&amp;lt;/config&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 5. Usage
&lt;/h3&gt;

&lt;p&gt;Add &lt;code&gt;\Your\Vendor\Model\TypeMapper&lt;/code&gt; as a property $typeMapper in the client class constructor and use &lt;code&gt;$this-&amp;gt;typeMapper&amp;gt;get($field, $X, $Y)&lt;/code&gt; to implement this functionality.&lt;/p&gt;

&lt;p&gt;If you ever need to add a new type, simply create the class in the relavant directory, make it implement the interface and declare it in the di.xml as per Step 4. Modifying is simple and limited to the types you modify increasing quality of your code.&lt;/p&gt;

&lt;p&gt;Give me a shout out if you found this interesting or helpful.&lt;/p&gt;

</description>
      <category>magento</category>
      <category>php</category>
      <category>patterns</category>
      <category>switch</category>
    </item>
    <item>
      <title>Create a Magento 2 Patch to override core</title>
      <dc:creator>Graham Crocker</dc:creator>
      <pubDate>Tue, 02 Nov 2021 18:54:06 +0000</pubDate>
      <link>https://forem.com/grahamcrocker/creating-a-magento-2-patch-1a2c</link>
      <guid>https://forem.com/grahamcrocker/creating-a-magento-2-patch-1a2c</guid>
      <description>&lt;p&gt;You may run into a situation where you need to modify core for some reason or another. In my career as a Magento2 developer there were many instances where we needed to do this and there are many ways to do it such as overriding/extending via preference or using a plugin, but I found that if you need to fix a Magento bug the best way of doing it is via a git patch, because if anything in the vendor/magento folder changes it will not apply the change and will be more visible to you, rather than spotting weird errors in logs a few years later when Magento2 fixes it. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fit0pjfpw5m17xxz0ixoz.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fit0pjfpw5m17xxz0ixoz.jpeg" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You may also submit this fix as a PR to the magento2 github repository, but be advised that there is a lot of overhead that goes into proving a problem exists &lt;a href="https://github.com/magento/magento2/pull/10102" rel="noopener noreferrer"&gt;https://github.com/magento/magento2/pull/10102&lt;/a&gt; and there are cases where this is not done such as this example case where we used a patch to fix an issue that was infrastructure related. &lt;br&gt;
&lt;a href="https://alanstorm.com/fixing-x-magento-tags-too-large-errors/" rel="noopener noreferrer"&gt;https://alanstorm.com/fixing-x-magento-tags-too-large-errors/&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  1. Commit the original state
&lt;/h3&gt;

&lt;p&gt;Go into root directory &lt;/p&gt;

&lt;p&gt;&lt;code&gt;git init&lt;/code&gt;&lt;br&gt;
&lt;code&gt;git add vendor/magento&lt;/code&gt;&lt;br&gt;
&lt;code&gt;git commit -m 'init'&lt;/code&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  2. Commit your change
&lt;/h3&gt;

&lt;p&gt;Make your code change&lt;br&gt;
&lt;code&gt;git add vendor/magento&lt;/code&gt;&lt;br&gt;
&lt;code&gt;git commit -m 'Description of your change'&lt;/code&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  3. Create the Patch
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;git format-patch -1 HEAD&lt;/code&gt;&lt;br&gt;
&lt;code&gt;cp x.patch patches/INITIALS-descriptionofchange.patch&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Ensure the relative paths are a/vendor/x/y&lt;/p&gt;
&lt;h3&gt;
  
  
  4. Test the patch
&lt;/h3&gt;

&lt;p&gt;Find the commit of init &lt;br&gt;
&lt;code&gt;git log&lt;/code&gt;&lt;br&gt;
Reset vendor folder&lt;br&gt;
&lt;code&gt;git reset --hard &amp;lt;commit hash&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Execute the patch to check whether it applies or not&lt;br&gt;
&lt;code&gt;if git apply --check patches/INITIALS-descriptionofchange.patch; then git apply patches/INITIALS-descriptionofchange.patch; fi"&lt;/code&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  5. Add script to composer.json to apply the patch
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Add to composer.json
"scripts": {
   "post-install-cmd": [
       "if git apply --check patches/INITIALS-descriptionofchange.patch; then git apply patches/INITIALS-descriptionofchange.patch; fi",
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  6. Conclusion
&lt;/h3&gt;

&lt;p&gt;Ensure to run &lt;code&gt;composer install&lt;/code&gt; to apply changes on CI/CD process or when pulling in other developers patches&lt;/p&gt;
&lt;h3&gt;
  
  
  Troubleshooting
&lt;/h3&gt;

&lt;p&gt;If your machine can't handle git add to &lt;code&gt;vendor/magento&lt;/code&gt; then go directly to the folder you need changing and do the process from there rather than from root directory. After you copy over the patch match the relative paths (where there is a/... and b/...) with the full path like &lt;code&gt;a/vendor/magento&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;If your patch is not applying, to find out why&lt;br&gt;
&lt;br&gt;
 &lt;code&gt;git apply --reject x.patch&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

</description>
      <category>git</category>
      <category>magento</category>
      <category>magento2</category>
    </item>
    <item>
      <title>Create a custom virtual class logger in Magento2</title>
      <dc:creator>Graham Crocker</dc:creator>
      <pubDate>Thu, 16 Sep 2021 20:38:38 +0000</pubDate>
      <link>https://forem.com/grahamcrocker/create-a-custom-virtual-class-logger-in-magento2-57g0</link>
      <guid>https://forem.com/grahamcrocker/create-a-custom-virtual-class-logger-in-magento2-57g0</guid>
      <description>&lt;p&gt;There are a number of ways to create a custom log in Magento2 and one of the nicest ways to do is by creating a Virtual class by using the Dependancy Injection mechanism.&lt;/p&gt;

&lt;p&gt;Create a typical skeleton module, in this case I named it YourVendor_Module and follow these steps&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Create app/code/YourVendor/Module/Logger/Reporter.php
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;If you already have a class with a logger, adapt it to fit this basic structure.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;At this stage this class writes info and error messages to the standard log filenames.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;?php
namespace YourVendor\Module\Logger;
use Psr\Log\LoggerInterface;

/**
 * Class Reporter
 * @package YourVendor\Module\Logger
 */
class Reporter
{
    /**
     * @var LoggerInterface
     */
    protected $logger;

    /**
     * Reporter constructor.
     * @param LoggerInterface $logger
     */
    public function __construct(
        LoggerInterface $logger
    )
    {
        $this-&amp;gt;logger = $logger;
    }

    /**
     * @param string $message
     * @param bool $print prints messages from command line
     */
    public function log(string $message, bool $print = true)
    {
        if ($print) {
            echo __($message . PHP_EOL);
        }
        $this-&amp;gt;logger-&amp;gt;info($message);
     }

    /**
     * @param string $message
     * @param bool $print prints messages from command line
     */
    public function logError(string $message, bool $print = true)
    {
        if ($print) {
            echo __($message . PHP_EOL);
        }
        $this-&amp;gt;logger-&amp;gt;error($message);
     }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Create virtual logger in app/code/YourVendor/Module/etc/di.xml
&lt;/h3&gt;

&lt;p&gt;This virtual class overrides &lt;code&gt;Magento\Framework\Logger\Monolog&lt;/code&gt; and links to Logger Handlers which is where the filenames are specified.&lt;/p&gt;

&lt;p&gt;As a matter of code readability namespace virtual classes under &lt;code&gt;YourVendor\Module\Virtual&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;virtualType name="YourVendor\Module\Virtual\Logger"
                 type="Magento\Framework\Logger\Monolog"
    &amp;gt;
        &amp;lt;arguments&amp;gt;
            &amp;lt;argument name="handlers" xsi:type="array"&amp;gt;
                &amp;lt;item name="debug" xsi:type="object"&amp;gt;
                    YourVendor\Module\Virtual\LoggerHandler
                &amp;lt;/item&amp;gt;
            &amp;lt;/argument&amp;gt;
        &amp;lt;/arguments&amp;gt;
    &amp;lt;/virtualType&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Create and Link to the DebugLoggerHandler virtual class in di.xml
&lt;/h3&gt;

&lt;p&gt;For simplicity, we log all messages to the yourvendor_module file by preferring the Base Logger, but if you require you can create as many virtual types as you want to route to what you like &lt;a href="https://devdocs.magento.com/guides/v2.4/config-guide/log/custom-logger-handler.html"&gt;More Info&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;virtualType name="YourVendor\Module\Virtual\LoggerHandler"
                 type="Magento\Framework\Logger\Handler\Base"
    &amp;gt;
        &amp;lt;arguments&amp;gt;
            &amp;lt;argument name="fileName" xsi:type="string"&amp;gt;/var/log/yourvendor_module.log&amp;lt;/argument&amp;gt;
        &amp;lt;/arguments&amp;gt;
    &amp;lt;/virtualType&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you want to route Errors you can do something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;virtualType name="YourVendor\Module\Virtual\ErrorLoggerHandler"
                 type="Magento\Framework\Logger\Handler\Exception"
    &amp;gt;
        &amp;lt;arguments&amp;gt;
            &amp;lt;argument name="fileName" xsi:type="string"&amp;gt;/var/log/yourvendor_module_errors.log&amp;lt;/argument&amp;gt;
        &amp;lt;/arguments&amp;gt;
    &amp;lt;/virtualType&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then go back to Step 2 in the xml and add the following argument to the arguments handler&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;item name="error" xsi:type="object"&amp;gt;
                    YourVendor\Module\Virtual\ErrorLoggerHandler
                &amp;lt;/item&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Link the virtual classes with actual class in di.xml
&lt;/h3&gt;

&lt;p&gt;To override any Psr\Log\LoggerInterface in any class, you need to replace the type name and fill the logger argument with your virtual class&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;type name="YourVendor\Module\Logger\Reporter"&amp;gt;
        &amp;lt;arguments&amp;gt;
            &amp;lt;argument name="logger" xsi:type="object"&amp;gt;YourVendor\Module\Virtual\Logger&amp;lt;/argument&amp;gt;
        &amp;lt;/arguments&amp;gt;
&amp;lt;/type&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  5. Generate the files and upgrade
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;rm -rf generated/*&lt;/code&gt; or &lt;code&gt;bin/magento setup:di:compile&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;bin/magento setup:upgrade&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;rm -rf var/cache/*&lt;/code&gt; or &lt;code&gt;bin/magento c:f&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If this helped you out send me some virtual love and subscribe!&lt;/p&gt;

</description>
      <category>magento</category>
      <category>magento2</category>
      <category>logging</category>
      <category>php</category>
    </item>
  </channel>
</rss>
