Creating a Magento 2 Composer Module

ComposerA previous post described a very simple Magento 2 module for showing application state. This post shows how to convert it into a Magento 2 Composer package and add it to a site.

Disclaimer: This is not official Magento 2 documentation and represents personal opinions, not that of my employer. (In other words, I’m just mess’n ’round.) Oh, and things are under active development in the Magento 2 code base which may invalidate some of this in the future.

GitHub

The first step is to create a GitHub repository for the module. You can find the module from the previous post at https://github.com/alankent/magento2-module-show-application-state. If you are developing multiple modules, the easiest approach is to create a repository per module. During development Composer can then read the files directly from GitHub without you needing to use a tool such as Satis to create ZIP files for your modules.

The top level directory of the repository should be the top directory of the module (the contents of app/code/AlanKent/ShowApplicationState in my case). In this directory there is now a composer.json file describing the module. Composer can read the module directly from GitHub if you put the composer.json file in the root directory. Here is the composer.json file.

{
    "name": "alankent/module-show-application-state",
    "description": "Example using 'areas' - displays application state including a list of loaded modules",
    "require": {
        "magento/framework": "0.1.0-alpha89"
    },
    "type": "magento2-module",
    "version": "0.1.0",
    "extra": {
        "map": [
            [
                "*",
                "AlanKent/ShowApplicationState"
            ]
        ]
    },
    "authors": [
        {
            "name": "Alan Kent",
            "homepage": "https://alankent.wordpress.com/",
            "role": "Developer"
        }
    ]
}

The name element holds the Composer name by which we will refer to the module. The require element declares what other modules must be loaded and the version number we are compatible with. This version specifies an explicit version number of 0.1.0-alpha89. Later we will change this to 0.1.* so the dependency will keep working for future Magento 2 code drops. The version element declares the version of this package. The extra/map element says that all the code should go under the AlanKent/ShowApplicationState directory, which is relative to the app/code directory when type is set to magento2-module.

Packagist

In my post yesterday I briefly introduced the first push of Magento 2 Composer packages to packages.magento.com. I included in the post a top level composer.json file. Here is a revised version of this file requesting the new module to be included.

{
  "name": "alankent/store",
  "require": {
    "magento/magento-composer-installer": "*",
    "magento/product-community-edition": "0.1.0-alpha89",
    "alankent/module-show-application-state": "*"
  },
  "repositories": [
    {
      "type": "composer",
      "url": "http://packages.magento.com/"
    },
    {
      "type": "vcs",
      "url": "https://github.com/alankent/magento2-module-show-application-state"
    }
  ],
  "extra": {
    "magento-root-dir": "htdocs",
    "magento-deploystrategy": "copy"
  },
  "minimum-stability": "dev"
}

The changes were to include in the require element a reference to my new module (alankent/module-show-application-state, which matches the name in my modules composer.json file), and a new entries in the respositories element with a type of “vcs” (version control system) and a url pointing to my GitHub repository.

After saving these changes I ran

$ php composer.phar update
Loading composer repositories with package information
Updating dependencies (including require-dev)
 - Installing alankent/module-show-application-state (dev-master 4468d64)
 Cloning 4468d647be1a01ba17885ba7b7e8a8c412569c17

Writing lock file
Generating autoload files

Checking the htdocs/app/code directory showed that AlanKent/ShowApplicationState had been created, with the contents of the repository placed under that. This module does not require MySQL to be set up first. This means you can start up a web server on the code and point a browser at http://127.0.0.1/alpha89/index.php/showappstate (or whatever path you used in your web server setup). It should displayed a list of loaded modules. For me it did, cool!

Using Packagist.org

The above is a quick way to get a project up and going, but is a bit of a pain as each module GitHub repository needs to be added in the repositories section. This adds quite a bit of bulk to the composer.json file after a while. A better alternative is to add the package to a repository. The packages.magento.com package repository does not accept public contributions, so I decided to try and host my package on packagist.org. (You can create local repositories if you want to keep your module private – I do not cover that in this post.)

