<?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: Vladyslav Podorozhnyi 🇺🇦 🌻</title>
    <description>The latest articles on Forem by Vladyslav Podorozhnyi 🇺🇦 🌻 (@vpodorozh).</description>
    <link>https://forem.com/vpodorozh</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%2F1118141%2F906f57f0-17d6-493f-af8f-6fdf928aa691.jpg</url>
      <title>Forem: Vladyslav Podorozhnyi 🇺🇦 🌻</title>
      <link>https://forem.com/vpodorozh</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/vpodorozh"/>
    <language>en</language>
    <item>
      <title>Should you upgrade your Magento store at all? Developer view</title>
      <dc:creator>Vladyslav Podorozhnyi 🇺🇦 🌻</dc:creator>
      <pubDate>Fri, 27 Oct 2023 15:45:13 +0000</pubDate>
      <link>https://forem.com/run_as_root/should-you-upgrade-your-magento-store-at-all-developer-view-k8h</link>
      <guid>https://forem.com/run_as_root/should-you-upgrade-your-magento-store-at-all-developer-view-k8h</guid>
      <description>&lt;p&gt;Hello everyone. 👋 &lt;/p&gt;

&lt;p&gt;This article will be a bit less technical and more about the value of the upgrade for store owners. However, all of my thoughts would be described from the view of the developer that design, implement, and maintain such stores - so expect at least some level of technical expertise and judgments here.&lt;br&gt;&lt;br&gt;
Obviously, we would speak in contexts of Magento 2 / Adobe Commerce / Mage-OS - but I believe that findings and ideas described here would be useful for Shopware too, as well as for any other e-commerce system.&lt;br&gt;&lt;br&gt;
So - lets move on and have a small walkthrough of this topic. 🚶‍♀️🚶‍♂️&lt;/p&gt;




&lt;h2&gt;
  
  
  What does any upgrade bring us:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;new features&lt;/li&gt;
&lt;li&gt;new possibilities for developers&lt;/li&gt;
&lt;li&gt;bugfixes&lt;/li&gt;
&lt;li&gt;security fixes&lt;/li&gt;
&lt;li&gt;agility - possibility to upgrade faster to all future New versions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s check each point and ask next questions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What value does it bring to the project?&lt;/li&gt;
&lt;li&gt;Price?&lt;/li&gt;
&lt;li&gt;Do we need this value?&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  New features
&lt;/h3&gt;

&lt;h4&gt;
  
  
  What value does it bring to the project?
&lt;/h4&gt;

&lt;p&gt;New business possibilities for store owners. Or improved UX in case it is an upgrade of Hyvä.&lt;br&gt;&lt;br&gt;
Or improved code execution performance ( yes - I think it is a feature 😄 ).&lt;/p&gt;

&lt;h4&gt;
  
  
  Price?
&lt;/h4&gt;

&lt;p&gt;Is it free? - YES. Then we need it!  [ actually, it is not free - see my explanation below ]&lt;br&gt;&lt;br&gt;
That is what the client says 😄. For him, it looks like a FREE fancy addition to all the hard work that is going to be done.&lt;br&gt;&lt;br&gt;
Not all features from the upgrade can be easily adapted if we take into account all customization project already has - the minimum you should consider is styling, maximum - is rebuilding business logic from top to bottom.&lt;/p&gt;

&lt;h4&gt;
  
  
  Do we need this value?
&lt;/h4&gt;

&lt;p&gt;If the store owner needs a new feature - the store owner would ask for it explicitly. 😄&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Answer: more NO than yes.&lt;/strong&gt; At least in scope of the upgrade.&lt;/p&gt;




&lt;h3&gt;
  
  
  New Possibilities for Developers
&lt;/h3&gt;

&lt;h4&gt;
  
  
  What value does it bring to the project?
&lt;/h4&gt;

&lt;p&gt;We have some variations here, but here is a compilation of the benefits it gives us:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;build code faster&lt;/li&gt;
&lt;li&gt;Make code less shaky &amp;amp; buggy&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Price?
&lt;/h4&gt;

&lt;p&gt;Is it free? - YES. Then we need it!&lt;br&gt;&lt;br&gt;
That is what the developer says 😄 (déjà vu). And again - it looks free, but there could be a clown that eats you alive. 🤡  &lt;/p&gt;

&lt;h4&gt;
  
  
  Do we need this value?
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Answer: It depends - but more NO than yes.&lt;/strong&gt; Unless it is a painless upgrade from php 5 to php 7 - then totally yes, yep?   😄&lt;br&gt;
Don't get me wrong - I love new technologies and new versions of old ones, and I wish to work on the edge of tech!  However, is it worth it here and now? 🤷&lt;/p&gt;

&lt;p&gt;I would plan tech stack upgrade not as an additional point to the system/extension upgrade - but as quite a big chunk of work. So plan carefully and do not think it will be magically transferred without any effort.&lt;/p&gt;




&lt;h3&gt;
  
  
  Bugfixes
&lt;/h3&gt;

&lt;h4&gt;
  
  
  What value does it bring to the project?
&lt;/h4&gt;

&lt;p&gt;less bugs 😅&lt;/p&gt;

&lt;h4&gt;
  
  
  Price?
&lt;/h4&gt;

&lt;p&gt;Is it a pain in the ass? - sure.&lt;br&gt;&lt;br&gt;
Is it more expensive than just fixing it by yourself? - usually, no.&lt;/p&gt;

&lt;h4&gt;
  
  
  Do we need this value?
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Answer: Yes in 99% of the cases. Other 1% - Developer decides.&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Upgrade is a tool to achieve a goal - in this case to fix the bug/bugs. But monkeypatch is also a tool 🙉.&lt;br&gt;&lt;br&gt;
Choose wisely!&lt;/p&gt;




&lt;h3&gt;
  
  
  Security Fixes
&lt;/h3&gt;

&lt;h4&gt;
  
  
  What value does it bring to the project?
&lt;/h4&gt;

&lt;p&gt;Shop won’t be spammed / hacked / Arbitrary code execution won't happen.&lt;br&gt;&lt;br&gt;
e.g. - no disappointed buyers attack you 😸&lt;/p&gt;

&lt;h4&gt;
  
  
  Price?
&lt;/h4&gt;

&lt;p&gt;Well - security is priceless 😎&lt;br&gt;&lt;br&gt;
However, sometimes it is cheaper to just transfer security fix changes - if you know what and where to transfer, which is not usually the case.&lt;br&gt;
Let’s say it this way - I would not risk the security of my client to save another couple of hours on an upgrade.&lt;/p&gt;

