Getting started with a Magento 2 module

I’ve been using Magento 2 for a few months now. I was lucky to be involved in the merchant_beta program and at the developer meetings in Berlin early last year. Yet lots has changed in the way we develop modules since I started working on the framework almost a year ago. One of the big changes is that now modules can be loaded from any location and require more configuration to setup.

Lets start by creating a new folder within app/code called Jcowie. Think of this as our top namespace.

Now within Jcowie create a new folder for HelloWorldModule This is going to be our actual working Module code. Now I’m still playing with ideas here as there is no technical reason why we need the Module suffix. Yet there are times where I will also have a HelloWorld namespace that is framework agnostic code that I will inject into the Magento 2 code.

Next up we need to add a composer.json manifest file. This is new to Magento 2 but not new to PHP. If you don’t know what composer is don’t worry there are many good guides on the internet and it WILL make you happy.

{
  "name": "Jcowie/HelloWorldModule",
  "description": "A Magento 2 module that creates a new page",
  "type": "magento2-module",
  "version": "1.0.0",
  "license": [
    "OSL-3.0",
    "AFL-3.0"
  ],
  "require": {
    "php": "~5.5.0|~5.6.0|~7.0.0",
    "magento/framework": "~100.0"
  },
  "autoload": {
    "files": [ "registration.php" ],
    "psr-4": {
      "Jcowie\\HelloWorldModule\\": ""
    }
  }
}

Now there are some really important parts of this file to consider and make sure when you create your own modules you do the same. First key part is the type

"type": "magento2-module",

When Magento-Composer parses the files it looks for this type to ensure that both hard and soft dependencies can be read and processed. Ill look into this more in another post but for now just remember this is what makes a Magento 2 module different to a regular ol PHP module.

The final part of configuration that we need to add is the autoloading, this is what Magento 2 will parse when composer runs to ensure this module is added to the autoloader class and its functionality added into Magento 2:

"autoload": {
  "files": [ "registration.php" ],
  "psr-4": {
    "Jcowie\\HelloWorldModule\\": ""
  }
}

There is nothing too complex going on here. Using the composer standard we set a autoload node and define our namespace as Jcowie\\HelloWorldModule and where the src files should be loaded from. In this case this directory. We then tell composer that there is a special file registration.php that has a purpose and needs to be ran to complete the autoloading process.

<?php

\Magento\Framework\Component\ComponentRegistrar::register(
    \Magento\Framework\Component\ComponentRegistrar::MODULE,
    'Jcowie_HelloWorldModule',
    __DIR__
);

Now this file almost never changes for all of the modules you will write, Just remember to update the Module name with yours.

Finally to make sure that Magento 2 knows about our module and when running php bin/magento setup:upgrade we know that it will load and process our module we need to update our projects composer.json file. Now this step depends on how you are building a module. This guide assumes that you are working on what I call a ‘Client Project’ where there is no shareable code so it will be added as part of the project itself. If you think or are working on a Module for the community then the workflow will be different.

"autoload": {
        "psr-4": {
            "Jcowie\\": "app/code/Jcowie/"
        },
    },

All we are doing here is to register the Jcowie namespace and where the files for this are located. Running composer dump-autoload will ensure all of the class maps etc are re generated and ready for the application. Finally if we run php bin/magento setup:upgrade we will see in the output that our module is now registered and just to confirm open up app/etc/config.php and you will see a reference to our new module.

What im working on next is how we can put our code anywhere we want. With the power we get from PSR autoloading we should be able to load code directly from src or any location we specify.

How to use PHPSpec and Magento 2 a quick start

Using PHPSpec in Magento 1 was never an easy task. There was no Dependency Injection so it made it hard to actually inject dependencies into our classes. This in turn had the knock on effect of forcing us to actually test the classes our subjects were being used in. Whats the big deal I hear you say? Well. Why should we have to bootstrap the entire framework just to unit test a piece of behavior we are developing? Most of the time ( and if we are designing ) our code well we should be de coupled from the framework so there is a clear divide between business logic and framwework code. more on this in future posts

Now this has all changed, Setting up and using PHPSpec in our code is simples. Lets start by using composer :) Yes composer to install the dependencies into our project.

Open up your composer.json manifest for the project ( if you are using the docker images I am then the sources files are located in src folder.) Hunt down the line that defines require-dev and add in:

"phpspec/phpspec": "2"

Save the file and a quick composer update and we will have bin/phpspec at our disposal.

Now we have our unit testing tool installed lets have a talk about what we are going to build. Well I wanted to show something that is simple and as we are learning PHPSpec and de coupled design here I didn’t want to have to cover lots of Magento 2 concepts. So we are going to add a new command to bin/magento that will say Hello $name; not what you might call rocket science and probably violates all of the design principles for over engineering but meh.

