You're looking at all the posts published under the tag #Magento.

Migrate a Magento project from any server to Cloudways

A complete guide with everything you need to know to successfully migrate any Magento project living on any server to Cloudways.
8 min read
Migrate a Magento project from any server to Cloudways

While the title up there make this post looks like a promoted and/or paid one, that's not the case (I wish), and instead this is an honest appreciation of Cloudways and how it has made my life easy while dealing with servers, specially considering that I don't think myself as an advanced devOps.

Recently at the company I work for we had the task to move an existing Magento project, already in production and publicly accessible, from a server outside Cloudways to a server managed by Cloudways.

What's next is going to be long, and everything described could take around 3 days (one day for the server creation, another day for testing the new instance, and a third day for the actual site moving), depending on your skills.

So let's get to it.

Preparation

Make sure to tick a few boxes before moving forward by checking if you have the following:

  • Credentials to access the original server over SSH and SFTP, with enough permissions to manipulate files within the web root folder (usually public_html or www), use scp, tar, and cat for server configuration files such as the one for Nginx and Varnish.
  • Server permissions to use mysqldump to create a database backup, or a way to get your hands on a database backup of the project living on the original server.
  • Access or a point of contact for managing DNS changes for the project’s domain.
  • Access to the payment gateway accounts and/or credentials needed to configure the payment methods.

Finally, make sure to identify which third-party services currently connect directly to the server over SFTP or similar, because those will need to be reconfigured to point to the new IP and/or to be given new SFTP credentials.

Create the server in Cloudways

Decide on the server size and create it on Cloudways, making sure to factor in how much space you will need for the database, files, and "maneuver" as you will be moving zipped files from the original server to unzip them later.

Server creation example using a "PHP - Custom App" for a Magento Open Source 2.2

Cloudways offers Magento-ready instances for specific versions. If the project you are moving uses a Magento version already listed in Cloudways you can select this version from the dropdown that appears when creating the server, otherwise make sure to select “PHP - Custom App” instead.

This is important because, for example, if you are moving a Magento 2.2 version to Cloudways you can not select “Magento - Version 2.4.1 (with Elasticsearch)” from the dropdown as this instance will come with Elasticsearch 7 out of the box and you won’t be able to downgrade it to Elasticsearch 6 (which is the required version for Magento 2.2 using ElasticSuite).

If you need to go down the route of selecting “PHP - Custom App”, then you must contact Cloudways Support for them to "flag" this instance as Magento internally, after creating it.

This is because Cloudways offers specific configurations and performance-oriented settings for each type of instance, like an internal logic in place to load the specific Varnish settings a Magento project would require.

Cloudways Support flagging a "PHP - CustomApp" as Magento internally

