4

Is it possible to implement an abstract base model in Codeigniter? I have created a abstract class which has basic CRUD functionalities but I am receiving a "Cannot instantiate abstract class" error.

The abstract model is located in /application/core/my_model.php and is simply

abstract class MY_Model extends CI_Model
{

The Cannot instantiate abstract class" is on line 174 of /system/core/Common.php

It looks like Codeigniter is trying to instantiate my_model.php when it loads which I'm guessing is due to files in the /core folder being used as a place to extend core system files such as Controller and Model. Is there any way to stop this? I was trying to autoload the model using Phil Sturgeon's native autoload but it hasn't helped.

/*
| -------------------------------------------------------------------
|  Native Auto-load
| -------------------------------------------------------------------
| 
| Nothing to do with cnfig/autoload.php, this allows PHP autoload to work
| for base controllers and some third-party libraries.
|
*/

function __autoload($class)
{
    if(strpos($class, 'CI_') !== 0)
    {
        @include_once( APPPATH . 'core/'. $class . EXT );
    }
}

I realise an easy way to do this would to just be to include the file at the top of every model I wanted to use it in but obviously this would be less than optimal.

GazNicoll
  • 51
  • 1
  • 6

3 Answers3

3

Why make MY_Model abstract? You just can put in all your CRUD functions in MY_Model and extend your models from MY_Model instead of CI_Model. No need to use the autoload too as CodeIgniter allows you to extend CI_Model with MY_Model as long as you put in the core folder.

A good MY_Model example is the one from Jamie Rumbelow. You find it here: https://github.com/jamierumbelow/codeigniter-base-model/

Good luck!

Cheers

Bart

2

I am attempting to tweak the Loader class into accepting abstracts and interfaces.

First I'm putting all my edits into my /application/core/MY_Loader.php:

    <?php  if ( ! defined('BASEPATH')) exit('No direct script access allowed');
/**
 * CodeIgniter
 *
 * An open source application development framework for PHP 5.1.6 or newer
 *
 * @package     CodeIgniter
 * @author      ExpressionEngine Dev Team
 * @copyright   Copyright (c) 2008 - 2011, EllisLab, Inc.
 * @license     http://codeigniter.com/user_guide/license.html
 * @link        http://codeigniter.com
 * @since       Version 1.0
 * @filesource
 */

// ------------------------------------------------------------------------
// Joseff Betancourt - 11/14/2012 modified code by adding a autoloader and interface loader copied from model loader.

/**
 * Loader Class
 *
 * Loads views and files
 *
 * @package     CodeIgniter
 * @subpackage  Libraries
 * @author      ExpressionEngine Dev Team
 * @category    Loader
 * @link        http://codeigniter.com/user_guide/libraries/loader.html
 */
class MY_Loader extends CI_Loader {

    // All these are set automatically. Don't mess with them.
    /**
     * Nesting level of the output buffering mechanism
     *
     * @var int
     * @access protected
     */

    protected $_ci_abstracts_paths      = array();
    /**
     * List of paths to load models from
     *
     * @var array
     * @access protected
     */
    protected $_ci_interfaces_paths     = array();
    /**
     * List of paths to load helpers from
     *
     * @var array
     * @access protected
     */

    protected $_ci_abstracts            = array();
    /**
     * List of loaded interfaces
     *
     * @var array
     * @access protected
     */
    protected $_ci_interfaces           = array();
    /**
     * List of loaded helpers
     *
     * @var array
     * @access protected
     */

    /**
     * Constructor
     *
     * Sets the path to the view files and gets the initial output buffering level
     */

    function __construct()
    {
        parent::__construct();
        $this->_ci_abstracts_paths = array(APPPATH);
        $this->_ci_interfaces_paths = array(APPPATH);
        log_message('debug', "Loader Class Initialized");
    }

    // --------------------------------------------------------------------

    /**
     * Initialize the Loader
     *
     * This method is called once in CI_Controller.
     *
     * @param   array
     * @return  object
     */
    public function initialize()
    {

        $this->_ci_abstracts = array();
        $this->_ci_interfaces = array();
        $this->_ci_autoloader();

        return $this;
    }

    // --------------------------------------------------------------------

