Welcome Guest, Not a member yet? Register   Sign In
Extending the Class returned by the CI Database Class
#1

[eluser]ptrippett[/eluser]
My pet peeve about the Database class has been that it $this->db->get_where([...]) etc returned a class that just contained data. And I wanted to manipulate that data without having to pass it into another object of function etc. I wanted some basic OO principles to apply and be able to specify functions in that class which manipulated that data for me.

For Example
Code:
$post = $this->blog_model->getPost(1);
$post->syndicate();

My idea is to have "Skeleton" classes (sounded cool at the time) whcih could be returned from the model instead of the generic stdClass as per usual. So this is how i went about it...

Firstly, because CodeIgniter loads classes when required by use of $this->load->library() i need to preload the classes used for these skeletons putting include_once throughout my models was not really an option as it looked untidy so i created a Hook to load the skeleton classes which would reside in their own directory.

system/application/config/config.php
Code:
$config['enable_hooks'] = TRUE;

system/application/config/hooks.php
Code:
/**
* Hook to load Skeleton classes used by My_Model::apply()
*/
$hook['pre_controller'][] = array(
    'class'    => 'Skeleton',
    'function' => 'load',
    'filename' => 'Skeleton.php',
    'filepath' => 'hooks'
);

system/application/hooks/Skeleton.php
Code:
<?php

    define('SKEL_DIR', APPPATH . 'skeleton');

    class Skeleton {
    
        /**
         *
         */
        function load() {
        
            if ( file_exists(SKEL_DIR) ) {
                
                if (file_exists(SKEL_DIR . '/cSkeleton.php')) include_once SKEL_DIR . '/cSkeleton.php';
            
                $files = scandir(SKEL_DIR);
                foreach ($files as $file) {
                    if (substr($file,strlen($file) - 4, 4) == '.php') {
                        include_once SKEL_DIR . '/' . $file;
                    }
                }
            
            }
        
        }
    
    }
// END OF FILE
// system/application/hooks/Skeleton.php

Now any files i put into system/application/skeleton/ would be automatically loaded and could be accessible at any point in the application. The next step was to edit my base model to allow me to copy the data returned from the Database class into the class i want.

I also have a class called cSkeleton.php which I use as a base class for all my skeleton classes...

system/application/skeleton/cSkeleton.php
Code:
<?php

    class cSkeleton {
        
        /**
         * INTERNAL: Internal Code Igniter Reference
         */
        private $CI;
        
        /**
         *
         */
        function __construct() {
            $this->CI = get_instance();
        }
        
        /**
         * Copy variables from one object into this class
         */
        function __apply($data) {
            foreach($data as $key => $value) {
                $this->$key = $value;
            }
        }
        
    }

// END OF FILE
// system/application/skeleton/cSkeleton.php

system/application/libraries/MY_Model.php
Code:
class MY_Model extends Model {
    
    function MY_Model() {
        parent::Model();
    }
    
    /**
     * Apply Values of one class in to the target class
     */
    function apply($class_name, $data) {
        
        if (is_array($data)) {
            
            $items = array();
            foreach ($data as $key => $item) {
                $items[$key] = $this->apply($class_name, $item);
            }
            
        } else {
            
            $target = new $class_name();
            foreach($data as $key => $value) {
                $target->$key = $value;
            }
            return $target;
            
        }
        
    }
    
}

// END OF FILE
// system/application/libraries/MY_Model.php

To start using the code we will create a skeleton class for our blog posts.

system/application/skeleton/cPost.php
Code:
<?php

    class cPost extends cSkeleton {

        function syndicate() {
            // XMLRPC Ping this post to somewhere worthy
        }

    }
// END OF FILE
// system/application/akeleton/cPost.php

in our blog_model we can return the data from the CI Database class and "transform" the returned object into our cPost class

system/application/models/post_model.php
Code:
<?php

    class Post_model extends MY_Model {

        function get_recent_posts() {
            $this->db->from('posts');
            $this->db->order_by('created DESC');
            $this->db->limit(5);
            $postResult = $this->db->get();
            return $this->apply('cPost', $postResult->result());
        }

        function get_last_post() {
            $this->db->from('posts');
            $this->db->order_by('created DESC');
            $this->db->limit(1);
            $postResult = $this->db->get();
            return $this->apply('cPost', $postResult->row());
        }

    }
// END OF FILE
// system/application/models/Post_model.php

And from your controller
Code:
$this->load->model('post_model');
$posts = $this->post_model->get_recent_posts();
foreach($posts as $post) {
    $post->syndicate();
}

or

Code:
$this->load->model('post_model');
$post = $this->post_model->get_last_post();
$post->syndicate();

This probably breaks a few MVC principles but it does the job i wanted it to Smile

It would be interesting to hear any comments from the CI community.
#2

[eluser]dmitrybelyakov[/eluser]
Hi,

I've read your post through but don't think i rally get it. What exact advantages does your approach offer?
#3

[eluser]peterbz[/eluser]
EDITED: Nevermind.




Theme © iAndrew 2016 - Forum software by © MyBB