Whatever the Application type you choose, you still need to contact Cloudways Support to ask them to:

  • Downgrade Composer from version 2 to 1 by running composer self-update --1 (you can't do it because you don't have enough server permissions to do so).
  • Install a few Elasticseach plugins by running bin/elasticsearch-plugin install analysis-phonetic; bin/elasticsearch-plugin install analysis-icu; bin/elasticsearch-plugin install analysis-smartcn; on the /usr/share/elasticsearch folder (another thing you won't be able to do due to permissions).

You still need to configure a few bits here and there using the Cloudways Dashboard to make sure you match your project needs, including but not limited to the following items:

  • PHP version, execution limit, memory limit, error reporting, and timezone.
  • MySQL version and timezone.
  • Nginx WAF module to be Cloudflare, Sucuri or disabled.
  • Elasticsearch version, if needed.
  • Redis, if needed.
  • Varnish, if needed.

I can't tell what you need to put in those fields as that depends on the project.

Configure the Application

Inside the "Domain Management" section, make sure to configure the project's domain and all the additional necessary domains (like the ones used for different stores if your Magento project is configured to use multiple websites and stores).

"Domain Management" configuration inside "Application Management"

Your "SSL Certificate" configuration will depend on what your project needs.

Since you are moving from one server to other, chances are you already have a SSL certificate that you can use, otherwise the Let's Encrypt option is quite good.

If you are using a firewall such as Cloudflare or Sucuri, the certificate might be "living there" already so there's nothing you need to configure here.

Do not add any cron in the "Cron Job Management" section for now.

We'll come back to the "Application Management" configuration later at the end.

Stop crons on the original server

By editing the crontab with crontab -e on the original server, or by contacting the original server hosting provider, disable all the crons by adding # at the beginning of each line.

An example of editing the crons with crontab -e and commenting out every line

This needs to happen before you move any file from the original server to Cloudways, and before the database backup creation.

Technically speaking, you want to avoid Magento to keep on writing any file in, for example, the var folder within the Magento root folder, related to Orders processing or Catalog management (like stock updates), and you want Magento to stop altering tables related also to those entities, which are the usual stuff affected by crons (among many other things).

If, for example, there are pending Order and/or Catalog tasks, they will be taking place in Cloudways after we finish the migration and we enable back the crons on the new server.

Move the files

Make sure to move the pub/media folder from within the Magento root folder, and any other custom folder and file that doesn't live in the repository.

Usually, there are some custom folders and files inside the pub and var folders that are not created automatically.

If you happen to be using Capistrano on the original server you would have a shared folder in the web root. In that case, just move that folder from the original server to Cloudways.

The way I do it is by creating a .tar.gz file with everything in it, but any cache folder...

[user_original]:~$ tar cvzfh shared.tar.gz shared/ --exclude='pub/media/catalog/product/cache';
tar cvzfh filename.tar.gz folder-to-zip/ --exclude='path/to/exclude';

...and then I transfer it from the original server to Cloudways using scp...

[user_original]:~$ scp shared.tar.gz user_cloudways@18.133.133.133:/mnt/data/home/master/applications/example/public_html
scp filename.tar.gz dest_username@123.dest.ip.123:/path/where/to/transfer

Make sure to push this file into the public_html folder in the Cloudways server, so you just need to unzip it there with tar -xvf shared.tar.gz; and then delete it.

Something I would like to do, just in case, after this big move, is to fix any potential permissions issue with the files and folders I just unzipped, by reseting those permissions using the Cloudways Dashboard.

Application Management setting to reset files/folder permissions

Enable Maintenance Mode

Put the site living on the original server into Maintenance Mode by running bin/magento maintenance:enable before proceeding.

For what it's worth, the downtime starts now.

Move the database

Using mysqldump create a database backup on the original server.

[user_original]:~$ mysqldump -uroot -p database_name > database_export.sql;

For security reasons, make sure this happens on a folder not accessible over the browser, like on the private_html folder or anywhere outside the web root.

Once done, push this .sql file to the Cloudways server using scp again.

[user_original]:~$ scp database_export.sql user_cloudways@18.133.133.133:/mnt/data/home/master/applications/example/private_html
scp filename.sql dest_username@123.dest.ip.123:/path/where/to/transfer

For the same security reasons, make sure to push this file to the private_html folder in the Cloudways server.

Once the transfer is completed, and before anything else, you need to remove the DEFINER from the .sql backup to avoid any "Access denied; you need (at least one of) the SUPER privilege(s) for this operation" issue while trying to import it.

Basically, run the following command on the private_html folder in Cloudways (or anywhere you pushed the .sql file before).

[user_cloudways]:~$ sed 's/\sDEFINER=`[^`]*`@`[^`]*`//g' -i database_export.sql;

Now the backup is ready to be imported into the actual database in the new server.

For this to happen you need to contact Cloudways Support with the request.

Cloudways Support handling the database import

After the import, make sure to delete the .sql file.

Configure Magento

By accessing directly to the recently imported database in Cloudways, and not through the Magento Admin Panel, make sure to have the correct details for Varnish and Elasticsearch, keeping in mind that Cloudways uses port 8081 for Varnish, and 127.0.0.1:9200 for Elasticsearch.

Make sure there's no reference in the core_config_data table to the IP for the old server that you might need to change for the IP of the new one.

Go back to the files, and edit the app/etc/env.php file you transferred from the original server to fix the credentials for connecting to the new database living in Cloudways now.

In that same file, correct the details for Redis (if used) keeping in mind that Cloudways uses 127.0.0.1 and 6379 for the host and port, respectively.

Once done, clear cache and reindex to make sure everything is working.

Configure the Application again

Go back to the "Application Management" in the Cloudways Dashboard and configure, in the new server, all the crons you commented out on the original server, making sure to use the right paths for the new server.

Inside the "Application Settings", configure the "WEBROOT" to point to the pub folder from within the Magento root folder.

Point the domain to the new server

First thing first, make sure Magento is still on Maintenance Mode on the new server, and whitelist your IP to be able to access the site (using the bin/magento maintenance:allow-ips command).

Now change the DNS settings to point the project's domain to the new server.

The whole idea is to open the site only for you in order to test it before allowing real customers. So, while the DNS change propagates, some customers will hit the site on the old server (that will show the maintenance page forever and ever), and others will hit the site on the new Cloudways server (that it will also show the maintenance page while you test it).

Now is when you test the site and make sure everything is okay.

Disable Maintenance Mode

Once you are done with the testing open the site, now in Cloudways, to the public, by disabling the Maintenance Mode (with the bin/magento maintenance:disable command).

The downtime ends now.

Magento and FORBIDDEN/12/index read-only / allow delete (api)

If you are unlucky enough to be reading this article it means that you are seeing that error when trying to reindex Magento. This happens because you are probably running out of disk space and Elasticsearch turns itself into read only mode because, well, there's no more space to do stuff.
1 min read
Magento and FORBIDDEN/12/index read-only / allow delete (api)

If you are unlucky enough to be reading this article it means that you are seeing that error when trying to reindex Magento.

ElasticSuite Category Indexing index process unknown error:
{"error":{"root_cause":[{"type":"cluster_block_exception","reason":"blocked by: [FORBIDDEN/12/index read-only / allow delete (api)];"}],"type":"cluster_block_exception","reason":"blocked by: [FORBIDDEN/12/index read-only / allow delete (api)];"},"status":403}
ElasticSuite Thesaurus Indexing index process unknown error:
{"error":{"root_cause":[{"type":"cluster_block_exception","reason":"blocked by: [FORBIDDEN/12/index read-only / allow delete (api)];"}],"type":"cluster_block_exception","reason":"blocked by: [FORBIDDEN/12/index read-only / allow delete (api)];"},"status":403}

This happens because you are probably running out of disk space and Elasticsearch turns itself into read only mode because, well, there's no more space to do stuff.

In order to get out of this situation, you need to first disable that Elasticsearch feature of switching to read only mode (otherwise it will happen again) by running the following...

curl -XPUT -H "Content-Type: application/json" http://localhost:9200/_cluster/settings -d '{"persistent":{"cluster.routing.allocation.disk.threshold_enabled":false}}'

...and then get Elasticsearch out of the read only mode with the next command:

curl -XPUT -H "Content-Type: application/json" http://localhost:9200/_all/_settings -d '{"index.blocks.read_only_allow_delete": null}'

Besides this, you'll need to do some disk clean up to recover some space.

Missing query string parameters when using Varnish on Magento

I had a simple task that consisted on getting some query string parameters from the URL, from the PHP $_GET variable, to store them in Magento. What sounded like a very straightforward task got complicated because the $_GET variable was missing some of the those parameters I needed to save.
2 min read
Missing query string parameters when using Varnish on Magento

I had this weird issue a time ago with a simple task that consisted on getting some query string parameters from the URL, from the PHP $_GET variable, to store them in Magento after the Customer places the Order.

What sounded like a very straightforward task got complicated because the $_GET variable was missing some of the those parameters I needed to save.

Debugging this issue made me realise that all the missing parameters where those starting with utm. I was literally going to this example URL...

http://example.com/test.php?utm_source=Source&utm_medium=Medium&utm_content=Content&utm_term=Term&siteId=42689&siteName=naguel.com

...to get the following incomplete output when printing the $_GET variable:

The cause of this issue was Varnish's recommended VCL configuration for Magento that includes the following inside the sub vcl_recv {}.

# Remove all marketing get parameters to minimize the cache objects
    if (req.url ~ "(\?|&)(gclid|cx|ie|cof|siteurl|zanpid|origin|fbclid|mc_[a-z]+|utm_[a-z]+|_bta_[a-z]+)=") {
        set req.url = regsuball(req.url, "(gclid|cx|ie|cof|siteurl|zanpid|origin|fbclid|mc_[a-z]+|utm_[a-z]+|_bta_[a-z]+)=[-_A-z0-9+()%.]+&?", "");
        set req.url = regsub(req.url, "[?|&]+$", "");
}

That was literally subtracting all marketing-related GET parameters from the URL, including those starting with utm_.

The solution is quite simple: remove the utm_[a-z]+ from the if conditional and restart Varnish to apply the changes.

UTM parameters?

UTM stands for "Urchin Tracking Module" (who cares, right?), which are basically marketing-related parameters to track marketing campaigns, and to know how people interact with the site.

In my case I was dealing with them because of a Rakuten integration with Magento.

More information on what they are and what we can do with them can be found on "The Ultimate Guide to Using UTM Parameters" post from Neil Patel's blog.

Proper way to configure asynchronous email sending in Magento

Magento recommends to configure this to be async for performance, so the email dispatch process doesn't block nor delay what the customer is doing, or to avoid long cron runs when an ERP is updating orders status. I also recommended to configure this to be async to avoid having unsent emails.
2 min read
Proper way to configure asynchronous email sending in Magento

There are two ways to configure how Magento send order related emails (order confirmation, invoice, shipment and credit memo emails):

  • Either immediately when an action is performed (for example, right after you place an order).
  • Or cron-based (asynchronous).

Magento itself recommends to configure this to be asynchronous for performance reasons, so the email dispatch process doesn't block nor delay what the customer is doing (for example, less time consumption when the customer is placing an order), or to avoid long cron runs (or total crons block) when an ERP is updating orders status in bulk.

Enabling the “Asynchronous email notifications” setting moves processes that handle checkout and order processing email notifications to the background.

Configuration best practices by Magento

For my personal experience, it is also recommended to configure emails sending to be asynchronous to avoid having unsent emails.

When this is configured as immediately (not async), if an email fails to be dispatched because of any reason (like a server timeout) then Magento won’t try to send that email again in the future and the customer won’t be ever notified of his/her action.

Basically, there are a lot of good reasons to have this to be async.

Before setting this up

If you are going live for the first time on a new site there's not much to worry about, but if you are changing this setting from not-async to cron-based on an already live site then there's something to consider first.

The way this works when it's configured to be async is that Magento will rely on a cron to pick up from the database (from those tables related to Orders, Invoices, Shipments and Credit Memos) all those entities where the email was not dispatched, and it will send it.

Problem is that Magento doesn't care much about the date of those entities.

For example, if there’s an Order from 2019 whose order confirmation email was not sent because of any reason (for example, because there was a PHP error that stopped the execution), the cron will now pick this Order up and Magento will send the email related to that order.

To avoid this behaviour we should mark all pending emails from Orders, Invoices, Shipments, and Credit Memos as if they doesn't need to be dispatched, which can be done by executing the following SQL statements before enabling the asynchronous sending.

UPDATE sales_order SET send_email = 0;
UPDATE sales_invoice SET send_email = 0;
UPDATE sales_shipment SET send_email = 0;
UPDATE sales_creditmemo	 SET send_email = 0;

What we are doing here is ignoring the sending of emails for old Orders (and Invoices, Shipments and Credit Memos) so when we change this to be async the cron will only cares for future stuff.

Changing the configuration

Enable the “Asynchronous sending” option under “Stores → Settings → All Stores → Sales → Sales Email → General Settings”.

Magento and a deadlock found when trying to get lock

Basically, when you have no way to "unlock" an index, and after you already tried the reset and manual reindex, just change the prefix in your app/etc/env.php file to something new, and try to reindex again.
1 min read
Magento and a deadlock found when trying to get lock

"What now?" issues every now and then with Magento are the norm, and I got this one a few days ago:

SQLSTATE[40001]: Serialization failure: 1213 Deadlock found when trying to get lock; try restarting transaction, query was: INSERT INTO `catalog_product_index_eav_temp` SELECT DISTINCT [...]

Apparently the server went down for a second, or MySQL went down... or something happened and a reindex process was interrupted (in my case the one for the "Product EAV" index), and there was no way to make it work again.

Even the usual bin/magento indexer:reset and then a manual reindex with bin/magento indexer:reindex didn't do the trick and the only difference was that instead of the previous error I was getting the classic one:

Product EAV index is locked by another reindex process. Skipping.

Lucky me, I find this tweet by @willemwigman on Twitter that basically indicates that, when you have no way to "unlock" an index and after you already tried the reset and manual reindex, spot the lock node within your app/etc/env.php file and change the prefix within the inner config node.

<?php
return [
    [...]
    'lock' => [
        'provider' => 'db',
        'config' => [
            'prefix' => 'new-prefix-here'
        ]
    ]
];

Reset the indexes and try to reindex again.

That alone should do the trick.

Create and apply a patch in Magento

Magento usually has bugs after the release of a version, that are later corrected on subsequent releases. Using patches to change core files is not a bad practice when the goal behind it is to bring core fixes existing on newer versions to the one we have running, avoiding so a full upgrade.
4 min read
Create and apply a patch in Magento

Magento usually has bugs (thank you, Captain Obvious) after the release of a version, that are later corrected on subsequent releases (known issues).

Using patches to change core files in Magento is not a bad practice when the goal behind it is to bring core fixes existing on newer versions to the one we have running, avoiding so a full upgrade.

How to apply a patch

I'm starting the other way around as applying a patch might be more common than actually creating one, specially because Magento itself releases patches for its own platform, third-party vendors release patches for their extensions, and the community also makes patches for others to use.

There's a well known Composer package named cweagans/composer-patches that handles the patches and applies them to the original Magento modules every time you run composer install and/or composer update.

Technically speaking, this tool installs all Composer packages required, then removes the Magento modules affected by the patches we are including, and re-install those modules with the code changes applied.

➜  ~ composer install
Gathering patches from patch file.
Removing package magento/module-customer so that it can be re-installed and re-patched.
  - Removing magento/module-customer (102.0.5)
Loading composer repositories with package information
Installing dependencies (including require-dev) from lock file
Package operations: 1 install, 0 updates, 0 removals
Gathering patches from patch file.
Gathering patches for dependencies. This might take a minute.
  - Installing magento/module-customer (102.0.5): Loading from cache
  - Applying patches for magento/module-customer
    patches/composer/magento/module-customer/22952.patch (#22952 - Delegated account creation fails with custom attributes present in customer address)

For making this happen, first you need to add your .patch file in the patches/composer/magento/[module-name]/ folder (relative to the Magento root, at the same level your composer.json file lives).

The name of the .patch file doesn't really matter, but I personally like to use the number of the GitHub issue where the known issue was discussed, if that even exists. Otherwise, any descriptive name would do.

For the [module-name] folder name use the Composer package name containing the files you are patching.

As an example, the Composer package name for the Magento_Customer module is module-customer. This information is on the composer.json file of each package, inside the vendor folder.

Finally, on the root composer.json file, inside the extra node create the patches node and define the patch to apply as the following example.

"extra": {
    "composer-exit-on-patch-failure": true,
    "patches": {
        "magento/module-customer": {
            "#22952 - Delegated account creation fails with custom attributes present in customer address": "patches/composer/magento/module-customer/22952.patch"
        }
    }
}

See that I'm adding a description to what the patch does, and the GitHub issue number, followed by the relative path to the .patch file itself.

How to create a patch

Identify the core file you would like to change using a patch.

For the sake of an example, I'm going to patch the vendor/magento/module-customer/Model/Address.php file to apply a fix for the "Delegated account creation fails with custom attributes present in customer address" issue reported in the magento/magento2 repo itself.

Create an exact copy of the file, and make all the necessary changes.

If you are using PhpStorm, you can create a "scratch file" to hold any code temporary, like a draft, by pressing CMD + Shift + N.

Comparison between the original file and the one with the changes

Output the changes into a .patch file using the diff command on the Terminal while standing in the root of your Magento.

➜  ~ diff -Naur path/to/old/file.php path/to/new/file.php > example.patch

Following my example, I need to execute the following...

~ diff -Naur vendor/magento/module-customer/Model/Address.php /Users/nahuelsanchez/Library/Application\ Support/JetBrains/PhpStorm2021.1/scratches/scratch_113.php > example.patch

...in order to get my example.patch file.

My example.patch file generated before any manual intervention

Unfortunately, here's when we need to perform some manual intervention on the generated example.patch file.

First, remove the date and time appearing the end of the first two lines.

Second, you can see that the first path is the real path to the original file living in the vendor folder. Add a a/ prefix to it.

Third, replace the second path with the first path, but using the b/ prefix instead.

Fourth, and finally, add a new line on top of everything with diff --git a/vendor/core/file.php b/vendor/core/file.php.

Before and after the manual intervention on my example.patch file

Do not touch anything else there, not even the empty lines at the end (if any).

The resulting file ready to be used as a patch should look as the following example:

diff --git a/vendor/magento/module-customer/Model/Address.php b/vendor/magento/module-customer/Model/Address.php
--- a/vendor/magento/module-customer/Model/Address.php
+++ b/vendor/magento/module-customer/Model/Address.php
@@ -159,7 +159,9 @@
         $customAttributes = $address->getCustomAttributes();
         if ($customAttributes !== null) {
             foreach ($customAttributes as $attribute) {
-                $this->setData($attribute->getAttributeCode(), $attribute->getValue());
+                if (isset($attribute)) {
+                    $this->setData($attribute->getAttributeCode(), $attribute->getValue());
+                }
             }
         }

Install Elasticsearch for Magento 2 on macOS

Depending on the Magento version you have, or the Elasticsearch version you want, the process of getting this work locally can be easy or a nightmare I'll try to simplify here in this post.
5 min read
Install Elasticsearch for Magento 2 on macOS

Depending on the Magento version you have, or the Elasticsearch version you want, the process of getting this work locally can be easy or a nightmare I'll try to simplify here in this post.

If you already know what version to install, skip the following and jump straight to the installation instructions.

What Elasticsearch version do you need?

If we dive into the official Magento documentation and take a quick look at the different changes made to it across time, we can learn a few things.

Magento 2.3.4 documentation

Available at the magento/devdocs' GitHub repo, tag 2.3.4.

Magento 2.3.1 adds support for Elasticsearch 6.x and it is enabled by default. Magento still provides connectivity for Elasticsearch 2.x and 5.x but these must be enabled in order to use these versions.

Magento 2.3.5 documentation

Available at the magento/devdocs' GitHub repo, tag 2.3.5.

Magento 2.3.5 adds support for Elasticsearch 7.x.x (default) and 6.8.x. Both ES 2.x and 5.x are End of Life and are no longer supported in Magento.

Magento 2.4 documentation

Available at the magento/devdocs' GitHub repo, branch 2.4.1-develop.

You must install and configure Elasticsearch 7.6.x before upgrading to Magento 2.4.0.
Magento does not support Elasticsearch 2.x, 5.x, and 6.x.

Or, to summarize:

  • For Magento 2.3.1 to Magento 2.3.4 you need to install Elasticsearch 6.x.
  • For Magento 2.3.5 to Magento 2.3.n (anything before 2.4.0) you'll need to install Elasticsearch 7.x.x. It works also with 6.8.x, but version 7.x.x is easy to install.
  • For Magento 2.4.0 and up you need to install Elasticsearch 7.6.x. No support for previous versions.

If you are working with multiple Magento projects on different versions, I would suggest you to install Elasticsearch 6.x to support from Magento 2.3.1 to Magento 2.3.5, and everything before Magento 2.4.

If you have all projects on Magento 2.3.5 and up then install Elasticsearch 7.6.x.

Install Elasticsearch 6.x on macOS

You need Homebrew first, so please install it by doing the following:

/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"

You can check the official Homebrew page for more information about it and alternative installations options.

Then, we need to add a third-party repo to Homebrew and a few packages, including Elasticsearch itself.

brew tap elastic/tap; brew cask install homebrew/cask-versions/adoptopenjdk8; brew install elasticsearch@6.8;

When you do that you'll get a lot of output on your terminal, but you need to only care for the installation path of the last elasticsearch@6.8 package.

➜  brew install elasticsearch@6.8;
==> Downloading https://homebrew.bintray.com/bottles/elasticsearch%406.8-6.8.8.catalina.bottle.tar.gz
Already downloaded: /Users/nahuelsanchez/Library/Caches/Homebrew/downloads/01d0782011cbecdb3b1125469ee2ed60ef07926a8f36752c64d1b8c2b763736d--elasticsearch@6.8-6.8.8.catalina.bottle.tar.gz
==> Pouring elasticsearch@6.8-6.8.8.catalina.bottle.tar.gz
==> /usr/local/Cellar/elasticsearch@6.8/6.8.8/bin/elasticsearch-keystore create
==> Caveats
Data: /usr/local/var/lib/elasticsearch/
Logs: /usr/local/var/log/elasticsearch/elasticsearch_nahuelsanchez.log
Plugins: /usr/local/var/elasticsearch/plugins/
Config: /usr/local/etc/elasticsearch/

elasticsearch@6.8 is keg-only, which means it was not symlinked into /usr/local,
because this is an alternate version of another formula.

If you need to have elasticsearch@6.8 first in your PATH run:
  echo 'export PATH="/usr/local/opt/elasticsearch@6.8/bin:$PATH"' >> ~/.zshrc
  
To have launchd start elasticsearch@6.8 now and restart at login:
  brew services start elasticsearch@6.8
Or, if you don't want/need a background service you can just run:
  elasticsearch
==> Summary
🍺  /usr/local/Cellar/elasticsearch@6.8/6.8.8: 136 files, 103.4MB

The path below "Summary" is the one we need to mind, where mine is /usr/local/Cellar/elasticsearch@6.8/6.8.8.

Go to that folder and install a few Elasticsearch plugins.

bin/elasticsearch-plugin install analysis-phonetic bin/elasticsearch-plugin install analysis-icu; bin/elasticsearch-plugin install analysis-smartcn;

Now, because only God knows why, we need to fix something on this freshly installed Elasticsearch package, otherwise it won't start.

Standing again into that installation path, cd into the libexec/config folders, and edit the jvm.options file.

Comment the line it says 8:-Xloggc:logs/gc.log by adding a # at the beginning of it, and below add 8:-Xloggc:/tmp/logs_gc.log.

This is how the file might ended up looking

You are basically done now.

Start Elasticsearch by doing brew services start elasticsearch@6.8.

➜  brew services start elasticsearch@6.8
==> Successfully started `elasticsearch@6.8` (label: homebrew.mxcl.elasticsearch@6.8)

To check if everything is good, you can do a cURL to the Elasticsearch instance running.

➜  curl "http://localhost:9200/_nodes/settings?pretty=true"
{
  "_nodes" : {
    "total" : 1,
    "successful" : 1,
    "failed" : 0
  },
  "cluster_name" : "elasticsearch_brew",
  "nodes" : {
    "ArBWW_c3QRunhfQn460sxQ" : {
      "name" : "ArBWW_c",
      "transport_address" : "127.0.0.1:9300",
      "host" : "127.0.0.1",
      "ip" : "127.0.0.1",
      "version" : "6.8.8",
      "build_flavor" : "oss",
      "build_type" : "tar",
      "build_hash" : "2f4c224",
      "roles" : [
        "master",
        "data",
        "ingest"
      ],
      "settings" : {
        "client" : {
          "type" : "node"
        },
        "cluster" : {
          "name" : "elasticsearch_brew"
        },
        "http" : {
          "type" : {
            "default" : "netty4"
          }
        },
        "node" : {
          "name" : "ArBWW_c"
        },
        "path" : {
          "data" : [
            "/usr/local/var/lib/elasticsearch"
          ],
          "logs" : "/usr/local/var/log/elasticsearch",
          "home" : "/usr/local/Cellar/elasticsearch@6.8/6.8.8/libexec"
        },
        "transport" : {
          "type" : {
            "default" : "netty4"
          }
        }
      }
    }
  }
}

That host and port on that cURL command (which by default is localhost:9200) is the host and port you need to use for configuring Elasticsearch in Magento. If it doesn't work, try the same cURL call but with 127.0.0.1:9200.

Remember to brew services stop elasticsearch@6.8 when you are done working so it's not running forever in your computer.

Install Elasticsearch 7.x on macOS

You need Homebrew first, so please install it by doing the following:

/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"

You can check the official Homebrew page for more information about it and alternative installations options.

Then, we need to add a third-party repo to Homebrew and the Elasticsearch package itself.

brew tap elastic/tap; brew install elastic/tap/elasticsearch-full;

When you do that you'll get a lot of output on your terminal, but you need to only care for the installation path of the elastic/tap/elasticsearch-full package, which is located just at the end below "Summary".

For example, mine says /usr/local/Cellar/elasticsearch-full/7.8.0.

Go to that folder and install a few Elasticsearch plugins.

bin/elasticsearch-plugin install analysis-phonetic bin/elasticsearch-plugin install analysis-icu; bin/elasticsearch-plugin install analysis-smartcn;

That's all, basically.

Start Elasticsearch by doing brew services start elastic/tap/elasticsearch-full.

To check if everything is good, you can do a cURL to the Elasticsearch instance by running curl "http://localhost:9200/_nodes/settings?pretty=true" which will output a big JSON.

That host and port on that cURL command (which by default is localhost:9200) is the host and port you need to use for configuring Elasticsearch in Magento. If it doesn't work, try the same cURL call but with 127.0.0.1:9200.

Remember to brew services stop elastic/tap/elasticsearch-full when you are done working so it's not running forever in your computer.

Can I have both installed at the same time?

No... well, I didn't try it but I think you can't. I would avoid such experiment.

What you can do for sure is uninstall one and install the other on demand.

If you do that you need to uninstall the Elasticsearch plugins you install it and install them again so you ended up with the right plugins version compatible with the Elasticsearch version currently installed.

Anything else?

Reindex Magento before trying to load the store.

Magento virtual themes or why theme changes don't show up in the front end

The CSS is okay but the Layout XML is not being taken into consideration. The front end kind of knows which theme to load, but it's loading it only partially. What it's going on? How do we fix it?
2 min read
Magento virtual themes or why theme changes don't show up in the front end

You have a Magento 2 theme created (at least you can see it in your IDE and we're going to assume there's no typo in the theme.xml nor in the registration.php), you can also see it in the Admin, and finally you have it configured for your store.

Then you go to the front end and you see a Frankenstein's monster: the CSS is okay but the Layout XML is not being taken into consideration. The front end kind of knows which theme to load, but it's loading it only partially.

What it's going on? How do we fix it?

One of the possibilities is that the theme is set as Virtual instead of Physical in the database, causing behaviors such as the described above.

Magento 2 happen to have 3 types of themes, of which I have found very little information in the official documentation (just a minor mention in the Troubleshooting section of the 2.0 "Apply and configure a storefront theme" page but gone in the 2.2 version of it) but a convincing explanation in this StackExchange question.

We have "Physical themes" which are the well-known themes such as Blank and Luma, second we have "Virtual themes" which appears to be themes that extend a Physical theme but doesn't exist in the files (nothing in app/design), and finally we have the "Staging themes" which I have no idea what they are nor how they behave (sorry).

These 3 different types of themes are defined in the Interface Magento\Framework\View\Design\ThemeInterface as 0, 1, and 2 for Physical, Virtual and Staging, respectively.

If you go to your database (using the Terminal, PhpMyAdmin or an app like TablePlus) and take a look at the theme table, chances are that you'll find something like this:

If you encounter a 1 in the type column for your theme, there's your problem. Change it to 0 and everything will be fixed.

How the theme became Virtual without anybody setting it anywhere?

You're probably wondering how that 1 ended up there without anybody setting it (at least you're sure you didn't as you just find out about all this).

As described, Virtual themes are themes that doesn't exist in the code, meaning there are no files for that theme (how Magento expect us to use this type of theme is for me unknown).

If you're using a version control system such as Git maybe (maybe) you ended up for a moment in a branch without the theme files and somehow loaded the front end or ran a bin/magento command. For a moment only, Magento saw in the database that a theme is set and active but found no files in the code for such theme, so it changed it to Virtual.

About how hard it is to find a front end developer capable of work with Magento

It is difficult to find someone who is already a front end developer but never worked with Magento. Will he be able to? Will he understand and like the platform? Is he willing to despite what he says during the interview?
4 min read
About how hard it is to find a front end developer capable of work with Magento

Is it hard to find someone willing (and able?) to work with Magento. And I'm not talking about finding an already "senior" Magento front end developer with n years of experience with that eCommerce platform and/or working in that specific field already.

No. What I mean is that it is difficult to find someone who is already a front end developer but never worked with Magento. Will he be able to? Will he understand and like the platform? Is he willing to despite what he says during the interview?

I already wrote an article about how to interview a front end developer, and I'm not sure if I strengthened enough the idea of it being a kind of a bet.

How to interview a front end developer and not die in the process
Being an interviewer it’s not an easy task as the candidate’s chances of getting the job depends not only on the candidate’s technical skills but also on the ability of the interviewer to rule on the truth of those said skills in a 60 minutes talk.

In this scenario we're betting twice as we not only need to discover if the person being interviewed possesses the skills to be a good developer but also if he or she can perform with the specific platform.

That fuzzy line between back end and front end within Magento

I encountered a lot of different "profiles" while interviewing. While the spectrum is really broad let me summarize this into two groups (nevertheless, not sure about the names I'm giving to them, but bear with me):

  • Web designers, or people focused only on the HTML and CSS (maybe jQuery?) part of the coding process. People whose main task is to convert a given PSD file into "a web".
  • Developers using the "hard tools" for coding complex logical solutions with, and not limited to, JavaScript and all its modern flavours.

The first group is excellent in that PSD to xHTML conversion process but they sort of depend on somebody to add the functionality to their creations, and the later group fails when the QA team compares what they delivered with the original design files.

So, someone in between, right? Right. But why?, because of that fuzzy line between back end and front end within Magento. :)

It depends on how the company you work for is structured, and how the roles for back end and front end are implemented there, but under my experience I can tell that none of these groups that represents the two extremes of the wide range of developers I interview can work with Magento, or at least they can't on their own as they will always lack what the other groups knows... and we need both on the front end side of a Magento project for it to go live.

Again, under my experience, while working with people that falls into this two categories I either faced the problem of things looking good but not working as expected, or things that do what they're are supposed to do but with so many differences when I open the designs (and if you are thinking "it's only colors", think responsive web design).

How we find that "in between"?

Spoiler alert: I don't know for sure, but I can tell you what I'm doing right now and hopefully it will help you achieve the same as I'm looking for if you're conduction interviews.

I recently had a meeting with somebody from the recruiting team at the company I work for in order to polish the "mechanism" to identify people too attached to the mentioned groups in order to dismiss them at the beginning of the process, and there are a lot of hints in the candidate's CV that you can use.

Exhibit A is this imaginary resume that includes skills such as HTML, CSS, SASS and jQuery, which is excellent because we need those skills. Following we find a lot of background experience building corporate websites only, but never using a CMS as there is no mention for WordPress, Joomla, Drupal, or similar.

Finally, the work experience is a mix of marketing and design, mostly a transition from the later to a developer. The court rules guilty for being too much of a member of the web designers group.

A second example is this also imaginary resume that includes all the fancy words I already used to, such as Angular, AngularJS, React, VUE, Node... you name it... and a background experience that mostly includes working on the back end side of some sites.

I don't have to tell you to which group this imaginary candidate belongs to.

So, again, someone in between. Find that resume that have a mix of the trending topic's keywords for the first and second group, and you got yourself a feasible Magento front end developer. Or work the other way around: burn the resumes that falls into the extremes and what lays in the middle is the people you should interview next.

No group is better than the other

Don't get me wrong... or let me clarify.

I do not think one group is better than the other one, and if you belong to the first one there's no obligation to learn what the other group knows, neither the other way around.

Let me put another short perspective into this: if the company you work for (or the company you applied for, or your company if you happens to have one) really separates this two groups into really two separates job positions, and if you only have to worry about the PSD to xHTML thing because somebody else is handling the functionality... good for you, and go for it, as again this depends on the scenario you're standing.

That's why I mentioned something about the way a company is structured before, because in some places you might not need to worry about the "in between" as it doesn't apply.

Happy hunting.

Install Composer on OS X and use it globally

Magento 2 needs Composer, so let install it on OS X in a way it can be used globally.
1 min read
Install Composer on OS X and use it globally

Magento 2 needs Composer, so let install it on OS X in a way it can be used globally.

Considering that installing Composer will create a composer.phar file, and our intention is to use it everywhere, anytime, regardless the project we're working on, I decided to install it (meaning let the composer.phar be created) in /Applications/Composer (it makes sense to me, but you can choose the destination folder you want).

Installing Composer

First thing first, let's go to /Applications, create the Composer folder, and get in.

cd /Applications/
mkdir Composer
cd Composer

Now, we can install Composer for real using cURL.

curl -sS https://getcomposer.org/installer | php

If everything goes as expected we'll get a nice message saying something like "Composer successfully installed to..." and we can proceed to the next step.

Making it globally available

Now that we have the composer.phar file, we need (or want?) to have it globally available by typing composer in the command line. To achieve this let's start by moving our composer.phar file to /usr/bin/.

sudo mv composer.phar /usr/bin/

And finally let's create an alias on our bash profile.

alias composer="php /usr/bin/composer.phar"

Reload the command line profile, and that's all it takes to get Composer on OS X. You can now go to any folder you want and type composer about. If something nice appears it means everything is okay.

I might be skipping the part where I'm supposed to explain how to edit and reload the bash profile. It was on purpose because it always different depending on the terminal you use (for example mines it's located in ~/.zshrc because I use Oh My ZSH!).

You can try the following in order to access and edit the bash profile.

vim ~/.bash_profile