Performing CRUD Operations with Models in Phalcon

Phalcon is a high-performance PHP framework built as a C-extension, offering unmatched speed, low resource usage, and an elegant MVC architecture. One of its most powerful components is the ORM (Object-Relational Mapping) system, which allows developers to interact with the database through PHP classes called models. Instead of writing SQL manually, developers can use intuitive methods to perform all CRUD operations—Create, Read, Update, and Delete—with clean and maintainable syntax.

CRUD operations are the backbone of every application that stores, retrieves, modifies, or deletes data. Phalcon’s ORM makes these operations simple by abstracting SQL queries while still giving developers complete control. With built-in validation, data mapping, relationships, and transactional safety, Phalcon models enable developers to focus on business logic rather than database details.

This comprehensive guide explains how to perform CRUD operations in Phalcon using models, complete with examples, best practices, and advanced concepts.

1. Introduction Why CRUD with Models Matters

Every application that interacts with a database relies on CRUD operations. Phalcon simplifies this through its ORM:

  • Create → Insert new records
  • Read → Retrieve data
  • Update → Change existing records
  • Delete → Remove records

Models serve as a bridge between your PHP code and the database layer. They represent database tables, with columns mapped to properties.

1.1 Benefits of Using Phalcon Models for CRUD

  • Clean and intuitive syntax
  • Automatic SQL generation
  • Built-in security (preventing SQL injection)
  • Relationship handling
  • Event hooks for custom logic
  • Metadata caching
  • Validation support

Whether you’re building basic forms or complex enterprise systems, CRUD with Phalcon models gives you both simplicity and power.


2. Understanding Phalcon Models

A model in Phalcon is typically stored in:

app/models/Users.php

2.1 Basic Model Structure

use Phalcon\Mvc\Model;

class Users extends Model
{
public $id;
public $name;
public $email;
}

Each property maps to a database column.

2.2 Automatic Table Mapping

The model name (Users) automatically maps to the table users unless you override it:

public function getSource()
{
return "my_custom_table";
}

3. CRUD Operation #1: Creating Records (INSERT)

Creating records in Phalcon is extremely straightforward.

3.1 Creating a New Record

$user = new Users();

$user->name  = "John Doe";
$user->email = "[email protected]";

if ($user->save()) {
echo "User created!";
} else {
echo "Failed to create user!";
}

3.2 Save Method

save() automatically:

  • Validates data
  • Generates SQL (INSERT)
  • Escapes inputs
  • Returns true or false

3.3 Handling Validation Messages

if (!$user->save()) {
foreach ($user->getMessages() as $message) {
    echo $message, "<br>";
}
}

3.4 Creating a Record with Array Input

$user = new Users();

$user->assign(
[
    "name"  => "Alice",
    "email" => "[email protected]",
]
); $user->save();

3.5 Mass Assignment Protection

Phalcon only assigns fields registered in your model’s whitelist:

public $allowedFields = ['name', 'email'];

This prevents malicious data inserts.


4. CRUD Operation #2: Reading Records (SELECT)

Reading data is essential in any application. Phalcon provides multiple retrieval methods.


4.1 Finding All Records

$users = Users::find();

This returns a result set, iterable like an array.

Looping Through Records

foreach ($users as $user) {
echo $user->name;
}

4.2 Finding First Record

$user = Users::findFirst();

Find First with Condition

$user = Users::findFirst("id = 5");

Or with arrays:

$user = Users::findFirst([
"conditions" => "email = :email:",
"bind"       => ["email" => "[email protected]"],
]);

4.3 Finding with Conditions

Using the find() Method

$users = Users::find([
"conditions" => "active = 1 AND role = 'admin'",
]);

Using Bind Parameters

$users = Users::find([
"conditions" => "id > :id:",
"bind"       => ["id" => 10],
]);

Bind parameters prevent SQL injection.


4.4 Ordering Results

$users = Users::find([
"order" => "name ASC"
]);

4.5 Limiting Results

$users = Users::find([
"limit" => 5,
]);

4.6 Combining Conditions, Ordering, Limit

$users = Users::find([
"conditions" => "role = 'editor'",
"order"      => "created_at DESC",
"limit"      => 10,
]);

4.7 Using PHQL (Phalcon Query Language)

Phalcon provides PHQL for more SQL-like queries.

$phql = "SELECT * FROM Users WHERE role = :role:";
$users = $this->modelsManager->executeQuery($phql, ["role" => "admin"]);

5. CRUD Operation #3: Updating Records (UPDATE)

Updating records is similar to inserting, but Phalcon handles the difference automatically.


5.1 Fetching and Updating a Record

$user = Users::findFirst(5);

$user->name = "New Name";

$user->save();

Phalcon generates an UPDATE automatically.


5.2 Updating with assign()

$user->assign(
$this->request->getPost(),
['name', 'email']
);

5.3 Checking for Errors

if (!$user->save()) {
foreach ($user->getMessages() as $msg) {
    echo $msg;
}
}

5.4 Update Conditions

You cannot update without fetching. You must:

  1. Find record
  2. Modify
  3. Save

5.5 Updating Multiple Records Using PHQL

