It's Over Magento!

So I’ve always had a hate-hate relationship with Magento (there never was love). Ours was a convenient relationship; I needed something to run an online bookstore we wanted to start, and all the other solutions we looked at didn’t fit the bill. We wanted to accept payments in Trinidad and Tobago dollars, something you don’t get easier with hosted solutions. We also wanted something that had broad support and a variety of templates. Let’s be frank; we settled for Magento.

Getting setup with Magento was no work in the park. It took 3 developers a couple hundred hours to setup the instance just right. We tuned and hardened that install till we got it to run just how we wanted. We even wrote our own module to get the payments just how we wanted it. It was a learning experience, and one might have imagined we’d have learned our lesson.

Fast-forward two years, and we’re now looking at helping other businesses establish their online presence. Setting up something quick and offering a wide array of templates to choose from is still critical. We’ve been approached to help folks set up their e-commerce presence, and a quote to build a bespoke system is eye-watering. The challenge for us is how do we balance making something affordable while keeping the lights on? I had the bright idea to look at Magento again. In my defense, I was coming off the high of exorcising old PHP demons. I’d gotten this sweet setup using Docker, where I was practicing Test-Driven Development (TDD), doing Domain Driven Design (DDD), and able to step through and debug code. If I could do it with Symfony, I could do it with Magento, right? (TLDR; NOPE! Magento is a piece of crap and this post is me throwing in the towel)

Docker

Using the same principles to get set up with Magento did help in the beginning. I set up Docker containers for my Magneto install (something we were unable to achieve in our first foray). The image for the webserver container looked something like this:

FROM php:7.4-apache
RUN pecl install xdebug-3.0.1
RUN docker-php-ext-enable xdebug
RUN echo "xdebug.mode=debug" >> /usr/local/etc/php/php.ini
RUN apt-get update && apt-get install -y \
        libfreetype6-dev \
        libjpeg62-turbo-dev \
        libpng-dev \
        git \
        zlib1g-dev \
        libzip-dev \
        unzip \
        libjpeg62-turbo-dev \
        libmcrypt-dev \
        libedit-dev \
        libedit2 \
        libxslt1-dev \
        libonig-dev \

    && docker-php-ext-configure gd --with-freetype --with-jpeg\
    && docker-php-ext-install -j$(nproc) gd \
    && docker-php-ext-install zip \
    && docker-php-ext-install mysqli && docker-php-ext-enable mysqli \
    && docker-php-ext-install pdo pdo_mysql \
    && docker-php-ext-install opcache bcmath intl mbstring soap xsl sockets
RUN a2enmod rewrite && service apache2 restart
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
COPY ./composer.json /var/www/html/composer.json
COPY ./bin /var/www/html/bin
COPY ./app/bootstrap.php /var/www/html/app/bootstrap.php
COPY ./auth.json /var/www/html/auth.json
RUN composer install

The Docker compose file that setup the other services used 3rd party images and looked like this:

version: "3.1"
services:
  redis:
    image: redis:3.2
  db:
    image: mysql:8
    restart: always
    environment:
      - MYSQL_ROOT_PASSWORD=$MYSQL_ROOT_PASSWORD
      - MYSQL_DATABASE=$MYSQL_DATABASE
      - MYSQL_USER=$MYSQL_USER
      - MYSQL_PASSWORD=$MYSQL_PASSWORD
    ports:
      - "3306:3306"
  testdb:
    image: mysql:8
    restart: always
    environment:
      - MYSQL_ROOT_PASSWORD=$MYSQL_ROOT_PASSWORD
      - MYSQL_DATABASE=$MYSQL_DATABASE
      - MYSQL_USER=$MYSQL_USER
      - MYSQL_PASSWORD=$MYSQL_PASSWORD
    ports:
      - "3307:3306"
  selenium:
#    hostname: selenium.magento2.docker
    image: 'selenium/standalone-chrome:latest'
    ports:
      - 4444
    environment:
      - JAVA_OPTS=-Dwebdriver.chrome.whitelistedIps=
    depends_on:
      webserver:
        condition: service_started
  webserver:
    build: .
    ports:
      - "6080:80"
    volumes:
      - /Users/<User Name>/PhpstormProjects/MyModule:/var/www/html/app/code/MyProject/MyModule
      - ./composer.json:/var/www/html/composer.json
      - ./dev:/var/www/html/dev
      - ./app/design:/var/www/html/app/design
      - ./app/etc:/var/www/html/app/etc
      - ./app/autoload.php:/var/www/html/app/autload.php
      - ./app/bootstrap.php:/var/www/html/app/bootstrap.php
      - ./pub:/var/www/html/pub
    environment:
      APACHE_DOCUMENT_ROOT: /var/www/html/public
      #change the line below with your IP address
      XDEBUG_CONFIG: remote_host=192.168.1.100
    depends_on:
      - db
      - es01
      - redis
  es01:
    image: elasticsearch:7.9.3
    environment:
      - xpack.security.enabled=false
      - discovery.type=single-node
      - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
    ports:
      - 9200:9200
      - 9300:9300

