Models & the ORM

In the Controllers page you saw $this->Articles->find()->all() "just work" without any setup. That's CakePHP's ORM (Object-Relational Mapper) at work — its own fluent, safe query builder for talking to your database, built specifically for CakePHP rather than a third-party library like Doctrine or Eloquent. Understanding it means understanding two related but distinct kinds of classes.

Table Classes vs. Entity Classes

ClassRepresentsTypical file
ArticlesTableThe whole articles table — query logic, business rules, validationsrc/Model/Table/ArticlesTable.php
ArticleOne row/record from that table, with its own properties (title, body, ...)src/Model/Entity/Article.php

A Table object is the thing you query — it's how you ask "give me all articles" or "save this article." Each individual result you get back is an Entity object, one per database row, carrying that row's own data as plain properties like $article->title.

Note: True to convention over configuration, you don't have to write either file. If src/Model/Table/ArticlesTable.php or src/Model/Entity/Article.php doesn't exist, CakePHP auto-generates a basic, working version of it behind the scenes — you only create the file yourself once you need custom behavior.

Worked Example: A Custom Table Class

Once you do need custom logic — say, a reusable query for "only published articles" — you create the Table class yourself and add a finder method. Finder methods are named findSomething() and are called by dropping the find prefix: find('published').

<?php
namespace App\Model\Table;

use Cake\ORM\Query;
use Cake\ORM\Table;

class ArticlesTable extends Table
{
    public function findPublished(Query $query, array $options)
    {
        return $query->where(['Articles.published' => true]);
    }
}

With that in place, any call to $this->Articles->find('published') anywhere in the app automatically applies that WHERE condition — the custom logic lives in one place instead of being copy-pasted across every controller that needs it.

Accessing a Table From a Controller

This is the same "just works" magic from the Controllers page, explained fully now: because your controller is named ArticlesController, CakePHP infers you'll want the Articles model and loads it automatically as $this->Articles — no constructor code, no manual instantiation.

$articles = $this->Articles->find('published')->all();

The ORM Is CakePHP's Own Query Builder

It's worth being explicit: CakePHP's ORM is not Doctrine, not Eloquent, and not a wrapper around either — it's a query builder written specifically for CakePHP. Every method call you chain, like where(), order(), or limit(), builds up a SQL query behind the scenes while automatically escaping values to protect against SQL injection.

$query = $this->Articles->find()
    ->where(['Articles.published' => true])
    ->order(['Articles.created' => 'DESC'])
    ->limit(10);

Because every value passed in is bound safely rather than concatenated into raw SQL, using the ORM this way already sidesteps the SQL injection risks covered on Core PHP's Security page — as long as you build conditions through the query builder rather than string-concatenating raw SQL yourself.

Next steps: Before any of this ORM code can talk to a real database, CakePHP needs connection credentials. Head to Database Configuration to set that up.