$phql = "UPDATE Users SET active = 0 WHERE last_login < :date:";
$result = $this->modelsManager->executeQuery(
$phql,
&#91;"date" =&gt; "2024-01-01"]
);

6. CRUD Operation #4: Deleting Records (DELETE)

Deleting records is as straightforward as inserting or updating.


6.1 Fetch Then Delete

$user = Users::findFirst(10);

if ($user) {
$user-&gt;delete();
}

6.2 Handling Delete Failures

if (!$user->delete()) {
foreach ($user-&gt;getMessages() as $msg) {
    echo $msg;
}
}

6.3 Deleting Multiple Records Using PHQL

$phql = "DELETE FROM Users WHERE active = 0";
$this->modelsManager->executeQuery($phql);

7. Model Events for CRUD Operations

Phalcon models have built-in events that let you execute custom logic during CRUD operations.


7.1 Before Save Event

public function beforeSave()
{
$this-&gt;updated_at = date("Y-m-d H:i:s");
}

7.2 Before Create Event

public function beforeCreate()
{
$this-&gt;created_at = date("Y-m-d H:i:s");
}

7.3 Before Delete Event

public function beforeDelete()
{
if ($this-&gt;is_protected) {
    return false; // prevent delete
}
}

7.4 After Fetch Event

public function afterFetch()
{
$this-&gt;full_name = $this-&gt;first_name . " " . $this-&gt;last_name;
}

8. Validations During CRUD Operations

Phalcon supports model-level validations.

Example Validation

public function validation()
{
$this-&gt;validate(new EmailValidator(&#91;
    "field" =&gt; "email",
]));
return $this-&gt;validationHasFailed() != true;
}

9. Transactions for Safe CRUD

Transactions ensure that multiple operations succeed or fail together.

Example Transaction

$manager = new TransactionManager();
$transaction = $manager->get();

try {
$user = new Users();
$user-&gt;setTransaction($transaction);
$user-&gt;name = "Test";
if (!$user-&gt;save()) {
    $transaction-&gt;rollback();
}
$transaction-&gt;commit();
} catch (\Exception $e) {
echo $e-&gt;getMessage();
}

10. Relationships and CRUD

Phalcon supports:

  • hasOne
  • hasMany
  • belongsTo
  • hasManyToMany

Example Relationship Fetch

$user = Users::findFirst(1);
$posts = $user->posts;

Deleting With Constraints

$user->delete(); // deletes related records if configured

11. CRUD Performance Optimizations

11.1 Use Metadata Caching

$di->set("modelsMetadata", function () {
return new Memory();
});

11.2 Use Resultset Hydration

Users::find([
"hydration" =&gt; Resultset::HYDRATE_ARRAYS
]);

11.3 Limit and Paginate Large Queries

Users::find(["limit" => 50, "offset" => 100]);

11.4 Avoid Overusing findFirst() in Loops

Bad:

foreach ($ids as $id) {
Users::findFirst($id);
}

Good:

Users::find("id IN ({ids:array})");

12. Real-World CRUD Examples


12.1 User Registration (Create)

$user = new Users();

$user->assign($this->request->getPost(), ["name", "email", "password"]);

$user->password = $this->security->hash($user->password);

$user->save();

12.2 Product Listing (Read)

$products = Products::find([
"order" =&gt; "created_at DESC",
"limit" =&gt; 20
]);

12.3 Updating User Profile (Update)

$user = Users::findFirstById($id);

$user->assign($this->request->getPost(), ["name", "email"]);

$user->save();

12.4 Soft Delete Example

Instead of deleting:

public function beforeDelete()
{
$this-&gt;deleted = 1;
return false;
}

13. Common Mistakes with CRUD in Phalcon

Avoid the following:

❌ Performing heavy logic in controllers

❌ Not checking save() return value

❌ Overusing PHQL unnecessarily

❌ Not sanitizing input

❌ Forgetting to use bind parameters

❌ Fetching too much data

❌ Writing SQL manually when not needed


14. Best Practices for CRUD Operations

14.1 Use Services for Business Logic

Controllers should coordinate, not compute.

14.2 Always Validate Input

Use validators or forms.

14.3 Use PHQL Only When Required

Default ORM methods are enough in many cases.

14.4 Use Transactions for Multi-Step Operations

Guarantees safety.

14.5 Use Soft Deletes for Important Tables

Prevents accidental data loss.

14.6 Use Relationships for Linked Data

Avoid manually writing JOINs when ORM can do it.

14.7 Cache Metadata and Results When Possible

Improves speed.

14.8 Keep Models Clean and Focused

Only database-related logic should live in models.


15. Complete CRUD Workflow Example

Controller

class UserController extends Controller
{
public function saveAction()
{
    $data = $this-&gt;request-&gt;getPost();
    $this-&gt;userService-&gt;createOrUpdate($data);
}
}

Service

public function createOrUpdate($data)
{
$user = Users::findFirstById($data&#91;'id']) ?? new Users();
$user-&gt;assign($data, &#91;'name', 'email']);
return $user-&gt;save();
}

CRUCIAL: Controller is lightweight.


Comments

Leave a Reply

Your email address will not be published. Required fields are marked *