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
trueorfalse
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:
- Find record
- Modify
- Save
5.5 Updating Multiple Records Using PHQL
$phql = "UPDATE Users SET active = 0 WHERE last_login < :date:";
$result = $this->modelsManager->executeQuery(
$phql,
["date" => "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->delete();
}
6.2 Handling Delete Failures
if (!$user->delete()) {
foreach ($user->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->updated_at = date("Y-m-d H:i:s");
}
7.2 Before Create Event
public function beforeCreate()
{
$this->created_at = date("Y-m-d H:i:s");
}
7.3 Before Delete Event
public function beforeDelete()
{
if ($this->is_protected) {
return false; // prevent delete
}
}
7.4 After Fetch Event
public function afterFetch()
{
$this->full_name = $this->first_name . " " . $this->last_name;
}
8. Validations During CRUD Operations
Phalcon supports model-level validations.
Example Validation
public function validation()
{
$this->validate(new EmailValidator([
"field" => "email",
]));
return $this->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->setTransaction($transaction);
$user->name = "Test";
if (!$user->save()) {
$transaction->rollback();
}
$transaction->commit();
} catch (\Exception $e) {
echo $e->getMessage();
}
10. Relationships and CRUD
Phalcon supports:
hasOnehasManybelongsTohasManyToMany
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" => 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" => "created_at DESC",
"limit" => 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->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->request->getPost();
$this->userService->createOrUpdate($data);
}
}
Service
public function createOrUpdate($data)
{
$user = Users::findFirstById($data['id']) ?? new Users();
$user->assign($data, ['name', 'email']);
return $user->save();
}
CRUCIAL: Controller is lightweight.
Leave a Reply