Open up your terminal and run ./bin/phpspec describe Jcowie/HelloWorld/Model/HelloWorld what we are saying here is that we want to describe a new class called HelloWorld. Now if you followed the previous guide and you should then you will notice that I am describing code within HelloWorld yet the module we created previously was called HelloWorldModule. Now the reason is because I want to show how we can decouple framework agnostic code from code that is bound to the framework.

Lets run the spec ./bin/phpspec r select yes to get all the code generation. Lets look at the output.

~/P/M/m/src ❯❯❯ phpspec r                                                                                                                                                        master ✚ ✱ ◼

      Jcowie\HelloWorld\Model\HelloWorld

  10  ! is initializable
        class Jcowie\HelloWorld\Model\HelloWorld does not exist.

----  broken examples

        Jcowie/HelloWorld/Model/HelloWorld
  10  ! is initializable
        class Jcowie\HelloWorld\Model\HelloWorld does not exist.


1 specs
1 examples (1 broken)
19ms

  Do you want me to create `Jcowie\HelloWorld\Model\HelloWorld` for you?
                                                                         [Y/n]
Y
Class Jcowie\HelloWorld\Model\HelloWorld created in /Users/jamescowie/Projects/Magento2/magento2-docker-compose/src/app/code/Jcowie/HelloWorld/Model/HelloWorld.php.


      Jcowie\HelloWorld\Model\HelloWorld

  10  ✔ is initializable


1 specs
1 examples (1 passed)
16ms

Great so looking at the output PHPSpec has found that the class HelloWorld did not exist under app/code and it has created this for us. Lets write our first spec also known as a unit test. Open up the spec file and add in:

/**
<?php
class HelloWorldSpec extends ObjectBehavior
{
     * should return hello world
     */
    function it_should_return_hello_world()
    {
        $this->sayHello()->shouldReturn('Hello World');
    }
}

To find out more about PHPSpec and how we write specs Allan MacGregor has a great MageCast on this. But what we are saying is that when we call the method sayHello it should return us Hello World.

Running PHPSpec now ( ./bin/phpspec r) we should see some more information:

phpspec run                                                                                                                                                    ⏎ master ✚ ✱ ◼

      Jcowie\HelloWorld\Model\HelloWorld

  10  ✔ is initializable
  18  ! should return hello world
        method Jcowie\HelloWorld\Model\HelloWorld::sayHello not found.

----  broken examples

        Jcowie/HelloWorld/Model/HelloWorld
  18  ! should return hello world
        method Jcowie\HelloWorld\Model\HelloWorld::sayHello not found.


1 specs
2 examples (1 passed, 1 broken)
25ms

  Do you want me to create `Jcowie\HelloWorld\Model\HelloWorld::sayHello()`
  for you?
                                                                         [Y/n]
Y
  Method Jcowie\HelloWorld\Model\HelloWorld::sayHello() has been created.


      Jcowie\HelloWorld\Model\HelloWorld

  10  ✔ is initializable
  18  ✘ should return hello world
        expected "Hello World", but got null.

----  failed examples

        Jcowie/HelloWorld/Model/HelloWorld
  18  ✘ should return hello world
        expected "Hello World", but got null.


1 specs
2 examples (1 passed, 1 failed)
15ms

Great what is this saying. Well PHPSpec cant find the method in the class for sayHello so it has asked if we want PHPSpec to create it for us. Finally it ran the tests and all failed as the method expected us to return a string but got null. In the Red Green Refactor TDD loop its now time to write some code. Open up the Model class in HelloWorld namespace and to the method return 'Hello World'

Great so now running PHPSpec we have all passing tests but this is no good as its not actually being used in our class. Lets fix that by creating a new Command Class. Add a new folder to app/code/Jcowie/HelloWorldModule for Commands and create GenerateCommand class with the following contents:

<?php
namespace Jcowie\HelloWorldModule\Commands;

use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Command\Command;

class GeneratorCommand extends Command
{
    /** @var \Jcowie\HelloWorld\Model\HelloWorld $helloWorldModel */
    private $helloWorldModel;

    /**
     * GeneratorCommand constructor.
     * @param \Jcowie\HelloWorld\Model\HelloWorld $helloWorld
     */
    public function __construct(\Jcowie\HelloWorld\Model\HelloWorld $helloWorld)
    {
        $this->helloWorldModel = $helloWorld;
        parent::__construct();
    }

    protected function configure()
    {
        $this->setName('jcowie:helloworld');
        $this->setDescription('Hello World');

        parent::configure();
    }

    protected function execute(InputInterface $input, OutputInterface $output)
    {
        $output->writeln($this->helloWorldModel->sayHello());
    }
}