&lt;h4&gt;
  
  
  Do we need this value?
&lt;/h4&gt;

&lt;p&gt;Yes - we need it. Always.&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Answer: Yes.&lt;/strong&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  Agility - you can upgrade faster to all future NEW versions
&lt;/h3&gt;

&lt;h4&gt;
  
  
  What value does it bring to the project?
&lt;/h4&gt;

&lt;p&gt;Constant upgrades -&amp;gt; minimise time spent on one particular upgrade.&lt;br&gt;&lt;br&gt;
e.g. - clean your home once a week and it takes you half an hour, do it once a year - you would cry for help and won’t finish in a days 🙉&lt;/p&gt;

&lt;h4&gt;
  
  
  Price?
&lt;/h4&gt;

&lt;p&gt;Do upgrades constantly - usually, once a quarter you have a patch upgrade, and some small p1, p2, p… versions during the quarter. Also, once 1,5 years, you do the bigger upgrade (Minor by semantic ver and somewhat Major based on Magento lifecycle ).&lt;br&gt;
In return, you will get a healthy webshop with a high level of agility to get new versions. Recent security patches once again reminded us how important it is to be on the edge (see &lt;a href="https://helpx.adobe.com/security/products/magento/apsb23-50.html" rel="noopener noreferrer"&gt;APSB23-50&lt;/a&gt; ).&lt;/p&gt;

&lt;h4&gt;
  
  
  Do we need this value?
&lt;/h4&gt;

&lt;p&gt;I afraid so.&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Answer: Yes.&lt;/strong&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  Sum Up
&lt;/h3&gt;

&lt;p&gt;We need upgrades. As a minimum for security and agility needs. But also to keep the shop healthy and without technical debt.&lt;br&gt;&lt;br&gt;
It also gets in use for other points from our list and can be useful or harmful for different points.&lt;br&gt;&lt;br&gt;
Upgrade, as an ability, needs to be used mindfully but not blindly 🙈 Build correct expectations from your upgrade, estimate, and plan your work accordingly.&lt;br&gt;
Last but not least - explain why upgrades are needed and what is going to be delivered with this specific release.&lt;/p&gt;

&lt;p&gt;Hope my a bit structured thought flow will gonna help you to understand upgrades, what we need to take out of them, and how to communicate it right.  &lt;/p&gt;

&lt;p&gt;Chao Kako ☕ &lt;/p&gt;

</description>
      <category>adobecommerce</category>
      <category>magento2</category>
      <category>mageos</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Cron Jobs in Magento 2: How to Adjust Schedules and Cron Groups Seamlessly</title>
      <dc:creator>Vladyslav Podorozhnyi 🇺🇦 🌻</dc:creator>
      <pubDate>Fri, 22 Sep 2023 18:53:56 +0000</pubDate>
      <link>https://forem.com/run_as_root/cron-jobs-in-magento-2-how-to-adjust-schedules-and-cron-groups-seamlessly-4noj</link>
      <guid>https://forem.com/run_as_root/cron-jobs-in-magento-2-how-to-adjust-schedules-and-cron-groups-seamlessly-4noj</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Hello!&lt;/p&gt;

&lt;p&gt;In today's article, I'll cover a topic that's not too tricky, but can be a bit annoying without some useful tips. Specifically, we'll dive into Magento 2 ( Mage-OS / Adobe Commerce ) to discuss cron jobs, their configurations, and the process of rescheduling them and altering their execution group.&lt;/p&gt;

&lt;p&gt;You might wonder, why bother changing the schedule or group of a cron job? Let's find out.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Adjust the Cron Schedule?
&lt;/h2&gt;

&lt;p&gt;Occasionally, a cron job might execute too frequently, leading to server load issues or even deadlocks. On the other hand, if it doesn't run often enough, you could be left waiting for emails that take an hour to send. This scenario becomes particularly tricky when these cron jobs are associated with third-party extensions.&lt;/p&gt;

&lt;p&gt;Let's imagine we are using 3rd party Feed and Mailing extensions, and we are not OK with their cron jobs schedule.&lt;br&gt;&lt;br&gt;
How can you modify the execution patterns of these cron jobs without tampering with the third-party code directly? By implementing changes in your own codebase – your module. Let's explore this next.&lt;/p&gt;
&lt;h2&gt;
  
  
  Rescheduling Cron Jobs
&lt;/h2&gt;

&lt;p&gt;Let's have a look at the cron job of the Feed extension &lt;code&gt;feed_generation&lt;/code&gt;.  &lt;/p&gt;

&lt;p&gt;Here is its definition:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;config&lt;/span&gt; &lt;span class="na"&gt;xmlns:xsi=&lt;/span&gt;&lt;span class="s"&gt;"http://www.w3.org/2001/XMLSchema-instance"&lt;/span&gt; &lt;span class="na"&gt;xsi:noNamespaceSchemaLocation=&lt;/span&gt;&lt;span class="s"&gt;"urn:magento:module:Magento_Cron:etc/crontab.xsd"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;group&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"default"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      ...
        &lt;span class="nt"&gt;&amp;lt;job&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"feed_generation"&lt;/span&gt; &lt;span class="na"&gt;instance=&lt;/span&gt;&lt;span class="s"&gt;"\Feed\Cron\FeedGenerator"&lt;/span&gt; &lt;span class="na"&gt;method=&lt;/span&gt;&lt;span class="s"&gt;"execute"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;schedule&amp;gt;&lt;/span&gt;* * * * *&lt;span class="nt"&gt;&amp;lt;/schedule&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/job&amp;gt;&lt;/span&gt;
      ...
    &lt;span class="nt"&gt;&amp;lt;/group&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/config&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Executing every minute, this job can impose a significant server load. We certainly don't need a new feed every 60 seconds!&lt;/p&gt;