    /**
     * Abstracts Loader
     *
     * This function lets users load and instantiate models.
     *
     * 11/14/2012 - Joseff Betancourt - Cloned from Models
     *
     * @param   string  the name of the class
     * @param   string  name for the abstract
     * @param   bool    database connection
     * @return  void
     */
    public function abstracts($abstracts, $name = '', $db_conn = FALSE)
    {
        if (is_array($abstracts))
        {
            foreach ($abstracts as $babe)
            {
                $this->abstracts($babe);
            }
            return;
        }

        if ($abstracts == '')
        {
            return;
        }

        $path = '';

        // Is the abstracts in a sub-folder? If so, parse out the filename and path.
        if (($last_slash = strrpos($abstracts, '/')) !== FALSE)
        {
            // The path is in front of the last slash
            $path = substr($abstracts, 0, $last_slash + 1);

            // And the model name behind it
            $abstracts = substr($abstracts, $last_slash + 1);
        }

        if ($name == '')
        {
            $name = $abstracts;
        }

        if (in_array($name, $this->_ci_abstracts, TRUE))
        {
            return;
        }

        $CI =& get_instance();
        if (isset($CI->$name))
        {
            show_error('The model name you are loading is the name of a resource that is already being used: '.$name);
        }

        $abstracts = strtolower($abstracts);

        foreach ($this->_ci_abstracts_paths as $mod_path)
        {
            if ( ! file_exists($mod_path.'abstracts/'.$path.$abstracts.'.php'))
            {
                continue;
            }

            if ($db_conn !== FALSE AND ! class_exists('CI_DB'))
            {
                if ($db_conn === TRUE)
                {
                    $db_conn = '';
                }

                $CI->load->database($db_conn, FALSE, TRUE);
            }

            if ( ! class_exists('CI_Abstracts'))
            {
                load_class('Abstracts', 'core');
            }

            require_once($mod_path.'abstracts/'.$path.$abstracts.'.php');

            $abstracts = ucfirst($abstracts);

            $CI->$name = new $abstracts();

            $this->_ci_abstracts[] = $name;
            return;
        }

        // couldn't find the abstracts
        show_error('Unable to locate the abstracts you have specified: '.$abstracts);
    }

    // --------------------------------------------------------------------

    /**
     * Interface Loader
     *
     * This function lets users load and instantiate interfaces.
     *
     * 11/14/2012 - Joseff Betancourt - Cloned from Models
     *
     * @param   string  the name of the class
     * @param   string  name for the interface
     * @param   bool    database connection
     * @return  void
     */
    public function interfaces($interfaces, $name = '', $db_conn = FALSE)
    {
        if (is_array($interfaces))
        {
            foreach ($interfaces as $babe)
            {
                $this->interfaces($babe);
            }
            return;
        }

        if ($interfaces == '')
        {
            return;
        }

        $path = '';

        // Is the abstracts in a sub-folder? If so, parse out the filename and path.
        if (($last_slash = strrpos($interfaces, '/')) !== FALSE)
        {
            // The path is in front of the last slash
            $path = substr($interfaces, 0, $last_slash + 1);

            // And the model name behind it
            $interfaces = substr($interfaces, $last_slash + 1);
        }

        if ($name == '')
        {
            $name = $interfaces;
        }

        if (in_array($name, $this->_ci_interfaces, TRUE))
        {
            return;
        }

        $CI =& get_instance();
        if (isset($CI->$name))
        {
            show_error('The interface name you are loading is the name of a resource that is already being used: '.$name);
        }

        $interfaces = strtolower($interfaces);

        foreach ($this->_ci_interfaces_paths as $mod_path)
        {
            if ( ! file_exists($mod_path.'interfaces/'.$path.$interfaces.'.php'))
            {
                continue;
            }

            if ($db_conn !== FALSE AND ! class_exists('CI_DB'))
            {
                if ($db_conn === TRUE)
                {
                    $db_conn = '';
                }

                $CI->load->database($db_conn, FALSE, TRUE);
            }

            if ( ! class_exists('CI_Interfaces'))
            {
                load_class('Interfaces', 'core');
            }

            require_once($mod_path.'interfaces/'.$path.$interfaces.'.php');

            $interfaces = ucfirst($interfaces);

            $CI->$name = new $interfaces();

            $this->_ci_interfaces[] = $name;
            return;
        }

        // couldn't find the interfaces
        show_error('Unable to locate the interfaces you have specified: '.$interfaces);
    }

    // --------------------------------------------------------------------


    /**
     * Autoloader
     *
     * The config/autoload.php file contains an array that permits sub-systems,
     * libraries, and helpers to be loaded automatically.
     *
     * @param   array
     * @return  void
     */
    private function _ci_autoloader()
    {
        // Abstracts models
        if (isset($autoload['abstracts']))
        {
            $this->model($autoload['abstracts']);
        }

        // Interfaces models
        if (isset($autoload['interfaces']))
        {
            $this->model($autoload['interfaces']);
        }

    }