Most of this is just boiler plate Symfony console command code. What is interesting to us is the __construct() function where we are injecting our dependency in

/** @var \Jcowie\HelloWorld\Model\HelloWorld $helloWorldModel */
private $helloWorldModel;

/**
 * GeneratorCommand constructor.
 * @param \Jcowie\HelloWorld\Model\HelloWorld $helloWorld
 */
public function __construct(\Jcowie\HelloWorld\Model\HelloWorld $helloWorld)
{
    $this->helloWorldModel = $helloWorld;
    parent::__construct();
}

Pretty simple stuff but we inject into our class a instance of our plain ol PHP class that has the single method and assign this into a variable so that when we output data to the CLI we can call sayHello without having to create a new instance of the class. This makes testing of classes so much easier as we can if wanted test this Command class by replacing what could be more complex in the dependency injected. E.g. if its a remote webservice that we are calling. Yet I would consider this to be more of a integration test that a unit test.

To complete the module we need to add a final piece of M2 configuration that is the DI.xml file and this is used to register our command on the command registration stack

<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../lib/internal/Magento/Framework/ObjectManager/etc/config.xsd">
    <type name="Magento\Framework\Console\CommandList">
        <arguments>
            <argument name="commands" xsi:type="array">
                <item name="moduleCommand" xsi:type="object">Jcowie\HelloWorldModule\Commands\GeneratorCommand</item>
            </argument>
        </arguments>
    </type>
</config>

All that is worth noting here is that we inject into moduleCommand the reference to our GeneratorCommand so that when someone runs bin/magento our command can be added to this list. I will look at exploring the DI in lots more posts and videos soon.

What I want you to take from this most is that its really easy to de couple code from Magento 2 and create more testable objects that are not constrained by the limitations of the framework. This helps us think about Unit testing, Integration testing and Functional testing in new ways as we can have a clear seperation of our domain an inject that into smaller controllers and framework containers.

My talks from 2015 on Magento

Well 2015 was an amazing year for me. I had the opportunity to talk as so many conferences and meetups, I met so many new people and got to spend time with old friends.

Trying to find a highlight is so hard. Vegas and Imagine was mind blowing. Such a large conference and on a scale I have never experienced before. Being able to stand on stage and talk about something I really belive in with BDD and TDD with Magento 2 was great, but really being on stage with Allan MacGreggor and Joshua Warren was amazing. I have loved working with Allan on so many fun projects and this year has been so much fun working on Magecasts

Then being in NYC thanks for the amazing Kimberly was fantastic. I was really nervous about talking BDD in-front of a room of so many people and in a place as amazing as NYC. But thankfully the talk went without a hitch and I really enjoyed talking to so many people afterwards about how I use BDD as a communication tool and how Behat can help automate the conversation. Then having time to run through some small workshops on PHPSpec and de coupling Magento 2 module design was ace.

Being Agile

I love agile and I am lucky to work for a true Agile company, However I see lots of articles and book referring to being “Agile” so what does it mean to be “Agile” ? Is it a button that we press and we become Agile ? Is it a special meal we need to eat that allows us to become Agile? Is it following the Agile guides having all the meetings and following the Agile manifesto to the letter? Does any of these make us more Agile?

At our yearly christmas party I was asked the question “What is the most important part of Agile for me?” At the time I was thrown, Take a second to think about this if there is one part of Agile that you could not live without what would it be? Is it one of the ceremonies? A principle?.. After thinking about this for a while my answer at the time was “Standups”.

Why Standups? Out of all of the ceremonies that I could have chosen planning, retro Example workshop etc why this one..

For me I really enjoy the standups as a meeting that aligns the team together, A well run short standup meeting can give me all the information I need for the day. I know what my commitment for the day is, I know what my team members are working on and I am aware of blockers that people might have and areas that they are working on.

So how does this fit into the post title of being Agile? Well I spent the break thinking more about this question and what Agile really does mean to be. Why is it that I love to work in an Agile way as apposed to just being fed requirements.

This is by no means a perfect answer and in reality its just my interpretation of what being Agile means to me. Being Agile is being open to change through conversation. Yes there are still constraints that we need to work within, But being aware of them and open to change in an iterative way is what makes me feel passionate about software development and is currently my definition of being Agile.

Behaviour Driven Development and Magento

I recently gave a talk on using BDD to develop Magento modules. The talk was not recorded but goes through a breif history of Software development and finishes with a high level overview of using PHPSpec and Behat. The highlight of the talk for me was Cynefin and hopefuly the people that came to PHPNW user group took some of the ideas away with them.

I will be writing up more on my exploration with Cynefin over the coming weeks so watch this space.