&lt;p&gt;To modify its frequency, we'll craft a module, &lt;code&gt;Devto_ChangeFeedSchedule&lt;/code&gt;, and incorporate an &lt;code&gt;etc/config.xml&lt;/code&gt; file within.&lt;br&gt;&lt;br&gt;
So, the structure would look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Devto_ChangeFeedSchedule/
│
├── etc/
│   ├── config.xml      # our config file
│   └── module.xml      
│
├── registration.php    
│
└── composer.json       
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;etc/config.xml&lt;/code&gt; should contain:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?xml version="1.0"?&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;config&lt;/span&gt; &lt;span class="na"&gt;xmlns:xsi=&lt;/span&gt;&lt;span class="s"&gt;"http://www.w3.org/2001/XMLSchema-instance"&lt;/span&gt;
        &lt;span class="na"&gt;xsi:noNamespaceSchemaLocation=&lt;/span&gt;&lt;span class="s"&gt;"urn:magento:module:Magento_Store:etc/config.xsd"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;default&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;crontab&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;default&amp;gt;&lt;/span&gt; &lt;span class="c"&gt;&amp;lt;!-- cron group --&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;jobs&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;feed_generation&amp;gt;&lt;/span&gt; &lt;span class="c"&gt;&amp;lt;!-- job name --&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;schedule&amp;gt;&lt;/span&gt;
                            &lt;span class="nt"&gt;&amp;lt;cron_expr&amp;gt;&lt;/span&gt;0 * * * *&lt;span class="nt"&gt;&amp;lt;/cron_expr&amp;gt;&lt;/span&gt; &lt;span class="c"&gt;&amp;lt;!-- new cron execution schedule --&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;/schedule&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;/feed_generation&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;/jobs&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/default&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/crontab&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/default&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/config&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And last but not least - add sequence into &lt;code&gt;etc/module.xml&lt;/code&gt; to declare dependency over Feed module and configs apply order:&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:Module/etc/module.xsd"&amp;gt;
    &amp;lt;module name="Devto_ChangeFeedSchedule" &amp;gt;
        &amp;lt;sequence&amp;gt;
            &amp;lt;!-- makes Devto_ChangeFeedSchedule configs be applied after Feed extension configs --&amp;gt;
            &amp;lt;module name="Vendor_FeedExtesnion"/&amp;gt;
        &amp;lt;/sequence&amp;gt;
    &amp;lt;/module&amp;gt;
&amp;lt;/config&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the module &lt;code&gt;Devto_ChangeFeedSchedule&lt;/code&gt; is activated and the cache cleared, your new job schedule will take effect - run every hour but not a minute.&lt;/p&gt;

&lt;p&gt;But what about cron groups? Let's delve into that next.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Need to Change Cron Group
&lt;/h2&gt;

&lt;p&gt;Imagine we're still working with the Feed and Mailing third-party extensions. These extensions utilize distinct cron groups, each operating as an isolated process. Thanks to this, cron groups can run concurrently/parallely, using individual processes.&lt;/p&gt;

&lt;p&gt;Now, suppose these two cron jobs conflict, resulting in data inconsistencies in the Feed and Emails. Worse yet, &lt;code&gt;deadlocks&lt;/code&gt; can crop up. You might ask, &lt;strong&gt;"Why the deadlocks?"&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Well, imagine these extensions read &amp;amp; write the same table, say &lt;code&gt;sales_order_item&lt;/code&gt;. When they run simultaneously, the stage is set for potential deadlocks. 🤷‍♂️&lt;/p&gt;

&lt;p&gt;If only these jobs were executed sequentially! By relocating them to a shared group, you can ensure they no longer run in parallel.&lt;/p&gt;
&lt;h2&gt;
  
  
  Altering the Cron Job's Group
&lt;/h2&gt;

&lt;p&gt;A straightforward (though not perfect) solution would be to transfer the Mailing extension's cron job to the &lt;code&gt;default&lt;/code&gt; group, where the Feed extension's job resides. Here's the Mailing cron job definition:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;config&lt;/span&gt; &lt;span class="na"&gt;xmlns:xsi=&lt;/span&gt;&lt;span class="s"&gt;"http://www.w3.org/2001/XMLSchema-instance"&lt;/span&gt; &lt;span class="na"&gt;xsi:noNamespaceSchemaLocation=&lt;/span&gt;&lt;span class="s"&gt;"urn:magento:module:Magento_Cron:etc/crontab.xsd"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;group&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"mailing_extension"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; &lt;span class="c"&gt;&amp;lt;!-- mailing cron group --&amp;gt;&lt;/span&gt;
      ...
        &lt;span class="nt"&gt;&amp;lt;job&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"send_mails_from_mailing_extension"&lt;/span&gt; &lt;span class="na"&gt;instance=&lt;/span&gt;&lt;span class="s"&gt;"\Mailing\Cron\SendMailsCron"&lt;/span&gt; &lt;span class="na"&gt;method=&lt;/span&gt;&lt;span class="s"&gt;"execute"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;schedule&amp;gt;&lt;/span&gt;*/20 * * * *&lt;span class="nt"&gt;&amp;lt;/schedule&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/job&amp;gt;&lt;/span&gt;
      ...
    &lt;span class="nt"&gt;&amp;lt;/group&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/config&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, a small, &lt;strong&gt;non-intuitive trick&lt;/strong&gt; is essential here: Simply changing the group by redefining &lt;code&gt;crontab.xml&lt;/code&gt; won't work. Instead, you need to craft a new cron job while deactivating the old one. This step is necessary since &lt;code&gt;crontab.xml&lt;/code&gt; configurations (especially groups) can't be overridden, only augmented.&lt;/p&gt;

&lt;p&gt;For instance, merely defining the &lt;code&gt;send_mails_from_mailing_extension&lt;/code&gt; cron job in the &lt;code&gt;default&lt;/code&gt; group would make it run in both the &lt;code&gt;default&lt;/code&gt; and &lt;code&gt;mailing_extension&lt;/code&gt; groups.&lt;/p&gt;

&lt;p&gt;To disable &lt;code&gt;send_mails_from_mailing_extension&lt;/code&gt;, an easy trick is to schedule it for February 30th—a date that doesn't exist!&lt;/p&gt;

