Hello Stranger. Hover over that neat pattern below because each orb contains an interesting link... and don't forget to register, so the pattern can archive what you clicked!

Be the first to comment on this post

It Begins With Composer

How Composer is allowing me to keep my sanity.

It was mid-2014 when I finally decided to force myself to use Composer in my projects, and it was right around that same time when Magento development became ...

It was mid-2014 when I finally decided to force myself to use Composer in my projects, and it was right around that same time when Magento development became enjoyable. I suppose, in hindsight, the push to incorporate Composer in my projects should have come as no surprise. After all, The PHP community had been using Composer for about two years by then, and I’d been building Magento modules, integrations and entire projects – both Community and Enterprise – for approximately three years… we were deep in the Magento Dark Ages back then; there were no easy ways to deploy Magento features into Magento projects, and the only good tool around was the Modman script, which helped a lot, but it had a few limitations such as full windows support and external library importing, so most of us resorted to doing the ol’ “dragin’-and’droppin’” dance we did whenever we “installed” new features into our Magento projects. Agencies everywhere were filled with deafening cries, prayers and pleas from developers lamenting their need for a good Package Manager… Needless to say, clients and projects were getting bigger; features and modules were also growing in both size and complexity. Magento developers everywhere felt the boom… 2014 was the year.


It was a year of stress and growth: a switch of companies, a new girlfriend, a deceased pet cat, a dizzying ERP integration, a Giftcard integration… so many things took place back then, but more importantly, it was back then when I started to feel like my knowledge of the Magento platform had grown just enough for me to feel comfortable with it. After three years dealing with it, I was just starting to feel so comfortable that I began to “include” external libraries (E.G. PHP_OFFICE) and use tools like Composer… off I went; a proper “experienced newbie,” illuminated by nothing more than good intentions, and we all know how the road to hell is paved! Sure, I got myself in a whole lot of trouble using these neat new things, but the process has been incredibly fun, and I’ve learned so much that I’d like to share some of it.


Stressful Beginnings


At the beginning of 2014, after doing some minor Android development, I found myself no longer willing to do Magento development in the way most agencies and developers were doing it (I.E. dragging and dropping with impunity). My frustrations only increased every time I found myself digging through previous Magento builds just to pull out features, to add into other ongoing projects. It felt unclean! The processes in place for Magento development we had back then turned me into a code scavenger, and oftentimes the scavenging didn’t go so well. I complained more than ever to the poor souls willing to listen! Something had to change.


Having been exposed to Maven and Gradle, I began to look for equivalent tools in PHP; Secretly, I was very jealous of Fenders who wouldn’t shut up about their glorious NPM, and whenever I brought up Composer to them I felt ashamed because I knew that it didn’t make Magento development any easier for me back then. I tried Maven , but it was too big, and, alas, didn’t really provide me with the ease to deploy my packages, themes or modules into their corresponding codePools when it was a module, or package/theme folders when it was a package/theme; in the end, Maven didn’t give me what I was after. I even tried a – defunct now – tool called “Mavento,” I believe, but nothing came of that either.


Composer had always been there on the periphery of my Magento projects. I used it once or twice in other non-Magento projects to build smaller solutions using open-source PHP libraries that were easier to include and "autoload" than magento features. Those libraries were definitely easier to manage once included in my projects by simply including adding Packagist to my project's composer.json. Composer had what I wanted minus the “deployment” functionality to satisfy Magento’s namespacing requirements and expected Controller file paths in Mage_Core_Controller_Varien_Router_Standard, but then some clever people came up with the Magento-Composer-Installer! I gave it a try. Happiness and joy were sure to come. The following will show you how I began.


The Approach


As a matter of habit and personal preference, I use Beanstalk as my code host and deployment manager. It’s a very powerful tool, and I will recommend it whenever I can; especially if the plan is to build an Enterprise or Community Magento project. For most of my clients, a Bronze account ($15/month) will suffice. This account gives enough room for a features repo and a main project repo, whose main branches ( master, qa) will become the deployable project branches: master to staging/live servers, and qa to QA server(s).


The next two figures represent of my goal in terms of branch management: They’re a visual representation of how I arrange my code in the repos I use for any given project. This type of code and branch management allows me to take full advantage of Composer because it provides me with the ability to simultaneously build project features, build global features, and build the projects themselves; commiting and pushing code not only to my project, but to my personal features repo for reusing. Additionally, it gives me the control I need to deploy features from any repo into any of the projects I'm currently building, and, if needed, create their own dedicated feature branches if the feature happens to be heavily customized.


Features Repo: a branch per feature!
Ex: my_git@beanstalkapp.com:/features.git
Project Repo
Ex: git@beanstalkapp.com:/project.git


"Connecting" the Repos


