What the CRUD? Create, Read, Update and Destroy with Magento2

Just a quick note, at the time of writing this article the version of Magento 2 being used is 2.2.2

So you’ve decided to deep dive into the complex and rich world of Magento 2 and want to create an entity and perform basic CRUD actions on it. If you’re used to PHP MVC frameworks like Laravel or Symphony you may have noticed already that performing these relatively simple actions in Magento 2 is more complicated than it first appears. In this post I’ll go though the basic setup of a module in Magento 2 and create a custom database model so we can see how its done.

Assumed knowledge:

  • OO-PHP
  • How to create and register a basic module in Magento 2
  • How to create a basic route, and a new page in Magento 2

What we’ll cover in this tutorial step by step:

  • Create and register a Magento 2 module
  • Create an install script to install a table for the model we want to CRUD
  • Create the Model class (more specifically classes) for Magento 2 to interact with
  • Create a basic Create controller to handle a post from an imaginary front end page

I just want the code!

No worries, here’s a link to the github repo for what I’m about to go through:

Step 1 – Setup our module:

Firstly lets create a basic module in Magento 2 and register it. For the demo we’ll be calling our module CRUD, under a namespace C3. Once done you should have a folder structure that looks like the following:

Basic Module structure

Don’t forget to populate your module.xml file with the relevant config script before proceeding. It should look something like this:

Step 2 – Install Script to create Database:

Now that we have our module setup let’s start by creating a new entity in our database so we have something to work with. Create a new folder called ‘Setup’ under your module namespace, and create a new file called InstallSchema.php inside it.

We’re going to call our model Notes. So open ‘InstallSchema.php’ and we’ll start building the install script that will setup this new table for us.

In here we're going to place our Collection.php class. You may have noticed we did not call this Notes, or NotesCollection. This is because Magento expects to find the relevant collection class from the namespace for the resource model it links to, and expects to find a folder under the resource model name, and the collection class within.  As you'll see this model is where we specify which database table to link to, and which column to use for its id.

(Its not immediately obvious by the screenshot, but the bottom Note.php file is located under the Model directory, and the upper is located under the ResourceModel directory).

Why expand our database model into three separate classes?

Magento2 has opted to extract database models into three separate classes to allow for the greatest flexibility from within your application. It allows for you to apply only database specific logic to say the resource model, and business specific logic to your abstract model, and collection behaviour to (you guessed it) your collection model.

Step 4 – Set Up A Route: Telling Magento How to Find Your Controllers

Time to create a controller to handle CRUD operations on our module. We’ll start with setting up a basic route in our module. Create a ‘frontend’ directory under your modules ‘etc’ directory. In here create your ‘routes.xml’ file.

Here we’ve basically set our route path to start with

Step 5 – CRUD via Controllers:

Now we’ll create our controller directory in the root directory of our module with a subdirectory of ‘Notes’: C3\CRUD\Controller\Note. Now we need to create controller classes for each CRUD action we want. For the purposes of length, I will show a simple ‘Create’ class only, however once you see how its done it will be pretty simple to build your own read, update and destroy classes.

One note about Magento2 controllers you must create a whole new controller class per action you want to perform. So for instance, if we were to go ahead and build out our Read, Update and Destroy classes too our Controller directory should look like something like this.

Ok, so let’s make the Create.php class:

<?php namespace C3\CRUD\Controller\Note; use C3\CRUD\Model\NoteFactory; // Factory classes are dynamically generated classes by Magento use C3\CRUD\Model\ResourceModel\Note; use Magento\Framework\App\Action\Action; use Magento\Framework\App\Action\Context; use Magento\Framework\App\Request\Http; use Magento\Framework\Exception\AlreadyExistsException; class Create extends Action { protected $helper; protected $request; protected $note; protected $noteResource; protected $noteFactory; public function __construct( Context $context, Note $noteResource, Http $request, NoteFactory $noteFactory ) { $this->request      = $request;
        $this->noteFactory  = $noteFactory;
        $this->noteResource = $noteResource;
     * Execute action for our controller.
    public function execute()
        $postData = $this->request->getPost();
        if (!empty($postData)) {
            //Probably best do some form of validation here but for tutorial purposes, we wont for now
            $title = $postData['title'];
            $issue = $postData['content'];
            $newNote = $this->note
                  'title' => $title,
                  'issue' => $issue,
            //Try to save the new note
            try {
                $this->messageManager->addSuccessMessage("New Ticket: $title Created");
            } catch (\Exception $e) {
                //Add a error message if we cant save the new note from some reason
                $this->messageManager->addErrorMessage('Unable to save this ticket, please call your service provider');
        } else {
            // Add a helpful error message
            $this->messageManager->addErrorMessage("No data was posted");
        // Return to where we came from, here we're assuming the read notes page.
        return $this->_redirect("*/*/read");

As you’ll notice we’ve referenced dependency in called   ‘C3\CRUD\Model\NoteFactory’. This is a dynamically generated class from Magento for all Abstract Model classes. It allows for the instantiation of new models without the need for you to declare their dependencies as well. Don’t worry if your IDE is upset with you initially when declaring the factory dependency; at the time Magento fires the request for that class, it will automatically generate it for you.

Note: Whilst the use of a Factory class is not strictly required when dealing with Model instantiation , we recommend using it as it provides more control over object creation, and in turn helps avoid object reuse.

Ok But, Where’s the Frontend?

I leave that to you dear reader, to figure out how to post a form to this controller. Here is a helpful link to Alan Storms page in creating a basic module in your Magento2 application to get you started.

The reason why I haven’t included this is this particular tutorial is due to frontend development in Magento 2 is deserving of its own blog post. Also stated at the beginning of this post, we expect you to know how to create a new page before attempting CRUD.

Step 6 – Profit

There you have it, we created our database model Magento 2 style, made a database script to install our model table in your stores DB, and finally created a quick route, and controller action to demonstrate how to connect all the pieces. A quick recap of the steps are as follows:

  • Create and register your Module
  • Create an install script to make your db table/s
  • Create your three model classes in your modules ‘Model’ directory
  • Use model factories to instantiate and work with your models

I hope this short tutorial was helpful in wrapping your head around how Magento 2 expects your database models to be instantiated and why. I’ll be posting more of these articles in the future, and any constructive feedback is welcome.

A quick note on why we provide these tutorial-style blog posts – they help us and others get on with the interesting part of development. The point is to help grasp what layouts are required quickly, so you can get on with actual coding, rather than piecing together how things are to work in the first place.

About the author

Team C3