Category: CodeIgniter

Using ActiveRecord Library Separately from CodeIgniter

CodeIgniters ActiveRecord library is a nice implementation (though modified) of ActiveRecord design pattern. It comes bundled with CodeIgniter. So if you want to use Just this library in your application individually instead of using full CodeIgniter framework, what to do?

Today I’ve spent some time and separate the ActiveRecord library from CodeIgniter. Now you can use it via a class named ActiveRecordFactory. Here’s how to do it (I separate only the MySQL driver) .

I separate the “database” folder under the “system” directory of CodeIgniter. Then I removed all folders in database/drivers except “mysql”. (I need only MySQL, :D)

I modified the class definition at database/drivers/mysql_driver.php
Previously It Was : class CI_DB_mysql_driver extends CI_DB {
Now : class CI_DB_mysql_driver {

I modified the class definition at database/DB_driver.php
Previously It Was : class CI_DB_driver {
Now : class CI_DB_driver extends CI_DB_mysql_driver {

And finally I create a ActiveRecord Factory to instantiate codeigniters Active Record Properly. Here is the code

File: database/ActiveRecordFactory.php


<?
/**
 * This is a Factory to create an instance of Code Igniters Active Record Class
 *
 * @author Hasin [http://hasin.wordpress.com]
 */

define(BASEPATH,"./");
include_once (
"drivers/mysql/mysql_driver.php");
include_once (
"DB_driver.php");
include_once (
"DB_active_rec.php");
include_once (
"DB_result.php");
include_once (
"drivers/mysql/mysql_result.php");

function log_message(){/*just suppress logging*/}

Class ActiveRecordFactory {
    private static 
$dsn;
    private static 
$CIAR;
    public static function 
Factory($dsn=null)
    {
        if (!empty(
$dsn)) self::$dsn $dsn;
        if (empty(
self::$CIAR))
        {
            if (!empty(
self::$dsn))
            
self::$CIAR = new CI_DB_active_record($dsn) ;
            else 
            throw new 
Exception("Please give a DSN in this format 'driver://username:password@hostname/database'");
        }
        return 
self::$CIAR;
    }
}
?>

Now You can test The ActiveRecord library like this

<?
include(“database/ActiveRecordFactory.php”);
$activerecord = ActiveRecordFactory::Factory(“mysql://user:password@localhost/test”);
$activerecord = ActiveRecordFactory::Factory();

$activerecord->select(“name”);
$activerecord->where(“id=”,”10″);
$activerecord->orwhere(“id=”,”19″);
print_r($activerecord->get(“users”)->result_array());

$activerecord->select(“name”);
$activerecord->where(“id=”,”10″);
$activerecord->orwhere(“id=”,”19″);
echo $activerecord->get(“users”)->num_rows();

$activerecord->insert(“users”,array(“name”=>”CodeIgniter”,”pass”=>”CI Rocks”));
?>

You can download the complete example from here http://hasin.javapark.net/ActiveRecord.zip

Prelude to foundation: Its time to go for a better PHP Framework

I remember those old days when I had to write everything by myself. I wrote huge libraries to work with MySQL. Then I learned PostgreSQL and SQLite but didn’t rewrote my old library to work with those, I was running short of time. So I forsake the opportunity to write a db library which works with them. What I did was plain code relevant to database specific portions. Oh ya, that was a long time ago.

Soon after that I came to know adoDB which made my dream come true. I was so much happy getting my all db specific works done in a much more smarter way. I get rid of database portability issues. I was very happy that time.

I learned smarty soon after I realize that my codes are getting ugly with all the inline HTMLs and PHPs. Nothing could be smarter than separating the presentation logic from the business layer. I am a big smarty fan since that time. It saves my sleep for many nights.

But again I am recurrently suffering from maintainability issues. I was not surprised to find that my code is becoming huge unmanageable giant and it takes huge time for refactoring the application. I was very sad those days. Oh what a disaster that was.

When working with my team members located remote places, I fall into a deep shit. How can we manage and track the changes done by us? Even I was getting strange code in my routine which I bet was not written by me!! It was a terrific job (more…)

Vulnerable bug in CodeIgniter which took us hours to fix our corrupted database

We use codeigniter internally to develop our web solutions in somewhere in… net limited. Day before yesterday we suffered a terrible situation for an internal bug in code igniter which corrupted data inside some tables of our application database and then it tooks hours to find the origin of that bug, to fix it and to repair the corrupted data. Let me explain what happened.

Lets guess that we have one table named “users” with the following field

1. user_id
2. username
3. password
4. email

At some point, if you want to update the password field of this table, for a particular user, what will you write in your code?


$data = array("password"=>$new_password);
$this->db->where("user_id",$user_id);
$this->db->update("users", $data);

CodeIgniter’s ActiveRecord creates the query like the following

UPDATE users set password='{$new_password}’ where user_id='{$user_id}’;

Well, it’s ok and the quesry seems pretty fine. Now what should happen if you pass a valid user id to this code? Password of only that user will be updated. But what will happen when the passed $user_id is null?? Thats the most pathetic part that Codeigniter ActiveRecord plays. Instead of generating the following query,

UPDATE users set password='{$new_password}’ where user_id=”;

CodeIgniter’s ORM actually generates the following

UPDATE users set password='{$new_password}’ where user_id;

You find the difference of the above two queries right? one contains “where user_id=” ” and another contains just “where user_id” . Now if your backend database is MySQL and this query executes? You know what the hell will happen? It will replace all the user’s password with this new password instead of failing as MySQL count the “where user_id” part equals to false and returns all users. But If your Database is PostgreSQL, it fails, you are lucky.

So day before yesterday we suffered this problem against our commercial application which corrupts our user profile data. We immediate fixed the issue from our backup db (well, we lost 3 data) and then we started to find out what actually went wrong and found this vulnerable bug in CI.

So we suggest the CodeIgniter team to fix the issue immediately and change their ORM code so that it creates the query like the following if the value of passed argument is null. because it will fail to execute in all db. Otherwise the fellow user’s of code igniter, prepare for the dooms day.

UPDATE users set password='{$new_password}’ where user_id=”;

Igniting Code with Code Igniter

Day by day I am loving codeigniter more and more. Its so awesome, rick ellis, you are da man. But unfortunately codeigniter was not properly tested with PostgreSQL. Yesterday I fixed active records insert_id() function to work properly with PostgreSQL. I am running it’s version 8.1

Here comes my modified system/database/driver/postgre/postgre_driver.php

function insert_id()
{
$v = pg_version($this->conn_id);
if (isset($v['server'])) $v = $v['server'];

$table = func_num_args() > 0 ? func_get_arg(0) : null;
$column = func_num_args() > 1 ? func_get_arg(1) : null;

if ($table == null && $v >= '8.1')
{
$sql='SELECT LASTVAL() as ins_id';
}
elseif ($table != null && $column != null && $v >= '8.0')
{
$sql = sprintf("SELECT pg_get_serial_sequence('%s','%s') as seq", $table, $column);
$query = $this->query($sql);
$row = $query->row();
$sql = sprintf("SELECT CURRVAL('%s') as ins_id", $row->seq);

}
elseif ($table != null)
{
// seq_name passed in table parameter
$sql = sprintf("SELECT CURRVAL('%s') as ins_id", $table);
}
/**
* another insert id Hack for PostgreSQL
* @author : Hasin Hayder
* @since : March 09, 2007
*/
elseif(is_null($v))
{
$sql='SELECT (LASTVAL()) as ins_id';
}
/**
* End Hack
*/
else
{
return pg_last_oid($this->result_id);
}
$query = $this->query($sql);
$row = $query->row();
return $row->ins_id;
}

Fixing ActiveRecord Class for CodeIgniter to use with PostgreSQL

Hi,

If you are using PostgreSQL and execute the following statement, it will defnitely generate error because of some internal properties in the model.

Controller Code


<?
$model
= $this->load->model("MyModel");
$this->mymodel->insert();
?>

where the model code is


<?
Class MyModel extends Model{
  public $username
;
  
public $password;

  public function insert()
  
{
    $this
->db->insert("table",$this);
  
}
}
?>

So guys – Here I fix the Active Record class in CodeIGniter. Here goes the solution. Just replace the existing insert() function in system\database\DB_active_rec.php

Dont worry, it wont hamper the functionality for other DB providers


/**
     * Insert
     *
     * Compiles an insert string and runs the query
     *
     * @access    public
     * @param    string    the table to retrieve the results from
     * @param    array    an associative array of insert values
     * @return    object
     */
    function insert($table = '', $set = NULL)
    
{
        
if ( ! is_null($set))
        
{
            $this
->set($set);
        
}

        if (count($this->ar_set) == 0)
        
{
            
if ($this->db_debug)
            
{
                
return $this->display_error('db_must_use_set');
            
}
            
return FALSE;
        
}

        if ($table == '')
        
{
            
if ( ! isset($this->ar_from[0]))
            
{
                
if ($this->db_debug)
                
{
                    
return $this->display_error('db_must_set_table');
                
}
                
return FALSE;
            
}

            $table = $this->ar_from[0];
        
}

        /**
         * A small hack for PostgreSQL
         * @author : Hasin Hayder [[email protected]]
         * @since : March 09, 2007
         */
        
if ($this->dbdriver=="postgre"){
            $clone
= $this->ar_set;
            foreach(
$this->ar_set as $key=>$val)
                if (
substr($key,0,1)=="_") unset($clone[$key]);
            
$sql = $this->_insert($this->dbprefix.$table, array_keys($clone), array_values($clone));
        
}
        
else
        
$sql = $this->_insert($this->dbprefix.$table, array_keys($this->ar_set), array_values($this->ar_set));
        
/**
         * End Hack
         */
        
        
$this->_reset_write();
        return
$this->query($sql);
    
}

Adding Layout support like RubyOnRails into CodeIgniter :: By Hooking

Recently I came across an interesting feature in RoR which is known as layout yielding. To understand what it is just imagine that you have a particular template file (layout) specific to your controller, and in that layout file you have a special block called yield. After that whenever there is some output from any method in your controller will be yielded exactly to that block in that layout. Which means all output from your controller will be first replaced into that “yield” block inside that layout, and then whole data will be displayed right away.

So what is the benefit of using that. Simple, it will help to design your website more flexibly. You have a layout, and inside that layout at a particular position you can output all data. This will just minimize the need of splitting a single template into smaller pieces like “header”, “body”, “footer” and so forth, and finally turning them into a single piece manually before rendering the final output.

Let’s have a look at the following image to understand the fact more clearly.

layout.gif

This is a typical website payout where some of its sections are marked. In this layout which sections you think changes most frequently? Definitely the section marked as p3. Now what usually happens when you design a site.

1. Either you write the whole file as template and change the parts you need
2. Or you split them into smaller components and merge those parts while delivering the final output.

Now, with this yield feature all we do is we create a single html file exactly as the design keeping the div of P3 blank. But inside that P3 container div, we will just write some text “{yield}”. Now because of this yield feature, whenever our controller generate some output using any view, CodeIgniter will automatically load that layout file first, then take the output from the view and merge them together.

To enable this excellent yield feature into CodeIgniter we need to write some hook. First, open the file named application/config/hooks.php and add the following line in it.

$hook['display_override'][] = array(
'class' => 'Yielder',
'function' => 'yield',
'filename' => 'Yielder.php',
'filepath' => 'hooks'
);

After that, inside application/hooks folder create a file named “Yielder.php” and inside that file, write the following code.

<? if (!defined('BASEPATH')) exit('No direct script access allowed');
class Yielder
{
function yield()
{
$ci= & get_instance();
$current_output = $ci->output->get_output();
$controller = $ci->uri->segment(1);
$layout = "system/application/layouts/{$controller}.php";
if (file_exists($layout)){
$output = $ci->load->file($layout,true);
$output = str_replace("{yield}",$current_output,$output);
}
else
$output = $current_output;
echo $output;
}
}
?>

And that’s it!! You are done.

Now all you have to do is create a folder inside your application folder named “layouts” and place the layout file named same as controller. For example if your controller name is “abcd” then your layout should be “abcd.php” – But remember to keep a section named {yield} inside that layout. Lets take a look at the complete example below

Controller: controllers/abcd.php

<?
class abcd extends Controller
{
function help()
{
$title = "";
for($i=0; $i";
}
$this->load->view("myviews",array("title"=>$title));
}

}
?>

View : views/myview.php

<?
echo $title;
?>

Layout : layouts/abcd.php

<h1>
Hi there
</h1>

{yield}

<h1>
Happy Ending
</h1>

And the final Output is

/****************************

Hi there

0
1
2
3
4
5
6
7
8
9

Happy Ending

****************************/