Getting Started with Doctrine in Concrete CMS

This is a community-contributed tutorial. This tutorial is over a year old and may not apply to your version of Concrete CMS.
Jan 20, 2021

Getting Started with Doctrine in Concrete CMS

Doctrine is a very flexible, simple (once you get to know it better) and powerful PHP library for database interactions primarily focused on the ORM = Object Relational Mapping and DBAL = DataBase Abstraction Layer.

The project website where you can get all required information is

The main words you will see most often in Doctrine:

Persistence - storing or saving data in storage

Entity - PHP object that can be identified over many requests by a unique identifier or primary key

What is ORM

From Wikipedia:

ORM is a programming technique for converting data between incompatible type systems using object-oriented programming languages. It translates the logical representation of the objects into a form that is capable of being stored in the database while preserving the properties of the objects and their relationships so that they can be reloaded as objects when needed.

From Doctrine Project:

Doctrine ORM provides transparent persistence for PHP objects. It uses the Data Mapper pattern at the heart, aiming for a complete separation of your domain/business logic from the persistence in a relational database management system. The benefit of Doctrine for the programmer is the ability to focus on the object-oriented business logic and worry about persistence only as a secondary problem.

An entity contains persistable properties. A persistable property is an instance variable of the entity that is saved into and retrieved from the database by Doctrine's data mapping capabilities.

Enough with pedantic jargon, you can read all the nitty-gritty detail on the project website and internet. Let's see how we can use Doctrine in Concrete CMS. In this guide we'll learn how to manage a database in a package with Doctrine, in particular:

  • How to create a database table with mapping PHP objects
  • How to to insert, update, delete and find objects in the database

A prerequisite to this tutorial is knowledge of OOP concepts and using custom code in Concrete CMS packages (

The tutorial can be adapted for non-package use by Concrete CMS overrides.

As we are talking about mapping, the most important things to remember are that classes are mapped to database tables, class properties are mapped to table columns, class methods are mapped to database queries. Tables, columns and relationships are defined using annotations.

To start with, let's consider a simple skeleton package which has 1 database table where some variables are stored - this variables are said to be persisted. The table will be mapped from a Skeleton PHP class. It doesn't matter how the class is called. However it's essential that the class name and namespace are consistent and in accordance with Concrete CMS guidelines. That is:

The package custom source is in the packages/ab_package_skeleton/src/PackageSkeleton folder.

The package controller defines the $pkgAutoloaderRegistries and implements the getEntityManagerProvider():

class Controller extends Package implements ProviderAggregateInterface
    protected $pkgAutoloaderRegistries = [
        'src/PackageSkeleton' => 'PackageSkeleton'
    public function getEntityManagerProvider()
        $provider = new StandardPackageProvider($this->app, $this, [
            'src/PackageSkeleton' => 'PackageSkeleton'
        return $provider;

The Skeleton class is defined in the same file name Skeleton.php in the packages/ab_package_skeleton/src/PackageSkeleton/Skeleton folder

The Skeleton class has the following namespace and requires the following Doctrine classes:

namespace PackageSkeleton\Skeleton;

use Doctrine\ORM\Mapping as ORM;
use Concrete\Core\Support\Facade\DatabaseORM;

Now to creating the table. You can call it whatever, but I'd recommend to call it with a reference to the class name, i.e. AbPackageSkeletonSkeletons. When you have multiple tables and multiple packages, to avoid clashes (what if someone else in another package has a table with the same name?). To create the table the following annotation syntax is used:

 * @ORM\Entity
 * @ORM\Table(name="AbPackageSkeletonSkeletons")

Then we need to define some table columns, e.g. a column 'id' - this will be a unique autoincementing integer for each entry or row and it will be mapped from the class property $id, and a column 'name' - this will store strings for each entry with the max length of 255 characters which cannot be empty and it will be mapped from the class property $name. To create the table columns the following annotation syntax is used:

* @ORM\Id
* @ORM\Column(type="integer")
* @ORM\GeneratedValue(strategy="AUTO")
protected $id;

* @ORM\Column(type="string",length=255,nullable=false)
protected $name;

Note the property names are mapped to the exactly the same column names.

So to get a table cell name value all you need is to know the value of the $name property. How to get that? As simple as as this:

public function getName()
    return $this->name;

Saving a name is also as simple as this:

public function setName($name)
    $this->name = $name;

The above getter and setter methods allow you to work with the table cell once the entity class is instantiated. So to get and set the table names from outside of the class, you simply use the class instance like so:

use PackageSkeleton\Skeleton\Skeleton;

$skeleton = $app->make(Skeleton::class);
$name = $skeleton->getName();
$skeleton->setName('some string');

An important thing to note is setting the class property value does just and only that, but it does NOT save or rather persist the value to the database. To persist it you need to ... well, persist it and then flush it to execute all updates:

public function save()
    $em = DatabaseORM::entityManager();

Deleting an entry is similar:

public function delete() {
    $em = DatabaseORM::entityManager();

For getting and setting the name values we need to know the entry IDs. We can use the Doctrine entityManager() to get the particular entry by its ID:

public static function getByID($id)
    $em = DatabaseORM::entityManager();
    return $em->find(get_class(), $id);

So to do that from outside the class we can do the following, e.g. get an entry with ID = 5 and save a different value of the name:

use PackageSkeleton\Skeleton\Skeleton;

$skeleton = $app->make(Skeleton::class);
$n = $skeleton->getByID(5);
echo $n;

$n = 'other name';

Note: If you later have added new table columns, clear the cache. Otherwise the ORM might ignore the new table columns when updating the entity.


  • Associations
  • Searching
  • Sorting
  • Filtering

Further useful resources: - source code of the skeleton package

Recent Tutorials
How To Add Page Last Updated To Your Concrete CMS Pages
Mar 7, 2023

Concrete CMS has a page attribute you can add to a global area called "Page Date Modified." Here's how to add it

How To Exclude Subpages from Navigation
Dec 24, 2022

How to exclude subpages from navigation - useful for a news or blog link in your main navigation where you don't want all the subpages to appear in a drop down menu.

How Can I Change The Maximum Size Of Uploaded files
Dec 13, 2022

This tutorial explains how to update your php settings.

Updating Concrete Themes from Version 8 to Version 9
Nov 24, 2022

This tutorial covers commonly encountered issues when upgrading a Concrete CMS theme from version 8 to version 9

Transferring ownership of an add-on and a theme
Nov 15, 2022
By katzueno.

If you encounter a Concrete CMS add-on or theme that you love but not being maintained, you may want to ask the author to help or take over the add-on or theme. Here is the quick step-by-step guide of how to transfer the ownership.

How to update Add-Ons if not on the Update Add-Ons Menu item
Jul 4, 2022

How to manually download an add-on and update it when your site's core versions isn't considered compatible with the add-on version.

Was this information useful?
Thank you for your feedback.