Implement CRUD Operations in CakePHP

November 06, 2023
Written by
Temitope Taiwo Oyedele
Contributor
Opinions expressed by Twilio contributors are their own
Reviewed by

CRUD is an acronym for four basic operations that can be performed on a database: Create, Read, Update, and Delete. They are a set of operations commonly used in database and database management systems for viewing and modifying data.

This tutorial will teach you the essentials of implementing CRUD operations in CakePHP. It illustrates how users can create, read, update, and delete records, thus providing a guide to managing data in your application in CakePHP.

Prerequisites:

Before we dive into the tutorial, make sure you have the following:

Bootstrap a new CakePHP application

To install CakePHP, navigate to the folder where you want to scaffold the project and run this command:

​​composer create-project --prefer-dist cakephp/app:~4.0 cakephp_crud \
    && cd cakephp_crud

When asked “Set Folder Permissions ? (Default to Y) [Y,n]?”, answer with Y.

The new CakePHP project will be available in a directory named cakephp_crud, and you'll have changed to that directory.

Configure the database

Once we've created, the next step is to connect to the database before starting up our development server. To do that, open up the project folder in your preferred code editor or IDE and navigate to config\app_local.php. 

Where it says Datasource and default section, change the default configuration by changing the host, username, password, and database properties to match the credentials of your database, like so:

Editing the database configuration in CakePHP

From the image above, the host was changed to 127.0.0.1, the username to root, the password was left blank, and the database was set to the one created earlier.

Set up the database for the project

To begin, we need a database with a table to store information about users. Create a database. You can name it anything, but I'm naming mine "crud". The next thing is to create a new table in your database called data using the migrations feature in CakePHP. The table needs to contain the following fields:

  • id: This field will serve as the unique identifier for each user. It should have a type of integer and be the table's primary index, with an auto-increment attribute attached to it.
  • name: This field will store the name of the data input. It should have a data type of varchar with a size of 255.
  • email: This field will have a datatype of varchar
  • phone_no: This will also have a datatype of varchar

To do this, open up the terminal and run this command:

bin/cake bake migration CreateData

This will create a migrations file in config/Migrations/ ending with _CreateData.php. Open that file in your preferred text editor or IDE and replace the body of the change() function with the following:

$table = $this->table('data');

$table->addColumn('name', 'string', [
    'default' => null,
    'limit' => 255,
    'null' => false,
]);

$table->addColumn('email', 'string', [
    'default' => null,
    'limit' => 255,
    'null' => false,
]);

$table->addColumn('phone_no', 'string', [
    'default' => null,
    'limit' => 255,
    'null' => false,
]);

$table->create();

Next, run this command to run the migration:

bin/cake migrations migrate

This will create a table called data in the database.

Now, start the development server in the terminal by running this command:

bin/cake server

The default CakePHP route showing that the development server meets all the prerequisites, and is ready to run.

Create a model and an entity

Creating and configuring the model and entity will be the next step. A model contains the information of the table where we will perform CRUD operations. The entity defines the columns for value assignment.

To create a model, navigate to the src\Model\Table directory and create a file called DataTable.php. Then, paste the following code into the file:

<?php
declare(strict_types=1);

namespace App\Model\Table;

use Cake\ORM\Query;
use Cake\ORM\RulesChecker;
use Cake\ORM\Table;
use Cake\Validation\Validator;

class DataTable extends Table
{
    /**
     * Initialize method
     *
     * @param array $config The configuration for the Table.
     * @return void
    */
    public function initialize(array $config): void
    {
        parent::initialize($config);
        $this->setTable('data');
        $this->setDisplayField('name');
        $this->setPrimaryKey('id');
    }

    /**
     * Default validation rules.
     *
     * @param \Cake\Validation\Validator $validator Validator instance.
     * @return \Cake\Validation\Validator
     */
    public function validationDefault(Validator $validator): Validator
    {
        $validator
            ->scalar('name')
            ->maxLength('name', 255)
            ->requirePresence('name', 'create')
            ->notEmptyString('name');

        $validator
            ->email('email')
            ->requirePresence('email', 'create')
            ->notEmptyString('email');

        $validator
            ->scalar('phone_no')
            ->maxLength('phone_no', 255)
            ->requirePresence('phone_no', 'create')
            ->notEmptyString('phone_no');

        return $validator;
    }
}

