CodeIgniter Forums

Full Version: result object library for returning Query Builder query results
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
Hi,
I created the following library, meant to be used in models so query results can be returned in different formats:

PHP Code:
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');

# /application/libraries/Return_object.php

Class Return_object
{
    private 
$result;
    private 
$ci;
    
    public function 
__construct()
    {
        
$this->ci = &get_instance();
        
        
$this->result = new StdClass();
        
        
$this->result->data null;
        
$this->result->status false;
        
$this->result->count 0;
        
$this->result->feedback '';
    }
    
 
   public function output($data=null$response_type=''$params=array())
 
   {
     
   // Get the data property
 
       if($data && is_object($data))
 
       {
     
       $this->result->status true;
     
       
            
if($data->num_rows() > 0)
     
       {
         
       $this->result->feedback $this->ci->db->last_query();
         
       $this->result->data $data->result_object();
         
       $this->result->count $data->num_rows();
         
   }
     
       elseif($data->num_rows == 0)
     
       {
         
       $this->result->feedback 'Nothing returned';
         
   }
        }
 
       
        
// Set the callback (note: if JSONP without callback requested, an error will already be thrown by sanitize)
 
       $callback = isset($params['callback']) ? $params['callback'] : null;
        
        
// Handle response type and return object
        
$response_type strtoupper($response_type);
        
        switch (
$response_type)
        {
         
   case 'JSON':
         
       return json_encode($this->result);
         
       break;
         
       
            case 
'JSONP'// JSON with Padding, to overcome cross-domain restrictions
         
       return $callback '(' json_encode($this->result) . ');';
         
       break;
         
           
            case 
'ARRAY':
         
       return (array) $this->result;
         
       break;
         
       
            default
:
         
       return $this->result;
        }
 
   }
 
   
    public 
function __destruct()
    {
        unset(
$this->result);
    }

 
So, I use the library in models like this:
First model: ra_promotors_model.php
PHP Code:
<?php if (!defined('BASEPATH')) exit('No direct script access allowed');

# /application/models/Ra_promotors_model.php

class Ra_promotors_model extends CI_Model
{
 
   private $table 'ra_promotors';

    public function 
__construct()
    {
        
parent::__construct();
        
 
              $this->load->database('assets');
 
              $this->load->library('return_object');
    }
     
   
    public 
function get()
 
          
        $query 
$this->db->get($this->table);        
        return $this
->return_object->output($query);
 
   }
}
?>

Second model: ra_users_model.php

PHP Code:
<?php if (!defined('BASEPATH')) exit('No direct script access allowed');

# /application/models/Ra_users_model.php

class Ra_users_model extends CI_Model
{
 
   private $table 'ra_users';

 public function 
__construct()
 {
 
parent::__construct();
 
 
        $this->load->database('assets');
 
        $this->load->library('return_object');
 }
 
   
    public 
function get()
 
          
        $query 
$this->db->get($this->table);        
        return $this
->return_object->output($query);
 
   }
}
?>


In a controller the model now returns a neat object, with a data property, a status property and as feedback, the last performed query...
Code:
$this->load->model('ra_promotors', 'rap_model');
$temp['promotors'] = $this->rap_model->get();
Gives the following result in the browser (see attached image)

Everything works fine until two models come into play:
Code:
$this->load->model('ra_promotors', 'rap_model');
$temp['promotors'] = $this->rap_model->get();
$this->load->model('ra_users', 'rau_model');
$temp['users'] = $this->rau_model->get();

Now, the two parts of the array are displayed BUT I get the result of the 'ra_promors_model' twice!!!