I was wondering how to turn my package into something packagist.org would understand. A ZIP file perhaps? I had never actually asked the core Magento developers working on Composer what was involved. I decided to register myself first on packagist.org regardless (pretty straight forward). Luckily I did – the site explained how to add a new package pretty easily. I entered my GitHub repository URL (https://github.com/alankent/magento2-module-show-application-state.git) and clicked the big green “Check” button. It found the composer.json file and gave me a big green “Submit” button to press. Click. Done!

Well, almost done. My package was created at https://packagist.org/packages/alankent/module-show-application-state but searching did not seem to work. I guess it might update the search index periodically and it had not been done yet.

Further, there was a warning that I had not created a GitHub hook so new pushes to GitHub would automatically update packagist.org. I followed the instructions, which involved getting an API key from packagist.org and using that (and my packagist user name) to create a commit web-hook on GitHub for packagist. It took less than a minute. Nice!

But did it work?

I went back the my main project and pruned back the composer.json file to remove the extra repository I added. I also specified a version number of 0.2.* for the module to pick up the new version I create below.

{
  "name": "alankent/store",
  "require": {
    "magento/magento-composer-installer": "*",
    "magento/product-community-edition": "0.1.0-alpha89",
    "alankent/module-show-application-state": "0.2.*"
  },
  "repositories": [
    {
      "type": "composer",
      "url": "http://packages.magento.com/"
    }
  ],
  "extra": {
    "magento-root-dir": "htdocs",
    "magento-deploystrategy": "copy"
  },
  "minimum-stability": "dev"
}

I then jumped over to my module to make some changes. In particular, I increased the version value to 0.2.0 and I changed the dependency on magento/framework to be 0.1.* (was 0.1.0-alpha89). The 0.2.0 version is here. That way the module would be compatible with any 0.1 version.

But it did not show up in packagist. Then the penny dropped – I had forgotten to create a tag or branch. My preferred approach is to create a tag per stable version (such as 0.2.0) and a branch on which patches are created (0.2). When the first patch is released from the branch, it will be tagged as 0.2.1.

$ git tag 0.2.0
$ git push origin 0.2.0
$ git branch 0.2
$ git push origin 0.2

I jumped back over to packagist and viola, it showed 0.2.0. The GitHub hook had worked. I had not created a branch for 0.1.0, but I decided it was not important.  For the branch it had create 0.2.x-dev, which means “the latest version on the 0.2 branch”). (Hmmm, I think I don’t have something quite right here – packagist also created a 0.2.0.x-dev for my tag. I did not think it was supposed to do that. Oh well, maybe no-one will notice.)

So back to my alpha89 project to do an update.

$ php composer.phar update

Checking the composer.json file, it did pick up the new version with the improved dependency rules. So Composer successfully found the package on packagist.org. Success!

Conclusion

My goal of this blog post was to show how easy it is to create a new module in Magento 2 that can be loaded up using Composer. Using GitHub made it particularly easy with the web-hook to notify packagist.org when a new version is available.

There are some points worth noting however. It is usually more convenient while developing code to have the code sitting directly within your local Magento source tree. Then you can edit, save, and reload page to get the changes. Putting your module in a separate GIT repository however can cause some problems with nesting (GIT can get a bit fussy / confused). It may be better to put the module code off in a different directory and create a symbolic link to make it look like your new module is a part of the main Magento 2 directory tree to the web browser. (See also modman, which I am not going to cover here.)

I also suspect it may be better to add my extra modules to the composer.json file in the htdocs directory. Adding new packages is not so much of a problem, but removing optional packages from CE will require deleting lines from the composer.json file in the htdocs directory. So maybe new ones should be added there as well.

Regardless of which file is used to add modules, this post has hopefully shown how easy it is to create a new module and then load it using Composer into your Magento 2 site.

7 comments

  1. timopenstream · · Reply

    Does the guy on the photo show bras d’honneur to composer?

    1. It was a random image I found that had someone composing music with a computer keyboard and digital display. I, err, had not thought of your more creative interpretation of the image…

  2. Thank you Alan for sharing such a nice article, i was working on module In many cases, We may be required to add a customized functionality into Magento and the best approach to this is to create your own module. http://www.cloudways.com/blog/create-module-in-magento-2/

  3. Hi Alan ,
    i tried the steps that you have mentioned and when i ran composer update , i am seeing the modules in app/code folder as expected. The issue i am seeing is a git version of the module also resides inside vendor folder. Is that expected ?

    NOTE: i am using gitlab and have not yet used Packagist.org

    1. This blog post is pretty old now. If you create a GitHub repo for your module, these days I suggest one of two strategies. (1) add a new “repository” to the composer.json file that uses the “vcs” repository repository type to point to your GitHub repo. If you install using “composer install –prefer-source” it will do a git clone to under vendor/. You can just edit the code there and do git commits etc fine. It is a pretty easy way to work. Alternatively you can run git yourself to git clone your repo, then use the “path” repository type to point to the directory of all modules. This can be useful when putting multiple modules in the one repo. We are considering moving the core code to use this approach for 2.3. Might be worth a new blog post I guess!

  4. Hi Alan,

    I have a cuestion with my git repository and the users when I do composer. I have a module in a private git repository on my server. This module I sale in my website, and when a user buy it i want to create for this user a credentials (user and password) to he can install the module using composer (composer require vendor/module-name). How can I create credentials for each user that buys my module so they can only install it. I need to known what steps I have to folow to create a composer module with credentials.

    1. I guess that is up to the software you are using – but bucket, github, or hosting your own repo. I don’t have any direct experience trying to do that. Using a composer repo (rather than hit) was just a matter of password protecting the urls. You could do some php script for this with your own table of approved users.

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.