You have a bug with Magento, which does happens from time to time. Then, you stumble onto a PR on GitHub that looks like there’s already a fixed merged in or one pending. You could wait until the next release to get this update in, but that can take many weeks or months. And this is an important fix that you would really like to push out sometime in the next couple days, or even today.
There’s a way to apply the related commit by using the cweageans/composer-patches module with Git & composer.
Get the diff
Let’s say I have a bug with Magento where it isn’t translating salutations, such as prefixes or suffixes in a name. Then, I head to GitHub to see if there isn’t already a fix for it. Low & behold, here’s a closed issue that describes my exact situation. And it’s closed, because there’s already a merged PR about how to fix this.
Let’s go to the PR, and since it’s already merged into the current release branch, magento:2.4-develop
, we know that Magento has already code-reviewed this PR, and it’s already merged into the core.
When we go to the files changed, we can see that it’s not a huge update but more than a couple lines of code than we want to type. There’s a really easy way to grab the diff or patch file for this PR. Head back to he conversation tab, head up to the URL, and add in a .diff
extension, and the diff file will load. This is pretty neat, because we can now take this diff patch file and apply it to our Magento store.
Fix module & filesystem references
Now that we have the diff file, we need to update the file structure of the patched file. This isn’t an official patch file, which makes it a bit more difficult to apply. Note how the file is referencing the locations of Magento modules in app/code/Magento
— we need this location to be the composer location instead in the vendor
directory.
We will download this file, but we’ll first create a directory at patches/composer
to place it in. This is the recommended directory by the cweagans module, and is the place to put all of these diff files.
Now lets download it. We can do this right from a browser, but lets use curl for now by typing curl
and the URL of the diff, followed by an arrow, then followed by what I want to name it, which will be PR-30413.diff
.
Then we will need to rename all of these file locations. Let’s do a global search for app/code/Magento/Customer
, and replace it with vendor/magento/module-customer
.
We’ll need to also do the same for app/code/Magento/Sales
, renaming the references to vendor/magento/module-sales
.
We’ll also need to actually separate this file out into two different patch files, one for each module. This is because later on when we use the automated patch tool, it expects these diffs to relate 1:1 to a specific module.
Let's move the Sales-module-specific code to PR-30413-sales.diff
, and keep the Customer-module-specific code in the file and rename it to PR-30413-customer.diff
. Then, each file will only contain references to the related module, one for the customer
module, and one for the sales
module.
Test the patches
Now we are going test out applying the patch, but with the --dry-run
argument. We can type:
And, we'll see there's actually an error when applying one of the hunks on the patch:
When we look at this diff, every of these groups of changes is called a hunk. So if we look at the one applying to vendor/magento/module-customer/Helper/View.php
, it has two hunks, and they are each separated by one of these @
symbols in the changeset.
We need to find out exactly how and where this error is occuring. Patches only don’t apply when the diff changesets don’t exactly match the original file.
First we will check out the getCustomerName
function in the vendor/magento/module-customer/Helper/View.php
file. Let's start with the first line of the patch, which looks like it replaces the brackets in the @inheritdoc
docblock with text that doesn't have the brackets. Checking this out in the View
class, we’ll see that it already doesn't have brackets.
Usually we’d need to re-generate the diff or patch file, but this is overly complex. What we can do instead is just change the removal line to the same line as the addition line. This essentially makes it so that this update doesn’t happen at all.
Now we re-run the patch
command with --dry-run
, and we can see that all patches will now succeed. This is why you should always use --dry-run
rather than assuming a patch will succeed, because once you get this all set up in your automated deployments, a patch that bombs out can kill that deployment and prevent it from being pushed out in your pipeline.
We’ll do the same with the sales module, running patch --dry-run < m2-hotfixes/PR-30413-sales.diff
, and since everything is all good there as well, we are ready to automate this process.
Automate applying patches when deploying code
Now we could just run the patch
line again without the —dry-run
argument, and this will patch our files. But we want our patches to be automatically applied when we deploy our code. Luckily, there’s a really handle module called cweagans/composer-patches
that is extremely popular, and made for just this scenario.
First, we will need to install it with Composer. We can do that be executing:
composer require cweagans/composer-patches
This module uses a combination of being a Composer plugin along with git apply
behind the scenes to automatically apply the patch anytime the composer install
or composer update
command is executed. It looks for specific properties of the composer.json
file to determine what patches to apply.
Lets open up the composer.json
and go all the way to the bottom. Magento usually has an extra
property already containing the magento-force override
property value, but the cweagans
module looks for a property at extra.patches
, so let’s create that:
...
"extra": {
"magento-force": "override",
"patches": {
}
}
...
Next comes the module we wish to apply the patch to, which is relative to the vendor
directory. Since we want to apply our first patch to the customer module, this will be magento/module-customer
. Next, within this object is where we will define a key:value pair. The key will be a description of the patch we want to install. I like to prefix this with the patch filename which is the PR and number, and then the description of the PR or what it actually does. This will help us in the future if this starts failling out during an upgrade or something else. Our second value will be the location of the patch file, so this will be patches/composer/PR-30413-customer.diff
. We’ll then do the same thing with the sales
module.
Note that each of the keys must be different for the cweagans module to act on them. If we had the same key for both of these modules, even though they are for two different modules, it will only execute the first one it sees.
...
"extra": {
"magento-force": "override",
"patches": {
"magento/module-customer": {
"PR-30413-customer: Prefix and Suffix Translation": "patches/composer/PR-30413-customer.diff"
},
"magento/module-sales": {
"PR-30413-sales: Prefix and Suffix Translation": "patches/composer/PR-30413-sales.diff"
}
}
}
...
Now that these patch definitions have been setup, the cweagans
Composer plugin will detect these patches and execute it on any composer install
or composer update
command. Let’s run composer install
, and we’ll see that it detected a patch exists for the customer and slaes modules.
What it did is interesting; it actually completely removes the modules, installs them again, and then applies our diff patches on top of the modules. It does this just to make sure that it starts from a fresh clean base. This ensures that we get the same result every time to account for a situation where we may have actually modified one of these files in development.
Now that it has applied these updates, we can confirm it did be opening up one of the files that had changes in this diff.
For example, the vendor/magento/module-sales/Model/Order/Address.php
file. If we go to the getName()
module, we’ll see that the translation function is now wrapping the getPrefix()
and getSuffix()
function calls of the name:
Now you know how to apply Magento code changes from GitHub, and have them be automatically applied to an automated deployment pipeline.
Curious to explore the world of Magento further? I'm here to help with these 3 resources:
- Explore Magento 2 fundamentals & best practices course (1,000+ students)
- Grow your Magento expertise with all courses & lessons (700+ students)
- Learn visually with blocks of code & inline comments (3,000+ students)