Custom classes in CodeIgniter

2019-03-08 21:04发布

问题:

Seems like this is a very common problem for beginners with CodeIgniter, but none of the solutions I've found so far seems very relevant to my problem. Like the topic says I'm trying to include a custom class in CodeIgniter.

I'm trying to create several objects of the class below and place them in an array, thus I need the class to be available to the model.

I've tried using the load (library->load('myclass') functions within CodeIgniter which sort of works, except it tries to create an object of the class outside the model first. This is obviously a problem since the constructor expects several parameters.

The solutions I've found so far is

  1. A simple php include which seems fine enough, but since I'm new to CodeIgniter I want to make sure I'm sticking to it as much as possible.
  2. Creating a "wrapper class" as suggested here, however I'm uncertain how I would implement this.

The class I want to include, User.php

<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); 
class User{
    public $ID = 0;
    public $username = 0;
    public $access_lvl = 0;
    public $staff_type = 0;
    public $name = 0;    

    public function __construct($ID, $username, $access_lvl, $staff_type, $name) 
    {
        $this->ID = $ID;
        $this->username = $username;
        $this->access_lvl = $access_lvl;
        $this->staff_type = $staff_type;
        $this->name = $name;
    }

    public function __toString() 
    {
        return $this->username;
    }
}
?>

Method (Model) which needs the User.php

function get_all_users()
{
    $query = $this->db->get('tt_login');
    $arr = array();

    foreach ($query->result_array() as $row)
    {
        $arr[] = new User
        (
            $row['login_ID'],
            $row['login_user'],
            $row['login_super'],
            $row['crew_type'],
            $row['login_name']
        );
    }    

    return $arr;
}

And finally the controller,