&lt;p&gt;&lt;u&gt;nerd fact 🤓:&lt;/u&gt; &lt;em&gt;actually February 30 happened in Sweden in 1712&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Following our earlier approach, we'd add the following content to the &lt;code&gt;etc/config.xml&lt;/code&gt; in our module, &lt;code&gt;Devto_ChangeMailingJobGroup&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?xml version="1.0"?&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;config&lt;/span&gt; &lt;span class="na"&gt;xmlns:xsi=&lt;/span&gt;&lt;span class="s"&gt;"http://www.w3.org/2001/XMLSchema-instance"&lt;/span&gt;
        &lt;span class="na"&gt;xsi:noNamespaceSchemaLocation=&lt;/span&gt;&lt;span class="s"&gt;"urn:magento:module:Magento_Store:etc/config.xsd"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;default&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;crontab&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;default&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;jobs&amp;gt;&lt;/span&gt;
                    &lt;span class="c"&gt;&amp;lt;!-- Disable cron execution by scheduling it to Feb 30th. --&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;send_mails_from_mailing_extension&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;schedule&amp;gt;&lt;/span&gt;
                            &lt;span class="nt"&gt;&amp;lt;cron_expr&amp;gt;&lt;/span&gt;0 0 30 2 *&lt;span class="nt"&gt;&amp;lt;/cron_expr&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;/schedule&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;/send_mails_from_mailing_extension&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;/jobs&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/default&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/crontab&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/default&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/config&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, define a &lt;strong&gt;NEW&lt;/strong&gt; cron job in the &lt;code&gt;default&lt;/code&gt; group:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;config&lt;/span&gt; &lt;span class="na"&gt;xmlns:xsi=&lt;/span&gt;&lt;span class="s"&gt;"http://www.w3.org/2001/XMLSchema-instance"&lt;/span&gt; &lt;span class="na"&gt;xsi:noNamespaceSchemaLocation=&lt;/span&gt;&lt;span class="s"&gt;"urn:magento:module:Magento_Cron:etc/crontab.xsd"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;group&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"default"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; &lt;span class="c"&gt;&amp;lt;!-- default cron group --&amp;gt;&lt;/span&gt;
      ...
        &lt;span class="c"&gt;&amp;lt;!-- added devto prefix to cron name --&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;job&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"devto_send_mails_from_mailing_extension"&lt;/span&gt; &lt;span class="na"&gt;instance=&lt;/span&gt;&lt;span class="s"&gt;"\Mailing\Cron\SendMailsCron"&lt;/span&gt; &lt;span class="na"&gt;method=&lt;/span&gt;&lt;span class="s"&gt;"execute"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;schedule&amp;gt;&lt;/span&gt;*/20 * * * *&lt;span class="nt"&gt;&amp;lt;/schedule&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/job&amp;gt;&lt;/span&gt;
      ...
    &lt;span class="nt"&gt;&amp;lt;/group&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/config&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And do not forget to add Mailing module sequence into &lt;code&gt;etc/module.xml&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;config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd"&amp;gt;
    &amp;lt;module name="Devto_ChangeMailingJobGroup" &amp;gt;
        &amp;lt;sequence&amp;gt;
            &amp;lt;!-- makes Devto_ChangeMailingJobGroup configs be applied after Mailing extension configs --&amp;gt;
            &amp;lt;module name="Vendor_MailingExtesnion"/&amp;gt;
        &amp;lt;/sequence&amp;gt;
    &amp;lt;/module&amp;gt;
&amp;lt;/config&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After all changes, the module structure should look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Devto_ChangeMailingJobGroup/
│
├── etc/
│   ├── config.xml      # our config file
|   ├── crontab.xml     # our crontab definition file  
│   └── module.xml      
│
├── registration.php    
│
└── composer.json       
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once &lt;code&gt;Devto_ChangeMailingJobGroup&lt;/code&gt; is active and the cache refreshed, the &lt;code&gt;send_mails_from_mailing_extension&lt;/code&gt; job will be disabled. Meanwhile, its counterpart, &lt;code&gt;devto_send_mails_from_mailing_extension&lt;/code&gt;, will operate in the &lt;code&gt;default&lt;/code&gt; cron group alongside the Feed cron job.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Word of Caution on the Default Group
&lt;/h2&gt;

&lt;p&gt;It's vital to recognize that Magento's native cron jobs already densely populate the &lt;code&gt;default&lt;/code&gt; cron group. Overloading it might cause "traffic jams" if:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Jobs have lengthy runtimes:&lt;/strong&gt; If your tasks take too long to finish, they could end up waiting in line, slowing things down.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Overfrequent Scheduling:&lt;/strong&gt; Setting jobs to run too often can result in backlogs and potential overlaps.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Overpopulation:&lt;/strong&gt; Filling the group with too many tasks, even if they are quick, might overwhelm the system.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So, while the default group is convenient, treat it like a city's main highway during rush hour. It can handle a lot, but there's a limit before things get clogged. If you have too many heavy tasks or they're scheduled too frequently, you'll likely end up with delays.   &lt;/p&gt;

&lt;p&gt;In short, be mindful of the workload you're placing on the &lt;code&gt;default&lt;/code&gt; group and consider creating your own cron groups to avoid conflict of processes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;And that's the lowdown on Magento's cron jobs, how to shuffle their schedules, and adjust their groups without diving into third-party code directly. With the knowledge you've gathered here, tweaking those pesky cron jobs should be much easier 😊&lt;/p&gt;

&lt;p&gt;Thank you for reading!&lt;/p&gt;

</description>
      <category>magento2</category>
      <category>adobecommerce</category>
      <category>webdev</category>
      <category>mageos</category>
    </item>
    <item>
      <title>Adobe Commerce CLI commands - or how to create a problem out of nothing</title>
      <dc:creator>Vladyslav Podorozhnyi 🇺🇦 🌻</dc:creator>
      <pubDate>Tue, 15 Aug 2023 19:45:38 +0000</pubDate>
      <link>https://forem.com/run_as_root/adobe-commerce-cli-commands-or-how-to-create-a-problem-out-of-nothing-eda</link>
      <guid>https://forem.com/run_as_root/adobe-commerce-cli-commands-or-how-to-create-a-problem-out-of-nothing-eda</guid>
      <description>&lt;h3&gt;
  
  
  Table Of Contents
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Intro&lt;/li&gt;
&lt;li&gt;The Underlying Problem &amp;amp; Its Roots&lt;/li&gt;
&lt;li&gt;The Classic "Chicken or the Egg" Dilemma&lt;/li&gt;
&lt;li&gt;The Pitfall of CLI Command Constructors&lt;/li&gt;
&lt;li&gt;Strategies to Prevent These Errors&lt;/li&gt;
&lt;li&gt;Simplify with Automated Proxy Injection&lt;/li&gt;
&lt;li&gt;Summary&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Intro
&lt;/h2&gt;

&lt;p&gt;Hello everyone,  &lt;/p&gt;

&lt;p&gt;In this article, I'll provide practical examples showcasing how unexpected challenges can arise in Magento 2 &amp;amp; Adobe Commerce projects while building and/or using custom CLI commands from 3rd parties. This becomes especially significant while working having CI/CD and performing project setups and test execution in pipelines.&lt;/p&gt;

