Skip to content

ActiveRecord Class Mod

malaycake edited this page Dec 23, 2014 · 39 revisions

This page describes a heavily modified version of the great ActiveRecord Class library. Please note that some of the methods and functionality of the original are deprecated. Also, the original library was trying to replicate Ruby on Rails' look'n'feel. This is no longer the case with this mod.

Last updated on 19 Jan 2008.

Changelog

Version 0.5.2

  • Added: ordering function

Version 0.5.1

  • Fixed: a bug in find() when it did not return a record by id.
  • Changed: Renamed joining_related_() to joining_().
  • Cleaned the source making it more readable.

Version 0.5

  • Added: query modifiers (see below).
  • Changed: the code structure. Warning! Some method name were changed.

Three modifiers to rule all queries

This library introduces the concept of query modifiers. Basically, a query modifier is a method you can chain to modify the result of any find*() method call. To better explain this we need an example application.

Let's pretend we are developing a simple book management system for a library. The system will track books and their authors. Each book title and author are stored as separate records in two tables: books and authors. A book can have multiple authors, that is why we've got a authors_books relationship table. Also, a book belongs to a genre, thus we have genres table.

We create three models:

class Book extends ActiveRecord
{
    function __construct ()
    {
        parent::ActiveRecord();
        $this->_has_and_belongs_to_many = array('authors');
        $this->_belongs_to = array('genres');
    }
}
class Author extends ActiveRecord
{
    function __construct ()
    {
        parent::ActiveRecord();
        $this->_has_and_belongs_to_many = array('books');
    }
}
class Genre extends ActiveRecord
{
    function __construct ()
    {
        parent::ActiveRecord();
        $this->_has_many = array('books');
    }
}

Joining related

Let's imaging we need to list all books in the database by specifying their name and genre. There is nothing simpler:

$this->load->model('book');
$books = $this->book->joining_genres()->find_all();
echo '<ul>';
foreach($books as $book)
    echo '<li>'.$book->name.' ('.$book->genre_name.')</li>';
echo '</ul>';

If you have more than one table your table belongs_to, instead of joining_*() each table you can use joining_related() method.

Counting related

OK, you've got your list. You will probably need a list of all genres to navigate it (i.e. filter the list). It's simple:

$this->load->model('genre');
$genres = $this->genre->counting_books()->find_all();
echo '<ul>';
foreach($genres as $genre)
    echo '<li>'.$genre->name.' ('.$genre->num_books.')</li>';
echo '</ul>';

Concatenating related

Let us enhance the first example. We are going to display all books' authors now. Previously, we needed to call fetch_related_authors() for each foreach iteration, which is not wise performance wise.

$this->load->model('book');
$books = $this->book->joining_genres()->concatenating_authors('name')->find_all();
echo '<ul>';
foreach($books as $book)
    echo '<li>'.$book->name.' by '.$book->authors.' ('.$book->genre_name.')</li>';
echo '</ul>';

Warning! You cannot use counting_() and concatenating_() together. Warning! concatenating_*() is only available for MySQL version 4.1 and higher

Other notable changes

No caching for now

The discover_table_columns() method and all caching logic were ditched. Some hacky benchmarking's shown no difference, but the library got cleaner and there is no more "OMG, I changed my DB structure, but the old one stuck in the cache, and I forgot about that!"

Find can only find

Which in plain English means you cannot add SQL clauses via some find*() method attribute. For this purpose use filtering() and searching() query modifiers. These are simple wrappers for CI's Active record where() and like() methods, respectively:

$this->book->filtering('year >',2006)->find_all();
$this->author->searching('name','John')->find_all();

Simpler __constructs

You no longer need to specify your $this->_class_name and $this->_table, unless their singular to plural conversion is more complex than adding 's'.

Download

File:ActiveRecord_Mod_v0.5.2.zip. File:ActiveRecord_Mod_v0.5.1.zip.

Development version (January 19, 2008)

File:Activerecord_v0.7_dev.zip. It is under constant development, so visit this page from time to time to see if a new version gets available.

Things you should keep in mind

  • API has changed a lot (see the code).

  • Only tested using MySQL DB driver.

  • CodeIgniter's own ActiveRecord library is no longer required. Use where(), like(), having(), limit(), group_by(), order_by() methods to set query clauses, group and sort.

  • You can count, sum, average, concatenate any number of columns. You can also count only those records that satisfy an arbitrary condition.

$this->book
    ->concatenate_authors('name');    // $record->author_names           
    ->concatenate_genres('name');     // $record->genre_names
    ->count_orders('id','YEAR(NOW()) = YEAR(book_orders.date_placed)','this_year'); // $record->orders_count_this_year
    ->sum_stock('quantity','stock');  // $record->stock_quantity_sum
    ->find_all();
  • You can no longer create() a record. Use the following pattern instead:
$this->book->set_values(array('name' => 'Book name', 'year' => 1990));
$this->book->save();
  • You can specify whether to return results fount as plain objects or ActiveRecord instances.
$books = $this->book->find_all(PLAIN_OBJECT); // will return an array of stdclass objects instead of ActiveRecord objects.
  • You can either return a single record found using find() method or assign it to self.
$this->book->find_by_id(12,ASSIGN_SELF);
echo $this->book->name;
  • You can specify columns to be selected using select() method. select(null) returns nothing.

  • You can create vary complex join statements, join distant relatives, perform count_(), sum_() and other operations on joined tables using new join syntax:

$this->load->model('member');
$this->load->model('user_account','user');

$member = $this->member
    ->select(null)
    ->sum_credits('amount')
    ->join($this->user
        ->count_reviews()
        ->count_responses()
        ->select('name'))
    ->find_by_id($id);

// $member->credit_amount_sum
// $member->user_reviews_count
// $member->user_responses_count
// $member->user_name
  • Implement serialize() and unserialize() methods in your own model to serialize data when you save() or update() and unserialize it when retrieved from the database using find_*() methods.
 
function serialize ( &$data )
{
    if ( property_exists($data,'some_blob'))
        $data->some_blob = serialize($data->some_blob);
}

function unserialize ( &$data )
{
    if ( property_exists($data,'some_blob'))
        $data->some_blob = unserialize($data->some_blob);
}

Category:Contributions::Libraries::Database

Clone this wiki locally