<?xml version="1.0" encoding="utf-8" standalone="yes"?><feed xmlns="http://www.w3.org/2005/Atom">
  <title></title>
  <subtitle></subtitle>
  <id>https://www.endpointdev.com/blog/tags/magento/</id>
  <link href="https://www.endpointdev.com/blog/tags/magento/"/>
  <link href="https://www.endpointdev.com/blog/tags/magento/" rel="self"/>
  <updated>2020-08-27T00:00:00+00:00</updated>
  <author>
    <name>End Point Dev</name>
  </author>
  
    <entry>
      <title>Containerizing Magento with Docker Compose: Elasticsearch, MySQL and Magento</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2020/08/containerizing-magento-with-docker-compose-elasticsearch-mysql-and-magento/"/>
      <id>https://www.endpointdev.com/blog/2020/08/containerizing-magento-with-docker-compose-elasticsearch-mysql-and-magento/</id>
      <published>2020-08-27T00:00:00+00:00</published>
      <author>
        <name>Kevin Campusano</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;img src=&#34;/blog/2020/08/containerizing-magento-with-docker-compose-elasticsearch-mysql-and-magento/banner.jpg&#34; alt=&#34;Banner&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://business.adobe.com/products/magento/open-source.html&#34;&gt;Magento&lt;/a&gt; is a complex piece of software, and as such, we need all the help we can get when it comes to developing customizations for it. A fully featured local development environment can do just that, but these can often times be very complex as well. It’d be nice to have some way to completely capture all the setup for such an environment and be able to get it all up and running quickly, repeatably&amp;hellip; even with a single command. Well, &lt;a href=&#34;https://www.docker.com/&#34;&gt;Docker&lt;/a&gt; containers can help with that. And they can be easily provisioned with the &lt;a href=&#34;https://docs.docker.com/compose/&#34;&gt;Docker Compose&lt;/a&gt; tool.&lt;/p&gt;