What I propose in the two previous figures, like I said, are my optimal conditions. A features repo where I use every branch as a packaged feature that I can easily fetch and deploy into the project repo’s master or qa branches. This – for the curious reader – is attained by simply adding one line to the project’s composer.json, which is a very important and multipurpose file; an essential piece to every project using Composer. Lastly, the second figure, shows the project repo, which usually has a master branch that is manually deployed to Live servers, and one or a few qa branches that can be merged to master whenever features have been approved for production deployment, or deployed into Staging and QA servers. Now, here’s how I get things started:


Create repos
git@beanstalkapp.com:/project.git
my_git@beanstalkapp.com:/features.git
Clone project repo
git clone git@beanstalkapp.com:/project.git
Add composer.json

project -> git@beanstalkapp.com:/project.git

composer.json

Add .gitignore

project -> git@beanstalkapp.com:/project.git

.gitignore

composer.json

Clone features repo inside "project"
git clone my_git@beanstalkapp.com:/features.git
Initial Status of project

project -> git@beanstalkapp.com:/project.git

.gitignore

composer.json

features -> my_git@beanstalkapp.com:/features.git


Nothing should have been committed to the project repo’s master branch up to this point. Before doing that, a few files or directories will probably need to be ignored from being tracked, added, and committed to the project repo; This is easily done by editing or adding a .gitignore file into the project repo. Additionally, we also have to tell Composer to build our project for us; essentially, we tell Composer to “go to these repos, bring these features, and deploy them into project repo’s magento folder,” and we do that by editing or adding the project’s composer.json. So these two files ( .gitignore and composer.json) should look like this:


.gitignore

## Composer ##
composer.lock
vendor
magento/composer.json
features
composer.json

{
"repositories": [
{
"type": "composer",
"url": "http://packages.firegento.com"
},
{
"type" : "git",
"url" : "my_git@beanstalkapp.com:/features.git"
}
],
"require":{
"magento/core": "1.9.1.0-patch2"
},
"extra":{
"magento-root-dir" : "magento/",
"magento-deploystrategy" : "copy"
},
"minimum-stability": "dev"
}

We Run Composer
composer update -v --prefer-source
Final Status of project

project -> git@beanstalkapp.com:/project.git

.gitignore

composer.json

composer.lock

features -> my_git@beanstalkapp.com:/features.git

magento

vendor

magento

core -> magento.git


At this point the project is pretty much set up and ready to be customized according to the specifications and expectations. This is the moment when the features repo/directory becomes the main point of focus because it’s there where feature branches are created and packaged; However, for the sake of discussion, let’s touch upon a few details before building features and integrating them.


Upon closer inspection of this local environment, it becomes clear that Composer, somehow, “deployed” the Magento core files into the magento directory. Essentially, what this means is that during this deployment process, the Magento-Composer-Installer tool copied every file that was "checked-out" into vendor/magento/core by Composer into the magento directory. And how does the Magento-Composer-Installer tool know where to copy those files into the magento directory? Well that’s what the composer.json and, more often than not, the modman files are there for


modman


Upon navigating into vendor/magento/core and not seeing a modman file, don’t panic. There should definitely be a composer.json file somewhere in there. Some inspection of that file will show that it contains a property called "extra". This is a very important piece because it holds an array that is used by the Magento-Composer-Installer to figure out what needs to be deployed, and where. The name of that particular array is "map" for obvious reasons: it contains the key:value mapping pairs that the Magento-Composer-Installer will use when it deploys (by copying, or symlinking) feature files. The first key:value pair that is contained within map[] looks like this ["app","app"]. This, in very high level terms, is understood by the Magento-Composer-Installer to mean, "Deploy everything in this "app" folder ( vendor/magento/core/app) into the "app" folder contained in this project's target Magento folder, which happens to be the magento/app folder in this case." The Magento-Composer-Installer performs the same action for all of the key:value pairs it finds in map[]; Even if these key:value pairs happen to be specific file names ( E.G. ["favicon.ico","favicon.ico"])


Now, it should be evident why there is no modman file in vendor/magento/core. The map[] array contained inside of the "extra" property replaced it, and when developing their own magento packages, developers often do the same, but I prefer to use modman files just to have a good separation between my package definitions and the package mappings.



Let's Start Buildin'


It’s now time to focus all attention to the features repo/directory, where, as I said before, the goal is to create one branch per feature; In this case, when I say “feature,” it could be anything big or important enough to be packaged: a module with its own entities, a frontend package, a frontend theme, some custom shell scripts, etc. I tend to follow a loose naming convention for these branches/features. For example, oftentimes I will name my branch CompanyNamespce_ModuleName_ProjectName and I will name the feature companynamespace/companynamespace_modulename in the project's composer.json. The same goes for packages: the branch name in that case would be something like CompanyNamespace_PackageName_ProjectName and the package name something like companynamespace/companynamespace_packagename. The reason I follow this convention is very simple: whenever I build a feature that I may want to reuse in multiple projects I simply drop the last part of the branch name – which is used to indicate target projects – and push that feature to my global features repo: so, CompanyNamespace_PackageName_ProjectName becomes CompanyNamespace_PackageName in the features repo, and now there's a new global feature that I can add to my personal library and boilerplate offerings. So how about we start growing our features repo!?