&lt;p&gt;Introducing the main "hero" of our article: &lt;a href="https://github.com/alankent/magento2devbox-skeleton/issues/4" rel="noopener noreferrer"&gt;ISSUE: magento setup command is throwing magento.flag does't exist.&lt;/a&gt;. We'll take a deeper look into this issue, understanding its roots and finding ways to address it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Disclaimer:&lt;/strong&gt; &lt;em&gt;Just a quick heads-up on naming - both Adobe Commerce and Magento 2 are covered in this article. After all, Adobe Commerce is Magento. Long Live Magento!&lt;/em&gt; 😁&lt;/p&gt;

&lt;h2&gt;
  
  
  The Underlying Problem &amp;amp; Its Roots
&lt;/h2&gt;

&lt;p&gt;While working on various Magento 2 projects, I frequently encountered snags when performing a &lt;code&gt;clean&lt;/code&gt; setup of a project using a freshly created database. This usually happens during integration or functional test setups in pipelines &amp;amp; local environments. Still, this issue isn't exclusive to that scenario and can pop up during upgrades, migrations, or deployments.&lt;/p&gt;

&lt;p&gt;The culprits? Here are two pesky errors you might recognize:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

The default website isn't defined. Set the website and try again.


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;For Commerce editions, there's also:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

SQLSTATE[42S02]: Base table or view not found: 1146 Table
 'magento2.flag' doesn't exist, query was: 
SELECT flag.* FROM flag WHERE (flag.flag_code='staging')


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;When you see these during the &lt;code&gt;php bin/magento setup:install&lt;/code&gt; command execution, they offer little insight into the real issue. After a few hair-pulling hours of research, you'd discover these errors stem from the premature invocation of the DB resource, triggering the initialization of config and state entities from the Magento DB.&lt;/p&gt;

&lt;p&gt;But here's the conundrum: You can't fetch data from the DB at this point because there aren't any tables present yet. They only appear and populate during the installation process, not beforehand.  ¯_(ツ)_/¯&lt;/p&gt;

&lt;h2&gt;
  
  
  The Classic "Chicken or the Egg" Dilemma
&lt;/h2&gt;

&lt;p&gt;So, we're caught in a circular problem: trying to install the system but getting stopped because it's not installed. Confusing, right? 🤪&lt;/p&gt;

&lt;p&gt;This happens because someone, somewhere, is prematurely calling the DB connection resource. It is obvious, and I hope you would be as grateful as Sam Eliot is for this revelation insight.&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%2Ft77vk8glx0375v3ge19x.png" 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%2Ft77vk8glx0375v3ge19x.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This &lt;strong&gt;"someone"&lt;/strong&gt; should be inside &lt;code&gt;bin/magento&lt;/code&gt; and called before actual command execution. Usually, CLI command constructors are guilty here. Let me explain why.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Pitfall of CLI Command Constructors
&lt;/h2&gt;

&lt;p&gt;For clarity, take a look at this code:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DoSmthing&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;?string&lt;/span&gt; &lt;span class="nv"&gt;$dummyConfigValue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;ScopeConfigInterface&lt;/span&gt; &lt;span class="nv"&gt;$storeManager&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;dummyConfigValue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$storeManager&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'some/dummy/path'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;ScopeInterface&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;SCOPE_STORE&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;getValue&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;?string&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;dummyConfigValue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DummyCommand&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Command&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="mf"&gt;...&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;DoSmthing&lt;/span&gt; &lt;span class="nv"&gt;$model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$model&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;parent&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="mf"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The root of the problem lies in the constructors of both the command and model classes.&lt;br&gt;&lt;br&gt;
In the &lt;code&gt;DoSmthing&lt;/code&gt; class, the system attempts to retrieve a config value. This action calls the &lt;code&gt;store&lt;/code&gt;, &lt;code&gt;store_website&lt;/code&gt;, and &lt;code&gt;core_config_data&lt;/code&gt; tables when creating the &lt;code&gt;DummyCommand&lt;/code&gt; object.&lt;br&gt;&lt;br&gt;
And guess what happens when this code runs before the tables are ready? Yep, errors appear!&lt;/p&gt;

&lt;h4&gt;
  
  
  Lets repeat one more time:
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;We execute &lt;code&gt;php bin/magento setup:install&lt;/code&gt; ;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;bin/magento&lt;/code&gt; application creates all instances of CLI commands;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;DummyCommand&lt;/code&gt; instance depends on the &lt;code&gt;DoSmthing&lt;/code&gt; model. So &lt;code&gt;DoSmthing&lt;/code&gt; should be created before &lt;code&gt;DummyCommand&lt;/code&gt; object creation;&lt;/li&gt;
&lt;li&gt;Application performs creation of &lt;code&gt;DoSmthing&lt;/code&gt; model  and executes &lt;code&gt;DoSmthing::__construct&lt;/code&gt; method;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;DoSmthing::__construct&lt;/code&gt; method calls to the config manager;&lt;/li&gt;
&lt;li&gt;Config manager calls to a chain of tables that are not created yet;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ERROR&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Strategies to Prevent These Errors
&lt;/h2&gt;

&lt;p&gt;Remember this golden rule: Every CLI command class initializes every time you invoke &lt;code&gt;php bin/magento&lt;/code&gt;. So, here's how to save yourself future headaches:&lt;/p&gt;

&lt;h3&gt;
  
  
  Rule 1 - Be Wise
&lt;/h3&gt;

&lt;p&gt;It is not the most wise solution to call for potential resource consuming operations like DB/API calls in &lt;code&gt;__construct&lt;/code&gt; methods of CLI command classes. Those operations will be constantly executed even once you'd have a wish to get only a list of available commands.  &lt;/p&gt;

&lt;h3&gt;
  
  
  Rule 2 - Be Ready
&lt;/h3&gt;

&lt;p&gt;Steer clear of resources in &lt;code&gt;__construct&lt;/code&gt; that might NOT be available during runtime, especially since Magento tables aren't present during &lt;code&gt;setup:install&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Rule 3 - Fix The Problem
&lt;/h3&gt;

