CodeIgniter 4 model and Query Builder interview questions
Twelve CodeIgniter 4 interview questions on models and the Query Builder, with clear answers and code: mass assignment, soft deletes, casting, SQL safety.
Updated 6 min read

In this article
CodeIgniter 4 interviews almost always include the data layer, because that is where real bugs and security holes live. Expect questions on how the Model differs from the Query Builder, how mass assignment is prevented, soft deletes, timestamps, escaping and bindings, pagination, and features added in recent releases such as model field casting (4.5) and primary key validation (4.7).
Below are twelve questions interviewers ask, each with a short answer you can say out loud and, where it helps, a few lines of code. The answers match CodeIgniter 4.7, the current release line in October 2026. Learn the reasoning, not just the method names; good interviewers follow up with "why".
Fundamentals
1. What is the difference between the Model and the Query Builder?
The Query Builder ($db->table('orders')) builds and runs SQL for any table and returns raw results. A CodeIgniter\Model wraps one table and adds conveniences on top: find(), insert(), update(), save(), validation, allowed-field protection, timestamps, soft deletes, events, casting and pagination. A model can use Query Builder methods in a chain, such as $model->where('status', 'paid')->findAll(), but the user guide warns that if you end the chain with a builder method that returns results, such as get(), the model's events and features do not apply. End chains with a model method.
2. Show a typical model definition
<?php
namespace App\Models;
use CodeIgniter\Model;
class OrderModel extends Model
{
protected $table = 'orders';
protected $primaryKey = 'id';
protected $returnType = 'array';
protected $useSoftDeletes = true;
protected $useTimestamps = true;
protected $allowedFields = ['customer_id', 'status', 'total'];
protected array $casts = [
'id' => 'int',
'customer_id' => 'int',
'total' => 'float',
];
protected $validationRules = [
'customer_id' => 'required|is_natural_no_zero',
'status' => 'required|in_list[new,paid,shipped]',
];
}Point out that the primary key is not in $allowedFields; the user guide says it never should be.
3. How does CodeIgniter prevent mass assignment?
With $allowedFields. On insert and update the model removes any key that is not listed, so a user who adds is_admin=1 to a form cannot set it. $protectFields is true by default; switching it off removes the protection. An empty $allowedFields with protection on throws an exception rather than silently saving nothing.

4. What does save() do?
It decides between insert and update. If the data contains the primary key, save() calls update(); otherwise it inserts. It returns a boolean, so call getInsertID() afterwards when you need the new ID. A follow-up worth knowing: since 4.5, $updateOnlyChanged is true, so saving an entity with no changes throws a DataException ("There is no data to update").
Data integrity and lifecycle
5. How do soft deletes work?
With $useSoftDeletes = true, delete() sets the deleted_at column instead of removing the row, and find*() methods exclude soft-deleted rows. Use withDeleted() to include them, onlyDeleted() to list only them, delete($id, true) to delete permanently and purgeDeleted() to remove every soft-deleted row. A good answer adds the catch: unique indexes still see soft-deleted rows, so re-registering a deleted email can fail at the database level.
6. What is model field casting, and when would you use entities instead?
Added in 4.5, $casts converts database values to PHP types when reading (for example, integer columns that arrive as strings) and back when saving. Types include int, float, bool, json, datetime, timestamp and csv; 4.7 added enum casting for backed and unit PHP enums, written as enum[App\Enums\Status]. Entities are classes that represent a row and can hold behaviour, such as a getFullName() method or a password setter that hashes. The user guide says not to combine model casting with entity property casting on the same data.
7. What changed with primary keys in 4.7?
insert() (when the key is not auto-increment), update() and delete() now validate primary key values before running a query. Values such as null, 0, '0', empty strings, booleans and empty or nested arrays throw an InvalidArgumentException. This stops bugs such as $model->update(null, $data) updating every row. Legacy schemas that really use 0 as a key can override validateID().
Query Builder and SQL safety
8. Is the Query Builder safe from SQL injection?
Values passed to methods such as where(), like(), insert() and update() are escaped automatically, and identifiers are protected. The exceptions are deliberate: passing false as the escape argument, and RawSql objects (since 4.2), where the user guide says you must escape values and protect identifiers yourself. For hand-written SQL, use query bindings with ? or named :name: placeholders rather than concatenating strings.
$rows = db_connect()->table('orders')
->select('orders.id, orders.total, customers.name')
->join('customers', 'customers.id = orders.customer_id')
->where('orders.status', $status)
->groupStart()
->where('orders.total >', 100)
->orWhere('customers.vip', 1)
->groupEnd()
->orderBy('orders.created_at', 'DESC')
->limit(20)
->get()
->getResultArray();groupStart() and groupEnd() add the parentheses, so the OR cannot widen the status filter. That is a classic follow-up question.