Initially, I mounted the project root to the document root, but Docker on Mac performs terribly with PHP. There is an issue with the file system that disproportionately affects PHP projects because of the many files that get called on each request. The fix? Install composer in the image and install dependencies in the container. Of course, this move was not without consequences. I had to now be a bit more specific about what I was mounting. The Magento framework dependencies are not simply in the public packagaist repo but in Adobe’s own repo; so I had to get the credentials in the container in a way that I’m not proud of.

We are working on a module for Magento that is in its own repo. To get things in the right place I mounted the module to the app/code folder through Docker. I tried symlinks early on but that didn’t work (you have to do some configuring to get Magento to be happy with symlinks I believe)

The other problem I had with Magento and Docker is that the project requires a lot of memory. Magento 2.4 now requires Elasticsearch (damn it, Adobe) and it kept crashing during specific commands because the Elasticsearch container kept running out of memory. The fix for that issue was to set up a public Elasticsearch instance for myself and another developer to use (every step I took, I felt dirtier and dirtier).

Install

The install was pretty easy, all things considered. I did have some issues at first landing the exact config I wanted, but once I got it (and documented it), I installed and re-installed it a few times. There is an issue doing an install with a app/etc/env.php already being there. Also, because of the Elasticsearch requirement the install would error out if the Elastichsearch container went down during the install (the memory hog that is PHP I had to run the command without memory limits).

TDD

It is possible to be test-driven with Magento. On the surface, it shouldn’t be too bad; there are examples of unit, functional and integration tests. The problem is the beast is so big that it’s tricky to wrap your hands around it. I got unit tests going but to be honest, unit tests weren’t really useful here because I had to mock so much and what we were working on didn’t really have much business logic to make the unit tests valuable. This meant I had to turn to integration tests. Integration tests was the straw that broke the camel’s back. After patiently wrangling things, hacks to get the commands to run in the container, I hit some weird error that it couldn’t instantiate an interface (of course you can’t and that’s usually indicative of something wrong with the dependency injection that should be fixed with a quick jump start using bin/magento setup:di:compile but no dice). Even if I got it running, it would have been after setting it to do a clean up after each test which guaranteed it would run like crap.

I also had a look at setting up functional tests using the MFT framework and long story short that too was a waste of time. Got selenium etc setup but never got even the core tests running without issue.

DDD

Generally, Magento is built on DDD principles that should be helpful, but the abstractions are too much to bear. At some point, you have to commit to something. I hated it in Java, and it was sad to see the disease spread to PHP. I get it; Java developers started the Magento project but have we forgotten what to do when in Rome? I don’t like this implementation of Model View ViewModel (MVVM), or maybe I don’t like MVVM at all, I don’t know. I like that everything was a module and that modules have a standard structure, I disliked the module’s complexity.

Community

I wasn’t there for the early days of Magento but I suspect Adobe being involved hasn’t all been suitable for the project. Everything is Adobe branded, but I don’t get that it’s good for users (or their brand, frankly). The documentation that Adobe has put together just makes you go in circles. I don’t think the community pay off is there because modules for Magento community are not equal to the WordPress community. The themes tend to be very expensive compared to other platforms. It feels like everything they do is geared towards pushing you to their own cloud-hosted solution that is really expensive.

That said, I found some excellent resources that helped me move the football down the field, and I’m glad for that.

Conclusion

This direction wasted a ton of my time and one of my developer’s time (this was the poor chap’s introduction to PHP). I really really wanted this to work but the amount of time spent getting set up to do “basic” things that we’re accustomed to is too much. Since picking up Go I’ve been really critical of PHP. I try not to be dogmatic, hell I recently changed my stance on Wordpress after looking at it again for an episode of LinkWe. That said I’m writing this post as a deterrent to future me or any other member of my team from bringing up Magento as a viable solution. It doesn’t make sense for us and we’d probably be better off writing applications ourselves in Go.

I heart open source but projects like Magento scar developers. I think broadly, it’s interesting that so much projects are in PHP. I could understand the historical reason, but it’s time to move on, I guess. Not saying we are the ones to make the waves but based on how painful it has been to find suitable open source projects; it might be time for the Wepala team to double down (triple down) and start cranking out stuff that makes sense for us, our customers, and the wider community.

Additional Links