If I do NOT USE the return object library, I get the two different query results - as expected-...
So it has something to do with the library I created :-(

Anyone?

Thanks for the help!

Zelf
When you load the library, if it has already been loaded previously, the existing instance of the library is returned, and the constructor is not executed again.

In both cases, you return $this->result, which is a property which has been assigned an instance of stdClass. Since this is a class, when you assign the result of $this->return_object->output($query) to a variable (or, in your case, an index in an array), you're creating a reference to the instance of $this->result in $this->return_object.

Honestly, I'm not 100% sure why you would get the first one twice, rather than the second one twice, unless the second one didn't return a result.

You should be able to fix this by using `clone` when returning $this->result from output(), or by defining $this->result as an array, then casting it to an object as needed. You may also want to reset $this->result in output(), since anything you don't set will still be set to the value from the previous call, so, currently:
- if you pass a falsey value for $data, everything in $this->result will be from the previous call
- if you pass a truthy object for $data, but num_rows() is less than or equal to 0 for some reason, $this->result->data and $this->result->count will be from the previous call

Also, note that last_query() doesn't work if the current database configuration has save_queries disabled.
(10-21-2015, 12:43 PM)mwhitney Wrote: [ -> ]When you load the library, if it has already been loaded previously, the existing instance of the library is returned, and the constructor is not executed again.

In both cases, you return $this->result, which is a property which has been assigned an instance of stdClass. Since this is a class, when you assign the result of $this->return_object->output($query) to a variable (or, in your case, an index in an array), you're creating a reference to the instance of $this->result in $this->return_object.

Honestly, I'm not 100% sure why you would get the first one twice, rather than the second one twice, unless the second one didn't return a result.

You should be able to fix this by using `clone` when returning $this->result from output(), or by defining $this->result as an array, then casting it to an object as needed. You may also want to reset $this->result in output(), since anything you don't set will still be set to the value from the previous call, so, currently:
- if you pass a falsey value for $data, everything in $this->result will be from the previous call
- if you pass a truthy object for $data, but num_rows() is less than or equal to 0 for some reason, $this->result->data and $this->result->count will be from the previous call

Also, note that last_query() doesn't work if the current database configuration has save_queries disabled.

Hello mwhitney,
Thanks for your answer. I thought, each time a model is loaded, a separate instance of return_object 'lives' in that model (so the constructor of this class is loaded for each instance)...
It seems I will have to enhance my CI-related OOP knowledge  Blush

Kind regards,

Zelf
It can be a bit tricky at times, but it really comes down to the CI_Controller being a singleton and setting up some simple shortcuts to the CI_Controller instance. The CI_Controller singleton is setup like this (some comments and whitespace removed to trim down the code):

PHP Code:
class CI_Controller {
    
/**
     * Reference to the CI singleton
     */
    
private static $instance;

    public function 
__construct()
    {
        
self::$instance =& $this;

        
// Assign all the class objects that were instantiated by the
        // bootstrap file (CodeIgniter.php) to local class variables
        // so that CI can run as one big super object.
        
foreach (is_loaded() as $var => $class) {
            
$this->$var =& load_class($class);
        }

        
$this->load =& load_class('Loader''core');
        
$this->load->initialize();
        
log_message('info''Controller Class Initialized');
    }

    
/**
     * Get the CI singleton
     */
    
public static function &get_instance()
    {
        return 
self::$instance;
    }


To ensure the same instance is retrieved in most contexts, CodeIgniter.php defines the global get_instance() function as follows:

PHP Code:
    // Load the base controller class
    
require_once BASEPATH.'core/Controller.php';

    
/**
     * Returns current CI instance object
     */
    
function &get_instance()
    {
        return 
CI_Controller::get_instance();
    } 

When you load a model or library, the loader gets the CI_Controller instance, then checks whether the model/library has already been loaded on that instance. If it has already been loaded, the loader returns without doing anything else, unless you supply an alternate name in the call to the loader. If it has not been loaded, or you supply an alternate name for the library/model, it loads the library/model and assigns it to the CI_Controller instance as a property named with the name of the library/model or the alternate name.

The bulk of the code in CI_Model is a magic method to access the CI_Controller instance if a method or property isn't defined on the model:

PHP Code:
class CI_Model {
    public function 
__construct()
    {
        
log_message('info''Model Class Initialized');
    }

    
/**
     * Allows models to access CI's loaded classes using the same
     * syntax as controllers.
     */
    
public function __get($key)
    {
        
// Debugging note:
        //    If you're here because you're getting an error message
        //    saying 'Undefined Property: system/core/Model.php', it's
        //    most likely a typo in your model code.
        
return get_instance()->$key;
    }