The code above defines a model class named DataTable. This class represents the application's database table, data. It initializes the table's configuration, specifying its name, display field, and primary key. It also defines validation rules for the name, email, and phone_no fields, ensuring data integrity and adherence to specified constraints.

Moving on to create an entity, this time inside the src\Model/Entity folder, create a file called Data.php and paste the following into the file:

<?php

declare(strict_types=1);

namespace App\Model\Entity;

use Cake\ORM\Entity;

/**
 * Data Entity
 *
 * @property int $id
 * @property string $name
 * @property string $email
 * @property string $phone_no
*/
class Data extends Entity
{
    /**
     * @var array<string, bool>
     */
    protected $_accessible = [
        'name' => true,
        'email' => true,
        'phone_no' => true,
    ];
}

The code above defines an entity class named Data, that represents an individual record in a database table. The entity has properties for id, name, email, and phone_no, each with specific data types.

CakePHP provides a time-saving and efficient way for us to create the model and entity. We can also achieve the above task of creating the model and entity  by running this command:

bin/cake bake model Data

Running this command will create the model file DataTable.php inside the src/Model/Table folder. Also, we should see the entity file Data.php inside the src/Model/Entity folder.

Create the controller

The controller governs the application flow. Inside this controller file is where the CRUD methods will be. These methods handle create, read, delete, and update methods. To create a controller, in src\Controller create a file called DataController.php, and paste the following code into the file:

<?php

declare(strict_types=1);

namespace App\Controller;

/**
 * Data Controller
 *
 * @property \App\Model\Table\DataTable $Data
 * @method \App\Model\Entity\Data[]|\Cake\Datasource\ResultSetInterface paginate($object = null, array $settings = [])
 */
class DataController extends AppController
{
    /**
     * Index method
     *
     * @return \Cake\Http\Response|null|void Renders view
     */
    public function index()
    {
        $data = $this->paginate($this->Data);
        $this->set(compact('data'));
    }

    /**
     * View method
     *
     * @param string|null $id Data id.
     * @return \Cake\Http\Response|null|void Renders view
     * @throws \Cake\Datasource\Exception\RecordNotFoundException When record not found.
     */
    public function view($id = null)
    {
        $data = $this->Data->get($id, [
            'contain' => [],
        ]);
        $this->set(compact('data'));
    }

    /**
     * Add method
     *
     * @return \Cake\Http\Response|null|void Redirects on successful add, renders view otherwise.
     */
    public function add()
    {
        $data = $this->Data->newEmptyEntity();
        if ($this->request->is('post')) {
            $data = $this->Data->patchEntity($data, $this->request->getData());
            if ($this->Data->save($data)) {
                $this->Flash->success(__('The data has been saved.'));
                return $this->redirect(['action' => 'index']);
            }
            $this->Flash->error(__('The data could not be saved. Please, try again.'));
        }

        $this->set(compact('data'));
    }

    /**
     * Edit method
     *
     * @param string|null $id Data id.
     * @return \Cake\Http\Response|null|void Redirects on successful edit, renders view otherwise.
     * @throws \Cake\Datasource\Exception\RecordNotFoundException When record not found.
    */
    public function edit($id = null)
    {
        $data = $this->Data->get($id, [
            'contain' => [],
        ]);

        if ($this->request->is(['patch', 'post', 'put'])) {
            $data = $this->Data->patchEntity($data, $this->request->getData());
            if ($this->Data->save($data)) {
                $this->Flash->success(__('The data has been saved.'));
                return $this->redirect(['action' => 'index']);
            }
            $this->Flash->error(__('The data could not be saved. Please, try again.'));
        }

        $this->set(compact('data'));
    }