Start With clean-branch


Why? Well, this will be the branch that will hold the templates that will eventually be used whenever new features are created. This will also be the main branch in the features repo, and, obviously, will become the branch after which every feature branch will be based. This is how that’s done:


Features Repo: clean-branch
Ex: my_git@beanstalkapp.com:/features.git
Features Repo: clean-branch

features -> my_git@beanstalkapp.com:/features.git

.modman.template

composer.json.template

readme.md.template


Let's Add A Package


Now that clean-branch has been created, we have a starting point for our features, so we can now create our feature branch (E.G. Modulismo_ExamplePackage_ExampleProject ) where we can define our package (e.g modulismo/modulismo_examplepackage) in the branch's composer.json.


Modulismo_ExamplePackage_ExampleProject
Ex: my_git@beanstalkapp.com:/features.git
Features Repo: Modulismo_ExamplePackage_ExampleProject

features -> my_git@beanstalkapp.com:/features.git

app

code

local

Modulismo

ExamplePackage

readme.md

etc

modules

Modulismo_ExamplePackage.xml

design

frontend

modulismo

default

readme.md

skin

frontend

modulismo

default

readme.md

.gitignore

modman

composer.json

readme.md


Any Magento developer should quickly see what’s happening here. If the Modulismo_ExamplePackage_ExampleProject branch looks very much like a Magento frontend Package with a local codePool for a module called ExamplePackage it’s because it is. These directories will (potentially) contain one theme called default inside the Modulismo package. They will also (potentially) contain a local module called ExamplePackage where all of this project's customization that is too small or too custom to be its own module (E.G. class rewrites, attribute creations through update scripts, project custom helper classes, etc) can be implemented. The next step, after all of the necessary files are created, is to “register” or “define” this branch as a Magento (deployable) module or feature for Composer and the Magento-Composer-Installer. We do that by editing this branch’s modman and composer.json files like so:


modman

app/code/local/Modulismo/ExamplePackage/ app/code/local/Modulismo/ExamplePackage/

app/etc/modules/ExamplePackage.xml app/etc/modules/ExamplePackage.xml

app/design/frontend/modulismo/default/ app/design/frontend/modulismo/default/

skin/frontend/modulismo/default/ skin/frontend/modulismo/default/
composer.json
{
"name": "modulismo/modulismo_examplepackage",
"type": "magento-module",
"description": "ExamplePackage developed by Modulismo",
"require": {
"magento-hackathon/magento-composer-installer" : "*"
},
"authors": [
{
"name": "Author",
"email": "author@modulismo.com"
}
]
}

At this point it can be said that a Magento, Composer-deployable, feature named modulismo/modulismo_examplepackage has been created in the Modulismo_ExamplePackage_ExampleProject branch of the features repo. It can also be safely said that the feature encapsulated within the Modulismo_ExamplePackage_ExampleProject branch has files that the Magento-Composer-Installer can deploy into the project's target magento directory according to the mapping that is contained in this branch's modman file, which, after close inspection, slightly resembles the values contained within the map[] array we saw in the composer.json file that allowed the Magento-Composer-Installer to deploy all of the Magento files into the project's magento directory.


The Final Step


Hurray! The initial setups have concluded at this point. The feature we want to include in the project has been commited into the features repo, and anyone with access to that repo can deploy it into their project repo's magento directory, and that is achieved by updating this projects main composer.json and running the composer update -v --prefer-source command:


composer.json

{
"repositories": [
{
"type": "composer",
"url": "http://packages.firegento.com"
},
{
"type" : "git",
"url" : "my_git@beanstalkapp.com:/features.git"
}
],
"require":{
"magento/core": "1.9.1.0-patch2",
"modulismo/modulismo_examplepackage": "dev-Modulismo_ExamplePackage_ExampleProject"
},
"extra":{
"magento-root-dir" : "magento/",
"magento-deploystrategy" : "copy"
},
"minimum-stability": "dev"
}
composer update -v --prefer-source

Done!


Hopefully all, if not some, of this helped you, or at least sparked a thirst for knowledge somewhere. If either or both of these two things happened, then my goal has been reached! I'd appreciate your comments; whether you think this is ridiculously awesome or incredibly stupid. Leave a comment and let me know. I will do my best to reply.

Be the first to comment on this post