9. How do you count rows without losing the query?
countAllResults() resets the builder by default, like get(). Pass false to keep the conditions so you can fetch the page of results afterwards. To inspect SQL without running it, use getCompiledSelect(false), again passing false if you want to keep building.
10. How do you insert or update many rows efficiently?
Use insertBatch() and updateBatch() instead of looping single queries. Since 4.3 the builder also has upsert() and upsertBatch(), which insert new rows or update existing ones based on a unique key. Mention that model batch methods trigger beforeInsertBatch and related events, also added in 4.3.
Performance and design
11. How would you process a million rows or show a paginated list?
For background jobs, use $model->chunk(500, static function ($row) { ... }), which fetches rows in batches and calls the closure once per row, so memory stays flat. For screens, use $model->paginate(20) and render $model->pager->links() in the view. Add indexes for the columns you filter and sort on; pagination does not fix a full table scan.
12. CodeIgniter has no ORM relationships. How do you avoid N+1 queries?
The CodeIgniter Model deliberately does not define relations like Laravel's Eloquent. Load related data with a join, or collect the IDs and fetch the related rows with one whereIn() query, then attach them in PHP. Saying this clearly, and mentioning that the debug toolbar's database tab shows every query a request ran, is usually what the interviewer wants.
Quick-reference table
| Topic | Key API | Since |
|---|---|---|
| Mass assignment | $allowedFields, $protectFields | 4.0 |
| Raw SQL fragments | RawSql | 4.2 |
| Upserts and batch events | upsert(), upsertBatch(), $beforeInsertBatch | 4.3 |
| Field casting, changed-only updates | $casts, $updateOnlyChanged | 4.5 |
| Enum casting, primary key validation | enum[...], validateID() | 4.7 |
How to prepare
- Build one small CRUD feature end to end: migration, model with validation, controller and a feature test. Interviewers can tell when answers come from practice.
- Know the security story: allowed fields, automatic escaping, bindings and where escaping stops.
- Read the latest change logs. Being able to say what 4.7 changed, and that 4.7.5 was a security release, shows you keep up.
- Expect a whiteboard query with a join, a grouped
ORcondition and pagination; practise writing it with the Query Builder from memory.
If you are hiring rather than interviewing, ask candidates to explain a bug they fixed in a data layer. The answer tells you more than any definition of findAll().
Frequently asked questions
Does CodeIgniter 4 have an ORM?
It has a model layer with entities, casting, validation and events, but no relationship mapping like Eloquent or Doctrine. Related data is loaded with joins or separate queries.
Should the primary key be in $allowedFields?
No. The user guide says the primary key should never be an allowed field.
save()uses its presence in the data to choose update over insert.Are Query Builder values escaped automatically?
Yes, values passed to builder methods are escaped. Escaping is skipped when you pass
falseas the escape argument or useRawSql, and then you are responsible for escaping.What is the difference between withDeleted() and onlyDeleted()?
withDeleted()returns active and soft-deleted rows;onlyDeleted()returns only soft-deleted rows. Both apply to the nextfind*()call.Which CodeIgniter version added model field casting?
Model field casting with
$castsarrived in 4.5.0. Enum casting was added in 4.7.0.Why does update(null, $data) throw an exception in 4.7?
Since 4.7.0, models validate primary key values before running insert, update or delete queries.
null,0, empty strings and similar values throw anInvalidArgumentExceptioninstead of risking an update of every row.
Your next customer could start right here.
Introduce your tool, product or business to readers exploring technology.
Explore placements and request a private quote
Sources
- Using CodeIgniter's Model — CodeIgniter Foundation
- Query Builder Class — CodeIgniter Foundation
- Using Entity Classes — CodeIgniter Foundation
- Queries: Query Bindings — CodeIgniter Foundation
- Version 4.7.0 change log — CodeIgniter Foundation
Written with substantial AI assistance. Facts checked against cited sources. Editorial policy.
Comments
No comments yet. Be the first to join the conversation.
Leave a comment
Every comment is read by a moderator before it appears. Your email address is never published; it is used only to contact you about this comment.
More from Cloudsoft Zone

Industry updateCodeIgniter
CodeIgniter 4.7.5 security release: what to patch and how
CodeIgniter 4.7.5 fixes six advisories, including a critical upload bypass and Parser code execution. See who is exposed and how to upgrade safely.
By Cloudsoft Zone

Troubleshooting guideCodeIgniter
CodeIgniter 4 validation not working? 9 causes and fixes
Fix CodeIgniter 4 validation problems fast: wrong input source, lost errors after redirect, permit_empty, strict JSON rules, is_unique placeholders and more.
By Cloudsoft Zone

Industry updateCakePHP
What's new in CakePHP 5.4: ORM changes, DTO mapping and a built-in container
CakePHP 5.4 changes default eager loading, enforces entity-table ownership and adds request-to-DTO mapping, an optional built-in DI container, typed unhydrated queries and JSON streaming. Here is what to check before upgrading.
By Cloudsoft Zone