    /**
     * Delete method
     *
     * @param string|null $id Data id.
     * @return \Cake\Http\Response|null|void Redirects to index.
     * @throws \Cake\Datasource\Exception\RecordNotFoundException When record not found.
    */
    public function delete($id = null)
    {
        $this->request->allowMethod(['post', 'delete']);
        $data = $this->Data->get($id);
        if ($this->Data->delete($data)) {
            $this->Flash->success(__('The data has been deleted.'));
        } else {
            $this->Flash->error(__('The data could not be deleted. Please, try again.'));
        }

        return $this->redirect(['action' => 'index']);
    }
}

So here's a breakdown of the CRUD methods in the controller:

  • Create: The add() method is responsible for creating a new record. It first creates an empty entity using newEmptyEntity(), then patches the entity with data from the request using patchEntity(), and finally saves the entity to the database using save()
  • Read: The index() method retrieves all records from the database using paginate(), and the view() method retrieves a single record by its ID using get()
  • Update: The edit() method retrieves a record by its ID using get(), patches the entity with data from the request using patchEntity(), and then saves the updated entity to the database using save().
  • Delete: The delete() method retrieves a record by its ID using get() and then deletes the record from the database using delete().

CakePHP provides a time-saving and efficient way for us to create controllers. We can also achieve the above task of creating controllers by running this command:

bin/cake bake controller DataController

This will create a controller for us as well as the codes.

Create the templates

The CRUD operations will need view files (templates) to achieve this. Navigate to the templates folder and create a folder called Data. Inside this data folder, create four files: add.php, edit.php, index.php, view.php.

The add.php creates a user interface for adding new data records to a database table. The edit.php creates a user interface for editing an existing data record. The index.php creates a template to display a list of data records in a tabular format. The view.php creates a template to display the details of a single data record.

Paste the following into add.php:

<?php
/**
 * @var \App\View\AppView $this
 * @var \App\Model\Entity\Data $data
 */
?>
<div class="row">
  <aside class="column">
    <div class="side-nav">
      <h4 class="heading"><?= __('Actions') ?></h4>
      <?= $this->Html->link(__('List Data'), ['action' => 'index'], ['class' => 'side-nav-item']) ?>
    </div>
  </aside>
  <div class="column-responsive column-80">
    <div class= "data form content">
      <?= $this->Form->create($data) ?>
      <fieldset>
       <legend><?= __('Add Data') ?></legend>
         <?php
           echo $this->Form->control('name');
           echo $this->Form->control('email');
           echo $this->Form->control('phone_no');
         ?>
      </fieldset>
      <?= $this->Form->button(__('Submit')) ?>
      <?= $this->Form->end() ?>
    </div>
  </div>
</div>

 For edit.php, paste this:

<?php
/**
 * @var \App\View\AppView $this
 * @var \App\Model\Entity\Data $data
 */
?>
<div class="row">
  <aside class="column">
    <div class="side-nav">
      <h4 class="heading"><?= __('Actions') ?></h4>
        <?= $this->Form->postLink(
          __('Delete'),
          ['action' => 'delete', $data->id],
          ['confirm' => __('Are you sure you want to delete # {0}?', $data->id), 'class' => 'side-nav-item']
        ) ?>
        <?= $this->Html->link(__('List Data'), ['action' => 'index'], ['class' => 'side-nav-item']) ?>
      </div>
    </aside>
    <div class="column-responsive column-80">
      <div class= "data form content">
        <?= $this->Form->create($data) ?>
        <fieldset>
          <legend><?= __('Edit Data') ?></legend>
          <?php
            echo $this->Form->control('name');
            echo $this->Form->control('email');
            echo $this->Form->control('phone_no');
          ?>
        </fieldset>
      <?= $this->Form->button(__('Submit')) ?>
      <?= $this->Form->end() ?>
    </div>
  </div>
</div>

For index.php, paste this:

<?php
/**
  * @var \App\View\AppView $this
  * @var iterable<\App\Model\Entity\Data> $data
  */