    // --------------------------------------------------------------------

}

/* End of file Loader.php */
/* Location: ./system/core/Loader.php */

Then I'm copying the core Model.php into Abstracts.php and Interfaces.php (replace word Model with either or) and placing those in the application/core/ folder.

In Autoload I added

/*
| -------------------------------------------------------------------
|  Auto-load Interfaces
| -------------------------------------------------------------------
| Prototype:
|
|   $autoload['interfaces'] = array('interface1', 'interface2');
|
*/

$autoload['interfaces'] = array();


/*
| -------------------------------------------------------------------
|  Auto-load Abstracts
| -------------------------------------------------------------------
| Prototype:
|
|   $autoload['abstracts'] = array('abstract1', 'abstract2');
|
*/

$autoload['abstracts'] = array();

and finally I added a directory in the app folder for abstracts and interfaces. Not fully proven yet but I think this is a bit of a more holistic approach allowing a person to load the abstract when needed to be referenced.

Also you'd create an abstract inside the abstract folder like this:

abstract class MY_Model extends CI_Model
{

blah 

}
Joseff
  • 21
  • 2
  • https://github.com/Joseffb/CIAI.git is the code I've put together. Hopefully it can help and people can contribute to anything I've messed up on. – Joseff Nov 15 '12 at 02:12
  • Just to clarify... You really have to hack(or extend) the CORE in order to get this to work on CI? FML, why not just use FuelPHP...?? or something that actually supports common OOP practices? Why go through this much effort? – rckehoe Sep 22 '13 at 11:56
0

Save file in /application/core/MY_model.php:

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

class MY_model extends CI_Model
{
    public function __construct()
    {
        parent::__construct();
    }

    // Return all records in the table
    public function get_all($table)
    {
        $q = $this->db->get($table);
        if($q->num_rows() > 0)
        {
            return $q->result();
        }
        return array();
    }

    // Return only one row
    public function get_row($table,$primaryfield,$id)
    {
        $this->db->where($primaryfield,$id);
        $q = $this->db->get($table);
        if($q->num_rows() > 0)
        {
            return $q->row();
        }
        return false;
    }

    // Return one only field value
    public function get_data($table,$primaryfield,$fieldname,$id)
    {
        $this->db->select($fieldname);
        $this->db->where($primaryfield,$id);
        $q = $this->db->get($table);
        if($q->num_rows() > 0)
        {
            return $q->result();
        }
        return array();
    }

    // Insert into table
    public function add($table,$data)
    {
        return $this->db->insert($table, $data);
    }

    // Update data to table
    public function update($table,$data,$primaryfield,$id)
    {
        $this->db->where($primaryfield, $id);
        $q = $this->db->update($table, $data);
        return $q;
    }

    // Delete record from table
    public function delete($table,$primaryfield,$id)
    {
        $this->db->where($primaryfield,$id);
        $this->db->delete($table);
    }

    // Check whether a value has duplicates in the database
    public function has_duplicate($value, $tabletocheck, $fieldtocheck)
    {
        $this->db->select($fieldtocheck);
        $this->db->where($fieldtocheck,$value);
        $result = $this->db->get($tabletocheck);

        if($result->num_rows() > 0) {
            return true;
        }
        else {
            return false;
        }
    }

    // Check whether the field has any reference from other table
    // Normally to check before delete a value that is a foreign key in another table
    public function has_child($value, $tabletocheck, $fieldtocheck)
    {
        $this->db->select($fieldtocheck);
        $this->db->where($fieldtocheck,$value);
        $result = $this->db->get($tabletocheck);

        if($result->num_rows() > 0) {
            return true;
        }
        else {
            return false;
        }
    }

    // Return an array to use as reference or dropdown selection
    public function get_ref($table,$key,$value,$dropdown=false)
    {
        $this->db->from($table);
        $this->db->order_by($value);
        $result = $this->db->get();

        $array = array();
        if ($dropdown)
            $array = array("" => "Please Select");

        if($result->num_rows() > 0) {
            foreach($result->result_array() as $row) {
            $array[$row[$key]] = $row[$value];
            }
        }
        return $array;
    }
}

then extend like so

class any_model extends MY_Model
{
    public function __construct()
    {
        parent::__construct();
    }
}
Niranjan N Raju
  • 12,047
  • 4
  • 22
  • 41
Abdul Manan
  • 2,255
  • 3
  • 27
  • 51