Where to place code in a Magento 2 module?

Magento 2, Coding tips, Controller

So in my day to day job i look into a whole lot of different kinds of Magento websites and extensions. Some are really good, some of them are bad. But a lot of them have one thing in common: Code is placed in weird places. A while ago i wrote this tweet which is about the frustration about this:

The goal of this post

The goal is not to blame anyone but to let people know what their options are. People are creatures of habit and like to follow patterns that they know. So when a new developer comes in and sees that certain code is placed in a certain folder, the might keep repeating that pattern.

Next to that, i think there are a lot of rules in Magento development that properly are written down somewhere, but are hard to find for inexperienced developers. The Helpers folder is one of those examples.

Where does this come from?

To answer this we have to take a look in the past, to Magento 1 to be more precise. Back when it was created, autoloaders weren't that common. So Magento 1 shipped with its custom autoloaders, which forces users to places their code in specific places. There were default locations for things like blocks, controllers, and models, but all code that didn't fit the default locations had to be put into the Helpers folder.

Also, that's why people tend to put a lot of code in the Models folder, although it's often not a model at all.

Let's go over a few predefines options.

Api

Here you place all your service contracts, what in fact are interfaces. In other words: When placing an interface here, you give a signal to other developers that they can use these interfaces to interact with.

Api/Data

These are also service contracts or interfaces, but these are not appropriate to instantiate directly. Instantiating is done by interacting with the objects in Api, or by using factories. Most types of interfaces you will find here are for models.

Blocks

Blocks in Magento are basically classes that are tied to "views", which are .phtml files. In Blocks you can put the logic you need for your .phtml files. But be warned that you don't put too much code in the Blocks. 

Controller

The files in this folder must follow a certain path for Magento to know where to look. Magento starts looking in this folder when your module is specified in a routes.xml file.

Model

Here goes everything that connects to your database:

  • Repositories
  • Data models
  • Resource models
  • Collections

Observer

All the logic that should into events. I have a personal preference for placing my code in a class with a name that explains what it does, in a folder that's named after the event. For example:

Observer/SalesOrderAfterSave/SaveDiscountExtensionAttributes.php

Plugin

Got a plugin on a class of an external module? Place it in this folder. I always try to recreate the path of the original class and type-hint the subject so that it's clear which class is targeted.

Setup

Here go all the setup scripts. Your class name must follow the Install/Update Schema/Data patterns (Magento 2.2), or you must place your classes in the Setup/Patch/Data or Setup/Patch/Schema folders (Magento 2.3+).

ViewModel

Although ViewModels are around since Magento 2.2, i still don't see them that much around. ViewModels allows you to inject extra logic into Blocks, which can be used in your templates. Yireo has written a blog that explains View Models more in-depth: ViewModels in Magento 2.

view

Here goes all your presentation code. Layout configuration, ui_component configuration, javascript, css, html and phtml files.

Where do i place Helpers?

Helpers are considered to be bad practice, Fabian Schmengler wrote a piece about the why in that in 2016. In Magento 1 we were forced to place just about everything in Helpers, in Magento 2 we are free to place our code wherever we want. I often tend to create a folder called "Service" to place generic code in. I've seen other examples like "Operation" too.

The only downside of not using Helpers is that you need to inject the dependencies your class has yourself by type hinting them in the constructor.

Where do i place ...?

Find a special spot for that. Is it too small for that? Create a folder called "Service" for example where you can put that. Is it part of a group of something? Feel free to create a special folder for that. The next developer will then know where to look if it is searching for something specific.

Want to respond?