&lt;p&gt;In this post, we’re going to go in depth into how to fully containerize a Magento 2.4 installation for development, complete with its other dependencies &lt;a href=&#34;https://www.elastic.co/&#34;&gt;Elasticsearch&lt;/a&gt; and &lt;a href=&#34;https://www.mysql.com/&#34;&gt;MySQL&lt;/a&gt;. By the end of it, we’ll have a single command that sets up all the infrastructure needed to install and run Magento, and develop for it. Let’s get started.&lt;/p&gt;
&lt;h3 id=&#34;magento-24-application-components&#34;&gt;Magento 2.4 application components&lt;/h3&gt;
&lt;p&gt;The first thing that we need to know is what the actual components of a Magento application are. Starting with 2.4, &lt;a href=&#34;https://devdocs.magento.com/guides/v2.4/install-gde/prereq/elasticsearch.html&#34;&gt;Magento requires access to an Elasticsearch&lt;/a&gt; service to power catalog searches. Other than that, we have the usual suspects for typical PHP applications. Here’s what we need:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;MySQL&lt;/li&gt;
&lt;li&gt;Elasticsearch&lt;/li&gt;
&lt;li&gt;A web server running the Magento application&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;In terms of infrastructure, this is pretty straightforward. It would cleanly translate into three separate machines talking to each other via the network, but in the Docker world, each of these machines become containers. Since we need multiple containers for our infrastructure, things like Docker Compose can come in handy to orchestrate the creation of all that. So let’s get to it.&lt;/p&gt;
&lt;h3 id=&#34;creating-a-shared-network&#34;&gt;Creating a shared network&lt;/h3&gt;
&lt;p&gt;Since we want to create three separate containers that can talk to each other, we need to ask the Docker engine to create a network for them. This can be done with this self-explanatory command:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker network create magento-demo-network&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;magento-demo-network&lt;/code&gt; is the name I’ve chosen for my network but you can choose whatever is most appropriate.&lt;/p&gt;
&lt;p&gt;You can run the following command to check your newly created network:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker network ls&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Output usually looks like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ docker network ls
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;NETWORK ID    NAME                  DRIVER  SCOPE
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;bd562b9cf5a4  bridge                bridge  local
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;adb9ec2365c5  host                  host    local
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;2dba8d97410e  magento-demo-network  bridge  local
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;c3473c60ed52  none                  null    local&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;There’s our &lt;code&gt;magento-demo-network&lt;/code&gt; network among other networks that Docker creates by default.&lt;/p&gt;
&lt;h3 id=&#34;containerizing-mysql&#34;&gt;Containerizing MySQL&lt;/h3&gt;
&lt;p&gt;Getting a MySQL instance up and running is super easy these days thanks to Docker. There’s already &lt;a href=&#34;https://hub.docker.com/_/mysql&#34;&gt;an official image for MySQL&lt;/a&gt; in &lt;a href=&#34;https://hub.docker.com/&#34;&gt;Docker Hub&lt;/a&gt; so we will use that. We can set it up with this command:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker run -d \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  --name magento-demo-mysql \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  --network magento-demo-network \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  --network-alias mysql \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -p 3306:3306 \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -v magento-demo-mysql-data:/var/lib/mysql \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -e MYSQL_ROOT_PASSWORD=password \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -e MYSQL_USER=kevin \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -e MYSQL_PASSWORD=password \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -e MYSQL_DATABASE=magento_demo \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  mysql:5.7&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And just like that, we have a running MySQL instance. Running &lt;code&gt;docker ps&lt;/code&gt; can get you a list of currently running containers. The one we just created should show up there.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ docker ps
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;CONTAINER ID  IMAGE      COMMAND                 CREATED         STATUS         PORTS                              NAMES
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;b73739ad5d66  mysql:5.7  &amp;#34;docker-entrypoint.s…&amp;#34;  22 seconds ago  Up 21 seconds  0.0.0.0:3306-&amp;gt;3306/tcp, 33060/tcp  magento-demo-mysql&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Let’s go through each one of the options from that command now to understand it better.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;docker run -d&lt;/code&gt;: Runs the container in detached mode. This means that it’s run in the background as a daemon. Control is returned to the console immediately.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--name magento-demo-mysql&lt;/code&gt;: This is the name of our container. Normally, Docker will generate random names for containers. In this case, we want to give it a name to refer to it with other Docker commands.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--network magento-demo-network&lt;/code&gt;: Tells Docker to run the container as part of the &lt;code&gt;magento-demo-network&lt;/code&gt; network that we created earlier. This is the network that we will use for all of our containers.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--network-alias mysql&lt;/code&gt;: This is the name of this container within the network. This is how other containers in the network will be able to reference it. We’ll see that come to life a bit later.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-p 3306:3306&lt;/code&gt;: Sets up our new MySQL container to allow connections over port &lt;code&gt;3306&lt;/code&gt;. This is MySQL’s default port, which Magento will use to connect to it. This basically says “requests coming over the network to port &lt;code&gt;3306&lt;/code&gt; of this container are going to be handled by the service installed in this container that listens to port &lt;code&gt;3306&lt;/code&gt;”. That service happens to be MySQL.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-v magento-demo-mysql-data:/var/lib/mysql&lt;/code&gt;: Creates a Docker volume. Specifically, we’re setting this one up to store the data files from MySQL. We need to do this so that the data stored in our MySQL container is persisted across shutdowns. &lt;code&gt;magento-demo-mysql-data&lt;/code&gt; is the name of the volume and &lt;code&gt;/var/lib/mysql&lt;/code&gt; is the directory within the MySQL container where that volume is mounted. In other words, any files stored in that directory are going to be stored within the volume instead. The volume is stored by Docker in the host machine, outside the container. &lt;code&gt;/var/lib/mysql&lt;/code&gt; is the default directory where MySQL stores databases.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-e MYSQL_ROOT_PASSWORD=password&lt;/code&gt;: Is the password for the root user for MySQL. This is passed into the containerized MySQL via environment variables. Hence the &lt;code&gt;-e&lt;/code&gt; option.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-e MYSQL_USER=kevin&lt;/code&gt;: Creates a new login in MySQL with &lt;code&gt;kevin&lt;/code&gt; as its username.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-e MYSQL_PASSWORD=password&lt;/code&gt;: Sets the word &lt;code&gt;password&lt;/code&gt; as the password for that &lt;code&gt;kevin&lt;/code&gt; user.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-e MYSQL_DATABASE=magento_demo&lt;/code&gt;: Creates a database named &lt;code&gt;magento_demo&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;mysql:5.7&lt;/code&gt;: This is the image that we’re using for our container. &lt;code&gt;5.7&lt;/code&gt; specifies the version that we want to run. &lt;a href=&#34;https://hub.docker.com/_/mysql&#34;&gt;The &lt;code&gt;mysql&lt;/code&gt; image in Docker Hub&lt;/a&gt; contains a few more versions. Or “tags”, in Docker words.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;connecting-to-this-container&#34;&gt;Connecting to this container&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;docker ps&lt;/code&gt; showed us that our container was running. We can also interact with it. Here are a couple of ways of doing it:&lt;/p&gt;
&lt;h4 id=&#34;connecting-from-within-the-container&#34;&gt;Connecting from within the container&lt;/h4&gt;
&lt;p&gt;The easiest way of connecting to the MySQL instance is by running &lt;code&gt;mysql&lt;/code&gt; CLI client from within the container itself. You can do that with:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker exec -it magento-demo-mysql mysql -u kevin -p&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Here’s how that command works:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;docker exec -it&lt;/code&gt; is used to run commands inside a container in interactive mode. Just what we need here in this case because we’re running &lt;code&gt;mysql&lt;/code&gt;, which is an interactive CLI.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;magento-demo-mysql&lt;/code&gt; is the name we gave our container in the &lt;code&gt;docker run&lt;/code&gt; command from before via the &lt;code&gt;--name magento-demo-mysql&lt;/code&gt; option. This is why it’s useful to give names to containers: so we can use them in commands like this.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;mysql -u kevin -p&lt;/code&gt; is the command that’s run within the container. This is just the usual way of connecting to a MySQL server instance using the &lt;code&gt;mysql&lt;/code&gt; CLI client. We use &lt;code&gt;kevin&lt;/code&gt; because that’s what we set &lt;code&gt;MYSQL_USER&lt;/code&gt; to when we created our container before.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;After running the previous command, the console will ask you for your password. We set that to &lt;code&gt;password&lt;/code&gt; via &lt;code&gt;MYSQL_PASSWORD&lt;/code&gt; so that’s what we need to type in. This will eventually result in the &lt;code&gt;mysql&lt;/code&gt; prompt showing up. Run &lt;code&gt;show databases&lt;/code&gt; to confirm that the &lt;code&gt;magento_demo&lt;/code&gt; database that we specified via &lt;code&gt;MYSQL_DATABASE&lt;/code&gt; got created.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mysql&amp;gt; show databases;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+--------------------+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| Database           |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+--------------------+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| information_schema |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| magento_demo       |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+--------------------+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;2 rows in set (0.00 sec)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You can &lt;code&gt;Ctrl + D&lt;/code&gt; your way out of that when you’re done exploring the containerized MySQL instance.&lt;/p&gt;
&lt;h4 id=&#34;connecting-directly-from-the-host-machine&#34;&gt;Connecting directly from the host machine&lt;/h4&gt;
&lt;p&gt;We can also connect to the MySQL instance running in the container, directly from our host machine. We can use:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mysql -h localhost -P 3306 --protocol=tcp -u kevin -p&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;Note that it is required that the &lt;code&gt;mysql&lt;/code&gt; CLI client is installed in the host machine for this to work.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Same as before, &lt;code&gt;mysql&lt;/code&gt; will ask you for the password and, once typed in, it will give you its prompt.&lt;/p&gt;
&lt;h3 id=&#34;containerizing-elasticsearch&#34;&gt;Containerizing Elasticsearch&lt;/h3&gt;
&lt;p&gt;Like MySQL, there’s an official &lt;a href=&#34;https://hub.docker.com/_/elasticsearch&#34;&gt;Elasticsearch Docker image up in Docker Hub&lt;/a&gt;. As a result, getting a working Elasticsearch installation is a piece of cake. It’s done with a command like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker run -d \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  --name magento-demo-elasticsearch \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  --network magento-demo-network \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  --network-alias elasticsearch \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -p 9200:9200 \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -p 9300:9300 \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -e &amp;#34;discovery.type=single-node&amp;#34; \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  elasticsearch:7.8.1&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You can validate that the Elasticsearch is running with &lt;code&gt;curl localhost:9200/_cat/health&lt;/code&gt;. That should return something like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ curl localhost:9200/_cat/health
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;1597622135 23:55:35 docker-cluster green 1 1 0 0 0 0 0 0 - 100.0%&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Alright! That was easy enough. Again, thanks to Docker, we have an application that’s somewhat complex to set up, up and running in a matter of seconds.&lt;/p&gt;
&lt;p&gt;Like before, let’s dissect that command that we used. Very similar to the MySQL one, only with some Elasticsearch specific settings:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;docker run -d&lt;/code&gt;: Same as with the MySQL container, runs it in detached mode.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--name magento-demo-elasticsearch&lt;/code&gt;: Gives the container a friendly name.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--network magento-demo-network&lt;/code&gt;: Puts the container in the same network as the rest of our infrastructure.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--network-alias elasticsearch&lt;/code&gt;: Is the name by which other containers in the network can refer to this contianer.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-p 9200:9200&lt;/code&gt;: Opens port &lt;code&gt;9200&lt;/code&gt; so that other containers within the network can talk to this one.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-p 9300:9300&lt;/code&gt;: Same thing but for a different port.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-e &amp;quot;discovery.type=single-node&amp;quot;&lt;/code&gt;: Sets up the &lt;code&gt;discovery.type&lt;/code&gt; environment variable that the image uses to configure Elasticsearch with.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;elasticsearch:7.8.1&lt;/code&gt;: Specifies that our container will be running version &lt;code&gt;7.8.1&lt;/code&gt; of Elasticsearch.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;containerizing-magento&#34;&gt;Containerizing Magento&lt;/h3&gt;
&lt;p&gt;Now this is the step where things get a little bit more involved. Nothing crazy however, so let’s get into it.&lt;/p&gt;
&lt;h3 id=&#34;the-dockerfile&#34;&gt;The Dockerfile&lt;/h3&gt;
&lt;p&gt;There’s no image of Magento 2 that would be able to get us up and running as quickly as with MySQL or Elasticsearch, at least not that I could find, so we’re going to have to create our own. We can create our own images with the help of &lt;a href=&#34;https://docs.docker.com/engine/reference/builder/&#34;&gt;Dockerfiles&lt;/a&gt;. A Dockerfile is a file that contains all the specifications needed for a container. The Docker engine uses it to create images which can then be used as basis for running containers.&lt;/p&gt;
&lt;p&gt;Here’s a Dockerfile for Magento 2.4 that I came up with:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# /path/to/project/Dockerfile
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# Our image is based on Ubuntu.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;FROM ubuntu
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# Here we define a few arguments to the Dockerfile. Specifically, the
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# user, user id and group id for a new account that we will use to work
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# as within our container.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ARG USER=docker
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ARG UID=1000
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ARG GID=1000
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# Install PHP, composer and all extensions needed for Magento.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;RUN apt-get update &amp;amp;&amp;amp; apt-get install -y software-properties-common curl
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;RUN add-apt-repository ppa:ondrej/php
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;RUN apt-get update &amp;amp;&amp;amp; apt-get install -y php
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;RUN apt-get update &amp;amp;&amp;amp; apt-get install -y \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    php-mysql php-xml php-intl php-curl \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    php-bcmath php-gd php-mbstring php-soap php-zip \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    composer
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# Install Xdebug for a better developer experience.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;RUN apt-get update &amp;amp;&amp;amp; apt-get install -y php-xdebug
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;RUN echo &amp;#34;xdebug.remote_enable=on&amp;#34; &amp;gt;&amp;gt; /etc/php/7.4/mods-available/xdebug.ini
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;RUN echo &amp;#34;xdebug.remote_autostart=on&amp;#34; &amp;gt;&amp;gt; /etc/php/7.4/mods-available/xdebug.ini
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# Install the mysql CLI client.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;RUN apt-get update &amp;amp;&amp;amp; apt-get install -y mysql-client
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# Set up a non-root user with sudo access.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;RUN groupadd --gid $GID $USER \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;amp;&amp;amp; useradd -s /bin/bash --uid $UID --gid $GID -m $USER \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;amp;&amp;amp; apt-get install -y sudo \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;amp;&amp;amp; echo &amp;#34;$USER ALL=(root) NOPASSWD:ALL&amp;#34; &amp;gt; /etc/sudoers.d/$USER \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;amp;&amp;amp; chmod 0440 /etc/sudoers.d/$USER
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# Use the non-root user to log in as into the container.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;USER ${UID}:${GID}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# Set this as the default directory when we connect to the container.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;WORKDIR /workspaces/magento-demo
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# This is a quick hack to make sure the container has something to run
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# when it starts, preventing it from closing itself automatically when
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# created. You could also remove this and run the container with `docker
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# run -t -d` to get the same effect. More on `docker run` further below.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;CMD [&amp;#34;sleep&amp;#34;, &amp;#34;infinity&amp;#34;]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Feel free to go through the comments in the file above for more details, but essentially, this Dockerfile describes what a machine ready to run Magento should look like. It’s got PHP and all the necessary extensions, &lt;a href=&#34;https://xdebug.org/&#34;&gt;Xdebug&lt;/a&gt;, and &lt;a href=&#34;https://getcomposer.org/&#34;&gt;Composer&lt;/a&gt;. It also includes the &lt;code&gt;mysql&lt;/code&gt; CLI client.&lt;/p&gt;
&lt;p&gt;Importantly, it allows for creating a user account with sudo access. Later, we’ll use this capability to create a user account, inside the container that mimics the one we’re using in our host machine, effectively using the same user both inside and outside the container. The purpose of this is to make it possible to work on the Magento source code files from inside the container without having to deal with Linux permission issues when we try to do the same from outside the container (that is, directly via the host machine).&lt;/p&gt;
&lt;h3 id=&#34;the-image&#34;&gt;The image&lt;/h3&gt;
&lt;p&gt;Alright, now that we have our image defined in the form of our Dockerfile, let’s create it. To do that, we go into our project directory, create a new file named &lt;code&gt;Dockerfile&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cd /path/to/project
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;touch Dockerfile&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Then use a text editor to save the contents from above into it, and finally run this command:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker build \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  --build-arg USER=kevin \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  --build-arg UID=$(id -u) \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  --build-arg GID=$(id -g) \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -t magento-demo-web .&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Here’s what this all means:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;docker build&lt;/code&gt;: Is the command to build images from Dockerfiles.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--build-arg USER=kevin&lt;/code&gt;: Specifies the username for the account with sudo access that we will log into our container as. I’ve chosen &lt;code&gt;kevin&lt;/code&gt; here but you should use the one you’re logged in as on your machine.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--build-arg UID=$(id -u)&lt;/code&gt;: Uses the &lt;code&gt;id -u&lt;/code&gt; to pass in the Id of the currently logged in user.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--build-arg GID=$(id -g)&lt;/code&gt;: Uses the &lt;code&gt;id -g&lt;/code&gt; to pass in the Group Id of the currently logged in user.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-t magento-demo-web .&lt;/code&gt;: Specifies the name of the resulting image to be &lt;code&gt;magento-demo-web&lt;/code&gt;. The &lt;code&gt;.&lt;/code&gt; is a reference to the current working directory from where we’re running the command, which is where our Dockerfile is located.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Run &lt;code&gt;docker image ls&lt;/code&gt; and you should see our new home grown &lt;code&gt;magento-demo-web&lt;/code&gt; image along with the other ones that we’ve downloaded from Docker Hub:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;REPOSITORY        TAG     IMAGE ID      CREATED         SIZE
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;magento-demo-web  latest  90d311df434f  22 minutes ago  452MB
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mysql             5.7     718a6da099d8  12 days ago     448MB
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ubuntu            latest  1e4467b07108  3 weeks ago     73.9MB
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;elasticsearch     7.8.1   a529963ec236  3 weeks ago     811MB&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id=&#34;the-container&#34;&gt;The container&lt;/h3&gt;
&lt;p&gt;Ok, now that we have an image that’s capable of running Magento, let’s put it to work by creating a container based on it. We do that with:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker run -d \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  --name magento-demo-web \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  --network magento-demo-network \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  --network-alias web \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -p 5000:5000 \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -v ${PWD}:/workspaces/magento-demo \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  magento-demo-web&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Line by line, this is telling the Docker engine to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;docker run -d&lt;/code&gt;: Run the container in detached mode. You could also add the &lt;code&gt;-t&lt;/code&gt; argument which makes sure the container stays up and running even if there’s no program or service running within it. We don’t need that in this case though, because we defined our Dockerfile with that nifty &lt;code&gt;sleep infinity&lt;/code&gt; command.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--name magento-demo-web&lt;/code&gt;: Set the name of our container to &lt;code&gt;magento-demo-web&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--network magento-demo-network&lt;/code&gt;: Make our container part of the same network as the MySQL and Elasticsearch ones.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--network-alias web&lt;/code&gt;: Set our container’s name within the network.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-p 5000:5000&lt;/code&gt;: Open port &lt;code&gt;5000&lt;/code&gt; to access our soon-to-be-running Magento app.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-v ${PWD}:/workspaces/magento-demo&lt;/code&gt;: Create a new volume that makes our current working directory the same as the &lt;code&gt;/workspaces/magento-demo&lt;/code&gt; directory within the container. This is where we’ll store all the Magento files. Binding these directories makes it possible to access and modify the Magento files both from the container and from the host machine. This just makes things easier and more convenient for development purposes.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;magento-demo-web&lt;/code&gt;: Use this image.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Running &lt;code&gt;docker container ls&lt;/code&gt; will show a list of all running containers, including the one we just created:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker container ls
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;CONTAINER ID  IMAGE                COMMAND                 CREATED        STATUS        PORTS                                           NAMES
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;4af35c42e0bb  magento-demo-web     &amp;#34;/bin/bash&amp;#34;             5 minutes ago  Up 5 minutes  0.0.0.0:5000-&amp;gt;5000/tcp                          magento-demo-web
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;6c5ea65a7bd6  elasticsearch:7.8.1  &amp;#34;/tini -- /usr/local…&amp;#34;  2 hours ago    Up 2 hours    0.0.0.0:9200-&amp;gt;9200/tcp, 0.0.0.0:9300-&amp;gt;9300/tcp  magento-demo-elasticsearch
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;b73739ad5d66  mysql:5.7            &amp;#34;docker-entrypoint.s…&amp;#34;  3 hours ago    Up 3 hours    0.0.0.0:3306-&amp;gt;3306/tcp, 33060/tcp               magento-demo-mysql&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id=&#34;connecting-to-the-container&#34;&gt;Connecting to the container&lt;/h3&gt;
&lt;p&gt;With the container up and running, we can connect to it with:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker exec -it magento-demo-web bash&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You may remember this as the same command we used before to connect to the MySQL container. This time, however, we’re using it to connect to our &lt;code&gt;magento-demo-web&lt;/code&gt; container, referenced by the name we gave it, and running &lt;code&gt;bash&lt;/code&gt; on it in order to open a shell.&lt;/p&gt;
&lt;p&gt;After that, a prompt like this should show up:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;kevin@4af35c42e0bb:/workspaces/magento-demo$&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We’re now inside our container. Notice how we’re automatically taken to &lt;code&gt;/workspaces/magento-demo&lt;/code&gt;. This is just like we specified in our Dockerfile with the &lt;code&gt;WORKDIR&lt;/code&gt; command. Feel free to run &lt;code&gt;php -v&lt;/code&gt; or &lt;code&gt;composer -V&lt;/code&gt; to validate that the setup from our Dockerfile got all the way into our container:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;kevin@4af35c42e0bb:/workspaces/magento-demo$ php -v
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;PHP 7.4.9 (cli) (built: Aug  7 2020 14:30:01) ( NTS )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Copyright (c) The PHP Group
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Zend Engine v3.4.0, Copyright (c) Zend Technologies
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    with Zend OPcache v7.4.9, Copyright (c), by Zend Technologies
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    with Xdebug v2.9.6, Copyright (c) 2002-2020, by Derick Rethans
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;kevin@4af35c42e0bb:/workspaces/magento-demo$ composer -V
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Composer 1.10.1 2020-03-13 20:34:27&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id=&#34;talking-to-other-containers-in-the-network&#34;&gt;Talking to other containers in the network&lt;/h3&gt;
&lt;p&gt;We also need to validate that our containers are actually able to talk to each other via the network that we set up. If all went according to plan, still from within our &lt;code&gt;magento-demo-web&lt;/code&gt; container, this command should open a &lt;code&gt;mysql&lt;/code&gt; session:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mysql -h mysql -u kevin -p&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Notice how this time we don’t use &lt;code&gt;localhost&lt;/code&gt; or &lt;code&gt;127.0.0.1&lt;/code&gt; to connect to our MySQL instance. This time, we use &lt;code&gt;mysql&lt;/code&gt;. This is the network alias we gave out MySQL container, so this is how our &lt;code&gt;magento-demo-web&lt;/code&gt; sees it. To &lt;code&gt;magento-demo-web&lt;/code&gt;, the MySQL container is just another machine in the same network.&lt;/p&gt;
&lt;p&gt;Same deal for the Elasticsearch container. We can do something like this to talk to it:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;curl elasticsearch:9200/_cat/health&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Again, from the perspective of &lt;code&gt;magento-demo-web&lt;/code&gt;, this is just another machine in the network which it can reach by using the &lt;code&gt;elasticsearch&lt;/code&gt; network alias that we gave it when creating it.&lt;/p&gt;
&lt;h3 id=&#34;installing-magento-in-our-container&#34;&gt;Installing Magento in our container&lt;/h3&gt;
&lt;p&gt;Now that we have our environment ready for Magento, let’s install it. First order of business is to create the Composer project:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;composer create-project --repository-url=https://repo.magento.com/ magento/project-community-edition ./install&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If you’re familiar with Composer, then this should look very familiar to you. This command will download all the Magento files as specified by the &lt;code&gt;magento/project-community-edition&lt;/code&gt; project from the &lt;code&gt;https://repo.magento.com/&lt;/code&gt; repository. There are a few gotchas though:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;First, Magento is not openly available to download just like that. As such, Composer will ask for authentication in order to do so. Follow &lt;a href=&#34;https://devdocs.magento.com/guides/v2.4/install-gde/prereq/connect-auth.html&#34;&gt;this guide&lt;/a&gt; to obtain the authentication keys from the Magento Marketplace. When Composer asks for a username, type in the public key; when it asks for password, type in the private key.&lt;/li&gt;
&lt;li&gt;Second, you’ll notice that I specified &lt;code&gt;./install&lt;/code&gt; at the end of that command. This is where all the files will be downloaded. I’ve chosen this (an &lt;code&gt;install&lt;/code&gt; directory inside our current one) because &lt;code&gt;composer create-project&lt;/code&gt; will refuse to download the files in a directory that’s not empty. Ours isn’t, because we’ve got our Dockerfile in it. But that’s nothing to worry about, once Composer finishes downloading everything, we’ll just copy the files over to their rightful location at &lt;code&gt;/workspaces/magento-demo&lt;/code&gt;. You can do so with some Linux sorcery like this:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(shopt -s dotglob; mv -v ./install/* .)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This Composer operation will take a good while, but when it’s done, make sure to move all the contents of &lt;code&gt;./install&lt;/code&gt; into &lt;code&gt;/workspaces/magento-demo&lt;/code&gt;. We now need to actually install Magento:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;bin/magento setup:install \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  --base-url=http://localhost:5000 \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  --db-host=mysql \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  --db-name=magento_demo \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  --db-user=kevin \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  --db-password=password \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  --admin-firstname=admin \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  --admin-lastname=admin \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  --admin-email=admin@admin.com \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  --admin-user=admin \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  --admin-password=admin123 \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  --language=en_US \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  --currency=USD \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  --timezone=America/New_York \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  --use-rewrites=1 \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  --elasticsearch-host=elasticsearch \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  --elasticsearch-port=9200&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Even if you have never installed Magento before, the command above should be pretty straightforward. An interesting thing to note is how we’ve set up our database and Elasticsearch settings here:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  --db-host=mysql \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  --db-name=magento_demo \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  --db-user=kevin \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  --db-password=password \&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;and&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  --elasticsearch-host=elasticsearch \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  --elasticsearch-port=9200&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;--db-host&lt;/code&gt; is the hostname of the machine where the MySQL server is running. We use our container’s network alias here. &lt;code&gt;--db-name&lt;/code&gt; is the name of the database we created when initializing our container via the &lt;code&gt;MYSQL_DATABASE&lt;/code&gt; environment variable. &lt;code&gt;--db-user&lt;/code&gt; and &lt;code&gt;--db-password&lt;/code&gt; are the credentials for the login that we created in the same manner. &lt;code&gt;--elasticsearch-host&lt;/code&gt; is the network alias of our Elasticsearch container, and finally &lt;code&gt;--elasticsearch-port&lt;/code&gt; is the port that we configured it to listen to.&lt;/p&gt;
&lt;p&gt;As you can see, these are the same settings that we used to configure our MySQL and Elasticsearch containers. So make sure to do the same if you’ve been following along and decided to go with different values.&lt;/p&gt;
&lt;p&gt;Once that command is done, we’re ready. We have a working Magento. Try it out by running this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;php -S 0.0.0.0:5000 -t ./pub/ ./phpserver/router.php&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And navigating to &lt;code&gt;localhost:5000&lt;/code&gt; in your browser. You should see your empty Magento homepage.&lt;/p&gt;
&lt;h3 id=&#34;optional-installing-the-sample-data&#34;&gt;Optional: Installing the sample data&lt;/h3&gt;
&lt;p&gt;If you’re planning some custom extension, or to just play with Magento to get to know it better, you may want to add some sample data. Luckily, the Magento devs have graciously provided such a thing in the form of a Composer package. If you want, you can install it with this recipe:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;bin/magento sampledata:deploy
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;bin/magento setup:upgrade
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;bin/magento indexer:reindex
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;bin/magento cache:flush&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;bin/magento sampledata:deploy&lt;/code&gt; will also ask you for your Magento Makerplace keys so have them ready.&lt;/p&gt;
&lt;p&gt;So turn off the built-in PHP server, run these, wait a good while, and fire up the built in server once more. Your Magento app should now have a catalog and all sorts of other data loaded in.&lt;/p&gt;
&lt;h3 id=&#34;composing-it-all-together&#34;&gt;Composing it all together&lt;/h3&gt;
&lt;p&gt;Now that was a lot. It was much easier than having to set everything up from scratch without Docker, but still, I promised a minimal setup overhead. A single command. With Docker Compose we can do just that.&lt;/p&gt;
&lt;p&gt;For containers, the usual workflow is a three step process:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Create the Dockerfile (sometimes omitted if we have a readily available image like it was the case with MySQL and Elasticsearch).&lt;/li&gt;
&lt;li&gt;Create or download an image.&lt;/li&gt;
&lt;li&gt;Run the container.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Docker Compose can help us by capturing all the settings needed to create containers in a single YAML file; which then can be taken by a CLI tool (i.e. &lt;code&gt;docker-compose&lt;/code&gt;) and it can set up the complete infrastructure. This single file is named &lt;code&gt;docker-compose.yml&lt;/code&gt; and this is what it may look like for our current setup:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;version&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;3.8&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#888&#34;&gt;# Listing our three containers. Or &amp;#34;services&amp;#34;, as known by Docker Compose.&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;services&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#888&#34;&gt;# Defining our MySQL container.&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#888&#34;&gt;# &amp;#34;mysql&amp;#34; will be the network alias for this container.&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;mysql&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;        &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;image&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;mysql:5.7&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;        &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;container_name&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;magento-demo-mysql&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;        &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;networks&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;            &lt;/span&gt;- magento-demo-network&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;        &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;ports&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;            &lt;/span&gt;- &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;3306:3306&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;        &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;volumes&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;            &lt;/span&gt;- magento-demo-mysql-data:/var/lib/mysql&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;        &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;environment&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;            &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;MYSQL_ROOT_PASSWORD&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;password&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;            &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;MYSQL_USER&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;kevin&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;            &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;MYSQL_PASSWORD&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;password&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;            &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;MYSQL_DATABASE&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;magento_demo&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#888&#34;&gt;# Defining our Elasticsearch container&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#888&#34;&gt;# &amp;#34;elasticsearch&amp;#34; will be the network alias for this container.&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;elasticsearch&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;        &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;image&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;elasticsearch:7.8.1&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;        &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;container_name&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;magento-demo-elasticsearch&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;        &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;networks&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;            &lt;/span&gt;- magento-demo-network&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;        &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;ports&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;            &lt;/span&gt;- &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;9200:9200&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;            &lt;/span&gt;- &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;9300:9300&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;        &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;environment&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;            &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;discovery.type&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;single-node&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#888&#34;&gt;# Defining our custom Magento 2 container.&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#888&#34;&gt;# &amp;#34;web&amp;#34; will be the network alias for this container.&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;web&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;        &lt;/span&gt;&lt;span style=&#34;color:#888&#34;&gt;# The build section tells Docker Compose how to build the image.&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;        &lt;/span&gt;&lt;span style=&#34;color:#888&#34;&gt;# This essentially runs a &amp;#34;docker build&amp;#34; command.&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;        &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;build&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;            &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;context&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;.&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;            &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;dockerfile&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;Dockerfile&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;            &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;args&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;                &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;USER&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;kevin&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;                &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;UID&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1000&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;                &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;GID&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1000&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;        &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;container_name&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;magento-demo-web&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;        &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;networks&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;            &lt;/span&gt;- magento-demo-network&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;        &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;ports&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;            &lt;/span&gt;- &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;5000:5000&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;        &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;volumes&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;            &lt;/span&gt;- .:/workspaces/magento-demo&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#888&#34;&gt;# The volume that is used by the MySQL container&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;volumes&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;magento-demo-mysql-data&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#888&#34;&gt;# The network where all the containers will live&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;networks&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;magento-demo-network:&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;As you can see, most of &lt;code&gt;docker-compose.yml&lt;/code&gt; is more or less rewriting the &lt;code&gt;docker run&lt;/code&gt; commands in a YAML format. With the exception of the &lt;code&gt;web&lt;/code&gt; container/​service which includes a &lt;code&gt;build&lt;/code&gt; section that reflects the &lt;code&gt;docker build&lt;/code&gt; command that was used to take the Dockerfile and turn it into an image.&lt;/p&gt;
&lt;p&gt;If you want to try it out, make sure to remove all the infrastructure we’ve created, to avoid any conflicts. You can do so from your host machine with these commands:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker container rm -f magento-demo-web magento-demo-elasticsearch magento-demo-mysql
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker image rm magento-demo-web
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker network rm magento-demo-network
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker volume rm magento-demo-mysql-data&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Make sure you’re in the directory where the Dockerfile lives in the host machine. Then create a new &lt;code&gt;docker-compose.yml&lt;/code&gt; file and put all the content above into it. Finally, run:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker-compose up -d&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This will take a little while, but by the end of it, you’ll have a complete infrastructure with the three containers that we’ve created step by step throughout this article. With the &lt;code&gt;docker-compose.yml&lt;/code&gt; file, &lt;code&gt;docker-compose up&lt;/code&gt; essentially takes care of running all of our &lt;code&gt;docker build&lt;/code&gt; and &lt;code&gt;docker run&lt;/code&gt; commands.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;-d&lt;/code&gt; option means that the the command will run in the background and give you back control of your console. You can also run it without it if you want the console to show the logs from the containers.&lt;/p&gt;
&lt;p&gt;You can still see the logs even in detached mode with:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker-compose logs&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You can also inspect the running containers. For that, you can use:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker-compose ps&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Output will look something like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ docker-compose ps
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;           Name                         Command               State                       Ports                     
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;--------------------------------------------------------------------------------------------------------------------
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;magento-demo-elasticsearch   /tini -- /usr/local/bin/do ...   Up      0.0.0.0:9200-&amp;gt;9200/tcp, 0.0.0.0:9300-&amp;gt;9300/tcp
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;magento-demo-mysql           docker-entrypoint.sh mysqld      Up      0.0.0.0:3306-&amp;gt;3306/tcp, 33060/tcp             
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;magento-demo-web             sleep infinity                   Up      0.0.0.0:5000-&amp;gt;5000/tcp                        &lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Notice how &lt;code&gt;docker-compose ps&lt;/code&gt; gives us our container names just as we specified them in the &lt;code&gt;docker-compose.yml&lt;/code&gt; file.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;docker-compose&lt;/code&gt; has many other utilities, check them out with &lt;code&gt;docker-compose --help&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Now, same as before, we still need to open a terminal into our Magento container to run some installation commands on it. To do so, we can run the following command:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker-compose exec web bash&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Notice how with &lt;code&gt;docker-compose&lt;/code&gt; we refer to the container via its service name. That is, the name we gave the container under the &lt;code&gt;services&lt;/code&gt; section of &lt;code&gt;docker-compose.yml&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Of course, we can still use the same command that we used before, when we created our container directly with &lt;code&gt;docker&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker exec -it magento-demo-web bash&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now, once inside our container we need to install Magento again. Remember that we wiped out all the infrastructure we created manually, so these are fresh new containers; akin to new machines.&lt;/p&gt;
&lt;p&gt;If you were running this from scratch you would just go ahead and do&amp;hellip;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;composer create-project --repository-url=https://repo.magento.com/ magento/project-community-edition ./install&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;and&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(shopt -s dotglob; mv -v ./install/* .)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In this case, however, we already have all the Magento files in our directory, So we can save time and skip this step. We can reuse these files and just run &lt;code&gt;bin/magento setup:install&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;But since this is a new Magento installation, we do need to remove the config file before &lt;code&gt;setup:install&lt;/code&gt;’ing. So go ahead and…&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;rm app/etc/env.php&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;…then:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;bin/magento setup:install \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  --base-url=http://localhost:5000 \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  --db-host=mysql \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  --db-name=magento_demo \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  --db-user=kevin \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  --db-password=password \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  --admin-firstname=admin \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  --admin-lastname=admin \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  --admin-email=admin@admin.com \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  --admin-user=admin \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  --admin-password=admin123 \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  --language=en_US \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  --currency=USD \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  --timezone=America/New_York \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  --use-rewrites=1 \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  --elasticsearch-host=elasticsearch \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  --elasticsearch-port=9200&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;After a while, Magento will be fully installed in our new infrastructure created by Docker Compose and ready to be fired up via the PHP built in server:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;php -S 0.0.0.0:5000 -t ./pub/ ./phpserver/router.php&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id=&#34;bonus-interactive-debugging-with-visual-studio-code&#34;&gt;Bonus: Interactive debugging with Visual Studio Code&lt;/h3&gt;
&lt;p&gt;So this is a fully functioning Magento installation with files that we can edit to our heart’s content. In terms of a “fully featured” development environment, however, we need to spruce it up a bit.&lt;/p&gt;
&lt;p&gt;So install VS Code from &lt;a href=&#34;https://code.visualstudio.com/&#34;&gt;https://code.visualstudio.com/&lt;/a&gt; and install the &lt;a href=&#34;https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.vscode-remote-extensionpack&#34;&gt;Remote Development plugin&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Open a new VS Code window and open the command palette with &lt;code&gt;Ctrl + Shift + P&lt;/code&gt;. In there, type in &lt;code&gt;Remote-Containers: Attach to Running Container...&lt;/code&gt; and press &lt;code&gt;Enter&lt;/code&gt;. In the menu that shows up, select our &lt;code&gt;magento-demo-web&lt;/code&gt; container.&lt;/p&gt;
&lt;p&gt;That will result in a new VS Code instance that is connected to the container. Open an integrated terminal in VS Code and you’ll see:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2020/08/containerizing-magento-with-docker-compose-elasticsearch-mysql-and-magento/vscode.png&#34; alt=&#34;VS Code with Remote Development&#34;&gt;&lt;/p&gt;
&lt;p&gt;Now, install the &lt;a href=&#34;https://marketplace.visualstudio.com/items?itemName=felixfbecker.php-debug&#34;&gt;PHP Debug extension&lt;/a&gt; so that we can take advantage of that Xdebug that we installed in our container via our Dockerfile.&lt;/p&gt;
&lt;p&gt;Create a new launch configuration for interactive debugging with PHP by clicking on the “Run” button in the action bar to the left (&lt;code&gt;Ctrl + Shift + D&lt;/code&gt; also works). Click the “create a launch.json file” link in the pane that appears. Then, in the resulting menu at the top of the window, select the “PHP” option. Here’s a screen capture for guidance:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2020/08/containerizing-magento-with-docker-compose-elasticsearch-mysql-and-magento/opening_debug.jpg&#34; alt=&#34;Setting up debugging in VS Code&#34;&gt;&lt;/p&gt;
&lt;p&gt;That will result in a new &lt;code&gt;.vscode/launch.json&lt;/code&gt; file created that contains the launch configuration for the PHP debugger.&lt;/p&gt;
&lt;p&gt;Now let’s put a breakpoint anywhere, like in line 13 of the &lt;code&gt;pub/index.php&lt;/code&gt; file; press the “Start debugging” button in the “Run” pane, near the top left of the screen (making sure that the “Listen to XDebug” option is selected), and start up the PHP built in server from VS Code’s integrated terminal with &lt;code&gt;php -S 0.0.0.0:5000 -t ./pub/ ./phpserver/router.php&lt;/code&gt;. Now navigate to &lt;code&gt;localhost:5000&lt;/code&gt; in your browser and enjoy VS Code’s interactive debugging experience:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2020/08/containerizing-magento-with-docker-compose-elasticsearch-mysql-and-magento/debugging.png&#34; alt=&#34;Debugging Magento in VS codeCode&#34;&gt;&lt;/p&gt;
&lt;h3 id=&#34;summary&#34;&gt;Summary&lt;/h3&gt;
&lt;p&gt;Whew! That was quite a bit. In this blog post, we’ve done a deep dive into how to set up all the pieces of a Magento application using Docker containers: MySQL, Elasticsearch, and Magento itself. Then, we captured all that knowledge into a single &lt;code&gt;docker-compose.yml&lt;/code&gt; file which can be run with a single &lt;code&gt;docker-compose up&lt;/code&gt; command to provision all the infrastructure in our local machine. As a cherry on top, we set up interactive debugging of our brand new Magento application with VS Code. Thanks to the safety net provided by these tools, I feel like I’m ready to really dig into Magento and start developing customizations, or debugging existing websites. If you’ve been following along this far, dear reader, I hope you do too.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Our Vue Storefront “Proof of Concept” Experience</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2020/08/vue-storefront-magento-integration/"/>
      <id>https://www.endpointdev.com/blog/2020/08/vue-storefront-magento-integration/</id>
      <published>2020-08-10T00:00:00+00:00</published>
      <author>
        <name>Kürşat Kutlu Aydemir</name>
      </author>
      <content type="html">
        &lt;p&gt;Recently we experimented internally with integrating &lt;a href=&#34;https://www.vuestorefront.io/&#34;&gt;Vue Storefront&lt;/a&gt; and &lt;a href=&#34;https://business.adobe.com/products/magento/open-source.html&#34;&gt;Magento&lt;/a&gt; 2.3. Vue Storefront is an open source Progressive Web App (PWA) that aims to work with many ecommerce platforms.&lt;/p&gt;
&lt;p&gt;What initially piqued our interest was the possibility of integrating Vue Storefront with the venerable ecommerce back-end platform &lt;a href=&#34;https://www.interchangecommerce.org/i/dev&#34;&gt;Interchange&lt;/a&gt;, which many of our clients use. Vue Storefront’s promise of ease of integration with any ecommerce backend made us curious to see whether it would make a good modern front-end for Interchange.&lt;/p&gt;
&lt;p&gt;Since Vue Storefront seems to be most commonly used with Magento, we decided to start our experiment with a standard Vue Storefront/​Magento 2.3 proof-of-concept integration.&lt;/p&gt;
&lt;h3 id=&#34;poc-of-vue-storefrontmagento-23&#34;&gt;PoC of Vue Storefront/​Magento 2.3&lt;/h3&gt;
&lt;p&gt;OK, to be honest, at the beginning we blindly expected that Vue Storefront would be a copy/​paste front-end template solution that would fairly easily be made to work with its standard integration to a Magento backend. Sadly, this was not the case for us.&lt;/p&gt;
&lt;p&gt;Before beginning our journey here, to summarize the Vue Storefront integration with Magento let’s have a look at this diagram to see what components are included:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2020/08/vue-storefront-magento-integration/GitHub-Architecture-VS.png&#34; alt=&#34;VS Architecture&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Figure 1&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;At first, we wanted to see how all these components can be installed and run on a single server with modest resources.&lt;/p&gt;
&lt;p&gt;I walked through the &lt;a href=&#34;https://docs.vuestorefront.io/guide/&#34;&gt;Vue Storefront documentation&lt;/a&gt; and a few &lt;a href=&#34;https://medium.com/the-vue-storefront-journal/proof-of-concept-how-to-run-pwa-for-magento-in-a-week-c0fa04fadd3d&#34;&gt;blog posts&lt;/a&gt; to figure out Vue Storefront and Magento integration.&lt;/p&gt;
&lt;h3 id=&#34;preparing-the-environment&#34;&gt;Preparing the Environment&lt;/h3&gt;
&lt;p&gt;I downloaded and installed the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;OS: CentOS Linux 7 (64-bit)&lt;/li&gt;
&lt;li&gt;PHP 7.2.26&lt;/li&gt;
&lt;li&gt;Magento 2.3 with sample data&lt;/li&gt;
&lt;li&gt;Elasticsearch 5.6&lt;/li&gt;
&lt;li&gt;Redis&lt;/li&gt;
&lt;li&gt;Docker&lt;/li&gt;
&lt;li&gt;Vue Storefront API&lt;/li&gt;
&lt;li&gt;Vue Storefront&lt;/li&gt;
&lt;li&gt;mage2vuestorefront bridge&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Installation of those components is fairly easy. We started our virtual server with 4 GB of memory, which we thought should be plenty for a toy setup.&lt;/p&gt;
&lt;h3 id=&#34;indexing-elasticsearch-with-mage2vuestorefront&#34;&gt;Indexing Elasticsearch with mage2vuestorefront&lt;/h3&gt;
&lt;p&gt;PHP and Magento 2.3 had a series of memory usage issues. While running the mage2vuestorefront indexer Magento used most of the memory and caused Elasticsearch to go down each time I tried to index Elasticsearch. Then I configured PHP and Apache httpd server to use a very modest amount memory and made Magento 2.3 work on this server without making it crash due to unavailable memory but the performance became a nightmare on the PHP &amp;amp; Magento 2.3 side. mage2vuestorefront ran without issue, but very slowly.&lt;/p&gt;
&lt;p&gt;I am not very familiar with the Magento 2 API, but while indexing Elasticsearch with mage2vuestorefront it makes several individual API calls. Especially if you have several products in Magento 2, mage2vuestorefront calls the Magento API for almost all products. Due to PHP and Magento’s high memory usage available memory might become a problem during indexing.&lt;/p&gt;
&lt;p&gt;The Vue Storefront developers &lt;a href=&#34;https://docs.vuestorefront.io/guide/cookbook/elastic.html#_0-introduction&#34;&gt;explain&lt;/a&gt; a big reason why they chose Elasticsearch as one of the essential components of this integration:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Vue Storefront&lt;/em&gt; defines itself backend-agnostic PWA e-commerce solution where &lt;em&gt;Vue Storefront&lt;/em&gt; is a storefront as the name dictates, and &lt;em&gt;Elasticsearch&lt;/em&gt; works as a datastore for &lt;em&gt;catalog&lt;/em&gt; and its sibling data such as &lt;em&gt;taxrule&lt;/em&gt;, &lt;em&gt;products&lt;/em&gt; and so on.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;I am unsure if the backend-agnostic approach even really applies to the suggested implementation of Vue Storefront with Magento 2, or if does apply with significant headaches. I’ll detail my concerns about this later.&lt;/p&gt;
&lt;h3 id=&#34;viewing-the-vue-storefront-application&#34;&gt;Viewing the Vue Storefront Application&lt;/h3&gt;
&lt;p&gt;The responsive and offline supportive design of Vue Storefront is elegant. It uses Elasticsearch via vue-storefront-api as its indexing search engine to provide a seamless user experience. Although the overall integration and the produced data structure looks complex there are only a few catalog data stores that need to be indexed on Elasticsearch. Also, since this is an ecommerce integration, you can expect that only products and product-related data are going to be available in such an integration.&lt;/p&gt;
&lt;p&gt;In the Vue Storefront and Vue Storefront API app home directories (&lt;code&gt;vue-storefront/&lt;/code&gt; and &lt;code&gt;vue-storefront-api/&lt;/code&gt;) I ran &lt;code&gt;npm start&lt;/code&gt; to start the Node.js apps, then navigated to the Vue Storefront homepage to view its default template:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2020/08/vue-storefront-magento-integration/storefront_temphost_net.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Figure 2&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;There is a hamburger menu in the top-left which opens the categories menu of Vue Storefront:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2020/08/vue-storefront-magento-integration/storefronttemphostnet_cats_menu.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Figure 3&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;As I mentioned, the data being viewed is relatively simple according to the count of the catalog types (essentially Products, Categories, Attributes, and Tax Rules) which are already categorized and these are a bunch of specific sets of data.&lt;/p&gt;
&lt;p&gt;By searching through the categories Women &amp;gt; Tops &amp;gt; Jackets, the search output list is shown by Vue Storefront:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2020/08/vue-storefront-magento-integration/storefronttemphostnet_cats_search.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Figure 4&lt;/em&gt;&lt;/p&gt;
&lt;h3 id=&#34;designing-a-new-approach-for-interchange&#34;&gt;Designing a New Approach for Interchange&lt;/h3&gt;
&lt;p&gt;When exploring the features and components of Vue Storefront we discussed how we can adapt Interchange to use Vue Storefront. We ended up with two main options:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Keep the same approach of Vue Storefront’s current integration with Magento and other ecommerce backends using Elasticsearch and vue-storefront-api as the backend gateways&lt;/li&gt;
&lt;li&gt;Remove the Elasticsearch and vue-storefront-api dependencies and create a new API in Interchange producing similar API outputs to the ones vue-storefront-api &amp;amp; Elasticsearch are creating. I also found a similar approach where they integrated SAP Hybris with Vue Storefront. I thought that this way would fit our intention.&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id=&#34;inspired-by-a-similar-approach-of-vue-storefront--sap-commerce&#34;&gt;Inspired By A Similar Approach of Vue Storefront + SAP Commerce&lt;/h4&gt;
&lt;p&gt;&lt;a href=&#34;https://hybrismart.com/2019/02/13/vue-storefront-sap-commerce-open-source-pwa-storefront-integration-demo/&#34;&gt;Vue Storefront + SAP Commerce: Open-source PWA Storefront Integration (+DEMO)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This article discusses several approaches, including &lt;a href=&#34;https://github.com/DivanteLtd/storefront-integration-sdk&#34;&gt;Vue Storefront developers’ boilerplate integration&lt;/a&gt; and their custom solutions. In the following table they show different options and comments on their resolutions.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2020/08/vue-storefront-magento-integration/t1-1.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Figure 5&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;As you can see in Figure 5, they decided to go with the 4th option as their most suitable and least problematic solution. Here is their statement of why:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;After some analysis, we have come to believe that using custom middleware only for a bunch of simple requests is an additional burden and thick layer of complexity. We decided to get rid of it as well.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;So, option #4 turned out to be the best choice. For the option #4, we need to parse Magento and Elasticsearch APIs and generate the compatible responses.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Here is how their solution looks according to option #4:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2020/08/vue-storefront-magento-integration/p2.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Figure 6&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Using this approach, they decided to remove the Elasticsearch and Vue Storefront API components from the Vue Storefront boilerplate integration and developed their SAP Commerce custom Vue Storefront API to produce responses similar to the responses taken from Elasticsearch through Vue Storefront API to pretend that Vue Storefront is taking its responses from Elasticsearch API + Magento API.&lt;/p&gt;
&lt;p&gt;We decided to discard Elasticsearch when integrating Interchange with Vue Storefront for simplicity, and to cut down on the cost such additional search solutions create.&lt;/p&gt;
&lt;p&gt;After our discussions, I was inspired by this SAP Commerce + Vue Storefront integration demo. We finally decided to move forward with creating an Interchange API to produce the similar responses to those from the Elasticsearch &amp;amp; Magento APIs, so we could make the Interchange API pretend to be the Elasticsearch API + Magento API.&lt;/p&gt;
&lt;p&gt;We need to mention the Magento 2 API, or at least the Magento 2 data structure, because the Vue Storefront API and Vue Storefront boilerplate code is heavily designed to be taking ecommerce data (Products, Categories, Product Attributes, Tax Rules etc.) almost identical to the Magento API’s provided data.&lt;/p&gt;
&lt;p&gt;So our Interchange + Vue Storefront integration looks like this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2020/08/vue-storefront-magento-integration/Vsinterchange_arch.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Figure 7&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Compare Figure 1 with Figure 7 to see the difference between this approach and Vue Storefront’s suggested solution.&lt;/p&gt;
&lt;h4 id=&#34;interchange-api-prototype-design&#34;&gt;Interchange API Prototype Design&lt;/h4&gt;
&lt;p&gt;In order to achieve the API prototyping for our Interchange/​Vue Storefront integration, I cloned vue-storefront and vue-storefront-api, which I had previously integrated with Magento 2.3, into a new user, keeping all settings the same. This cloned Vue Storefront application started initially with the same configuration as vue-storefront-api and Elasticsearch were not excluded yet. So I decided to replace the vue-storefront-api endpoints that vue-storefront is calling with endpoints from the new Interchange API.&lt;/p&gt;
&lt;p&gt;As the default SAP Commerce/​Vue Storefront integration suggests, I walked through the outputs of search results in “product”, “category”, and “attributes”. Product data JSON output has some universal attributes like name or title, description, or price, but it also has some custom product attributes adapted from the Magento 2 API (specifically, the data structure of Magento is applied).&lt;/p&gt;
&lt;p&gt;At this point I started questioning if Vue Storefront is not really platform agnostic, or if it is, but with some headaches. The next section will detail these headaches but to summarize, adapting the data structure of new ecommerce backends to Vue Storefront is a real pain. It forces you to use its data structure since that’s a part of core development, and they rely mainly on Elasticsearch, and I guess because of that the developers of Vue Storefront believe that Elasticsearch is an essential part of this “agnostic” approach. Refer to &lt;a href=&#34;https://docs.vuestorefront.io/guide/data/elastic-queries.html#getting-data-from-elasticsearch&#34;&gt;Getting data from Elasticsearch&lt;/a&gt; for custom implementations using the &lt;a href=&#34;https://github.com/vuestorefront/vue-storefront/tree/master/core/lib&#34;&gt;core Elasticsearch lib&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Anyway, I moved on and produced product and product categories outputs that Vue Storefront expects as the search results at the homepage and the search box output results.&lt;/p&gt;
&lt;p&gt;Documentation is another pain. This is discussed further in the next section, but briefly, it’s hard to say that you could clearly pick the data structure that all kind of output results from the documentation. You need to dig into the TypeScript code, and the exact search output result is what is actually being expected.&lt;/p&gt;
&lt;p&gt;One conflict was that the identifiers are required to be numeric, not strings. If your ecommerce data structure doesn’t provide its identifiers as numbers you’ll need to alter and add a new numeric unique identifier along with the existing one, which is what I did.&lt;/p&gt;
&lt;p&gt;Product attributes also need to be adapted to Vue Storefront (really to Magento’s data structure). The attribute names, attribute lists, attributes’ assigned values, and categories attached to the products and configurable child products all come along with the product list on a search output. If your ecommerce data is not structured similarly to Magento’s data structure, you need to dig and restructure even for minimal changes like product attributes and configurable child products. For example, if your data structure doesn’t allow configurable child products then you can probably only provide each option (such as product color or size options) as a new product, which will be a real pain.&lt;/p&gt;
&lt;p&gt;Most of Vue Storefront’s app endpoints are defined in &lt;code&gt;config/default.json&lt;/code&gt; which is the default config on a new install. On the other hand, the ecommerce catalog data (which is indexed in Elasticsearch) is available through the Elasticsearch implementation which is by default defined in &lt;code&gt;lib/search/adapter/api/searchAdapter.ts&lt;/code&gt;. In my case I needed to alter this implementation, which is generating the Elasticsearch (actually the Vue Storefront API) endpoints dynamically by looking up the related catalog index (product, category, attributes, taxes, etc.), and change the Elasticsearch definition in the Vue Storefront config file to the new Interchange API, adapted to emulate the Vue Storefront API and Elasticsearch:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a61717;background-color:#e3d2d2&#34;&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;elasticsearch&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#a61717;background-color:#e3d2d2&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;httpAuth&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;host&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;/api/catalog&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;index&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;vue_storefront_catalog&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a61717;background-color:#e3d2d2&#34;&gt;...&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;I needed to set the Interchange API URL to the host property and not necessarily set an API-defining URL sub-path name to the index property above. This way our API would pretend to be the Elasticsearch endpoint as long as it can generate the JSON output that Vue Storefront expects.&lt;/p&gt;
&lt;p&gt;These configuration changes generally satisfied Vue Storefront’s expectations.&lt;/p&gt;
&lt;h3 id=&#34;common-and-exceptional-issues&#34;&gt;Common and Exceptional Issues&lt;/h3&gt;
&lt;p&gt;Many concerns and challenges are discussed in &lt;a href=&#34;https://hybrismart.com/2019/02/13/vue-storefront-sap-commerce-open-source-pwa-storefront-integration-demo/#h.4jhhd7ba1u4i&#34;&gt;this article on Vue Storefront + SAP Commerce integration&lt;/a&gt; which I mentioned earlier.&lt;/p&gt;
&lt;p&gt;A review of some issues I detailed earlier:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Required numeric unique IDs for all catalogs (products, categories, etc.)&lt;/li&gt;
&lt;li&gt;Poor documentation&lt;/li&gt;
&lt;li&gt;Catalog data structure is managed on the Vue Storefront side, heavily relying on its own data types and models. Your backend may need to be adapted to meet Vue Storefront’s expectations, even if you are using Elasticsearch.&lt;/li&gt;
&lt;li&gt;A structure which is constantly being reorganized, creating a big maintenance concern.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;Despite my concerns, I thought we could still implement our Interchange API with a front-end framework, but I hadn’t asked the question: Is it reusable enough to mantain? Each of our clients would have different UI and UX expectations. One UI template model could never be enough, which would mean continuous front-end template design and enough maintenance to match the operational cost of existing Interchange front-end solutions. Adapting existing data to Vue Storefront’s new data model would bring more costs along with it.&lt;/p&gt;
&lt;p&gt;While Vue Storefront’s offline working model is well designed, for the purpose of integrating with a non-Magento backend, this is overshadowed by its many issues. You would be better off looking elsewhere for a more modular, truly platform-agnostic, easier-maintained, component- and data model-independent front-end.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Magento 2: Creating a custom theme</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2020/06/magento-2-creating-a-custom-theme/"/>
      <id>https://www.endpointdev.com/blog/2020/06/magento-2-creating-a-custom-theme/</id>
      <published>2020-06-24T00:00:00+00:00</published>
      <author>
        <name>Juan Pablo Ventoso</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;img src=&#34;/blog/2020/06/magento-2-creating-a-custom-theme/paint-orange-blue.jpg&#34; alt=&#34;blue and yellow paint from a tube on a canvas&#34;&gt;
&lt;a href=&#34;https://flic.kr/p/Se3vkA&#34;&gt;Photo&lt;/a&gt; by &lt;a href=&#34;https://www.flickr.com/photos/mariaeklind/&#34;&gt;Maria Eklind&lt;/a&gt;, &lt;a href=&#34;https://creativecommons.org/licenses/by-sa/2.0/&#34;&gt;CC BY-SA 2.0&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;In my previous post, we went through the steps needed to &lt;a href=&#34;/blog/2020/04/magento-2-creating-a-custom-module/&#34;&gt;create a custom module in Magento 2&lt;/a&gt;. While modules consist of a set of classes to add new features to Magento, a theme controls how these features, and the entire website in general, will be displayed to the user. As stated in the &lt;a href=&#34;https://devdocs.magento.com/guides/v2.3/frontend-dev-guide/themes/theme-overview.html&#34;&gt;Magento guide&lt;/a&gt;, a theme uses a combination of custom templates, layouts, styles, and images to provide a consistent look and feel across a Magento store.&lt;/p&gt;
&lt;h3 id=&#34;creating-a-new-magento-2-theme&#34;&gt;Creating a new Magento 2 theme&lt;/h3&gt;
&lt;p&gt;We can create a theme based on a default “parent” theme or create a standalone theme from scratch. In most cases, I would recommend the first option. For this example, we will use &lt;a href=&#34;https://magento2-demo.magebit.com/&#34;&gt;Luma&lt;/a&gt; as our parent theme. The other option would be inheriting from the default “blank” theme.&lt;/p&gt;
&lt;p&gt;Here’s an initial task list to get our new theme ready:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Create a new directory for the theme&lt;/li&gt;
&lt;li&gt;Create the &lt;code&gt;registration.php&lt;/code&gt; script&lt;/li&gt;
&lt;li&gt;Create the &lt;code&gt;theme.xml&lt;/code&gt; information file&lt;/li&gt;
&lt;li&gt;Activate the new theme&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;creating-a-new-directory-for-the-theme&#34;&gt;Creating a new directory for the theme&lt;/h4&gt;
&lt;p&gt;While all our backend code should go in &lt;code&gt;app/code&lt;/code&gt;, the frontend content is expected to go in &lt;code&gt;app/design&lt;/code&gt;. And as our theme will only apply design changes to the frontend content, we should create the new directory for it under the path &lt;code&gt;app/design/frontend&lt;/code&gt;. If we want to create a theme for the admin area instead, we need to create the directory inside &lt;code&gt;app/design/adminhtml&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Let’s create a directory named &lt;code&gt;EndPoint&lt;/code&gt; (our vendor name, continuing with the example from our previous article) and a subdirectory inside it, &lt;code&gt;MyTheme&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#038&#34;&gt;cd&lt;/span&gt; {website_root}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mkdir -p app/design/frontend/EndPoint/MyTheme&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4 id=&#34;creating-registrationphp&#34;&gt;Creating registration.php&lt;/h4&gt;
&lt;p&gt;Similar to the file we created for our module, &lt;code&gt;registration.php&lt;/code&gt; tells Magento to register the new theme with the name and location we specify. Our file will be located at &lt;code&gt;app/design/frontend/EndPoint/MyTheme/registration.php&lt;/code&gt; and should have the following content:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;?php
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;\Magento\Framework\Component\ComponentRegistrar::&lt;span style=&#34;color:#369&#34;&gt;register&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    \Magento\Framework\Component\ComponentRegistrar::&lt;span style=&#34;color:#369&#34;&gt;THEME&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;frontend/EndPoint/MyTheme&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;__DIR__&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This way, Magento will know what path our theme will have.&lt;/p&gt;
&lt;h4 id=&#34;creating-themexml&#34;&gt;Creating theme.xml&lt;/h4&gt;
&lt;p&gt;The next step is to create our theme information file, where we will specify the theme name and parent theme. So our &lt;code&gt;app/design/frontend/EndPoint/MyTheme/theme.xml&lt;/code&gt; file should have the following content:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#c00;font-weight:bold&#34;&gt;&amp;lt;?xml version=&amp;#34;1.0&amp;#34;?&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;theme&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;xmlns:xsi=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;http://www.w3.org/2001/XMLSchema-instance&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;xsi:noNamespaceSchemaLocation=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;urn:magento:framework:Config/etc/theme.xsd&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;title&amp;gt;&lt;/span&gt;MyTheme&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;parent&amp;gt;&lt;/span&gt;Magento/luma&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/parent&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/theme&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;em&gt;Optional&lt;/em&gt;: If we want our theme to be easily distributed as a package, we can also create a &lt;code&gt;composer.json&lt;/code&gt; file in the theme’s root directory. The content for the file should be as follows, specifying the theme’s description, dependencies, version, and license types:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;endpoint/mytheme&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;description&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;My Theme by End Point&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;require&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;magento/theme-frontend-luma&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;100.0.*&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;magento/framework&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;100.0.*&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;type&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;magento2-theme&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;version&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;100.0.1&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;license&amp;#34;&lt;/span&gt;: [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;OSL-3.0&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;AFL-3.0&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;autoload&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;files&amp;#34;&lt;/span&gt;: [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;registration.php&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4 id=&#34;activating-our-new-theme&#34;&gt;Activating our new theme&lt;/h4&gt;
&lt;p&gt;That was easy! We have everything we need to activate our new theme. To do that we log in to our admin area and enable our theme. Once in the dashboard, we need to go to Content &amp;gt; Design &amp;gt; Configuration, edit our store view, and select our new theme from the dropdown list:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;magento-2-creating-a-custom-theme/magento-admin-select-theme.jpg&#34; alt=&#34;Selecting our theme&#34;&gt;&lt;/p&gt;
&lt;p&gt;Magento will search for new themes every time we log in to the admin area, so our new theme will appear on the list automatically.&lt;/p&gt;
&lt;h3 id=&#34;adding-custom-content&#34;&gt;Adding custom content&lt;/h3&gt;
&lt;p&gt;We have the basic structure for our theme, but when enabled, it will look the same as its parent theme (Luma, in this case), since we didn’t add any design rules or static files yet. Let’s do some more things with our theme to change how it’s displayed in the frontend:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Create a custom &lt;code&gt;etc/view.xml&lt;/code&gt; file&lt;/li&gt;
&lt;li&gt;Add a custom logo&lt;/li&gt;
&lt;li&gt;Add static files (JavaScript, CSS, images, fonts)&lt;/li&gt;
&lt;li&gt;Add a custom layout&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;creating-a-custom-view-file&#34;&gt;Creating a custom view file&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;etc/view.xml&lt;/code&gt; controls many frontend configurations like the product thumbnail width, how the product image gallery is displayed, and the image magnifier tool, among other things. To add our custom view file to our theme, we need to copy the existing file from our parent theme. For Luma, it will be located at &lt;code&gt;vendor/magento/theme-frontend-luma/etc/view.xml&lt;/code&gt;. To copy the file, we need to run the following in our website’s root:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mkdir -p app/design/frontend/EndPoint/MyTheme/etc
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cp vendor/magento/theme-frontend-blank/etc/view.xml app/design/frontend/EndPoint/MyTheme/etc/view.xml&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And then we can use our preferred text editor to change the values we want, like setting a custom size for the images in the category page grid:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;image&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;id&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;category_page_grid&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;type&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;small_image&amp;#34;&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;width&lt;/span&gt;&amp;gt;300&amp;lt;/&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;width&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;height&lt;/span&gt;&amp;gt;300&amp;lt;/&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;height&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;/&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;image&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4 id=&#34;adding-a-custom-logo&#34;&gt;Adding a custom logo&lt;/h4&gt;
&lt;p&gt;Adding a logo to our theme is really simple. We just need to save our picture in SVG format as &lt;code&gt;web/images/logo.svg&lt;/code&gt;. If we want to use a different filename or format for our logo, we will have to create a default layout file for our theme in the path &lt;code&gt;/Magento_Theme/layout/default.xml&lt;/code&gt; inside our theme root with content similar to this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;page&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;xmlns:xsi=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;http://www.w3.org/2001/XMLSchema-instance&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;xsi:noNamespaceSchemaLocation=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;urn:magento:framework:View/Layout/etc/page_configuration.xsd&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;referenceBlock&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;logo&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;arguments&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;argument&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;logo_file&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;xsi:type=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;string&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;gt;&lt;/span&gt;images/custom_logo.png&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/argument&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;argument&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;logo_width&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;xsi:type=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;number&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;gt;&lt;/span&gt;300&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/argument&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;argument&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;logo_height&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;xsi:type=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;number&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;gt;&lt;/span&gt;200&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/argument&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;argument&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;logo_alt&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;xsi:type=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;string&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;gt;&lt;/span&gt;Custom logo name&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/argument&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/arguments&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/referenceBlock&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/page&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We can use different image formats such as SVG, PNG, or JPG. We can also use a custom width and height for the logo, and set a custom alternate text.&lt;/p&gt;
&lt;h4 id=&#34;adding-static-files-javascriptcssimagesfonts&#34;&gt;Adding static files (JavaScript/​CSS/​images/​fonts)&lt;/h4&gt;
&lt;p&gt;All the static files should be located inside the &lt;code&gt;web&lt;/code&gt; directory. Common static files include JavaScript files, stylesheets, images, and fonts. The JavaScript files should be located at &lt;code&gt;web/js&lt;/code&gt;, stylesheets at &lt;code&gt;web/css&lt;/code&gt;, images at &lt;code&gt;web/images&lt;/code&gt;, and our custom fonts should be located at &lt;code&gt;web/fonts&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;All the static files will be published as direct links, without any processing from Magento, at the &lt;code&gt;pub/static/frontend/EndPoint/MyTheme/en_US&lt;/code&gt; path. The default locale/​language is en_US; we can change it for our theme if needed.&lt;/p&gt;
&lt;h4 id=&#34;adding-a-custom-layout&#34;&gt;Adding a custom layout&lt;/h4&gt;
&lt;p&gt;Finally, if we want to use the new assets we added and have custom content on different sections of our website, we need to extend or override the existing layout files from our parent theme.&lt;/p&gt;
&lt;p&gt;For example, if we want to add a reference to new stylesheet and JavaScript files we added, we need to extend the existing header layout from our parent theme. To do this, we will create a new layout file located at &lt;code&gt;Magento_Theme/layout/default_head_blocks.xml&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;page&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;xmlns:xsi=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;http://www.w3.org/2001/XMLSchema-instance&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;xsi:noNamespaceSchemaLocation=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;urn:magento:framework:View/Layout/etc/page_configuration.xsd&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;&amp;lt;!-- Custom stylesheet --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;css&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;src=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;css/mytheme.css&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;&amp;lt;!-- Custom JavaScript --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;script&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;src=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;js/mytheme.js&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/page&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This way we will be adding a reference to a new stylesheet named &lt;code&gt;mytheme.css&lt;/code&gt; that we have inside the &lt;code&gt;web/css&lt;/code&gt; directory of our theme, and a new script named &lt;code&gt;mytheme.js&lt;/code&gt; inside the &lt;code&gt;web/js&lt;/code&gt; directory.&lt;/p&gt;
&lt;p&gt;After we make all the desired changes to our theme, we will need to tell Magento to update the frontend. We need to deploy the new changes and clear the cache. We can achieve that by running this from our website root:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;php bin/magento setup:static-content:deploy
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;php bin/magento cache:clean&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This process can take some minutes to complete. After it’s done, we can go to our website to see if the frontend changes are applied:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;magento-2-creating-a-custom-theme/magento-frontend.jpg&#34; alt=&#34;Website homepage&#34;&gt;&lt;/p&gt;
&lt;p&gt;Looks awesome! Of course, there’s a lot more we can do from there, from extending or overriding layouts from modules (Magento or third-party) to bundling scripts or using Less files for our custom styles. But, that is material for later posts, so that’s all for now! Please add any questions you might have below.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Magento 2: Creating a custom module</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2020/04/magento-2-creating-a-custom-module/"/>
      <id>https://www.endpointdev.com/blog/2020/04/magento-2-creating-a-custom-module/</id>
      <published>2020-04-01T00:00:00+00:00</published>
      <author>
        <name>Juan Pablo Ventoso</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;img src=&#34;/blog/2020/04/magento-2-creating-a-custom-module/bridge-wires.jpg&#34; alt=&#34;Bridge with wires&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://unsplash.com/photos/q4ZBGVzJskE&#34;&gt;Photo&lt;/a&gt; by &lt;a href=&#34;https://unsplash.com/@olajidetunde&#34;&gt;Babatunde Olajide&lt;/a&gt;, cropped from original&lt;/p&gt;
&lt;p&gt;A Magento module is a set of classes and routines that will depend on and interact with other Magento classes in order to add a specific feature to a Magento application. While a &lt;a href=&#34;https://devdocs.magento.com/guides/v2.3/frontend-dev-guide/themes/theme-overview.html&#34;&gt;theme&lt;/a&gt; is orientated towards the front-end and user experience, a &lt;a href=&#34;https://devdocs.magento.com/guides/v2.3/architecture/archi_perspectives/components/modules/mod_intro.html&#34;&gt;module&lt;/a&gt; is orientated towards backend logic and application flow.&lt;/p&gt;
&lt;p&gt;We will need to create a custom module if we want to add or change the existing logic at a level where Magento doesn’t provide a setting or option for it. For example, if our business has a specific feature or set of features or requirements that are not common to the market, a module can fill that gap for us.&lt;/p&gt;
&lt;h3 id=&#34;creating-a-basic-magento-2-module&#34;&gt;Creating a basic Magento 2 module&lt;/h3&gt;
&lt;p&gt;Creating a simple module in Magento 2 is not that hard. We will need to accomplish the following tasks:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Create a new directory for the module&lt;/li&gt;
&lt;li&gt;Create a &lt;code&gt;registration.php&lt;/code&gt; script&lt;/li&gt;
&lt;li&gt;Create a &lt;code&gt;etc/module.xml&lt;/code&gt; information file&lt;/li&gt;
&lt;li&gt;Install the new module&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;creating-a-new-directory-for-the-module&#34;&gt;Creating a new directory for the module&lt;/h4&gt;
&lt;p&gt;Where should the new directory for our module be placed? We have two options to choose from:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;app/code/{vendor}/&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;vendor/{vendor}/&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If your module is intended for a specific website you’re working on, you can use the first option. If you’re creating a module with the intention of it being used on several websites, it’s best to choose the second option. We’ll use the first for this example.&lt;/p&gt;
&lt;p&gt;Let’s create a directory named &lt;code&gt;EndPoint&lt;/code&gt; (our vendor name) with a subdirectory inside it, &lt;code&gt;MyModule&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#038&#34;&gt;cd&lt;/span&gt; {website_root}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mkdir -p app/code/EndPoint/MyModule&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4 id=&#34;creating-the-registrationphp-script&#34;&gt;Creating the registration.php script&lt;/h4&gt;
&lt;p&gt;The &lt;code&gt;registration.php&lt;/code&gt; file tells Magento to register the new module under a specific name and location. Let’s create a file named &lt;code&gt;app/code/EndPoint/MyModule/registration.php&lt;/code&gt; with the folllowing content:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;?php
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;\Magento\Framework\Component\ComponentRegistrar::&lt;span style=&#34;color:#369&#34;&gt;register&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    \Magento\Framework\Component\ComponentRegistrar::&lt;span style=&#34;color:#369&#34;&gt;MODULE&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;EndPoint_MyModule&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;__DIR__&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We’re telling Magento that our module will be named EndPoint_MyModule.&lt;/p&gt;
&lt;h4 id=&#34;creating-the-etcmodulexml-information-file&#34;&gt;Creating the etc/module.xml information file&lt;/h4&gt;
&lt;p&gt;Now, let’s create our module information file, where we’ll specify the module version number. First, we need to create the &lt;code&gt;etc&lt;/code&gt; directory inside &lt;code&gt;app/code/EndPoint/MyModule&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mkdir app/code/EndPoint/MyModule/etc&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;then create &lt;code&gt;module.xml&lt;/code&gt; with the following content:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#c00;font-weight:bold&#34;&gt;&amp;lt;?xml version=&amp;#34;1.0&amp;#34;?&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;config&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;xmlns:xsi=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;http://www.w3.org/2001/XMLSchema-instance&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;xsi:noNamespaceSchemaLocation=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;urn:magento:framework:Module/etc/module.xsd&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;module&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;EndPoint_MyModule&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;setup_version=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;1.0.0&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/module&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/config&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4 id=&#34;installing-the-new-module&#34;&gt;Installing the new module&lt;/h4&gt;
&lt;p&gt;That’s it! We have everything we need to install our new module. Now we need to tell Magento we want to install and enable our new module. So from our website root we need to run:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;php bin/magento setup:upgrade&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Magento will output a list of module names and configuration updates, and our new module &lt;code&gt;EndPoint_MyModule&lt;/code&gt; should be listed in that output.&lt;/p&gt;
&lt;h3 id=&#34;adding-a-custom-route-to-our-module&#34;&gt;Adding a custom route to our module&lt;/h3&gt;
&lt;p&gt;Now we have a working, enabled module, but it’s not doing anything yet! What’s a simple way to check that our module is enabled? Let’s set up a custom route, so if we hit a URL like &lt;code&gt;https://{our_website}/mymodule/test/helloworld&lt;/code&gt; we can return a custom response from a controller.&lt;/p&gt;
&lt;p&gt;Creating a custom route will need some steps on its own:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Create a new directory for the controller&lt;/li&gt;
&lt;li&gt;Create a &lt;code&gt;etc/routes.xml&lt;/code&gt; file&lt;/li&gt;
&lt;li&gt;Create the controller&lt;/li&gt;
&lt;li&gt;Upgrade the new module&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;creating-a-new-directory-for-the-controller&#34;&gt;Creating a new directory for the controller&lt;/h4&gt;
&lt;p&gt;First we need to create a new directory where the new PHP controller for our custom route will live. The new directory path should be:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;app/code/EndPoint/MyModule/Controller&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;We can create as many directory levels we want, depending on our desired path. For example, if we create a class named &lt;code&gt;Index&lt;/code&gt; in &lt;code&gt;app/code/EndPoint/MyModule/Controller&lt;/code&gt;, the URL that will be routed to this controller will be &lt;code&gt;https://{our_website}/mymodule/index&lt;/code&gt; (the “Controller” directory is ignored). If we create a class named &lt;code&gt;HelloWorld&lt;/code&gt; in &lt;code&gt;app/code/EndPoint/MyModule/Controller/Test&lt;/code&gt;, the resulting URL will be &lt;code&gt;https://{our_website}/mymodule/test/helloworld&lt;/code&gt;.&lt;/p&gt;
&lt;h4 id=&#34;creating-the-etcroutesxml-file&#34;&gt;Creating the etc/routes.xml file&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;routes.xml&lt;/code&gt; will tell Magento what base URL will be used for our module. First, we need to create the “frontend” directory where the routes.xml file needs to be placed:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mkdir app/code/EndPoint/MyModule/etc/frontend&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In this example, we want the base URL to be &lt;code&gt;MyModule&lt;/code&gt;, so we need to create an XML file inside the new directory that will route all requests made to the given URL to our module controllers:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#c00;font-weight:bold&#34;&gt;&amp;lt;?xml version=&amp;#34;1.0&amp;#34; ?&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;config&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;xmlns:xsi=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;http://www.w3.org/2001/XMLSchema-instance&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;xsi:noNamespaceSchemaLocation=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;urn:magento:framework:App/etc/routes.xsd&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;router&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;id=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;standard&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;route&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;frontName=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;mymodule&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;id=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;mymodule&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;module&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;EndPoint_MyModule&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/route&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/router&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/config&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4 id=&#34;creating-the-controller&#34;&gt;Creating the controller&lt;/h4&gt;
&lt;p&gt;If we want to respond to requests for &lt;code&gt;https://{our_website}/mymodule/test/helloworld&lt;/code&gt; we first need to create the base &lt;code&gt;Controller&lt;/code&gt; directory and the &lt;code&gt;Test&lt;/code&gt; subdirectory:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mkdir -p app/code/EndPoint/MyModule/Controller/Test&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Under this directory, we’ll create our custom Magento controller. All route controllers should extend &lt;code&gt;\Magento\Framework\App\Action\Action&lt;/code&gt;. We also need to have a public &lt;code&gt;construct()&lt;/code&gt; method to pass the context to our ancestor and an &lt;code&gt;execute()&lt;/code&gt; function that will be called when the URL is hit:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;?php
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;namespace&lt;/span&gt; EndPoint\MyModule\Controller\Test;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;HelloWorld&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;extends&lt;/span&gt; \Magento\Framework\App\Action\Action
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;__construct&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        \Magento\Framework\App\Action\Context &lt;span style=&#34;color:#369&#34;&gt;$context&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;parent&lt;/span&gt;::&lt;span style=&#34;color:#369&#34;&gt;__construct&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#369&#34;&gt;$context&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        );
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;execute&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;echo&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Hello world!&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4 id=&#34;upgrading-the-new-module&#34;&gt;Upgrading the new module&lt;/h4&gt;
&lt;p&gt;We have everything in place to tell Magento we have new changes to be deployed. How we do that? First, we need to upgrade our Magento setup. But since we added a new controller that gets parameters from the dependency injector in the construct, we also need to compile the dependency injection engine (including factories, proxies, and interceptors). And finally, we need to clear the cache so new content will be served from our custom URL:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;php bin/magento setup:upgrade
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;php bin/magento setup:di:compile
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;php bin/magento cache:flush&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This process can take a few minutes to complete, but after it’s done we can try to reach our new custom URL. If we get a response like the one below:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2020/04/magento-2-creating-a-custom-module/magento-hello-world-response.jpg&#34; alt=&#34;Hello world!&#34;&gt;&lt;/p&gt;
&lt;p&gt;That means our module is working!&lt;/p&gt;
&lt;p&gt;That’s all for now. In upcoming posts, we’ll start complicating things a bit by overriding Magento classes with our custom ones and creating custom controllers that will return information from the Magento core classes. We will also explore how to customize the front-end by creating a theme. Don’t forget to add any questions, suggestions, or issues in the comments below!&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Ecommerce sales tax primer</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2020/01/ecommerce-sales-tax-primer/"/>
      <id>https://www.endpointdev.com/blog/2020/01/ecommerce-sales-tax-primer/</id>
      <published>2020-01-13T00:00:00+00:00</published>
      <author>
        <name>Elizabeth Garrett Christensen</name>
      </author>
      <content type="html">
        &lt;p&gt;Co-authored by &lt;a href=&#34;/team/greg-hanson/&#34;&gt;Greg Hanson&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2020/01/ecommerce-sales-tax-primer/image-0.jpg&#34; alt=&#34;Roman tax burning&#34;&gt;
&lt;a href=&#34;https://www.flickr.com/photos/internetarchivebookimages/14591980537&#34;&gt;Source image&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Tax collection is one of the topics &lt;em&gt;du jour&lt;/em&gt; for those of us in the ecommerce industry. Since state and local authorities are now able to levy taxes on ecommerce goods, taxation for online stores has become quite complicated. The purpose of this post is to give you some next steps and ideas on implementation if you’re new to the topic and need to know how to get started on tax collection for your ecommerce business.&lt;/p&gt;
&lt;p&gt;Current ecommerce sales tax policy stems from the 2018 U.S. Supreme Court decision South Dakota v. Wayfair, Inc. Since that decision, favoring South Dakota, 30 states have enacted legislation to require ecommerce stores to pay sales tax if they fit the definition of having an ‘economic nexus’, that is, they do enough business in the state to be worth taxing.&lt;/p&gt;
&lt;h3 id=&#34;talk-to-your-tax-accountant&#34;&gt;Talk to your Tax Accountant&lt;/h3&gt;
&lt;p&gt;So the first and most important note is to get your own legal counsel in regards to your taxes. There are many rules and things are changing every month with local and state authorities, so you’ll need reliable counsel on the topic.&lt;/p&gt;
&lt;p&gt;If you’re looking for someone to help, make sure this person has:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Knowledge about product variants. For example, clothing may not be taxed in some areas.&lt;/li&gt;
&lt;li&gt;Familiarity with tax policy in the entire country, and not just one local retail area.&lt;/li&gt;
&lt;li&gt;The ability to help you determine in which states you have a tax ‘nexus’ and need to pay sales tax. For many small businesses, you might only do enough business to pay sales tax in your home state and a few large ones like California or New York.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;research-software&#34;&gt;Research Software&lt;/h3&gt;
&lt;p&gt;Luckily for anyone starting to collect sales tax, there are some very good SaaS (software as a service) systems out there to make tax collection, reporting, and compliance easy. These software systems typically integrate with your ecommerce store by providing the store with the correct tax amount for the sale and collecting information for you on your reports and filing information to get the taxes paid to the correct authorities. Some systems might even file things for you.&lt;/p&gt;
&lt;p&gt;After you’ve worked with your tax accountant on what you need, start looking at the available software. The two main companies End Point has worked with are &lt;a href=&#34;https://www.taxjar.com/&#34;&gt;TaxJar&lt;/a&gt; and &lt;a href=&#34;https://www.avalara.com/us/en/index.html&#34;&gt;Avalara&lt;/a&gt;, though there are a number of other vendors in the market.&lt;/p&gt;
&lt;img src=&#34;/blog/2020/01/ecommerce-sales-tax-primer/image-1.svg&#34; alt=&#34;TaxJar&#34; width=&#34;48%&#34; style=&#34;padding-right: 2%&#34; /&gt;
&lt;img src=&#34;/blog/2020/01/ecommerce-sales-tax-primer/image-2.svg&#34; alt=&#34;Avalara&#34; width=&#34;48%&#34; style=&#34;padding-left: 2%&#34; /&gt;
&lt;h4 id=&#34;why-use-an-automated-tax-solution&#34;&gt;Why use an automated tax solution?&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;They automatically update tax rates as they change with local and state regulation.&lt;/li&gt;
&lt;li&gt;They can integrate into your checkout processes via API or plugin to automatically calculate the correct tax for the right location and product.&lt;/li&gt;
&lt;li&gt;They can have product-specific taxes, for things that might not be taxed like food and clothing in certain states.&lt;/li&gt;
&lt;li&gt;They give you end-of-year reports and help with your payments.&lt;/li&gt;
&lt;li&gt;You can customize settings, in case there are some states where you have a ‘nexus’ (are subject to sales tax) and other places where sales tax payments are not required.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;what-to-look-for-in-automated-tax-software&#34;&gt;What to look for in automated tax software:&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Choose a reputable company with large brand presence. You don’t want to do anything experimental and unreliable here.&lt;/li&gt;
&lt;li&gt;Pick something with documentation you can understand. You don’t want working with your tax software to be a pain, and you might need to refer to their documentation when configuring it or changing things in the future. Make sure that company speaks your language and you can easily get answers to your questions.&lt;/li&gt;
&lt;li&gt;This company should be able to guide you through all aspects of tax collections, filing and payment. You will legally be liable for taxes on all sales in states where you have nexus, &lt;em&gt;whether you collect the taxes or not&lt;/em&gt;! So make sure the company you select can work with you not only to set up the system, but on an ongoing basis providing support in filing reports and making payments.&lt;/li&gt;
&lt;li&gt;Integration with your platform is a key component of what you choose. Both Avalara and TaxJar have existing plugins for sites running on WordPress, Shopify, BigCommerce, Magento, and others. Keep in mind though that the integration might be different depending on your platform. If you’re on a custom platform, talk to your development team about integrations; they can read the docs and give you an estimate and recommendation for ease of implementation (that’s where we come in for many clients). For Interchange stores, we have integration code for both TaxJar and Avilara to leverage.&lt;/li&gt;
&lt;li&gt;Consider how your inventory or ERP system might be affected. Many of our ecommerce clients sell in-house or over the phone. Consider how your other systems might need to tie into this new tax system.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;implementation&#34;&gt;Implementation&lt;/h3&gt;
&lt;p&gt;So you’ve done the hard part, right? You sorted out what states you need to be compliant with and picked a software solution. Now all you need to do is get it working. This is really where your software consultant, such as us at End Point, would come in and get you to the finish line. The steps to implementation are:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Set up your account and pay for your tax solution software.&lt;/li&gt;
&lt;li&gt;Work with the tax solution provider to set up any required bank accounts or payment channels.&lt;/li&gt;
&lt;li&gt;Configure your settings.&lt;/li&gt;
&lt;li&gt;Share the API key and information with your developer.&lt;/li&gt;
&lt;li&gt;Test the implementation. I recommend you do this with several orders in different scenarios: products that do and don’t have sales tax, locations that do and don’t have tax, locations that tax shipping, etc. Test all the variants you know about.&lt;/li&gt;
&lt;li&gt;Go live with your tax solution on your site.&lt;/li&gt;
&lt;li&gt;Make sure to check back for your reports and filings for later in the year.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Need help picking a system or looking at implementation? &lt;a href=&#34;/contact/&#34;&gt;Call us today&lt;/a&gt; and we can help.&lt;/p&gt;
&lt;h3 id=&#34;other-resources&#34;&gt;Other resources&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://www.salestaxinstitute.com/sales_tax_faqs/wayfair-economic-nexus&#34;&gt;https://www.salestaxinstitute.com/sales_tax_faqs/wayfair-economic-nexus&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.thebalancesmb.com/how-to-collect-report-and-pay-state-sales-taxes-399043&#34;&gt;https://www.thebalancesmb.com/how-to-collect-report-and-pay-state-sales-taxes-399043&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.bigcommerce.com/blog/ecommerce-sales-tax/&#34;&gt;https://www.bigcommerce.com/blog/ecommerce-sales-tax/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.taxjar.com/guides/intro-to-sales-tax/&#34;&gt;https://www.taxjar.com/guides/intro-to-sales-tax/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

      </content>
    </entry>
  
    <entry>
      <title>Running Magento 2 in Windows with XAMPP</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2019/03/running-magento-2-windows-xampp/"/>
      <id>https://www.endpointdev.com/blog/2019/03/running-magento-2-windows-xampp/</id>
      <published>2019-03-22T00:00:00+00:00</published>
      <author>
        <name>Juan Pablo Ventoso</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;img src=&#34;/blog/2019/03/running-magento-2-windows-xampp/e-commerce-safe.jpg&#34; alt=&#34;Ecommerce&#34; /&gt;&lt;br&gt;&lt;a href=&#34;https://burst.shopify.com/photos/computer-security-lock-and-payment?q=e-commerce&#34;&gt;Photo by Nicole De Khors&lt;/a&gt; · &lt;a href=&#34;https://burst.shopify.com/licenses/shopify-some-rights-reserved&#34;&gt;Burst, Some Rights Reserved&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Magento is an open source ecommerce platform, written in PHP and relying on MySQL/​MariaDB for persistence. According to &lt;a href=&#34;https://trends.builtwith.com/shop&#34;&gt;BuiltWith&lt;/a&gt;, Magento is the third most used platform in ecommerce websites. It began its life in 2008 with its first general release, and a major update (Magento 2) was released in 2015.&lt;/p&gt;
&lt;p&gt;And now, more than three years after, Magento 1 is slowly dying: There won’t be any more quality fixes or security updates from June 2020, and there won’t be extended support for fixes or new payment methods. So the obvious choice will be Magento 2 from now on.&lt;/p&gt;
&lt;p&gt;But is it fully tested yet? Is it stable enough? If we already have a website running with Magento 1, what should we do? Migrating to Magento 2 is not just hitting an “Update” button: Themes are incompatible, most extensions won’t work, and of course, there’s a big set of changes to get familiar with.&lt;/p&gt;
&lt;p&gt;So a good approach might be to get a clean Magento 2 version deployed locally, to look what we need to do to get our website updated and running, test the backend, find where the configuration sections are located, and so on. And many business users, and even some developers like myself, have Microsoft Windows installed on our computers.&lt;/p&gt;
&lt;h3 id=&#34;environment-setup&#34;&gt;Environment setup&lt;/h3&gt;
&lt;p&gt;The environment I used for this testing installation was Windows 10 Professional. As a first step, we’ll need to make sure that &lt;code&gt;localhost&lt;/code&gt; is published in our local hosts file:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Navigate to the folder &lt;code&gt;%SystemRoot%\system32\drivers\etc&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Backup the existing hosts file&lt;/li&gt;
&lt;li&gt;Open a text editor with administrator rights&lt;/li&gt;
&lt;li&gt;Open the hosts file&lt;/li&gt;
&lt;li&gt;Make sure the first line after the commented (#) lines is &lt;code&gt;127.0.0.1 localhost&lt;/code&gt; and the second is &lt;code&gt;::1 localhost&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Open a cmd window with administrator rights and run the command &lt;code&gt;ipconfig /flushdns&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Now we’re ready to install the environment needed to run Magento. I recommend using &lt;a href=&#34;https://www.apachefriends.org/&#34;&gt;XAMPP&lt;/a&gt;, a free Apache distribution for Windows that includes MariaDB, PHP, and Perl in a single package. Magento 2 currently runs with PHP 7.2 and it will not work with newer versions.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Install XAMPP 7.2 with default settings: &lt;a href=&#34;https://www.apachefriends.org/xampp-files/7.2.15/xampp-win32-7.2.15-0-VC15-installer.exe&#34;&gt;apachefriends.org/xampp-files/7.2.15/xampp-win32-7.2.15-0-VC15-installer.exe&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Download Magento 2. You will need to register on the website first: &lt;a href=&#34;https://magento.com/tech-resources/download&#34;&gt;magento.com/tech-resources/download&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Create a new “magento” subfolder (or whatever name you prefer) inside the htdocs folder in the XAMPP installation (usually &lt;code&gt;C:\xampp&lt;/code&gt;) and uncompress the Magento 2 archive there.&lt;/li&gt;
&lt;li&gt;Start the XAMPP Control Panel from the Windows start menu. In the “Apache” section, click the “Config” button and, on the menu that appears, select “PHP (php.ini)”. Remove the semicolon before the &lt;code&gt;extension=intl&lt;/code&gt;, &lt;code&gt;extension=soap&lt;/code&gt;, and &lt;code&gt;extension=xsl&lt;/code&gt; texts to enable the intl, soap and xsl extensions.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2019/03/running-magento-2-windows-xampp/xampp-control-panel.jpg&#34; /&gt;&lt;br&gt;&lt;small&gt;Screenshot of the XAMPP Control Panel with the PHP config menu displayed.&lt;/small&gt;&lt;/p&gt;
&lt;h3 id=&#34;mysql-and-magento-setup&#34;&gt;MySQL and Magento setup&lt;/h3&gt;
&lt;p&gt;We have all the files in place and the environment ready to start configuring the database and install Magento 2.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Start the Apache and MySQL services from the XAMPP Control Panel. Wait for the green status texts to appear.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create a “magento” (or whatever name you prefer) database in MySQL from phpMyAdmin, installed already on XAMPP: &lt;a href=&#34;http://localhost/phpmyadmin&#34;&gt;localhost/phpmyadmin&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Run the Magento 2 setup from &lt;a href=&#34;http://localhost/magento&#34;&gt;localhost/magento&lt;/a&gt; (replace the “magento” part of the URL with whatever name you have chosen to host Magento). If the setup program requires to do any additional configuration change, do it as instructed. Do a screenshot or save the final page contents for later use.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2019/03/running-magento-2-windows-xampp/magento-2-installation-success.jpg&#34; /&gt;&lt;br&gt;&lt;small&gt;Example of the page that will be displayed when the Magento 2 installation finishes.&lt;/small&gt;&lt;/p&gt;
&lt;h3 id=&#34;fixing-known-issues&#34;&gt;Fixing known issues&lt;/h3&gt;
&lt;p&gt;When we finish the installation process, we will have a Magento 2 instance running on our host. But we’re not ready yet! There are a couple known bugs with Magento 2 and XAMPP at the moment I’m writing this post:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;The Magento admin page may not work (it shows a blank page): Fix this issue by updating the “isPathInDirectories” function inside the &lt;code&gt;Validator.php&lt;/code&gt; file as instructed in &lt;a href=&#34;https://magento.stackexchange.com/questions/252188/magento-2-2-7-admin-panel-blank-page&#34;&gt;this article.&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;We might not be able to upload images (like a custom logo or product pictures): Fix this issue by updating the &lt;code&gt;design_config_form.xml&lt;/code&gt; file as shown &lt;a href=&#34;https://community.magento.com/t5/Magento-2-x-Technical-Issues/A-technical-problem-with-the-server-created-an-error-Try-again/m-p/115085#M7549&#34;&gt;here.&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;After we finish making these changes, we will need to &lt;b&gt;restart the Apache service&lt;/b&gt; from the XAMPP control panel. And that’s it! We should be ready to open the Magento front page, and login into the backend.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2019/03/running-magento-2-windows-xampp/magento-2-front-end.jpg&#34; /&gt;&lt;br&gt;&lt;small&gt;An empty home page with the default theme enabled in Magento 2.&lt;/small&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2019/03/running-magento-2-windows-xampp/magento-2-back-end.jpg&#34; /&gt;&lt;br&gt;&lt;small&gt;This is how the Magento 2 back-end dashboard looks like once we login.&lt;/small&gt;&lt;/p&gt;
&lt;h3 id=&#34;setting-up-the-store-a-roadmap&#34;&gt;Setting up the store: A roadmap&lt;/h3&gt;
&lt;p&gt;The purpose of this article is to get Magento 2 up and running on Windows computer, and that’s what I hope we’ve achieved so far. But just to point in a direction on what to do next, below is my usual roadmap to set up the store, add a few products and publish the catalog on the home page.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Change the default store name and logo from the section Content &amp;gt; Design &amp;gt; Configuration and choose “Edit” in the “Default store view”.&lt;/li&gt;
&lt;li&gt;Set up the default store configuration from the section Stores &amp;gt; Configuration.&lt;/li&gt;
&lt;li&gt;Create the product categories from the section Catalog &amp;gt; Categories. Set up a category name and its dependency from another category, or from the default master category.&lt;/li&gt;
&lt;li&gt;Create/edit the product attributes from the section Stores &amp;gt; Attributes (check the option “visible in storefront” to show the products in the product page). For example, custom attributes for book products would be Author, Publisher&amp;hellip;&lt;/li&gt;
&lt;li&gt;Add the attributes to the default attribute set from the section Stores &amp;gt; Attribute. This is required for the attributes to show up when adding a new product by default.&lt;/li&gt;
&lt;li&gt;Create a set of products from the section Catalog &amp;gt; Products. Set the custom variations for each product from the “Customizable options” section. Set up a short and long description, a custom SKU for each customizable option, and a tax mode.&lt;/li&gt;
&lt;li&gt;Add a widget to the homepage to show the product catalog from the section Content &amp;gt; Homepage &amp;gt; Edit &amp;gt; Show/Hide Editor &amp;gt; Add widget &amp;gt; Products list. Here we can also add any content we like, including HTML tags, images, and third-party content.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;It’s possible—​and pretty straightforward—​to run a Magento 2 instance in Windows with XAMPP. But this is just a kick-off, a simple overview: there’s a lot more to do if we want to get a full ecommerce website that is ready to scale up and support enterprise-​level traffic, like caching optimization, load balancing, and advanced server-​side monitoring.&lt;/p&gt;
&lt;p&gt;At End Point, we have professionals with a strong background in deploying powerful, reliable, fully responsive, fast, and SEO-​optimized ecommerce websites. We have experience in migrating Magento to newer versions, and even creating full Magento 2 websites from scratch. &lt;a href=&#34;/contact/&#34;&gt;Drop us a line if you want to hear more.&lt;/a&gt;&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Ecommerce Shakeups: Magento Acquisition and Etsy Rate Increases</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2018/06/ecommerce-shakeups/"/>
      <id>https://www.endpointdev.com/blog/2018/06/ecommerce-shakeups/</id>
      <published>2018-06-19T00:00:00+00:00</published>
      <author>
        <name>Steph Skardal</name>
      </author>
      <content type="html">
        &lt;img src=&#34;/blog/2018/06/ecommerce-shakeups/ecommerce-shakeups.jpg&#34; alt=&#34;Magento, Etsy&#34; /&gt;
&lt;p&gt;If you’ve been paying any attention to much in the ecommerce world, there have been a couple shakeups and transitions that could affect how you look at your ecommerce options these days.&lt;/p&gt;
&lt;h3 id=&#34;adobe-to-acquire-magento&#34;&gt;Adobe to Acquire Magento&lt;/h3&gt;
&lt;p&gt;A few weeks ago, &lt;a href=&#34;http://news.adobe.com/press-release/corporate/adobe-acquire-magento-commerce&#34;&gt;it was announced&lt;/a&gt; Adobe would acquire Magento in a large acquisition. We’ve seen Magento clients come and go. It used to be the case that the Magento Community version was free and open source, but lacking in features, and the Magento Enterprise version was not free and came with many more features but was closed source.&lt;/p&gt;
&lt;p&gt;But, times change, and admittedly I haven’t looked into the current Magento offerings until writing this post. The two current options for Magento are Magento Commerce Starter and Magento Commerce Pro, more details &lt;a href=&#34;https://web.archive.org/web/20180610090249/https://magento.com/trial/plans-comparison&#34;&gt;here&lt;/a&gt;. These plans are not for small potatoes, starting at $2k/mo. I can see how the cost of this is worth it in lieu of paying a full time developer, but this is not a good fit for small businesses just getting started.&lt;/p&gt;
&lt;p&gt;There at not many public details on the acquisition, other than bringing Magento to Adobe’s range of “physical and digital goods across a range of industries, including consumer packaged goods, retail, wholesale, manufacturing, and the public sector”. Only time will tell.&lt;/p&gt;
&lt;h3 id=&#34;etsy-hikes-rates&#34;&gt;Etsy Hikes Rates&lt;/h3&gt;
&lt;p&gt;I am personally connected to the craft industry by way of my own hobby, so I’ve heard rumblings of the changes going on within Etsy with a new CEO throughout the last year. They will be &lt;a href=&#34;https://craftindustryalliance.org/etsy-shuts-down-etsy-wholesale/&#34;&gt;shutting down Etsy wholesale&lt;/a&gt; as of July 31st, 2018, &lt;a href=&#34;https://craftindustryalliance.org/etsy-close-etsy-studio-etsy-manufacturing-focus-sales-growth-continues/&#34;&gt;closing Etsy Studio &amp;amp; Manufacturing&lt;/a&gt; later this year, and last week, they announced increasing transaction fees from 3.5% to 5% which will now also apply to shipping charges. With that money, they will be offering improved tools and marketing efforts. You can read the official announcement &lt;a href=&#34;https://www.etsy.com/seller-news/2018-june-update&#34;&gt;here&lt;/a&gt; and more Q&amp;amp;A from Etsy &lt;a href=&#34;https://www.etsy.com/teams/7722/discussions/discuss/18737155/&#34;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;There are so many overwhelming options when it comes to determining what the best ecommerce solution is for any size of ecommerce business, and whether or not now is the right time to move away from Etsy. There is no one size fits all approach, and the best solution is very dependent on the company size and sales, in-house personnel and their skillset, structural organization, and so much more.&lt;/p&gt;
&lt;p&gt;There are many competing SaaS options like &lt;a href=&#34;https://www.shopify.com/&#34;&gt;Shopify&lt;/a&gt;, &lt;a href=&#34;https://www.bigcommerce.com/&#34;&gt;Big Commerce&lt;/a&gt;, &lt;a href=&#34;https://www.bigcartel.com/&#34;&gt;Big Cartel&lt;/a&gt;, &lt;a href=&#34;https://www.squarespace.com/&#34;&gt;Squarespace&lt;/a&gt;. We’ve helped set up these software as a solution environments for many clients and they can be a great fit for business owners interested in offloading support and maintenance. In addition to SaaS options, &lt;a href=&#34;https://woocommerce.com/&#34;&gt;WooCommerce&lt;/a&gt; in WordPress is the most popular open-source [free] solution, although many plugins and themes for it are not free. There are plenty of other open source ecommerce options out there, running on a web framework/​language (Ruby on Rails, Django, MVC in PHP, etc.) of your choosing.&lt;/p&gt;
&lt;h3 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;Like I mentioned above, finding the right solution is very circumstantial. I tend to suggest starting with a low-investment starting point for ecommerce, in whatever shape or form that takes for you, and then incrementally moving up if that’s what you need.&lt;/p&gt;
&lt;p&gt;While Etsy might offer a nice low-investment starting point, more exposure and marketing tools, you ultimately aren’t building your own brand on your own domain. I have seen a handful of people in the craft industry keep their Etsy shop open for a specific audience but grow things on their own domain running on another SaaS platform.&lt;/p&gt;
&lt;p&gt;On the flip side, many of End Point clients have businesses that are further along and the more enterprisey versions or custom software development solutions make great sense for them, with or without in-house development staff.&lt;/p&gt;
&lt;p&gt;If you’re interested in finding out more about setting up a SaaS shop, moving away from Etsy, or understanding your options with Magento, &lt;a href=&#34;/contact/&#34;&gt;contact us&lt;/a&gt; to find out how we can support your business goals.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Reach customers and drive sales with MailChimp</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2016/08/reach-customers-and-drive-sales-with/"/>
      <id>https://www.endpointdev.com/blog/2016/08/reach-customers-and-drive-sales-with/</id>
      <published>2016-08-22T00:00:00+00:00</published>
      <author>
        <name>Josh Lavin</name>
      </author>
      <content type="html">
        &lt;p&gt;It’s a good idea for ecommerce stores to regularly contact their customers. This not only reminds customers that your business exists, but also allows the sharing of new products and resources that can enrich the lives of your customers and clients. One of the easiest ways to stay in touch is by using an email newsletter service, such as &lt;a href=&#34;http://mailchimp.com/&#34;&gt;MailChimp&lt;/a&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;img alt=&amp;quot;&amp;quot; border=&amp;quot;0&amp;quot; height=&amp;quot;200&amp;quot; src=&amp;quot;/blog/2016/08/reach-customers-and-drive-sales-with/image-0.png&amp;quot; title=&amp;quot;Freddie, the MailChimp mascot&amp;quot; width=&amp;quot;191&amp;quot;/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;MailChimp offers the regular suite of email newsletter services: lists, campaigns, and reports—​but in addition, they allow an ecommerce store to &lt;a href=&#34;http://mailchimp.com/connect-your-store/&#34;&gt;integrate sales data back into MailChimp&lt;/a&gt;. When you have &lt;strong&gt;detailed shopping statistics&lt;/strong&gt; for each subscriber, it opens new possibilities for customized marketing campaigns.&lt;/p&gt;
&lt;h3 id=&#34;endless-possibilities&#34;&gt;Endless possibilities&lt;/h3&gt;
&lt;p&gt;For example, imagine you have an email mailing list with 1,000 recipients. Instead of mailing the same generic newsletter to each subscriber, what if you could segment the list to identify your 100 best customers, and email them a special campaign?&lt;/p&gt;
&lt;p&gt;Additional ideas could include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Reach out to inactive subscribers, offering a coupon&lt;/li&gt;
&lt;li&gt;Invite your best customers to a secret sale&lt;/li&gt;
&lt;li&gt;Re-engage customers who placed items in their cart, but left without purchasing&lt;/li&gt;
&lt;li&gt;Offer complementary products to purchasers of &lt;em&gt;Product X&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;automatic-marketing&#34;&gt;Automatic marketing&lt;/h3&gt;
&lt;p&gt;Once your store has sales data for subscribers, and you’ve decided on the campaigns you want to run with this data, the next step is to &lt;strong&gt;automate the process&lt;/strong&gt;. This is where MailChimp’s &lt;a href=&#34;http://mailchimp.com/features/automation/&#34;&gt;Automation&lt;/a&gt; feature comes in. Spend some time up-front to craft the automated campaigns, then sit back and let MailChimp run them for you; day in, day out.&lt;/p&gt;
&lt;h3 id=&#34;steps-to-implement&#34;&gt;Steps to implement&lt;/h3&gt;
&lt;p&gt;There are several off-the-shelf &lt;a href=&#34;https://connect.mailchimp.com/collections/e-commerce&#34;&gt;integrations for ecommerce stores&lt;/a&gt;, including Magento and BigCommerce.&lt;/p&gt;
&lt;p&gt;Users of Perl and &lt;a href=&#34;/expertise/perl-interchange/&#34;&gt;Interchange&lt;/a&gt; can use our newly-released toolsets of the &lt;a href=&#34;http://p3rl.org/Mail::Chimp3&#34;&gt;Mail::Chimp3 CPAN module&lt;/a&gt; and the &lt;a href=&#34;https://github.com/jdigory/interchange-extras/tree/master/mailchimp&#34;&gt;integration for Interchange5&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;/contact/&#34;&gt;Contact us&lt;/a&gt; today for expert help integrating one of these solutions with your ecommerce store.&lt;/p&gt;
&lt;h3 id=&#34;go-beyond-the-simple-newsletter&#34;&gt;Go beyond the simple newsletter&lt;/h3&gt;
&lt;p&gt;Most businesses already have an email newsletter. Hopefully, you are sending regular email campaigns with it. This is a great first step. Going beyond this to segment your email list and reach out to these segments with relevant information to each of them, is the next step. Not only can this &lt;strong&gt;increase your sales&lt;/strong&gt;, but it also &lt;strong&gt;respects your clients’ and customers’ time and preferences&lt;/strong&gt;. It’s a win-win for all.&lt;/p&gt;
&lt;p&gt;Additional resource: &lt;a href=&#34;http://mailchimp.com/resources/guides/mailchimp-for-online-sellers/&#34;&gt;MailChimp for Online Sellers&lt;/a&gt;&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Sort product attribute options by the position property in Magento</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2016/01/sort-product-attribute-options-by/"/>
      <id>https://www.endpointdev.com/blog/2016/01/sort-product-attribute-options-by/</id>
      <published>2016-01-11T00:00:00+00:00</published>
      <author>
        <name>Piotr Hankiewicz</name>
      </author>
      <content type="html">
        &lt;h3 id=&#34;introduction&#34;&gt;Introduction&lt;/h3&gt;
&lt;div class=&#34;separator&#34; style=&#34;clear: both; text-align: center;&#34;&gt;&lt;a href=&#34;/blog/2016/01/sort-product-attribute-options-by/image-0.jpeg&#34; imageanchor=&#34;1&#34; style=&#34;clear: right; float: right; margin-bottom: 1em; margin-left: 1em;&#34;&gt;&lt;img border=&#34;0&#34; src=&#34;/blog/2016/01/sort-product-attribute-options-by/image-0.jpeg&#34;/&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Recently I was working with Magento 1.9.1, trying to get a list of dropdown attribute options sorted by the position property. However there is a known bug in Magento 1.9.1, where the position property is not respected.&lt;/p&gt;
&lt;p&gt;I looked for a patch to fix this issue, however there was no official patch, and none of the available community fixes were good enough. So again, I needed to fix it by myself.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tip!&lt;/strong&gt; If you know how to apply a patch file it is &lt;a href=&#34;https://gist.githubusercontent.com/peter-hank/4d33c4cad7efd8441c99/raw/377a8894f3766f045f523768fb4c237cdc6ed2ab/magento-1.9-patch-sorted-attribue-options&#34;&gt;here&lt;/a&gt;. If not, please continue.&lt;/p&gt;
&lt;h3 id=&#34;part-1&#34;&gt;Part 1&lt;/h3&gt;
&lt;p&gt;We need to overwrite some Magento core code unfortunately. The good thing is that there is a cool way of doing this in Magento so we don’t need to overwrite the files directly, we need to create a local copy.&lt;/p&gt;
&lt;p&gt;Copy app/code/Core/Mage/Catalog/Model/Resource/Product/Type/Configurable/Attribute/Collection.php file to app/code/local/Mage/Catalog/Model/Resource/Product/Type/Configurable/Attribute/Collection.php. You need to create the whole directory structure. If you use Unix system it is simple as: (running from Magento root)&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mkdir -p app/code/local/Mage/Catalog/Model/Resource/Product/Type/Configurable/Attribute/
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cp app/code/Core/Mage/Catalog/Model/Resource/Product/Type/Configurable/Attribute/Collection.php app/code/local/Mage/Catalog/Model/Resource/Product/Type/Configurable/Attribute/Collection.php&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id=&#34;part-2&#34;&gt;Part 2&lt;/h3&gt;
&lt;p&gt;Fill a local file with a content from this source: &lt;a href=&#34;https://gist.github.com/peter-hank/c917394ea9f1171ddeb8&#34;&gt;https://gist.github.com/peter-hank/c917394ea9f1171ddeb8&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&#34;notes&#34;&gt;Notes&lt;/h3&gt;
&lt;p&gt;After these changes it will work as expected and attribute options will be sorted by a position set.&lt;/p&gt;
&lt;p&gt;This fix should work for any Magento 1.9.* version.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Competing with the big players in e-commerce</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2011/06/competing-with-big-players-in-e/"/>
      <id>https://www.endpointdev.com/blog/2011/06/competing-with-big-players-in-e/</id>
      <published>2011-06-20T00:00:00+00:00</published>
      <author>
        <name>Ron Phipps</name>
      </author>
      <content type="html">
        &lt;p&gt;While attending the Internet Retailer Conference and Exhibition in San Diego last week I had a few moments to speak with Nathan Barling, the CTO for &lt;a href=&#34;https://www.shoebacca.com/&#34;&gt;Shoebacca.com&lt;/a&gt;. During our conversation he mentioned he would be speaking at the conference in the track titled: “Small Retailers: Winning Strategies in a tougher market”. I attended his talk and was impressed by the things that Shoebacca is doing to appear larger then they are, which helps them compete with the big players in their industry such as Zappos. The tactics that Nathan discussed can be applied to many industries in e-commerce and for all sizes of businesses, even those on limited budgets.&lt;/p&gt;
&lt;p&gt;One of the first things Nathan discussed was to make your policies clear and to highlight them on the site so that people are aware of the rules. Nathan recommends this, especially in the case of Shoebacca, where many of their policies encourage people to shop on their site, by reducing risk to the customer. Some of their policies include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Free ground shipping&lt;/li&gt;
&lt;li&gt;Free return shipping&lt;/li&gt;
&lt;li&gt;365 day return policy&lt;/li&gt;
&lt;li&gt;110% price match for 14 days&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Nathan mentioned many 3rd party tools that they leverage at their company, along with open source tools they are using to keep the costs down while providing a solid platform for their site. Some of the open source tools in use include Magento (e-commerce), Solr (search), and Gearman (jobs system).&lt;/p&gt;
&lt;p&gt;One area where Shoebacca is doing cool work is in their “shoe art”. These are clips and ads through the site which present the product in a unique and appealing way. They give the illusion that Shoebacca is a large company with an extensive art department and studio for shooting the product. They also leverage the art provided by their vendors such as ads and logos so that their customers associate the site with the quality of those vendors such as Nike and Puma. Another area where they are getting assistance from their vendors is with giveaway merchandise. This is merchandise the vendor provides them to giveaway with purchases of other items on the site. This could be a sticker, water bottle, or jacket, all promo items that Shoebacca does not have to pay for, but is of some value to the end customer and may push the customer to complete the order.&lt;/p&gt;
&lt;p&gt;One area that Nathan saw an immediate increase in conversions is with alternate payment methods. They chose to launch Amazon payments, and before they could even put a test order through someone had already checked out. Using alternate payment methods such as Amazon, Google or PayPal allows your customers to have the peace of mind that their credit card details are safe with a trusted company, while they are getting to know your company. Nathan said that many people on subsequent visits ended up entering their credit card directly into the site, after having a successful transaction through an alternate payment method.&lt;/p&gt;
&lt;p&gt;Shoebacca is also going to be launching a subscription program in which people pay a nominal yearly fee and receive benefits such as a coupon, access to exclusive sales, free swag, upgrade to free 2 day shipping. The subscription program is a neat idea to help get your customers to commit to using your site on an ongoing basis.&lt;/p&gt;
&lt;p&gt;I enjoyed my time away from the exhibit hall for Nathan Barling’s talk and took away quite a few things that I can suggest to my clients who are looking to improve their sales.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Spree Performance Benchmarking</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2011/05/spree-performance-benchmarking/"/>
      <id>https://www.endpointdev.com/blog/2011/05/spree-performance-benchmarking/</id>
      <published>2011-05-25T00:00:00+00:00</published>
      <author>
        <name>Steph Skardal</name>
      </author>
      <content type="html">
        &lt;p&gt;I see a lot of questions regarding Spree performance in the &lt;a href=&#34;https://groups.google.com/forum/#!forum/spree-user&#34;&gt;spree-user group&lt;/a&gt;, but they are rarely answered with metrics. I put together a quick script using the generic benchmark tool &lt;a href=&#34;https://httpd.apache.org/docs/2.0/programs/ab.html&#34;&gt;ab&lt;/a&gt; to review some data. Obviously, the answer to how well a site performs and scales is highly dependent on the host and the consumption of the web application, so the data here needs to be taken with a grain of salt. Another thing to note is that only two of the following use cases are running on Rails 3.0 — many of our current Spree clients are on Spree 0.11.2 or older. I also included one non-Spree Rails ecommerce application, in addition to a few non-Rails applications for comparison. All of the tests were run from my home network, so in theory there shouldn’t be bias on performance tests for sites running on End Point servers.&lt;/p&gt;
&lt;table cellpadding=&#34;5&#34; cellspacing=&#34;0&#34; width=&#34;100%&#34;&gt;&lt;tbody&gt;&lt;tr&gt;   &lt;td&gt;&lt;/td&gt;   &lt;td align=&#34;center&#34; colspan=&#34;4&#34;&gt;ab -n 100&lt;/td&gt;   &lt;td&gt;&lt;/td&gt;  &lt;/tr&gt;
&lt;tr&gt;   &lt;td&gt;&lt;/td&gt;   &lt;td align=&#34;center&#34;&gt;-c 2 homepage&lt;/td&gt;   &lt;td align=&#34;center&#34;&gt;-c 20 homepage&lt;/td&gt;   &lt;td align=&#34;center&#34;&gt;-c 2&lt;br/&gt;
product page&lt;/td&gt;   &lt;td align=&#34;center&#34;&gt;-c 20&lt;br/&gt;
product page&lt;/td&gt;   &lt;td&gt;&lt;/td&gt;  &lt;/tr&gt;
&lt;tr style=&#34;background-color:#424242;&#34;&gt;   &lt;td rowspan=&#34;2&#34;&gt;    &lt;b&gt;Client #1&lt;/b&gt;&lt;br/&gt;
Spree: 0.11.2&lt;br/&gt;
Hosting: 4 cores, 512 GB RAM&lt;br/&gt;
DB: MySQL&lt;br/&gt;
# Products: &lt;100   &lt;/td&gt;   &lt;td&gt;7.49&lt;/td&gt;   &lt;td&gt;24.75&lt;/td&gt;   &lt;td&gt;6.49&lt;/td&gt;   &lt;td&gt;19.87&lt;/td&gt;   &lt;td&gt;Requests per second&lt;/td&gt;  &lt;/tr&gt;
&lt;tr style=&#34;background-color:#424242;&#34;&gt;   &lt;td&gt;266.889&lt;/td&gt;&lt;td&gt;808.041&lt;/td&gt;&lt;td&gt;307.997&lt;/td&gt;&lt;td&gt;1006.552&lt;/td&gt;   &lt;td&gt;Time per request (ms)&lt;/td&gt;  &lt;/tr&gt;
&lt;tr&gt;   &lt;td rowspan=&#34;2&#34;&gt;    &lt;b&gt;Client #2&lt;/b&gt;&lt;br/&gt;
Spree 0.11.2&lt;br/&gt;
Hosting: Engineyard, medium instance&lt;br/&gt;
DB: MySQL&lt;br/&gt;
# Products: 100s   &lt;/td&gt;   &lt;td&gt;5.32&lt;/td&gt;&lt;td&gt;20.28&lt;/td&gt;&lt;td&gt;5.36&lt;/td&gt;&lt;td&gt;18.03&lt;/td&gt;   &lt;td&gt;Requests per second&lt;/td&gt;  &lt;/tr&gt;
&lt;tr&gt;   &lt;td&gt;375.713&lt;/td&gt;&lt;td&gt;986.309&lt;/td&gt;&lt;td&gt;373.289&lt;/td&gt;&lt;td&gt;1109.524&lt;/td&gt;   &lt;td&gt;Time per request (ms)&lt;/td&gt;  &lt;/tr&gt;
&lt;tr style=&#34;background-color:#424242;&#34;&gt;   &lt;td rowspan=&#34;2&#34;&gt;    &lt;b&gt;Client #3&lt;/b&gt;&lt;br/&gt;
Spree: 0.9.0&lt;br/&gt;
Hosting: 4 cores, 1 GB RAM&lt;br/&gt;
DB: PostgreSQL&lt;br/&gt;
# Products: &lt;100   &lt;/td&gt;   &lt;td&gt;4.91&lt;/td&gt;&lt;td&gt;25.39&lt;/td&gt;&lt;td&gt;1.98&lt;/td&gt;&lt;td&gt;6.54&lt;/td&gt;   &lt;td&gt;Requests per second&lt;/td&gt;  &lt;/tr&gt;
&lt;tr style=&#34;background-color:#424242;&#34;&gt;   &lt;td&gt;407.135&lt;/td&gt;&lt;td&gt;787.782&lt;/td&gt;&lt;td&gt;1011.875&lt;/td&gt;&lt;td&gt;3060.062&lt;/td&gt;   &lt;td&gt;Time per request (ms)&lt;/td&gt;  &lt;/tr&gt;
&lt;tr&gt;   &lt;td rowspan=&#34;2&#34;&gt;    &lt;b&gt;(Former) Client #4&lt;/b&gt;&lt;br/&gt;
Spree: 0.11.2&lt;br/&gt;
Hosting: Unknown&lt;br/&gt;
DB: PostgreSQL&lt;br/&gt;
# Products: &gt;5000   &lt;/td&gt;   &lt;td&gt;20.69&lt;/td&gt;&lt;td&gt;8.84&lt;/td&gt;&lt;td&gt;10.15&lt;/td&gt;&lt;td&gt;19.28&lt;/td&gt;   &lt;td&gt;Requests per second&lt;/td&gt;  &lt;/tr&gt;
&lt;tr&gt;   &lt;td&gt;96.673&lt;/td&gt;&lt;td&gt;2262.105&lt;/td&gt;&lt;td&gt;196.996&lt;/td&gt;&lt;td&gt;1037.146&lt;/td&gt;   &lt;td&gt;Time per request (ms)&lt;/td&gt;  &lt;/tr&gt;
&lt;tr style=&#34;background-color:#424242;&#34;&gt;   &lt;td rowspan=&#34;2&#34;&gt;    &lt;b&gt;Client #5&lt;/b&gt;&lt;br/&gt;
Spree: 0.11.2&lt;br/&gt;
Hosting: EngineYard, small instance&lt;br/&gt;
DB: MySQL&lt;br/&gt;
# Products: 1   &lt;/td&gt;   &lt;td&gt;12.28&lt;/td&gt;&lt;td&gt;16.23&lt;/td&gt;&lt;td&gt;N/A&lt;/td&gt;&lt;td&gt;N/A&lt;/td&gt;   &lt;td&gt;Requests per second&lt;/td&gt;  &lt;/tr&gt;
&lt;tr style=&#34;background-color:#424242;&#34;&gt;   &lt;td&gt;162.909&lt;/td&gt;&lt;td&gt;1231.945&lt;/td&gt;&lt;td&gt;N/A&lt;/td&gt;&lt;td&gt;N/A&lt;/td&gt;   &lt;td&gt;Time per request (ms)&lt;/td&gt;  &lt;/tr&gt;
&lt;tr&gt;   &lt;td rowspan=&#34;2&#34;&gt;    &lt;b&gt;Client #6&lt;/b&gt;&lt;br/&gt;
Spree: 0.40&lt;br/&gt;
Hosting: 4 cores, 1 GB RAM&lt;br/&gt;
DB: MySQL&lt;br/&gt;
# Products: 50-100   &lt;/td&gt;   &lt;td&gt;3.61&lt;/td&gt;&lt;td&gt;8.93&lt;/td&gt;&lt;td&gt;2.96&lt;/td&gt;&lt;td&gt;3.06&lt;/td&gt;   &lt;td&gt;Requests per second&lt;/td&gt;  &lt;/tr&gt;
&lt;tr&gt;   &lt;td&gt;553.569&lt;/td&gt;&lt;td&gt;2240.657&lt;/td&gt;&lt;td&gt;675.306&lt;/td&gt;&lt;td&gt;6539.433&lt;/td&gt;   &lt;td&gt;Time per request (ms)&lt;/td&gt;  &lt;/tr&gt;
&lt;tr style=&#34;background-color:#424242;&#34;&gt;   &lt;td rowspan=&#34;2&#34;&gt;    &lt;b&gt;SpreeDemo&lt;/b&gt;&lt;br/&gt;
Spree: Edge&lt;br/&gt;
Hosting: Heroku, 2 dynos&lt;br/&gt;
DB: Unknown&lt;br/&gt;
# Products: 100s   &lt;/td&gt;   &lt;td&gt;8.17&lt;/td&gt;&lt;td&gt;12.79&lt;/td&gt;&lt;td&gt;4.7&lt;/td&gt;&lt;td&gt;5.48&lt;/td&gt;   &lt;td&gt;Requests per second&lt;/td&gt;  &lt;/tr&gt;
&lt;tr style=&#34;background-color:#424242;&#34;&gt;   &lt;td&gt;244.831&lt;/td&gt;&lt;td&gt;1563.642&lt;/td&gt;&lt;td&gt;425.27&lt;/td&gt;&lt;td&gt;3652.927&lt;/td&gt;   &lt;td&gt;Time per request (ms)&lt;/td&gt;  &lt;/tr&gt;
&lt;tr&gt;   &lt;td rowspan=&#34;2&#34;&gt;    &lt;b&gt;Client #7&lt;/b&gt;&lt;br/&gt;
*custom Rails ecommerce app&lt;br/&gt;
Hosting: 1.0 GB RAM&lt;br/&gt;
DB: MySQL&lt;br/&gt;
# Products: 1000s   &lt;/td&gt;   &lt;td&gt;5.43&lt;/td&gt;&lt;td&gt;29.8&lt;/td&gt;&lt;td&gt;4.45&lt;/td&gt;&lt;td&gt;23.14&lt;/td&gt;   &lt;td&gt;Requests per second&lt;/td&gt;  &lt;/tr&gt;
&lt;tr&gt;   &lt;td&gt;368.409&lt;/td&gt;&lt;td&gt;671.082&lt;/td&gt;&lt;td&gt;448.962&lt;/td&gt;&lt;td&gt;864.24&lt;/td&gt;   &lt;td&gt;Time per request (ms)&lt;/td&gt;  &lt;/tr&gt;
&lt;tr style=&#34;background-color:#424242;&#34;&gt;   &lt;td rowspan=&#34;2&#34;&gt;    &lt;b&gt;Interchange Demo&lt;/b&gt;&lt;br/&gt;
Hosting: 4 cores, 2 GB RAM&lt;br/&gt;
DB: MySQL&lt;br/&gt;
# Products: &gt;500   &lt;/td&gt;   &lt;td&gt;7.41&lt;/td&gt;&lt;td&gt;55.27&lt;/td&gt;&lt;td&gt;7.5&lt;/td&gt;&lt;td&gt;13.93&lt;/td&gt;   &lt;td&gt;Requests per second&lt;/td&gt;  &lt;/tr&gt;
&lt;tr style=&#34;background-color:#424242;&#34;&gt;   &lt;td&gt;269.942&lt;/td&gt;&lt;td&gt;361.875&lt;/td&gt;&lt;td&gt;266.492&lt;/td&gt;&lt;td&gt;1435.51&lt;/td&gt;   &lt;td&gt;Time per request (ms)&lt;/td&gt;  &lt;/tr&gt;
&lt;tr&gt;   &lt;td rowspan=&#34;2&#34;&gt;    &lt;b&gt;Client #8&lt;/b&gt;&lt;br/&gt;
*PHP site,&lt;br/&gt;
serves fully cached pages&lt;br/&gt;
with nginx with no app server or db hits&lt;br/&gt;
Hosting: 4 cores, 4 GB RAM   &lt;/td&gt;   &lt;td&gt;10.81&lt;/td&gt;&lt;td&gt;30.54&lt;/td&gt;&lt;td&gt;6.05&lt;/td&gt;&lt;td&gt;9.87&lt;/td&gt;   &lt;td&gt;Requests per second&lt;/td&gt;  &lt;/tr&gt;
&lt;tr&gt;   &lt;td&gt;184.994&lt;/td&gt;&lt;td&gt;654.858&lt;/td&gt;&lt;td&gt;330.727&lt;/td&gt;&lt;td&gt;2027.092&lt;/td&gt;   &lt;td&gt;Time per request (ms)&lt;/td&gt;  &lt;/tr&gt;
&lt;tr style=&#34;background-color:#424242;&#34;&gt;   &lt;td rowspan=&#34;2&#34;&gt;    &lt;b&gt;Magento Demo&lt;/b&gt;&lt;br/&gt;
Hosting: Unknown&lt;br/&gt;
DB: Unknown&lt;br/&gt;
# Products: 100s   &lt;/td&gt;   &lt;td&gt;4.26&lt;/td&gt;&lt;td&gt;44.85&lt;/td&gt;&lt;td&gt;2.68&lt;/td&gt;&lt;td&gt;36.29&lt;/td&gt;   &lt;td&gt;Requests per second&lt;/td&gt;  &lt;/tr&gt;
&lt;tr style=&#34;background-color:#424242;&#34;&gt;   &lt;td&gt;469.831&lt;/td&gt;&lt;td&gt;445.931&lt;/td&gt;&lt;td&gt;745.472&lt;/td&gt;&lt;td&gt;551.11&lt;/td&gt;   &lt;td&gt;Time per request (ms)&lt;/td&gt;  &lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Here’s the same data in graphical form:&lt;/p&gt;
&lt;h3 id=&#34;requests-per-second&#34;&gt;Requests per Second&lt;/h3&gt;
&lt;p&gt;&lt;a href=&#34;/blog/2011/05/spree-performance-benchmarking/image-0-big.png&#34; onblur=&#34;try {parent.deselectBloggerImageGracefully();} catch(e) {}&#34;&gt;&lt;img alt=&#34;&#34; border=&#34;0&#34; id=&#34;BLOGGER_PHOTO_ID_5610751500251002546&#34; src=&#34;/blog/2011/05/spree-performance-benchmarking/image-0.png&#34; style=&#34;display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 740px;&#34;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&#34;time-per-request-ms&#34;&gt;Time Per Request (ms)&lt;/h3&gt;
&lt;p&gt;&lt;a href=&#34;/blog/2011/05/spree-performance-benchmarking/image-1-big.png&#34; onblur=&#34;try {parent.deselectBloggerImageGracefully();} catch(e) {}&#34;&gt;&lt;img alt=&#34;&#34; border=&#34;0&#34; id=&#34;BLOGGER_PHOTO_ID_5610751511624706978&#34; src=&#34;/blog/2011/05/spree-performance-benchmarking/image-1.png&#34; style=&#34;display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 740px;&#34;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;We expect to see high performance on some of the sites with significant performance optimization. On smaller VPS, we expect to see the the server choke with higher concurrency.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Ecommerce Solutions: What are the Options?</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2011/02/ecommerce-solutions-options/"/>
      <id>https://www.endpointdev.com/blog/2011/02/ecommerce-solutions-options/</id>
      <published>2011-02-28T00:00:00+00:00</published>
      <author>
        <name>Steph Skardal</name>
      </author>
      <content type="html">
        &lt;p&gt;Lately, I’ve been evaluating ecommerce options for use on &lt;a href=&#34;http://stephskardal.com/&#34;&gt;a side hobby/business&lt;/a&gt;. I’m obviously a developer, so in theory I could use one of End Point’s supported ecommerce frameworks or just write my own framework. But, my bottom line is that I don’t need the feature set offered by some of the ecommerce options out there and I don’t necessarily have the resources to develop a custom solution.&lt;/p&gt;
&lt;p&gt;In addition to personal interest, End Pointers constantly encounter potential clients who aim to get a better understanding of the cost of using open source, our preferred ecommerce solution. I put together two infographics on ecommerce options, ongoing cost, feature sets, and the ability to customize. Before anyone &lt;em&gt;flips out&lt;/em&gt; about the infographics, note that they represent &lt;strong&gt;my&lt;/strong&gt; broad generalizations regarding the ongoing cost, feature sets and ability to customize. I’m intimately familiar with some of these options and less familiar with a couple of them.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;/blog/2011/02/ecommerce-solutions-options/image-0-big.png&#34; onblur=&#34;try {parent.deselectBloggerImageGracefully();} catch(e) {}&#34;&gt;&lt;img alt=&#34;&#34; border=&#34;0&#34; id=&#34;BLOGGER_PHOTO_ID_5579241277111917426&#34; src=&#34;/blog/2011/02/ecommerce-solutions-options/image-0.png&#34; style=&#34;display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 561px; height: 397px;&#34;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Feature Set versus Ongoing Cost of Ecommerce Solutions&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;/blog/2011/02/ecommerce-solutions-options/image-1-big.png&#34; onblur=&#34;try {parent.deselectBloggerImageGracefully();} catch(e) {}&#34;&gt;&lt;img alt=&#34;&#34; border=&#34;0&#34; id=&#34;BLOGGER_PHOTO_ID_5579241280123454690&#34; src=&#34;/blog/2011/02/ecommerce-solutions-options/image-1.png&#34; style=&#34;display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 665px; height: 454px;&#34;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Ability to Customize versus Ongoing Cost of Ecommerce Solutions&lt;/p&gt;
&lt;p&gt;Some notes on on the ecommerce solutions shown in the infographics:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Online payment service (Paypal):&lt;/strong&gt; An online payment collection service like PayPal offers a minimal “ecommerce” feature set and might be suitable for someone looking to simply collect money. It also provides almost no ability to customize. The ongoing cost of PayPal is lower than many of the other options, where a percentage of each sale goes to PayPal.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Online catalog service (Etsy, eBay):&lt;/strong&gt; An online catalog service such as Etsy or eBay offers very basic ecommerce listing features, little to no ability to customize, but has relatively low ongoing cost. For example, Etsy charges $0.20 to list a single item for four months and takes 3.5% of the sales fee.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Hosted ecommerce (shopify, Big Cartel, Big Commerce, Yahoo Merchant):&lt;/strong&gt; A hosted ecommerce solution offers the ability to customize the appearance, typically in the form of a custom template language and has a basic ecommerce feature set. Ongoing costs would include the cost of the service, domain registration cost, and payment gateway (e.g. Authorize.NET) fees. Additional cost may apply if development services are required for building a custom template. Shopify’s basic solution costs $29/mo. with a max of 100 skus. Shopify offers several other higher-priced options which include more features and have less limitations. Most other hosted ecommerce solutions are similarly priced.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Open source ecommerce (Interchange, Spree, Magento, Zen Cart, osCommerce, prestaShop):&lt;/strong&gt; An open-source ecommerce solution tends to have generic ecommerce features, provides the opportunity for a large amount of customization, but is typically more expensive than hosted ecommerce solutions and online catalog services. The software itself is free, but ongoing costs of hosting (server, domain registration, SSL certificate), development (any piece of customization that does not fit into the generic mold), and payment gateway fees apply. One positive about open source ecommerce is that additional plugins or add-ons are produced by members of the community. If the business needs are satisfied by the community-available extensions, cost from additional development or customization may be eliminated or reduced. I’d also probably group in existing open source plugins or modules for open source CMS solutions like WordPress and Drupal into this category: the generic solution is free, but additional resources may be required for customization.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Enterprise ecommerce (ATG, Magento Enterprise):&lt;/strong&gt; From my experience, I’ve observed that enterprise ecommerce solutions tend to offer a large feature set and a similar ability to customize as open source frameworks. The ongoing cost is high: for example, Magento Enterprise starts at $12,990/year. In addition to the licensing cost, hosting, development, and payment gateway fees may apply.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Custom ecommerce (homegrown):&lt;/strong&gt; The cost of writing your own ecommerce framework depends on the functionality requirements. The ability to customize is unlimited since the entire solution is custom. The feature set is likely to be proportional to resources spent on the project. Ongoing costs here include hosting, development and payment gateway fees.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;which-one-do-you-choose&#34;&gt;Which one do you choose?&lt;/h3&gt;
&lt;p&gt;It has been my experience that most of End Point’s ecommerce clients need some type of customization: a custom appearance, discount functionality, shipping integration, payment gateway integration, social media integration, and other third-party integration. Choosing an option that allows easy customization tends to benefit our customers in the long run. We’re pretty biased at End Point towards open source ecommerce solutions, but my opinion recently is that with the advancement of web frameworks and web framework tools (e.g. gems in ruby), development of custom solutions can be done efficiently and may be a better option for a site that is outside the realm of standard ecommerce. For businesses not able to pay development or consulting costs, hosted ecommerce solutions are affordable and provide the essentials needed as the business grows.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Utah Open Source Conference 2010 part 1</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2010/11/utah-open-source-conference-2010-part-1/"/>
      <id>https://www.endpointdev.com/blog/2010/11/utah-open-source-conference-2010-part-1/</id>
      <published>2010-11-12T00:00:00+00:00</published>
      <author>
        <name>Jon Jensen</name>
      </author>
      <content type="html">
        &lt;p&gt;It’s been about a little over a month since the 2010 Utah Open Source Conference, and I decided to take a few minutes to review talks I enjoyed and link to my own talk slides.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Magento:&lt;/strong&gt; Mac Newbold of Code Greene spoke on the Magento ecommerce framework for PHP. I’ve somewhat familiar with Magento, but a few things stood out:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;He finds the Magento Enterprise edition kind of problematic because Varien won’t support you if you have any unsupported extensions. Some of his customers had problems with Varien support and went back to the community edition.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Magento is now up to around 30 MB of PHP files!&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;As I’ve heard elsewhere, serious customization has a steep learning curve.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The Magento data model is an EAV (Entity-Attribute-Value) model. To get 50 columns of output requires 50+ joins between 8 tables (one EAV table for each value datatype).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;There are 120 tables total in default install—​many core features don’t use the EAV tables for performance reasons.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Another observation I’ve heard in pretty much every conversation about Magento: It is very resource intensive. Shared hosting is not recommended. Virtual servers should have a minimum of 1/2 to 1 GB RAM. Fast disk &amp;amp; database help most. APC cache recommended with at least 128 MB.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;A lot of front-end things are highly adjustable from simple back-end admin options.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Saved credit cards are stored in the database, and the key is on the server. I didn’t get a chance to ask for more details about this. I hope it’s only the public part of a public/secret keypair!&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It was a good overview for someone wanting to go beyond marketing feature lists.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&#34;https://nodejs.org/&#34;&gt;Node.js&lt;/a&gt;:&lt;/strong&gt; Shane Hansen of Backcountry.com spoke on Node, comparing it to Tornado and Twisted in Python. He calls JavaScript “Lisp in C’s clothing”, and says its culture of asynchronous, callback-driven code patterns makes Node a natural fit.&lt;/p&gt;
&lt;p&gt;Performance and parallel processing are clearly strong incentives to look into Node. The echo server does 20K requests/sec. There are 2000+ Node projects on GitHub and 500+ packages in npm (Node Package Manager), including database drivers, web frameworks, parsers, testing frameworks, payment gateway integrations, and web analytics.&lt;/p&gt;
&lt;p&gt;A few packages worth looking into further:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;express — web microframework like Sinatra&lt;/li&gt;
&lt;li&gt;Socket-IO — Web Sockets now; falls back to other things if no Web Sockets available&lt;/li&gt;
&lt;li&gt;hummingbird — web analytics, used by Gilt.com&lt;/li&gt;
&lt;li&gt;bespin — “cloud JavaScript editor”&lt;/li&gt;
&lt;li&gt;yui3 — build HTML via DOM, eventbus, etc.&lt;/li&gt;
&lt;li&gt;connect — like Ruby’s Rack&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I haven’t played with Node at all yet, and this got me much more interested.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&#34;https://www.metasploit.com/&#34;&gt;Metasploit&lt;/a&gt;:&lt;/strong&gt; Jason Wood spoke on Metasploit, a penetration testing (or just plain penetrating!) tool. It was originally in Perl, and now is in Ruby. It comes with 590 exploits and has a text-based interactive control console.&lt;/p&gt;
&lt;p&gt;Metasploit uses several external apps: nmap, Maltego (proprietary reconnaissance tool), Nessus (no longer open source, but GPL version and OpenVAS fork still available), Nexpose, Ratproxy, Karma.&lt;/p&gt;
&lt;p&gt;The reconnaissance modules include DNS enumeration, and an email address collector that uses the big search engines.&lt;/p&gt;
&lt;p&gt;It can backdoor PuTTY, PDFs, audio, and more.&lt;/p&gt;
&lt;p&gt;This is clearly something you’ve got to experiment with to appreciate. Jason posted &lt;a href=&#34;https://web.archive.org/web/*/http://www.jwnetworkconsulting.com/downloads/utos-msf-2010.pdf&#34;&gt;his Metasploit talk slides&lt;/a&gt; which have more detail.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;So Many Choices: Web App Deployment with Perl, Python, and Ruby&lt;/strong&gt;: This was my talk, and it was a lot of fun to prepare for, as I got to take time to see some new happenings I’d missed in these three languages communities’ web server and framework space over the past several years.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&#34;https://jon.endpoint.com/utosc-2010-slides/&#34;&gt;slides give pointers&lt;/a&gt; to a lot of interesting projects and topics to check out.&lt;/p&gt;
&lt;p&gt;My summary was this. We have an embarrassment of riches in the open source web application world. Perl, Python, and Ruby all have very nice modern frameworks for developing web applications. They also have several equivalent solid options for deploying web applications. If you haven’t tried the following, check them out:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Perl: &lt;a href=&#34;http://perldancer.org/&#34;&gt;Dancer&lt;/a&gt;, &lt;a href=&#34;https://metacpan.org/release/Starman&#34;&gt;Starman&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Python: &lt;a href=&#34;http://flask.pocoo.org/&#34;&gt;Flask&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Ruby: &lt;a href=&#34;http://sinatrarb.com/&#34;&gt;Sinatra&lt;/a&gt;, &lt;a href=&#34;https://unicorn.bogomips.org/&#34;&gt;Unicorn&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That’s about half of my notes on talks, but all I have time for now. I’ll cover more in a later post.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Spree vs. Magento: Feature List Revisited</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2010/07/spree-vs-magento-feature-list-revisited/"/>
      <id>https://www.endpointdev.com/blog/2010/07/spree-vs-magento-feature-list-revisited/</id>
      <published>2010-07-13T00:00:00+00:00</published>
      <author>
        <name>Steph Skardal</name>
      </author>
      <content type="html">
        &lt;p&gt;A little over a month ago, I wrote an article on &lt;a href=&#34;/blog/2010/06/spree-vs-magento-feature-list/&#34;&gt;Spree vs. Magento Features&lt;/a&gt;. Recently, a client asked me to describe the features mentioned in that article. I thought this was another great opportunity to expand on my response to the client. So, here I am, revisiting ecommerce features in Spree and Magento. The &lt;a href=&#34;/blog/2010/06/spree-vs-magento-feature-list/&#34;&gt;original article&lt;/a&gt; can be referenced to compare availability of these features in Spree and Magento.&lt;/p&gt;
&lt;h3 id=&#34;features-on-a-single-product-or-group-of-product&#34;&gt;Features on a Single Product or Group of Product&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Product reviews and/or ratings&lt;/strong&gt;: functionality to allow customers to review and rate products. See &lt;a href=&#34;https://www.backcountry.com/the-north-face-denali-fleece-jacket-mens#reviews&#34;&gt;a Backcountry.com product page&lt;/a&gt; for an example.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Product QnA&lt;/strong&gt;: functionality allow customers to ask and answer questions on products. See &lt;a href=&#34;https://www.backcountry.com/the-north-face-denali-fleece-jacket-mens#qanda&#34;&gt;a Backcountry.com product page&lt;/a&gt; for an example.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Product SEO (URL, title, meta data control)&lt;/strong&gt;: functionality to allow site administrators to manage product URLs, product page titles, and product meta data.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Advanced/flexible taxonomy&lt;/strong&gt;: functionality to build a custom taxonomy / navigation structure for product browsing. For example, build multiple categories and subcategories with complex hierarchy. The taxonomy at &lt;a href=&#34;https://spreecommerce-demo.herokuapp.com/&#34;&gt;Spree’s demo&lt;/a&gt; includes two categories of brand and category and subcategories in each.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SEO for taxonomy pages&lt;/strong&gt;: functionality to allow site administrators to manage taxonomy URLs, taxonomy page titles, and taxonomy meta data.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Configurable product search&lt;/strong&gt;: functionality to allow the developers and site administrators to adjust parameters used in search, such as products to show per page, and to show products with no on hand stock.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Bundled products for discount&lt;/strong&gt;: functionality to allow site administrators to create bundles or group products together and then apply a discount to the entire bundle. For example, product X, Y and Z purchased together will yield a $10.00 discount.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Recently viewed products&lt;/strong&gt;:  functionality to show customers products they recently visited. This can be displayed on other product pages or the navigation pages to aid in navigation back to those products if the user would like to revisit the products. See the image below for an example of this functionality in action at &lt;a href=&#34;https://www.papersource.com/&#34;&gt;Paper Source&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href=&#34;/blog/2010/07/spree-vs-magento-feature-list-revisited/image-0-big.png&#34; onblur=&#34;try {parent.deselectBloggerImageGracefully();} catch(e) {}&#34;&gt;&lt;img alt=&#34;&#34; border=&#34;0&#34; id=&#34;BLOGGER_PHOTO_ID_5493450761146669666&#34; src=&#34;/blog/2010/07/spree-vs-magento-feature-list-revisited/image-0.png&#34; style=&#34;display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 333px;&#34;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Soft product support/downloads&lt;/strong&gt;: functionality to sell soft products such as mp3 or pdf files.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Product comparison&lt;/strong&gt;: functionality to allow customers to compare multiple products, such as a comparison of price or technical features. See &lt;a href=&#34;http://www.backcountry.com/store/compare.html?chk_compare_TNF0115=1&amp;amp;chk_compare_TNF3203=1&amp;amp;chk_compare_TNF4034=1&#34;&gt;Backcountry.com&lt;/a&gt; for an example.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Upsell&lt;/strong&gt;: functionality to encourage the customer to purchase a similar product from a higher price point, or to purchase an add on, may or may not include the functionality to allow site administrators to manage the upsell products.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cross sell&lt;/strong&gt;: functionality to encourage the customer to purchase related items, may or may not include the functionality to allow site administrators to manage the cross sell products. See the image below for an example of this functionality at &lt;a href=&#34;http://www.backcountry.com/&#34;&gt;Backcountry.com&lt;/a&gt; after adding an item to the cart.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href=&#34;/blog/2010/07/spree-vs-magento-feature-list-revisited/image-1-big.png&#34; onblur=&#34;try {parent.deselectBloggerImageGracefully();} catch(e) {}&#34;&gt;&lt;img alt=&#34;&#34; border=&#34;0&#34; id=&#34;BLOGGER_PHOTO_ID_5493450630306818274&#34; src=&#34;/blog/2010/07/spree-vs-magento-feature-list-revisited/image-1.png&#34; style=&#34;display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 323px;&#34;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Related items&lt;/strong&gt;: functionality to display related items on product pages, may or may not include the functionality to allow site administrators to manage the related products.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;RSS feed of products&lt;/strong&gt;: functionality to produce a RSS feed with product detail releases. &lt;a href=&#34;https://www.steepandcheap.com/&#34;&gt;steepandcheap.com&lt;/a&gt; offers a RSS feed, however, they are unique in that they offer a one deal at a time business model, so the RSS feed contains a stream of products for sale. This may or may not include the functionality to allow site administrators to manage the RSS feed contents.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Multiple images per product&lt;/strong&gt;: functionality to allow site administrators to upload multiple products per image. See &lt;a href=&#34;https://spreecommerce-demo.herokuapp.com/products/ruby-on-rails-baseball-jersey&#34;&gt;a Spree demo product&lt;/a&gt; for an example.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Product option selection (variants)&lt;/strong&gt;: functionality to allow site administrators to create and manage variants for products to offer multiple variants per product, such as variants by size and color. See &lt;a href=&#34;https://spreecommerce-demo.herokuapp.com/products/ruby-on-rails-baseball-jersey&#34;&gt;a Spree demo product&lt;/a&gt; for an example.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Wish list&lt;/strong&gt;: functionality to allow customers to create product wish lists. See &lt;a href=&#34;https://www.amazon.com/wishlist&#34;&gt;Amazon.com&lt;/a&gt; for a description of their wish list functionality.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Send product email to friend&lt;/strong&gt;: functionality to allow customers to send emails to their friend to visit a specific product.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Product tagging / search by tagging&lt;/strong&gt;: functionality to allow site administrators to assign tags to products for navigation or searching. See &lt;a href=&#34;http://www.citypass.com/blog/&#34;&gt;CityPass’s blog&lt;/a&gt; for an example of tag use in a content management system; in ecommerce context, the tags would navigate to a set of products instead of a set of blog articles.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Breadcrumbs&lt;/strong&gt;: functionality that renders the product navigation hierarchy on navigation and product pages to allow customers to navigate to previous pages visited. See &lt;a href=&#34;https://spreecommerce-demo.herokuapp.com/t/brand/rails&#34;&gt;a Spree demo navigation page&lt;/a&gt; for an example.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;cms-features&#34;&gt;CMS Features&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Blogging functionality&lt;/strong&gt;: functionality to allow site administrators to create, manage and display blog articles.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Static page management&lt;/strong&gt;: functionality to allow site administrators to create, manage, and display static pages such as “About Us”, “Information”.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Media management&lt;/strong&gt;: functionality to allow site administrators to create, manage, and display media such as images, video, audio. See the image below for an example of &lt;a href=&#34;https://www.wordpress.org&#34;&gt;WordPress’s&lt;/a&gt; dashboard for content management.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href=&#34;/blog/2010/07/spree-vs-magento-feature-list-revisited/image-2-big.png&#34; onblur=&#34;try {parent.deselectBloggerImageGracefully();} catch(e) {}&#34;&gt;&lt;img alt=&#34;&#34; border=&#34;0&#34; id=&#34;BLOGGER_PHOTO_ID_5493450626615883906&#34; src=&#34;/blog/2010/07/spree-vs-magento-feature-list-revisited/image-2.png&#34; style=&#34;display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 341px; height: 400px;&#34;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Contact us form&lt;/strong&gt;: functionality to allow customers to submit a request for contact. See &lt;a href=&#34;/contact/&#34;&gt;End Point’s contact page&lt;/a&gt; for an example.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Polls&lt;/strong&gt;: functionality to allow site administrators to create and manage basic polls, functionality to allow customers to submit answers to basic polls.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;checkout-support&#34;&gt;Checkout Support&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;One page checkout&lt;/strong&gt;: functionality to allow customers to complete checkout on one page, rather than move forward through checkout through multiple address, payment pages. See &lt;a href=&#34;https://www.papersource.com/&#34;&gt;Paper Source&lt;/a&gt; or &lt;a href=&#34;https://www.backcountry.com/&#34;&gt;Backcountry.com’s&lt;/a&gt; checkout processes for examples.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Guest checkout&lt;/strong&gt;: functionality to checkout without creating a user account. Checkout at the &lt;a href=&#34;https://spreecommerce-demo.herokuapp.com/&#34;&gt;Spree demo&lt;/a&gt; without being logged in for an example.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SSL support&lt;/strong&gt;: functionality to configure use of SSL during checkout. In Spree’s case, the site administrator may turn SSL off during development and on during production.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Discounts&lt;/strong&gt;: functionality to allow customers to apply discount coupons to orders for a percentage or dollar amount reduction.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Gift certificates&lt;/strong&gt;: functionality to allow customers to purchase and use gift certificates as credit for purchases.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Saved shopping cart&lt;/strong&gt;: the functionality to save the products in a customers shopping cart so their shopping cart will be pre-populated on their next visit.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Saved addresses&lt;/strong&gt;: functionality to allow customers to create and manage addresses to be selected during checkout for billing or shipping rather than requiring the customer to re-enter their address. See the image below for an example of using saved addresses during &lt;a href=&#34;https://www.backcountry.com&#34;&gt;Backcountry.com’s&lt;/a&gt; checkout.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href=&#34;/blog/2010/07/spree-vs-magento-feature-list-revisited/image-3-big.png&#34; onblur=&#34;try {parent.deselectBloggerImageGracefully();} catch(e) {}&#34;&gt;&lt;img alt=&#34;&#34; border=&#34;0&#34; id=&#34;BLOGGER_PHOTO_ID_5493450765449179810&#34; src=&#34;/blog/2010/07/spree-vs-magento-feature-list-revisited/image-3.png&#34; style=&#34;display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 184px;&#34;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&#34;shipping-support&#34;&gt;Shipping Support&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Real time rate lookup (UPS, USPS, FedEx)&lt;/strong&gt;: the functionality to request rates from UPS, USPS, or FedEx during checkout for more accurate rate pricing rather than using a flat shipping rate.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Order tracking&lt;/strong&gt;: functionality to allow the site administrators to enter order tracking information and allow the user to review that tracking information, may or may not include sharing this information in a “Your order has been shipped” email.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Multiple shipments per order&lt;/strong&gt;: functionality to allow site administrators to split orders into multiple packages if specific products can not be shipped at the same time or can not be shipped together. See the image below for an example of &lt;a href=&#34;https://www.spreecommerce.org/&#34;&gt;Spree’s&lt;/a&gt; backend shipping interface.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href=&#34;/blog/2010/07/spree-vs-magento-feature-list-revisited/image-4-big.png&#34; onblur=&#34;try {parent.deselectBloggerImageGracefully();} catch(e) {}&#34;&gt;&lt;img alt=&#34;&#34; border=&#34;0&#34; id=&#34;BLOGGER_PHOTO_ID_5493450647395677138&#34; src=&#34;/blog/2010/07/spree-vs-magento-feature-list-revisited/image-4.png&#34; style=&#34;display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 78px;&#34;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Complex rate lookup&lt;/strong&gt;: functionality to calculate ship rates based on weight or price.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Free shipping&lt;/strong&gt;: functionality to offer free shipping.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;payment-support&#34;&gt;Payment Support&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Multiple payment gateways&lt;/strong&gt;: integration of multiple payment gateways such as Authorize.NET, Beanstream, Paypal, SagePay, etc.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Authorize.Net&lt;/strong&gt;: integration of the Authorize.Net payment gateway; may or may include the use of profiles (Authorize.Net CIM).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Authorize and capture versus authorize only&lt;/strong&gt;: functionality to allow site administrators to configure whether or not credit cards should be authorized only during checkout completion or authorized and captured. If the credit card is authorized only, site administrators may finalize an order by capturing on the backend interface.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Google Checkout&lt;/strong&gt;: integration of &lt;a href=&#34;https://googlecheckout.blogspot.com/&#34;&gt;Google Checkout&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Paypal Express&lt;/strong&gt;: integration of &lt;a href=&#34;https://www.paypal.com/us/webapps/mpp/paypal-checkout&#34;&gt;Paypal Express&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;admin-features&#34;&gt;Admin Features&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Sales reporting&lt;/strong&gt;: the functionality to display sales statistics, such as profits on sales or year-over-year sales.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Sales management tools&lt;/strong&gt;: functionality to allow site administrators to create and manage product sales. For example, the site administrator might create a 50% off sale to cover 25% of the products to begin in a week and end in two weeks.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Inventory management&lt;/strong&gt;: functionality to allow the site administrator to manage individual inventory units and their current state (on_hand, shipped, backordered) and order assignment.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Purchase order management&lt;/strong&gt;: functionality to allow the site administrator to create and manage purchase orders. See the image below for an example of a potential backend interface for purchase orders in Spree.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href=&#34;/blog/2010/07/spree-vs-magento-feature-list-revisited/image-5-big.png&#34; onblur=&#34;try {parent.deselectBloggerImageGracefully();} catch(e) {}&#34;&gt;&lt;img alt=&#34;&#34; border=&#34;0&#34; id=&#34;BLOGGER_PHOTO_ID_5493450657470955906&#34; src=&#34;/blog/2010/07/spree-vs-magento-feature-list-revisited/image-5.png&#34; style=&#34;display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 71px;&#34;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Multi-tier pricing for quantity discounts&lt;/strong&gt;: functionality to allow customers to buy large quantities of products at a discount, the functionality to allow the site administrator to manage the large quantity product discounts.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Landing page tool&lt;/strong&gt;: functionality to create custom landing pages that may include targeted content or products, typically used for advertising or marketing.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Batch import and export of products&lt;/strong&gt;: functionality to allow the site administrator to import and export products via admin interface or script rather than entering each product individually.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Multiple sales reports&lt;/strong&gt;: See “Sales reporting” above.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Order fulfillment&lt;/strong&gt;: functionality to allow site administrators to manage fulfillment (inventory selection, shipping) of orders.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tax Rate Management&lt;/strong&gt;: functionality to manage tax rates per zone, where zones are defined by states and/or countries. Note that in Spree, zones can only be defined by a combination of states and countries and tax rates can be tied to one or more zones.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;user-account-features&#34;&gt;User Account Features&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;User addresses&lt;/strong&gt;: See “Saved addresses” above.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Feature rich user preferences&lt;/strong&gt;: integration of various user account tools, such as address management, profile management, order review, etc.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Order tracking history&lt;/strong&gt;: functionality to allow a customer to lookup their order history, may or may not include order tracking information.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;site-wide-features&#34;&gt;Site Wide Features&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Extensibility&lt;/strong&gt;: functionality to extend the ecommerce core with modular components.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Appearance Theming&lt;/strong&gt;: functionality to change the appearance of the site.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Ability to customize appearance at category or browsing level&lt;/strong&gt;: functionality to create and manage custom and varied appearances for product browsing pages. For example, the categories &amp;ldquo;Jackets&amp;rdquo; and &amp;ldquo;Pants&amp;rdquo; may have different appearances, motivated by marketing or advertising.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Localization&lt;/strong&gt;: the functionality to translate the ecommerce site to a different language. See the image below for a small example of localization in action in Spree.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href=&#34;/blog/2010/07/spree-vs-magento-feature-list-revisited/image-6-big.png&#34; onblur=&#34;try {parent.deselectBloggerImageGracefully();} catch(e) {}&#34;&gt;&lt;img alt=&#34;&#34; border=&#34;0&#34; id=&#34;BLOGGER_PHOTO_ID_5493450639007292738&#34; src=&#34;/blog/2010/07/spree-vs-magento-feature-list-revisited/image-6.png&#34; style=&#34;display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 170px; height: 45px;&#34;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Multi-store, single admin support&lt;/strong&gt;: functionality to manage multiple stores from a single administrative location. An example of this might include &lt;a href=&#34;http://store1.endpoint.com/&#34;&gt;http://store1.endpoint.com/&lt;/a&gt;, and &lt;a href=&#34;http://store2.endpoint.com/&#34;&gt;http://store2.endpoint.com/&lt;/a&gt;, where both can be managed at &lt;a href=&#34;http://admin.endpoint.com/&#34;&gt;http://admin.endpoint.com/&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Support for multiple currencies&lt;/strong&gt;: functionality to translate product prices between currencies.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Web service API&lt;/strong&gt;: functionality to retrieve data from the ecommerce application for third party use. See &lt;a href=&#34;https://guides.spreecommerce.org/api/&#34;&gt;Spree’s documentation&lt;/a&gt; on the Spree API.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;System wide SEO&lt;/strong&gt;: general site-wide SEO functionality including features such as sitemap, googlebase integration, URL management, page title management.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Google Analytics&lt;/strong&gt;: functionality to allow site administrators to create and manage Google Analytics Ids, functionality to track traffic and conversion on the frontend.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Active community&lt;/strong&gt;: an active developer community with frequent core and extension contributions.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Most of the features described above are well known to ecommerce developers, but this list might also serve as a good checklist to review with a potential client during the estimate process to make sure expectations of an ecommerce platform are managed, especially with a young platform such as Spree where some features are not yet included in the core.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Spree vs Magento: A Feature List Comparison</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2010/06/spree-vs-magento-feature-list/"/>
      <id>https://www.endpointdev.com/blog/2010/06/spree-vs-magento-feature-list/</id>
      <published>2010-06-07T00:00:00+00:00</published>
      <author>
        <name>Steph Skardal</name>
      </author>
      <content type="html">
        &lt;p&gt;Note: This article was written in June of 2010. Since then, there have been several updates to Spree. Check out the current &lt;a href=&#34;https://web.archive.org/web/20111027123306/http://www.spreecommerce.com/extensions?is_official=true&#34;&gt;Official Spree Extensions&lt;/a&gt; or review a list of all the &lt;a href=&#34;https://web.archive.org/web/20111010012147/http://spreecommerce.com/extensions&#34;&gt;Spree Extensions&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This week, a client asked me for a list of Spree features both in the core and in available extensions. I decided that this might be a good time to look through Spree and provide a comprehensive look at features included in Spree core and extensions and use Magento as a basis for comparison. I’ve divided these features into meaningful broader groups that will hopefully ease the pain of comprehending an extremely long list :) Note that the Magento feature list is based on their documentation. Also note that the Spree features listed here are based on recent 0.10.* releases of Spree.&lt;/p&gt;
&lt;h3 id=&#34;features-on-a-single-product-or-group-of-product&#34;&gt;Features on a Single Product or Group of Product&lt;/h3&gt;
&lt;table cellpadding=&#34;2&#34; cellspacing=&#34;0&#34; width=&#34;100%&#34;&gt;&lt;tbody&gt;
&lt;tr class=&#34;alt header&#34;&gt;&lt;td width=&#34;60%&#34;&gt;Feature&lt;/td&gt;&lt;td width=&#34;20%&#34;&gt;Spree&lt;/td&gt;&lt;td&gt;Magento&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Product reviews and/or ratings&lt;/td&gt;&lt;td&gt;Y, extension&lt;/td&gt;&lt;td&gt;Y&lt;/td&gt;&lt;/tr&gt;
&lt;tr class=&#34;alt&#34;&gt;&lt;td&gt;Product qna&lt;/td&gt;&lt;td&gt;N&lt;/td&gt;&lt;td&gt;N&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Product seo (url, title, meta data control)&lt;/td&gt;&lt;td&gt;N&lt;/td&gt;&lt;td&gt;Y&lt;/td&gt;&lt;/tr&gt;
&lt;tr class=&#34;alt&#34;&gt;&lt;td&gt;Advanced/flexible taxonomy&lt;/td&gt;&lt;td&gt;Y, core&lt;/td&gt;&lt;td&gt;Y&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Seo for taxonomy pages&lt;/td&gt;&lt;td&gt;N&lt;/td&gt;&lt;td&gt;Y&lt;/td&gt;&lt;/tr&gt;
&lt;tr class=&#34;alt&#34;&gt;&lt;td&gt;Configurable product search&lt;/td&gt;&lt;td&gt;Y, core&lt;/td&gt;&lt;td&gt;Y&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Bundled products for discount&lt;/td&gt;&lt;td&gt;Y, extension&lt;/td&gt;&lt;td&gt;Y&lt;/td&gt;&lt;/tr&gt;
&lt;tr class=&#34;alt&#34;&gt;&lt;td&gt;Recently viewed products&lt;/td&gt;&lt;td&gt;Y, extension&lt;/td&gt;&lt;td&gt;Y&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Soft product support/downloads&lt;/td&gt;&lt;td&gt;Y, extension&lt;/td&gt;&lt;td&gt;Y, I think so&lt;/td&gt;&lt;/tr&gt;
&lt;tr class=&#34;alt&#34;&gt;&lt;td&gt;Product comparison&lt;/td&gt;&lt;td&gt;Y, extension&lt;/td&gt;&lt;td&gt;Y&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Upsell&lt;/td&gt;&lt;td&gt;N&lt;/td&gt;&lt;td&gt;Y&lt;/td&gt;&lt;/tr&gt;
&lt;tr class=&#34;alt&#34;&gt;&lt;td&gt;Cross sell&lt;/td&gt;&lt;td&gt;N&lt;/td&gt;&lt;td&gt;Y&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Related items&lt;/td&gt;&lt;td&gt;Y, extension&lt;/td&gt;&lt;td&gt;Y&lt;/td&gt;&lt;/tr&gt;
&lt;tr class=&#34;alt&#34;&gt;&lt;td&gt;RSS feed of products&lt;/td&gt;&lt;td&gt;N&lt;/td&gt;&lt;td&gt;Y&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Multiple images per product&lt;/td&gt;&lt;td&gt;Y, core&lt;/td&gt;&lt;td&gt;Y&lt;/td&gt;&lt;/tr&gt;
&lt;tr class=&#34;alt&#34;&gt;&lt;td&gt;Product option selection (variants)&lt;/td&gt;&lt;td&gt;Y, core&lt;/td&gt;&lt;td&gt;Y&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Wishlist&lt;/td&gt;&lt;td&gt;Y, extension&lt;/td&gt;&lt;td&gt;Y&lt;/td&gt;&lt;/tr&gt;
&lt;tr class=&#34;alt&#34;&gt;&lt;td&gt;Send product email to friend&lt;/td&gt;&lt;td&gt;Y, extension&lt;/td&gt;&lt;td&gt;Y&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Product tagging / search by tagging&lt;/td&gt;&lt;td&gt;N&lt;/td&gt;&lt;td&gt;Y&lt;/td&gt;&lt;/tr&gt;
&lt;tr class=&#34;alt&#34;&gt;&lt;td&gt;Breadcrumbs&lt;/td&gt;&lt;td&gt;Y, core&lt;/td&gt;&lt;td&gt;Y&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h3 id=&#34;cms-features&#34;&gt;CMS Features&lt;/h3&gt;
&lt;table cellpadding=&#34;2&#34; cellspacing=&#34;0&#34; width=&#34;100%&#34;&gt;&lt;tbody&gt;
&lt;tr class=&#34;alt header&#34;&gt;&lt;td width=&#34;60%&#34;&gt;Features&lt;/td&gt;&lt;td width=&#34;20%&#34;&gt;Spree&lt;/td&gt;&lt;td&gt;Magento&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Blogging functionality&lt;/td&gt;&lt;td&gt;Y, extension&lt;/td&gt;&lt;td&gt;Y *extension&lt;/td&gt;&lt;/tr&gt;
&lt;tr class=&#34;alt&#34;&gt;&lt;td&gt;Static page management&lt;/td&gt;&lt;td&gt;Y, extension&lt;/td&gt;&lt;td&gt;Y&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Media management&lt;/td&gt;&lt;td&gt;N&lt;/td&gt;&lt;td&gt;Y&lt;/td&gt;&lt;/tr&gt;
&lt;tr class=&#34;alt&#34;&gt;&lt;td&gt;Contact us form&lt;/td&gt;&lt;td&gt;Y, extension&lt;/td&gt;&lt;td&gt;Y&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Polls&lt;/td&gt;&lt;td&gt;Y, extension&lt;/td&gt;&lt;td&gt;Y&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h3 id=&#34;checkout-support&#34;&gt;Checkout Support&lt;/h3&gt;
&lt;table cellpadding=&#34;2&#34; cellspacing=&#34;0&#34; width=&#34;100%&#34;&gt;&lt;tbody&gt;
&lt;tr class=&#34;alt header&#34;&gt;&lt;td width=&#34;60%&#34;&gt;Feature&lt;/td&gt;&lt;td width=&#34;20%&#34;&gt;Spree&lt;/td&gt;&lt;td&gt;Magento&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;One page checkout&lt;/td&gt;&lt;td&gt;N&lt;/td&gt;&lt;td&gt;Y&lt;/td&gt;&lt;/tr&gt;
&lt;tr class=&#34;alt&#34;&gt;&lt;td&gt;Guest checkout&lt;/td&gt;&lt;td&gt;Y, core&lt;/td&gt;&lt;td&gt;Y&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;SSL Support&lt;/td&gt;&lt;td&gt;Y, core&lt;/td&gt;&lt;td&gt;Y&lt;/td&gt;&lt;/tr&gt;
&lt;tr class=&#34;alt&#34;&gt;&lt;td&gt;Discounts&lt;/td&gt;&lt;td&gt;Y, core&lt;/td&gt;&lt;td&gt;Y&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Gift Certificates&lt;/td&gt;&lt;td&gt;N&lt;/td&gt;&lt;td&gt;Y&lt;/td&gt;&lt;/tr&gt;
&lt;tr class=&#34;alt&#34;&gt;&lt;td&gt;Saved Shopping Cart&lt;/td&gt;&lt;td&gt;N&lt;/td&gt;&lt;td&gt;Y&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Saved Addresses&lt;/td&gt;&lt;td&gt;Y, extension&lt;/td&gt;&lt;td&gt;Y&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h3 id=&#34;shipping-support&#34;&gt;Shipping Support&lt;/h3&gt;
&lt;table cellpadding=&#34;2&#34; cellspacing=&#34;0&#34; width=&#34;100%&#34;&gt;&lt;tbody&gt;
&lt;tr class=&#34;alt header&#34;&gt;&lt;td width=&#34;60%&#34;&gt;Feature&lt;/td&gt;&lt;td width=&#34;20%&#34;&gt;Spree&lt;/td&gt;&lt;td&gt;Magento&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Real time rate lookup (UPS, USPS, Fedex)&lt;/td&gt;&lt;td&gt;Y, extension&lt;/td&gt;&lt;td&gt;Y&lt;/td&gt;&lt;/tr&gt;
&lt;tr class=&#34;alt&#34;&gt;&lt;td&gt;Order tracking&lt;/td&gt;&lt;td&gt;N&lt;/td&gt;&lt;td&gt;Y&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Multiple shipments per order&lt;/td&gt;&lt;td&gt;Y, core&lt;/td&gt;&lt;td&gt;Y&lt;/td&gt;&lt;/tr&gt;
&lt;tr class=&#34;alt&#34;&gt;&lt;td&gt;Complex rate lookup&lt;/td&gt;&lt;td&gt;Y, extension&lt;/td&gt;&lt;td&gt;Y&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Free shipping&lt;/td&gt;&lt;td&gt;Y, extension&lt;/td&gt;&lt;td&gt;Y&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h3 id=&#34;payment-support&#34;&gt;Payment Support&lt;/h3&gt;
&lt;table cellpadding=&#34;2&#34; cellspacing=&#34;0&#34; width=&#34;100%&#34;&gt;&lt;tbody&gt;
&lt;tr class=&#34;alt header&#34;&gt;&lt;td width=&#34;60%&#34;&gt;Feature&lt;/td&gt;&lt;td width=&#34;20%&#34;&gt;Spree&lt;/td&gt;&lt;td&gt;Magento&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Multiple Payment Gateways&lt;/td&gt;&lt;td&gt;Y, core&lt;/td&gt;&lt;td&gt;Y&lt;/td&gt;&lt;/tr&gt;
&lt;tr class=&#34;alt&#34;&gt;&lt;td&gt;Authorize.net&lt;/td&gt;&lt;td&gt;Y, core&lt;/td&gt;&lt;td&gt;Y&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Authorize and capture versus authorize only&lt;/td&gt;&lt;td&gt;Y, core&lt;/td&gt;&lt;td&gt;Y&lt;/td&gt;&lt;/tr&gt;
&lt;tr class=&#34;alt&#34;&gt;&lt;td&gt;Google Checkout&lt;/td&gt;&lt;td&gt;Y, extension&lt;/td&gt;&lt;td&gt;Y&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Paypal Express&lt;/td&gt;&lt;td&gt;Y, extension&lt;/td&gt;&lt;td&gt;Y&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h3 id=&#34;admin-features&#34;&gt;Admin Features&lt;/h3&gt;
&lt;table cellpadding=&#34;2&#34; cellspacing=&#34;0&#34; width=&#34;100%&#34;&gt;&lt;tbody&gt;
&lt;tr class=&#34;alt header&#34;&gt;&lt;td width=&#34;60%&#34;&gt;Feature&lt;/td&gt;&lt;td width=&#34;20%&#34;&gt;Spree&lt;/td&gt;&lt;td&gt;Magento&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Sales reporting&lt;/td&gt;&lt;td&gt;Y, core&lt;/td&gt;&lt;td&gt;Y&lt;/td&gt;&lt;/tr&gt;
&lt;tr class=&#34;alt&#34;&gt;&lt;td&gt;Sales Management Tools&lt;/td&gt;&lt;td&gt;N&lt;/td&gt;&lt;td&gt;Y&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Inventory management&lt;/td&gt;&lt;td&gt;Y, core&lt;/td&gt;&lt;td&gt;Y&lt;/td&gt;&lt;/tr&gt;
&lt;tr class=&#34;alt&#34;&gt;&lt;td&gt;Purchase order management&lt;/td&gt;&lt;td&gt;N&lt;/td&gt;&lt;td&gt;Y&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Multi-tier pricing for quantity discounts&lt;/td&gt;&lt;td&gt;N&lt;/td&gt;&lt;td&gt;Y&lt;/td&gt;&lt;/tr&gt;
&lt;tr class=&#34;alt&#34;&gt;&lt;td&gt;Landing page tool&lt;/td&gt;&lt;td&gt;Y, extension&lt;/td&gt;&lt;td&gt;Y&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Batch import and export of products&lt;/td&gt;&lt;td&gt;Y, extension&lt;/td&gt;&lt;td&gt;Y&lt;/td&gt;&lt;/tr&gt;
&lt;tr class=&#34;alt&#34;&gt;&lt;td&gt;Multiple Sales reports&lt;/td&gt;&lt;td&gt;Y, core&lt;/td&gt;&lt;td&gt;Y&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Order fulfillment&lt;/td&gt;&lt;td&gt;Y, core&lt;/td&gt;&lt;td&gt;Y&lt;/td&gt;&lt;/tr&gt;
&lt;tr class=&#34;alt&#34;&gt;&lt;td&gt;Tax Rate Management&lt;/td&gt;&lt;td&gt;Y, core&lt;/td&gt;&lt;td&gt;Y&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h3 id=&#34;user-account-features&#34;&gt;User Account Features&lt;/h3&gt;
&lt;table cellpadding=&#34;2&#34; cellspacing=&#34;0&#34; width=&#34;100%&#34;&gt;&lt;tbody&gt;
&lt;tr class=&#34;alt header&#34;&gt;&lt;td width=&#34;60%&#34;&gt;Feature&lt;/td&gt;&lt;td width=&#34;20%&#34;&gt;Spree&lt;/td&gt;&lt;td&gt;Magento&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;User addresses&lt;/td&gt;&lt;td&gt;Y, extension&lt;/td&gt;&lt;td&gt;Y&lt;/td&gt;&lt;/tr&gt;
&lt;tr class=&#34;alt&#34;&gt;&lt;td&gt;Feature rich user preferences&lt;/td&gt;&lt;td&gt;N&lt;/td&gt;&lt;td&gt;Y&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Order tracking history&lt;/td&gt;&lt;td&gt;Y, core&lt;/td&gt;&lt;td&gt;Y&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h3 id=&#34;system-wide-features&#34;&gt;System Wide Features&lt;/h3&gt;
&lt;table cellpadding=&#34;2&#34; cellspacing=&#34;0&#34; width=&#34;100%&#34;&gt;&lt;tbody&gt;
&lt;tr class=&#34;alt header&#34;&gt;&lt;td width=&#34;60%&#34;&gt;Feature&lt;/td&gt;&lt;td width=&#34;20%&#34;&gt;Spree&lt;/td&gt;&lt;td&gt;Magento&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Extensibility&lt;/td&gt;&lt;td&gt;Y, core&lt;/td&gt;&lt;td&gt;Y&lt;/td&gt;&lt;/tr&gt;
&lt;tr class=&#34;alt&#34;&gt;&lt;td&gt;Appearance Theming&lt;/td&gt;&lt;td&gt;Y, core&lt;/td&gt;&lt;td&gt;Y&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Ability to customize appearance at category or browsing level&lt;/td&gt;&lt;td&gt;N&lt;/td&gt;&lt;td&gt;Y&lt;/td&gt;&lt;/tr&gt;
&lt;tr class=&#34;alt&#34;&gt;&lt;td&gt;Localization&lt;/td&gt;&lt;td&gt;Y, core&lt;/td&gt;&lt;td&gt;Y&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Multi-store, single admin support&lt;/td&gt;&lt;td&gt;Y, extension&lt;/td&gt;&lt;td&gt;Y&lt;/td&gt;&lt;/tr&gt;
&lt;tr class=&#34;alt&#34;&gt;&lt;td&gt;Support for multiple currencies&lt;/td&gt;&lt;td&gt;N&lt;/td&gt;&lt;td&gt;Y&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Web Service API&lt;/td&gt;&lt;td&gt;Y, core&lt;/td&gt;&lt;td&gt;Y&lt;/td&gt;&lt;/tr&gt;
&lt;tr class=&#34;alt&#34;&gt;&lt;td&gt;SEO System wide: sitemap, google base, etc&lt;/td&gt;&lt;td&gt;Y, extension&lt;/td&gt;&lt;td&gt;Y&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Google Analytics&lt;/td&gt;&lt;td&gt;Y, core&lt;/td&gt;&lt;td&gt;Y&lt;/td&gt;&lt;/tr&gt;
&lt;tr class=&#34;alt&#34;&gt;&lt;td&gt;Active community&lt;/td&gt;&lt;td&gt;Y, N/A&lt;/td&gt;&lt;td&gt;Y&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;The configurability and complexity of each feature listed above varies. Just because a feature is provided within a platform does not guarantee that it will meet the desired business needs. Magento serves as a more comprehensive ecommerce platform out of the box, but the disadvantage may be that adding custom functionality may require more resources (read: more expensive). Spree serves as a simpler base that may encourage quicker (read: cheaper) customization development simply because it’s in Rails and because the dynamic nature of Ruby allows for elegant extensibility in Spree, but a disadvantage to Spree could be that a site with a large amount of customization may not be able to take advantage of community-available extensions because they may not all play nice together.&lt;/p&gt;
&lt;p&gt;Rather than focus on the platform features, the success of the development depends on the developer and his/her skillset. Most developers will say that &lt;strong&gt;any&lt;/strong&gt; of the features listed above are doable in Magento, Spree, or Interchange (a Perl-based ecommerce platform that End Point supports) with an unlimited budget, but a developer needs to have an understanding of the platform to design a solution that is easily understood and well organized (to encourage readability and understandability by other developers), develop with standard principles like DRY and MVC-style separation of concerns, and elegantly abstract from the ecommerce core to encourage maintainability. And of course, be able to understand the business needs and priorities to guide a project to success within the given budget. Inevitably, another developer will come along and need to understand the code and inevitably, the business will often use an ecommerce platform longer than planned so maintainability is important.&lt;/p&gt;
&lt;p&gt;Please feel free to comment on any errors in the feature list. I’ll be happy to correct any mistakes. Now, off to rest before RailsConf!&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Spree at RailsConf</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2009/05/spree-at-railsconf/"/>
      <id>https://www.endpointdev.com/blog/2009/05/spree-at-railsconf/</id>
      <published>2009-05-11T00:00:00+00:00</published>
      <author>
        <name>Steph Skardal</name>
      </author>
      <content type="html">
        &lt;p&gt;Last week at &lt;a href=&#34;https://conferences.oreilly.com/rails2009&#34;&gt;RailsConf 2009&lt;/a&gt;, the Spree folks from End Point conducted a &lt;a href=&#34;https://conferences.oreilly.com/rails2009/public/schedule/stype/Bof&#34;&gt;Birds of a Feather&lt;/a&gt; session to discuss Spree, an End Point sponsored open source rails ecommerce platform. Below is some of the dialog from the discussion (paraphrased).&lt;/p&gt;
&lt;p&gt;Crowd: “How difficult is it to get Spree up and running from start to finish?”&lt;/p&gt;
&lt;p&gt;Spree Crew: “This depends on the level of customization. If a customer simple needs to reskin the site, this shouldn’t take more than a week (hopefully much less than a full week). If the customer needs specific functionality that is not included in core functionality or extensions, you may need to spend some time developing an &lt;a href=&#34;https://web.archive.org/web/20090513101129/http://wiki.github.com/schof/spree/extensions&#34;&gt;extension&lt;/a&gt;.”&lt;/p&gt;
&lt;p&gt;Crowd: “How difficult is it to develop extensions in Spree?”&lt;/p&gt;
&lt;p&gt;Spree Crew: “Spree extension work is based on the work of the &lt;a href=&#34;http://radiantcms.org/&#34;&gt;Radiant&lt;/a&gt; community. Extensions are mini-applications: they allow you to drop a pre-built application into spree to override or insert new functionality. Documentation for extensions is available at the &lt;a href=&#34;https://web.archive.org/web/20090513101129/http://wiki.github.com/schof/spree/extensions&#34;&gt;spree github wiki&lt;/a&gt;. We also plan to release more extensive Spree Guides documentation based on &lt;a href=&#34;http://guides.rubyonrails.org/&#34;&gt;Rails Guides&lt;/a&gt; soon.”&lt;/p&gt;
&lt;p&gt;Spree Crew: “How did you hear about Spree?”&lt;/p&gt;
&lt;p&gt;Crowd: “My client and I found it via search engines. My client thought that Spree looked like a good choice.”&lt;/p&gt;
&lt;p&gt;Spree Crew: “What other platforms did you consider before you found spree?”&lt;/p&gt;
&lt;p&gt;Crowd: “Magento”, “Substruct”, “My client considered Magento, but I know several people that have developed with Magento and have found it difficult to override core functionality.”&lt;/p&gt;
&lt;p&gt;Spree Crew: “What types of functionality were missing from Spree that you’d like to see developed in the future?”&lt;/p&gt;
&lt;p&gt;Crowd: “My client wanted checkout split into multiple steps instead of the new single page checkout. I was able to implement this by overriding the Spree checkout library and checkout views.”, “My client needed complex inventory management.”, “My client needed split shipping functionality.”&lt;/p&gt;
&lt;p&gt;Crowd: “What is the plan for Spree with regards to CMS development?”&lt;/p&gt;
&lt;p&gt;Spree Crew: “There has been some discussion on the &lt;a href=&#34;https://web.archive.org/web/20121016021052/http://groups.google.com/group/spree-user/search?q=cms&#34;&gt;integration of a CMS into Spree&lt;/a&gt;. No one in the Spree community appears to be currently working on this. Contributions in this area are welcome. Also, Yehuda Katz is giving a talk on &lt;a href=&#34;https://conferences.oreilly.com/rails2009/public/schedule/detail/7785&#34;&gt;mountable apps&lt;/a&gt;—​the Spree community would like to investigate the implications this has for Spree.”&lt;/p&gt;
&lt;p&gt;Crowd: “What are the next steps for localization, especially multilingual product descriptions?”&lt;/p&gt;
&lt;p&gt;Spree Crew: “This is on the radar for future Spree development. It is not currently in development, and again, contributions in this area are welcome.”&lt;/p&gt;
&lt;p&gt;From the discussion, I took away that some of the desired features for Spree are inventory management, split shipping functionality, cms integration, and improved localization. I hope that the application of Spree continues to contribute to it’s progress. The Spree Crew also hopes to showcase some of the sites referenced above at the spree site.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>SEO Ecommerce</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2009/04/ecommerce-and-seo/"/>
      <id>https://www.endpointdev.com/blog/2009/04/ecommerce-and-seo/</id>
      <published>2009-04-20T00:00:00+00:00</published>
      <author>
        <name>Steph Skardal</name>
      </author>
      <content type="html">
        &lt;p&gt;I recently read an article that discusses &lt;a href=&#34;https://web.archive.org/web/20090311033516/http://yoast.com/magento-seo/&#34;&gt;Magento SEO&lt;/a&gt; problems and solutions. This got me to think about common search engine optimization issues that I’ve seen in e-commerce. Below are some highlighted e-commerce search engine optimization issues. The &lt;a href=&#34;https://github.com/spree/demo&#34;&gt;Spree Demo&lt;/a&gt;, &lt;a href=&#34;http://demo.icdevgroup.org/i/demo1&#34;&gt;Interchange Demo&lt;/a&gt;, and &lt;a href=&#34;https://web.archive.org/web/20090716030438/http://demo.magentocommerce.com/&#34;&gt;Magento Demo&lt;/a&gt; are used as references.&lt;/p&gt;
&lt;h3 id=&#34;duplicate-home-pages-www-non-www-indexhtml&#34;&gt;Duplicate Home Pages (www, non-www, index.html)&lt;/h3&gt;
&lt;p&gt;Duplicate home pages can come in the form of a homepage with www and without www, a homepage in the form of http://www.domain.com/ and a homepage with some variation of “index” appended to the url, or a combination of the two. In the Interchange demo, &lt;a href=&#34;http://demo.icdevgroup.org/i/demo1&#34;&gt;http://demo.icdevgroup.org/i/demo1&lt;/a&gt; and &lt;a href=&#34;http://demo.icdevgroup.org/i/demo1/index.html&#34;&gt;http://demo.icdevgroup.org/i/demo1/index.html&lt;/a&gt; are duplicate, http://demo.spreecommerce.com/ and http://demo.spreecommerce.com/products/ in the Spree demo, and finally http://demo.magentocommerce.com/ and http://demo.magentocommerce.com/index.php in the Magento demo.&lt;/p&gt;
&lt;p&gt;External links positively influence search engine performance more if they are pointing to one index page rather than being divided between two or three home pages. Since the homepage most likely receives the most external links, this issue can be more problematic than other generated duplicate content. I’ve also seen this happen in several content management systems.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://web.archive.org/web/20090311033516/http://yoast.com/magento-seo/&#34;&gt;This article&lt;/a&gt; provides directions on mod_rewrite use to apply a 301 redirect from the www.domain.com/index.php homepage to www.domain.com. This solution or other redirect solutions can be applied to Spree, Interchange, and other ecommerce platforms.&lt;/p&gt;
&lt;h3 id=&#34;irrelevant-product-urls&#34;&gt;Irrelevant Product URLs&lt;/h3&gt;
&lt;p&gt;A search engine optimization best practice is to provide relevant and indicative text in the product urls. In the Interchange demo, the default catalog uses the product sku in the product url (http://demo.icdevgroup.org/i/demo1/os28073.html). In Magento and Spree, product permalinks with relevant text are used in the product url. In wordpress, the author has the ability to set permalinks for articles. I am unsure if Magento gives you the ability to customize product urls. Spree does not currently give you the ability to manage custom product permalinks. However, for all of these ecommerce platforms, these fixes may all be in the works since it is important for ecommerce platforms to implement search engine optimization best practices.&lt;/p&gt;
&lt;h3 id=&#34;duplicate-product-content&#34;&gt;Duplicate product content&lt;/h3&gt;
&lt;p&gt;I’ve observed several situations where products divided into multiple taxonomies results in duplicate content creation via different user navigation paths. For example, in the Spree demo, the “Ruby Baseball Jersey” can be reached through the Ruby brand page, the Clothing page, or the homepage. The three generated duplicate content urls are http://demo.spreecommerce.com/products/ruby-on-rails-ringer-t-shirt, http://demo.spreecommerce.com/t/brands/ruby/p/ruby-baseball-jersey, and http://demo.spreecommerce.com/t/categories/clothing/shirts/p/ruby-baseball-jersey.&lt;/p&gt;
&lt;p&gt;Another example of this can be found in the Interchange demo. The left navigation taxonomy tree provides links to any product url with “?open=X,Y,Z” appended to the url. The “open” query string indicates how the DHTML tree should be displayed. For example, the “Digger Hand Trencher” has a base url of http://demo.icdevgroup.org/i/demo1/os28076.html. Depending on which tree nodes are exploded, the product can be reached at http://demo.icdevgroup.org/i/demo1/os28076.html?open=0,11,13,19, http://demo.icdevgroup.org/i/demo1/os28076.html?open=0,11,13, etc. This standard demo functionality yields a lot of duplicate content.&lt;/p&gt;
&lt;p&gt;In Magento, products are the in the form of www.domain.com/product-name, although the article I mentioned above mentions that www.domain.com/category/product.html product urls were generated. Perhaps this was a recent fix, or perhaps the demo is configured to avoid generating this type of duplicate content.&lt;/p&gt;
&lt;p&gt;Duplicate product page content is often used to indicate which breadcrumb should display or to track user click-through behavior (for example, did a user click on a “featured product”? a “best seller”? a specific “product advertisement”?). In Interchange, session ids are appended to urls which is another source of duplicate content. Instead of using the url to track user navigation or behavior, several other solutions such as using cookies, using a ‘#’ (hash), or using session data can be used to avoid duplicate content generation.&lt;/p&gt;
&lt;h3 id=&#34;performance&#34;&gt;Performance&lt;/h3&gt;
&lt;p&gt;Performance should not be overlooked in ecommerce for search engine optimization. In March of 2008, Google wrote about &lt;a href=&#34;https://adwords.googleblog.com/2008/03/landing-page-load-time-will-soon-be.html&#34;&gt;how landing page load time will be incorporated into the Quality Score for Google Adwords&lt;/a&gt;—​which is also believed to apply to regular search results. And github recently released some data on &lt;a href=&#34;https://github.com/blog/368-keeping-googlebot-happy&#34;&gt;how performance improvements influenced http://www.github.com/ Googlebot visits&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Keeping a high content to text ratio, consolidation, minification, and gzipping CSS and JavaScript, and minimizing the use of JavaScript-based suckerfish can all improve search engine performance.&lt;/p&gt;
&lt;p&gt;The Interchange default catalog has a simple template with minimal css and javascript includes, so the developer is responsible for sticking to best performance practices. The Magento demo appears to have decent content to text ratio, but still requires 5 css files that should be consolidated and minified if they are included on every page. Finally, Spree has undergone some changes in the last month and is moving in the direction of including one consolidated javascript file plus any javascript required for extensions on every page, and the &lt;a href=&#34;https://groups.google.com/forum/#!topic/spree-user/gB3hZ3sgLpw&#34;&gt;upcoming release of Spree 0.8.0&lt;/a&gt; will have considerable frontend view improvements.&lt;/p&gt;
&lt;p&gt;Ecommerce platforms should have decent performance—​&lt;a href=&#34;http://yslow.org/&#34;&gt;yslow&lt;/a&gt; or &lt;a href=&#34;http://shop.oreilly.com/product/9780596529307.do&#34;&gt;this book on high performance website essentials&lt;/a&gt; are good resources.&lt;/p&gt;
&lt;h3 id=&#34;lacking-basic-cms-management&#34;&gt;Lacking basic CMS management&lt;/h3&gt;
&lt;p&gt;Basic CMS management such as the ability to manage and update page titles and page meta data is something that has been overlooked by ecommerce platforms in the past, but appears to have been given more attention recently. An ecommerce solution should also have functionality to create and manage static pages.&lt;/p&gt;
&lt;p&gt;The Interchange demo does not have meta description and keyword functionality, however, page titles are equal to product names which is an acceptable default. It’s also very simple to add a static content page (as a developer) and would require just a bit more effort to have this content managed by a database in Interchange. The Spree core is missing some basic CMS management such as page title and meta data management, but this functionality is currently in development. One Spree contributer developed &lt;a href=&#34;https://web.archive.org/web/20090619060215/http://github.com/PeterBerkenbosch/spree-static-content/tree/master&#34;&gt;a Spree extension that provides management of simple static pages using a WYSIWYG editor&lt;/a&gt;. At the moment, Magento appears to have the most traditional content management system functionality out of the box.&lt;/p&gt;
&lt;p&gt;Another area to improve CMS within Ecommerce is to determine a solution to integrate a blog. A quick Google search of “magento add blog” revealed &lt;a href=&#34;https://web.archive.org/web/20090508093030/http://chasesagum.com/setup-a-blog-inside-your-magento-store&#34;&gt;how to set up a wordpress blog in Magento with an extension&lt;/a&gt;. One of End Point’s clients, &lt;a href=&#34;https://www.ccibeauty.com/&#34;&gt;CCI Beauty&lt;/a&gt;, also has &lt;a href=&#34;https://www.ccibeauty.com/blog/&#34;&gt;wordpress integrated into their Interchange setup&lt;/a&gt;. Finally, there has been discussion about the development of “Spradiant”, or &lt;a href=&#34;https://groups.google.com/forum/#!searchin/spree-user/radiant%7Csort:date&#34;&gt;mixing spree and radiant&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Another missed opportunity in ecommerce platforms is finding a solution to elegantly blend content and product listings to target specific keywords. A “landing page” can have a page title, meta data, and content targeted towards a specific terms. http://www.backcountry.com/store/gear/arcteryx-vests.html and http://www.backcountry.com/store/gear/cargo-pant.html are examples of targeted terms with corresponding products. Going one step farther, search pages themselves can have managed content to attract keywords, such as a page title, and meta data for specific high traffic keywords with the related products. For example, http://www.domain.com/s/ruby_shirt could be a search page for “Ruby Shirt” which contains meaningful content and relevant products.&lt;/p&gt;
&lt;h3 id=&#34;mishandled-product-pagination&#34;&gt;Mishandled Product Pagination&lt;/h3&gt;
&lt;p&gt;Finding a search engine optimization solution for pagination can be a difficult problem in ecommerce. When there are less than 100 products for a site, this shouldn’t be an issue because a simple taxonomy can appropriately group the products with low crawl depth. A website with 10,000 products must balance between keeping a low taxonomy depth to minimize crawl depth and ensure that all products are listed and indexable.&lt;/p&gt;
&lt;p&gt;For example, products may be divided and fit into three levels of navigation: category, subcategory, and group. If there are 10,000 products, divided into 10 categories, 10 subcategories per category, and 10 product groups per category, 10 products can be shown on each group per page with no pagination. However, product taxonomy is not always so ideal. In some groups there may be 2 products and in others there may be 30. Pagination, or pages with an offset of product listings are generated to accommodate these product listings (for example, http://www.backcountry.com/store/group/61/Sun-Hats-Rain-Hats-Safari-Hats.html, http://www.backcountry.com/store/group/61/Sun-Hats-Rain-Hats-Safari-Hats-p1.html).&lt;/p&gt;
&lt;p&gt;A few problems can arise from the pagination solution. First, by web 2.0 standards, the content should be generated via ajax. An SEO friendly ajax solution must be implemented—​where the onclick event refreshes the content, but the links are still crawlable via search engine bots. Second, page 1 with no product offset will have 1 level less of crawl depth, therefore it will receive the most link juice from it’s parent page (subcategory). As a result, there must be thoughtful analysis of which products to present on that page: should high traffic pages get the traffic? should popular items be listed on the first page? should low traffic products be listed to try to bump the traffic on those pages? should products with the most “user interaction” (reviews, qna, ratings) be shown on that page? Another problem that comes up is that the page meta data and title will most likely be very similar since the content is a list of similar products. These two pages can essentially be competing for traffic and may be counted as duplicate content if the page titles and meta data are equal.&lt;/p&gt;
&lt;p&gt;Interchange uses the &lt;a href=&#34;http://docs.icdevgroup.org/cgi-bin/online/tags/more_list.html&#34;&gt;more list&lt;/a&gt; to handle pagination, but this functionality is not search engine friendly as it generates urls such as http://demo.icdevgroup.org/i/demo1/scan/MM=3ffffa066192cba677e1428d7461ddc9:10:19:10.html?mv_more_ip=1&amp;amp;mv_nextpage=results&amp;amp;mv_arg=, http://demo.icdevgroup.org/i/demo1/scan/MM=3ffffa066192cba677e1428d7461ddc9:20:27:10.html?mv_more_ip=1&amp;amp;mv_nextpage=results&amp;amp;mv_arg=, etc. The Spree demo had some pagination implementation, but upon recent frontend changes, it is no longer included in the demo. The Magento demo was carefully arranged so that product group pages have no more than 9 products to avoid showing any pagination functionality. However, when modifying the number of products displayed per group or using the “Sort By” mechanism, ?limit=Y and &amp;amp;order=X&amp;amp;dir=asc is appended to the url—​which can produce a large volume of duplicate content (try filters on &lt;a href=&#34;https://web.archive.org/web/20090227041133/http://demo.magentocommerce.com/apparel/shirts&#34;&gt;this page&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;It is difficult to determine which of the above problems is the most problematic. From personal experience, I have been involved in tackling all duplicate content issues, and then moving on to “optimization” opportunities such as enhancing the content management system. At the very least, developers and users of any ecommerce platform should be aware of common search engine optimization issues.&lt;/p&gt;

      </content>
    </entry>
  
</feed>