function index()
{
        $this->load->library('user');
        $this->load->model('admin/usersmodel', '', true);            

        // Page title
        $data['title'] = "Some title";
        // Heading
        $data['heading'] = "Some heading";
        // Data (users)
        $data['users'] = $this->usersmodel->get_all_users();

回答1:

If you have PHP version >= 5.3 you could take use of namespaces and autoloading features.

A simple autoloader library in the library folder.

<?php
class CustomAutoloader{

    public function __construct(){
        spl_autoload_register(array($this, 'loader'));
    }

    public function loader($className){
        if (substr($className, 0, 6) == 'models')
            require  APPPATH .  str_replace('\\', DIRECTORY_SEPARATOR, $className) . '.php';
    }

}
?>

The User object in the model dir. ( models/User.php )

<?php 
namespace models; // set namespace
if ( ! defined('BASEPATH')) exit('No direct script access allowed'); 
class User{
 ...
}

And instead of new User... new models\User ( ... )

function get_all_users(){
    ....
    $arr[] = new models\User(
    $row['login_ID'],
    $row['login_user'],
    $row['login_super'],
    $row['crew_type'],
    $row['login_name']
    );
    ...
}

And in controller just make sure to call the customautoloader like this:

function index()
{
        $this->load->library('customautoloader');
        $this->load->model('admin/usersmodel', '', true);            

        // Page title
        $data['title'] = "Some title";
        // Heading
        $data['heading'] = "Some heading";
        // Data (users)
        $data['users'] = $this->usersmodel->get_all_users();


回答2:

CodeIgniter doesn't really support real Objects. All the libraries, models and such, are like Singletons.

There are 2 ways to go, without changing the CodeIgniter structure.

  1. Just include the file which contains the class, and generate it.

  2. Use the load->library or load_class() method, and just create new objects. The downside of this, is that it will always generate 1 extra object, that you just don't need. But eventually the load methods will also include the file.

Another possibility, which will require some extra work, is to make a User_Factory library. You can then just add the object on the bottom of the file, and create new instances of it from the factory.

I'm a big fan of the Factory pattern myself, but it's a decision you have to make yourself.

I hope this helped you, if you have any questions that are more related to the implementation, just let me/us know.



回答3:

Including a class file is not a bad approach.

In our projects, we do the same, add an another layer to MVC, and thats a Service Layer which the Controllers calls and Service calls the Model. We introduced this layer to add Business Logic seperate.

So far, we have been using it, and our product has gone large too, and still we find no difficulty with the decision of including files that we had made in the past.



回答4:

Codeigniter has a common function to instantiate individual classes.

It is called load_class(), found in /system/core/Common.php

The function;

/**
* Class registry
*
* This function acts as a singleton.  If the requested class does not
* exist it is instantiated and set to a static variable.  If it has
* previously been instantiated the variable is returned.
*
* @access   public
* @param    string  the class name being requested
* @param    string  the directory where the class should be found
* @param    string  the class name prefix
* @return   object
*/

The signature is

load_class($class, $directory = 'libraries', $prefix = 'CI_')

An example of it being used is when you call the show_404() function.



回答5:

After a brief google search, I was inspired to make my own autoloader class. It's a bit of a hack, since I use custom Codeigniter library to preform auto-loading, but for me this is the best way, that I'm aware of, of loading all the classes, I require, without compromising my application architecture philosophy, to fit it into Codeigniter way of doing things. Some might argue that Codeigniter is not the right framework for me and that might be true, but I'm trying things out and playing around with various frameworks and while working on CI, I came up with this solution. 1. Auto-load new custom library by editing applicaion/config/autoload.php to include:

$autoload['libraries'] = array('my_loader');

and any other libraries you might need. 2. Then add library class My_loader. This class will be loaded on every request and when its constructor is run, it will recursively search through all sub-folders and require_once all .php files inside application/service & application/models/dto folders. Warning: folders should not have dot in the name, otherwise function will fail

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

class My_loader {

    protected static $_packages = array(
            'service',
            'models/dto'
            );

    /**
     * Constructor loads service & dto classes
     * 
     * @return void
     */
    public function __construct($packages = array('service', 'models/dto'))
    {
        // files to be required
        $toBeRequired = array();

        // itrate through packages
        foreach ($packages as $package) {
            $path = realpath(APPPATH . '/' . $package . '/');
            $toBeRequired = array_merge($toBeRequired, $this->findAllPhpFiles($path));
        }

        /**
         * Require all files
         */
        foreach ($toBeRequired as $class) {
            require_once $class;
        }
    }

    /**
     * Find all files in the folder
     * 
     * @param string $package
     * @return string[]
     */
    public function findAllPhpFiles($path)
    {
        $filesArray = array();
        // find everithing in the folder
        $all = scandir($path);
        // get all the folders
        $folders = array_filter($all, get_called_class() . '::_folderFilter');
        // get all the files
        $files = array_filter($all, get_called_class() . '::_limitationFilter');

        // assemble paths to the files
        foreach ($files as $file) {
            $filesArray[] = $path . '/' . $file;
        }
        // recursively go through all the sub-folders
        foreach ($folders as $folder) {
            $filesArray = array_merge($filesArray, $this->findAllPhpFiles($path . '/' . $folder));
        }

        return $filesArray;
    }

    /**
     * Callback function used to filter out array members containing unwanted text
     * 
     * @param string $string
     * @return boolean
     */
    protected static function _folderFilter($member) {
        $unwantedString = '.';
        return strpos($member, $unwantedString) === false;
    }

    /**
     * Callback function used to filter out array members not containing wanted text
     *
     * @param string $string
     * @return boolean
     */
    protected static function _limitationFilter($member) {
        $wantedString = '.php';
        return strpos($member, $wantedString) !== false;
    }
}


回答6:

After 18 hours I managed to include a library in my control without initialisation (the constructor was the problem, because of that and i could't use the standard codeiginiter $this->load->library() ). Follow the https://stackoverflow.com/a/21858556/4701133 . Be aware for further native class initialization use $date = new \DateTime()with back-slash in front otherwise the function will generate an error !