&lt;p&gt;If you're not in control of the troublesome code, inject a &lt;code&gt;Proxy&lt;/code&gt; using di configs.&lt;br&gt;&lt;br&gt;
We can easily solve our problem from the example with &lt;code&gt;DummyCommand&lt;/code&gt; by just adding those lines in &lt;code&gt;di.xml&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;

    &lt;span class="nt"&gt;&amp;lt;type&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"AbcTest\DummyConsoleCommand\Console\Command\DummyCommand"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;arguments&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;argument&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"model"&lt;/span&gt; &lt;span class="na"&gt;xsi:type=&lt;/span&gt;&lt;span class="s"&gt;"object"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;\AbcTest\DummyConsoleCommand\Model\DoSmthing\Proxy&lt;span class="nt"&gt;&amp;lt;/argument&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/arguments&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/type&amp;gt;&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Proxy is a well-known pattern, and in Magento, it is widely used to avoid unneeded code execution before we actually request it. In our example - &lt;code&gt;DoSmthing&lt;/code&gt; class object will not be instantiated until we do call for any method of this class.&lt;br&gt;&lt;br&gt;
No config retrieving while CLI class object creation - No error.&lt;br&gt;&lt;br&gt;
More details about proxy here: &lt;a href="https://developer.adobe.com/commerce/php/development/components/proxies/" rel="noopener noreferrer"&gt;Proxies&lt;/a&gt; .&lt;/p&gt;

&lt;h2&gt;
  
  
  Simplify with Automated Proxy Injection
&lt;/h2&gt;

&lt;p&gt;For those with a small number of extensions on their project, pinpointing the rogue CLI commands isn't too challenging. However, for more extensive, legacy projects or ones brimming with third-party extensions, identifying the culprits becomes an ordeal.&lt;/p&gt;

&lt;p&gt;To save time, we've crafted a solution to automate the proxy injection process. Introducing: &lt;strong&gt;&lt;a href="https://github.com/run-as-root/magento-cli-auto-proxy" rel="noopener noreferrer"&gt;run-as-root/magento-cli-auto-proxy&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This package injects &lt;code&gt;Proxy&lt;/code&gt; into all &lt;code&gt;__construct&lt;/code&gt; arguments of CLI commands, eligible for proxying. The result? No errors related to early resource access and, as a side affect, faster execution of &lt;code&gt;php bin/magento&lt;/code&gt; and php &lt;code&gt;bin/magento setup:install&lt;/code&gt;.  &lt;/p&gt;

&lt;p&gt;Check out the README for more details. We always appreciate feedback and contributions!&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Let's sum it up:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Unthoughtfully created CLI commands can unexpectedly lead to problems, even if they appear harmless at first.&lt;/li&gt;
&lt;li&gt;The core of the problem often arises from &lt;strong&gt;premature database calls&lt;/strong&gt;, leading to a classic "chicken or the egg" scenario where system installation gets stalled.&lt;/li&gt;
&lt;li&gt;One significant pitfall identified is the &lt;strong&gt;CLI command constructors&lt;/strong&gt; and their potential to wreak havoc when invoked at the wrong time.&lt;/li&gt;
&lt;li&gt;To combat these challenges, we laid out three guiding principles:

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Be Wise&lt;/strong&gt; by avoiding resource-intensive calls in constructors.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Be Ready&lt;/strong&gt; by steering clear of resources that might not be available.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fix The Problem&lt;/strong&gt; by utilizing proxies for problematic third-party code.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;To streamline and automate the solution, we introduced the package &lt;strong&gt;&lt;a href="https://github.com/run-as-root/magento-cli-auto-proxy" rel="noopener noreferrer"&gt;run-as-root/magento-cli-auto-proxy&lt;/a&gt;&lt;/strong&gt; which aims to automate proxy injections, offering a more efficient and error-free Magento experience.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Thank you for diving deep into this article. I welcome all your questions and appreciate any feedback you might have.&lt;br&gt;
Happy coding and... &lt;strong&gt;Long Live Magento&lt;/strong&gt;! 😁&lt;/p&gt;

</description>
      <category>magento2</category>
      <category>adobecommerce</category>
      <category>webdev</category>
      <category>development</category>
    </item>
    <item>
      <title>Extension Attributes in Adobe Commerce: Achieving Clean and Decoupled Code</title>
      <dc:creator>Vladyslav Podorozhnyi 🇺🇦 🌻</dc:creator>
      <pubDate>Thu, 13 Jul 2023 20:21:44 +0000</pubDate>
      <link>https://forem.com/run_as_root/extension-attributes-in-adobe-commerce-achieving-clean-and-decoupled-code-1i88</link>
      <guid>https://forem.com/run_as_root/extension-attributes-in-adobe-commerce-achieving-clean-and-decoupled-code-1i88</guid>
      <description>&lt;h3&gt;
  
  
  Table Of Contents
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Introduction&lt;/li&gt;
&lt;li&gt;Chapter 1: Understanding the Adobe Commerce Service Layer&lt;/li&gt;
&lt;li&gt;Chapter 2: Exploring the Idea of Extension Attributes&lt;/li&gt;
&lt;li&gt;Chapter 3: Extension Attributes and Service Contracts&lt;/li&gt;
&lt;li&gt;Chapter 4: Saving Data for Extension Attributes&lt;/li&gt;
&lt;li&gt;Chapter 5: Key Takeaways&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Welcome to our blog post, where we delve into the practical realm of Extension Attributes in Adobe Commerce (formerly Magento). If you've been working with Magento 2 for a while, you've probably encountered Extension Attributes and harnessed their capabilities. However, it's surprising how often developers overlook the valuable benefits that Extension Attributes offer, especially when compared to outdated methods of entity extension.&lt;/p&gt;

&lt;p&gt;In this article, we aim to shed light on the advantages of Extension Attributes and how they can enhance your development projects. We'll explore their integration with the Service Layer, providing you with insights on how to write clean, modular code that avoids unnecessary dependencies on other layers or extensions.&lt;/p&gt;

&lt;p&gt;Whether you're an experienced Adobe Commerce developer seeking to broaden your understanding or a newcomer eager to learn more, this article guides you to a bit deeper understanding of Extension Attributes' place &amp;amp; role in Adobe Commerce.&lt;/p&gt;

&lt;p&gt;Join us in exploring Extension Attributes. Let's dive in and discover the practical benefits they bring to your development workflow. Let's get started!&lt;/p&gt;




&lt;h2&gt;
  
  
  Chapter 1: Understanding the Adobe Commerce Service Layer
&lt;/h2&gt;