?>
<div class=" data index content">
  <?= $this->Html->link(__('New Data'), ['action' => 'add'], ['class' => 'button float-right']) ?>
  <h3><?= __('Data') ?></h3>
  <div class="table-responsive">
    <table>
      <thead>
        <tr>
        <th><?= $this->Paginator->sort('id') ?></th>
        <th><?= $this->Paginator->sort('name') ?></th>
        <th><?= $this->Paginator->sort('email') ?></th>
        <th><?= $this->Paginator->sort('phone_no') ?></th>
        <th class="actions"><?= __('Actions') ?></th>
      </tr>
    </thead>
    <tbody>
      <?php foreach ($data as $data) : ?>
      <tr>
        <td><?= $this->Number->format($data->id) ?></td>
        <td><?= h($data->name) ?></td>
        <td><?= h($data->email) ?></td>
        <td><?= h($data->phone_no) ?></td>
        <td class="actions">
          <?= $this->Html->link(__('View'), ['action' => 'view', $data->id]) ?>
          <?= $this->Html->link(__('Edit'), ['action' => 'edit', $data->id]) ?>
          <?= $this->Form->postLink(__('Delete'), ['action' => 'delete', $data->id], ['confirm' => __('Are you sure you want to delete # {0}?', $data->id)]) ?>
        </td>
      </tr>
      <?php endforeach; ?>
    </tbody>
  </table>
</div>
<div class="paginator">
  <ul class="pagination">
    <?= $this->Paginator->first('<< ' . __('first')) ?>
    <?= $this->Paginator->prev('< ' . __('previous')) ?>
    <?= $this->Paginator->numbers() ?>
    <?= $this->Paginator->next(__('next') . ' >') ?>
    <?= $this->Paginator->last(__('last') . ' >>') ?>
  </ul>
  <p><?= $this->Paginator->counter(__('Page {{page}} of {{pages}}, showing {{current}} record(s) out of {{count}} total')) ?></p>
  </div>
</div>

For view.php, paste this:

<?php
/**
 * @var \App\View\AppView $this
 * @var \App\Model\Entity\Data $data
 */
?>
<div class="row">
  <aside class="column">
    <div class="side-nav">
      <h4 class="heading"><?= __('Actions') ?></h4>
      <?= $this->Html->link(__('Edit Data'), ['action' => 'edit', $data->id], ['class' => 'side-nav-item']) ?>
      <?= $this->Form->postLink(__('Delete Data'), ['action' => 'delete', $data->id], ['confirm' => __('Are you sure you want to delete # {0}?', $data->id), 'class' => 'side-nav-item']) ?>
      <?= $this->Html->link(__('List Data'), ['action' => 'index'], ['class' => 'side-nav-item']) ?>
      <?= $this->Html->link(__('New Data'), ['action' => 'add'], ['class' => 'side-nav-item']) ?>
    </div>
  </aside>
  <div class="column-responsive column-80">
    <div class=" data view content">
      <h3><?= h($data->name) ?></h3>
      <table>
        <tr>
          <th><?= __('Name') ?></th>
          <td><?= h($data->name) ?></td>
        </tr>
        <tr>
          <th><?= __('Email') ?></th>
          <td><?= h($data->email) ?></td>
        </tr>
        <tr>
          <th><?= __('Phone No') ?></th>
          <td><?= h($data->phone_no) ?></td>
        </tr>
        <tr>
          <th><?= __('Id') ?></th>
          <td><?= $this->Number->format($data->id) ?></td>
        </tr>
      </table>
    </div>
  </div>
</div>

CakePHP provides a time-saving and efficient way for us to create the templates. We can also achieve the above task of creating the add.php, edit.php, index.php, view.php, by running this command:

bin/cake bake template Data

Running this command will create the files and the code as well.

 Test the application

Refresh the browser so you can see the changes. After that, you can check out the CRUD project by navigating to this link http://localhost:8765/data. Create a new data by clicking on the New Data button, fill out the details. You can also view, edit, and delete data, like the animation below.

An animation of carrying out the CRUD operations in CakePHP implemented in the application.

That's how to implement CRUD operations in CakePHP

So, in this article, we looked at how to perform crud operations in CakePHP. Understanding how to create, read, update, and delete data is fundamental to building robust, secure, and user-friendly web applications. CakePHP's built-in ORM (Object-Relational Mapping) system simplifies database interactions. Please share if you found this helpful!

Temitope Taiwo Oyedele is a software engineer and technical writer. He likes to write about things he’s learned and experienced.