CodeIgniter active record is a query builder, not an ORM

CodeIgniter 2 calls its database class Active Record, which is the name of a pattern in which an object knows how to load and save itself. What the class actually is is a query builder — a fluent way of assembling a SQL string, with no entities, no identity and no persistence anywhere in it. The distinction stops being pedantic the first time somebody expects a returned row to have a save() on it.

$rows = $this->db
    ->select('sku, name, price')
    ->where('active', 1)
    ->where_in('brand_id', $brands)
    ->order_by('name', 'ASC')
    ->limit(24)
    ->get('products')
    ->result();

// stdClass, not a Product
echo $rows[0]->sku;

// there is no $row->save(); an update is a second statement
$this->db->where('id', $id)
         ->update('products', array('price' => 4900));

Rows arrive as stdClass or plain arrays, so there is nothing to mutate and nothing to write back: an update repeats the primary key in a separate call. There is no identity map either, so loading the same product twice produces two unrelated objects, and a related row is another query rather than a property. The behaviour actually worth knowing is that the builder accumulates state on the shared $this->db object and only flushes it when a query runs — build a set of where() conditions, return early without calling get(), and the next query in the same request inherits them. That is the source of most of the “it works in isolation” bugs in a CodeIgniter model, and $this->db->last_query() is the fastest way to see it.