&lt;p&gt;As you may know already, Adobe Commerce has four Architecture layers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Presentation layer - point of user interaction with Adobe Commerce.&lt;/li&gt;
&lt;li&gt;Service layer - it is a "glue" between layers and a set of APIs used by modules and external systems to communicate with each other. Service contracts are in use here.&lt;/li&gt;
&lt;li&gt;Domain layer - contains the business logic of the module.&lt;/li&gt;
&lt;li&gt;Persistence layer - resource level, knows about DB and other storages.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;See more details here: &lt;a href="https://developer.adobe.com/commerce/php/architecture/layers/" rel="noopener noreferrer"&gt;Architectural layers overview&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The service layer is vital for understanding extension attributes, especially the principle of "service contracts" that is used as a system pillar in Adobe Commerce. If you do not know about "service contracts," - read this article &lt;a href="https://developer.adobe.com/commerce/php/development/components/service-contracts/" rel="noopener noreferrer"&gt;Service contracts&lt;/a&gt; and keep in mind that this is an agreement between parts of the system on the functionality list available for each party and how these functionalities could be triggered. REST API and API folder in modules are two bright examples of service contract implementation.&lt;/p&gt;

&lt;p&gt;The service layer allows to build long term, strictly defined relations between services, both internal and external.  &lt;/p&gt;

&lt;p&gt;While developing your extension and needing to change/extend one or another functionality of the system - you would need to rely on the Service layer, not any other. Only the Service layer would be able to provide you with a warranty to be changed in the nearest perspective - so there are no troubles with upgrades all the time, as the API definition is &lt;br&gt;
not changed often. I'm not even mentioning the benefits of explicit entry point definition, its parameters, and predictable output.&lt;/p&gt;




&lt;h2&gt;
  
  
  Chapter 2: Exploring the Idea of Extension Attributes
&lt;/h2&gt;

&lt;p&gt;Imagine you're working with a fundamental entity like an order in Adobe Commerce. This entity has its dedicated table called &lt;code&gt;sales_order&lt;/code&gt; and a considerable list of predefined properties. But what if you find yourself in need of adding another property to the order entity?&lt;/p&gt;

&lt;p&gt;Sure, you could take the easy route and directly modify the &lt;code&gt;sales_order&lt;/code&gt; table, implementing custom observers and models to handle the new property. However, doing so would take you into the Domain layer of another extension, which isn't good. The domain layer of other modules is the last resort for an extension, as they are changed quite often by the vendor. It means meddling with the system's inner workings - system you are out of control, risking compatibility issues during future upgrades. &lt;a href="https://en.wikipedia.org/wiki/Open%E2%80%93closed_principle" rel="noopener noreferrer"&gt;Open–closed principle&lt;/a&gt; - the system should be closed for direct changes but open for extending. In this case, modifying the entity directly would be considered changing the system controllable by the vendor Magento - not you.&lt;/p&gt;

&lt;p&gt;So, how can we add a property to the entity using the "right" tools?&lt;/p&gt;

&lt;p&gt;The answer lies in the power of Extension Attributes. 🙌&lt;/p&gt;

&lt;p&gt;Extension Attributes provide a sanctioned approach for extending entities without tampering with the core system's code. Instead of directly modifying the entity table, we leverage Extension Attributes to add new properties in a clean and modular way. By utilizing this method, we ensure our modifications align with the principles of the system's architecture, reducing the risk of compatibility issues and enabling smoother upgrades in the long run.&lt;/p&gt;

&lt;p&gt;In the following chapter, we'll explore how Extension Attributes work hand in hand with the Service Layer, enabling you to extend entities effectively and maintain a flexible and scalable codebase.&lt;/p&gt;




&lt;h2&gt;
  
  
  Chapter 3: Extension Attributes and Service Contracts
&lt;/h2&gt;

&lt;p&gt;Extension Attributes provide a "legitimate" way to modify Service contracts while maintaining backward compatibility for both providers and consumers. Although a simplified definition, it captures the essence of Extension Attributes in Adobe Commerce.&lt;/p&gt;

&lt;h4&gt;
  
  
  Extension Object: Leveraging Composition over Inheritance
&lt;/h4&gt;

&lt;p&gt;Adobe Commerce adopts the principle of "composition over inheritance" to achieve this flexibility within its development ecosystem. Each entity supporting Extension Attributes has a setter and getter for the Extension object. In the case of orders, it is an instance of a class that implements &lt;code&gt;\Magento\Sales\Api\Data\OrderExtensionInterface&lt;/code&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Configuring Extension Attributes with XML
&lt;/h4&gt;

&lt;p&gt;The behavior of the Extension object is configured through simple and straightforward XML configurations. It is in this configuration where we specify which extension attributes need to be added, define their types, and associate them with the respective entities.&lt;br&gt;&lt;br&gt;
For example:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;

// @see vendor/magento/module-sales/etc/extension_attributes.xml
&lt;span class="nt"&gt;&amp;lt;config&lt;/span&gt; &lt;span class="na"&gt;xmlns:xsi=&lt;/span&gt;&lt;span class="s"&gt;"http://www.w3.org/2001/XMLSchema-instance"&lt;/span&gt; &lt;span class="na"&gt;xsi:noNamespaceSchemaLocation=&lt;/span&gt;&lt;span class="s"&gt;"urn:magento:framework:Api/etc/extension_attributes.xsd"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;extension_attributes&lt;/span&gt; &lt;span class="na"&gt;for=&lt;/span&gt;&lt;span class="s"&gt;"Magento\Sales\Api\Data\OrderInterface"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;attribute&lt;/span&gt; &lt;span class="na"&gt;code=&lt;/span&gt;&lt;span class="s"&gt;"shipping_assignments"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"Magento\Sales\Api\Data\ShippingAssignmentInterface[]"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/extension_attributes&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/config&amp;gt;&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h4&gt;
  
  
  Support for Extension Attributes: The Role of ExtensibleDataInterface
&lt;/h4&gt;

&lt;p&gt;How does Adobe Commerce knows that order entity supports extension attributes?&lt;br&gt;&lt;br&gt;
Well, &lt;code&gt;\Magento\Sales\Api\Data\OrderInterface&lt;/code&gt; extends &lt;code&gt;\Magento\Framework\Api\ExtensibleDataInterface&lt;/code&gt;, which informs Adobe Commerce that the order entity will have Extension Attributes. The XML configuration specifies that the property &lt;code&gt;shipping_assignments&lt;/code&gt; is one of those Extension Attributes.&lt;/p&gt;
&lt;h4&gt;
  
  
  Generated Service Contracts
&lt;/h4&gt;

&lt;p&gt;Now that Adobe Commerce is aware of the Extension Attributes, what comes next?&lt;/p&gt;

&lt;p&gt;It generates &lt;code&gt;\Magento\Sales\Api\Data\OrderExtensionInterface&lt;/code&gt; in the &lt;code&gt;generated/code&lt;/code&gt; folder based on the collected XML configurations from &lt;code&gt;extension_attributes.xml&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The generated interface looks like this:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;

&lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;OrderExtensionInterface&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;\Magento\Framework\Api\ExtensionAttributesInterface&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="cd"&gt;/**
     * @return \Magento\Sales\Api\Data\ShippingAssignmentInterface[]|null
     */&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;getShippingAssignments&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="cd"&gt;/**
     * @param \Magento\Sales\Api\Data\ShippingAssignmentInterface[] $shippingAssignments
     * @return $this
     */&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;setShippingAssignments&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$shippingAssignments&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="mf"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Additionally, the implementation of the contract/interface, &lt;code&gt;\Magento\Sales\Api\Data\OrderExtension&lt;/code&gt;, is also generated.&lt;/p&gt;

&lt;h4&gt;
  
  
  Summary
&lt;/h4&gt;

&lt;p&gt;In essence, Adobe Commerce has automatically generated a new Service Contract for us based on our configurations. We can now use this contract to effortlessly set and retrieve values of Extension Attributes for the order entity, without the need to navigate through other layers apart from the Service Layer of the &lt;code&gt;Magento_Sales&lt;/code&gt; model.&lt;/p&gt;

&lt;p&gt;This outstanding move allows your newly added Extension Attribute to become an integral part of the Service Contracts and Service Layer. It remains independent of the implementation details of any modules, extensions, or the system itself that you do not have control over.&lt;/p&gt;

&lt;p&gt;Congratulations on leveraging this remarkable feature of Adobe Commerce!&lt;br&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%2Fknr8pbizzl731svw0ocd.png" 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%2Fknr8pbizzl731svw0ocd.png" alt="Outstanding move - Adobe Commerce"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the next chapter, we'll explore how to not get unwanted dependencies while further Extension Attributes implementation.&lt;/p&gt;




&lt;h2&gt;
  
  
  Chapter 4: Saving Data for Extension Attributes
&lt;/h2&gt;

&lt;p&gt;It's not always necessary to save Extension Attribute data persistently. Sometimes, it suffices to calculate their values on the fly when needed. However, let's assume we do need to save an attribute. &lt;/p&gt;

&lt;h4&gt;
  
  
  Main Steps for Saving Extension Attribute Data
&lt;/h4&gt;

&lt;p&gt;The Adobe Docs article, &lt;a href="https://developer.adobe.com/commerce/php/development/components/add-attributes/" rel="noopener noreferrer"&gt;Add extension attributes to entities&lt;/a&gt;, provides a comprehensive guide on how to achieve this. Let's go through a quick recap. To add data to an Extension Attribute, you typically follow these steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a plugin that intercepts the save operation of the entity to retrieve the Extension Attribute data and save it at the resource level.&lt;/li&gt;
&lt;li&gt;Create a plugin that intercepts the load operation of the entity to retrieve the Extension Attribute data and populate the entity using the Extension object.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;While there can be variations, this is the main scenario, requiring at least two plugins for saving and loading operations.&lt;/p&gt;

&lt;h4&gt;
  
  
  Where to Save Extension Attribute Data?
&lt;/h4&gt;

&lt;p&gt;Now, that's a brilliant question that leaves many stumped, but not you! 😄&lt;/p&gt;

&lt;p&gt;One option is to save the Extension Attribute data in the same table as the entity—for example, the &lt;code&gt;sales_order&lt;/code&gt; table for orders. However, this approach introduces a dependency on the Domain Layer of the &lt;code&gt;Magento_Sales&lt;/code&gt; module.&lt;/p&gt;

&lt;p&gt;And yes, you're right—it's not "legal" in our paradigm. We aim to extend the software, not modify it.&lt;/p&gt;

&lt;h4&gt;
  
  
  The Best Approach: Have Your Own Entity
&lt;/h4&gt;

&lt;p&gt;In my opinion, the best approach is to have your own entity to save and retrieve the Extension Attribute data. It doesn't matter what persistence layer you choose—whether it's a database, session, file, API, carrier pigeons, or even Morse code written on paper.&lt;/p&gt;

&lt;p&gt;It may seem like overkill, but it's the most sensible way to avoid unwanted dependencies on unmodifiable parts of the system. Your Extension Attribute implementation should not impact the core system or violate the Open-Closed principle. By cutting down all connections and dependencies on the extendable module, except for the Extension Attributes definition, you ensure the integrity of the Service Contracts and keep the system functioning as intended.&lt;/p&gt;

&lt;p&gt;This approach allows you to maintain a clean and decoupled codebase, free from unnecessary entanglements with uncontrollable system components.&lt;/p&gt;




&lt;h2&gt;
  
  
  Chapter 5: Key Takeaways
&lt;/h2&gt;

&lt;p&gt;As we conclude this article on Extension Attributes in Adobe Commerce, let's summarize the key takeaways:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Service Layer and Service Contracts&lt;/strong&gt;: The Service Layer and Service Contracts serve as excellent foundations for extending functionality. They provide stability, predictability, and explicit definitions, making them ideal for seamless extension development.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Persistence, Domain, and Presentation Layers&lt;/strong&gt;: On the other hand, the Persistence, Domain, and Presentation Layers are not suitable for direct extension. These layers can undergo changes with each release, making them less reliable for long-term extension development.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Extension Attributes and Service Contracts&lt;/strong&gt;: Extension Attributes are an integral part of the Service Layer and Service Contracts. Leveraging Extension Attributes enables effective extension development and facilitates communication across services within Adobe Commerce.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Avoid Saving Extension Attributes Inside the Entity&lt;/strong&gt;: It is crucial to refrain from saving Extension Attributes within the entity you are trying to extend. Doing so would introduce a dependency on the Persistence Layer of the extendable module, rendering the benefits of the Service Layer useless. Instead, opt for storing Extension Attribute data in a separate entity to maintain a clean and decoupled architecture.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;By adhering to these takeaways, you can build robust and scalable extensions that align with the principles of Adobe Commerce's architecture. Remember, the Service Layer and Service Contracts offer a stable foundation, while Extension Attributes provide a flexible and effective approach to extending functionality.&lt;/p&gt;

&lt;p&gt;Thank you for joining us in exploring Extension Attributes in Adobe Commerce. We hope you found this article informative and insightful.   &lt;/p&gt;

&lt;p&gt;Happy developing!&lt;/p&gt;

</description>
      <category>magento2</category>
      <category>adobecommerce</category>
      <category>webdev</category>
      <category>development</category>
    </item>
  </channel>
</rss>
