Let CI Framework Support service Layer

Keywords: PHP Database

   As you know, CodeIgniter Framework MVC is layered. Usually people write business logic into Controller, while Model is only responsible for dealing with databases.

   But as the business becomes more and more complex, controllers become more and more bloated. For example, when users place orders, there will inevitably be a series of operations: updating shopping carts, adding order records, adding credits to members, etc., and the process of placing orders may occur in many scenarios. If such code is placed in controllers, it will be very bloated and difficult to reuse, if it is placed in models. Coupling the persistence layer with the business layer. Now the company's project is that a lot of people write some business logic into the model, and other models are tuned in the model, that is, business layer and persistence layer are coupled. This is extremely unreasonable, making the model difficult to maintain, and methods difficult to reuse.
   Can we consider adding a business layer service in controller and model, which is responsible for business logic, and the encapsulated call interface can be reused by controller?

    In this way, the tasks at all levels are clear:
    Model(DAO): The work of the data persistence layer, which encapsulates the operation of the database.
    Service: Business logic layer, responsible for the logic application design of business module. The interface of service can be invoked in controller to realize business logic processing, which improves the reusability of general business logic. The interface of Model can be invoked when designing specific business implementation.
    Controller: Control layer, responsible for specific business process control, where the service layer is invoked to return data to the view
    View: Responsible for front-end page display, closely related to Controller.

Based on the above description, the implementation process is as follows:
(1) To enable CI to load service, the service directory is placed under application, because there is no service in CI system, a new extension MY_Service.php is created under application/core.

<?php

class MY_Service
{
    public function __construct()
    {
        log_message('debug', "Service Class Initialized");

    }

    function __get($key)
    {
        $CI = & get_instance();
        return $CI->$key;
    }
}
(2) Extend the CI_Loader implementation, load the service, and create a new MY_Loader.php file under application/core:
<?php

class MY_Loader extends CI_Loader
{
    /**
     * List of loaded sercices
     *
     * @var array
     * @access protected
     */
    protected $_ci_services = array();
    /**
     * List of paths to load sercices from
     *
     * @var array
     * @access protected
     */
    protected $_ci_service_paths       = array();

    /**
     * Constructor
     * 
     * Set the path to the Service files
     */
    public function __construct()
    {

        parent::__construct();
        load_class('Service','core');
        $this->_ci_service_paths = array(APPPATH);
    }

    /**
     * Service Loader
     * 
     * This function lets users load and instantiate classes.
     * It is designed to be called from a user's app controllers.
     *
     * @param   string  the name of the class
     * @param   mixed   the optional parameters
     * @param   string  an optional object name
     * @return  void
     */
    public function service($service = '', $params = NULL, $object_name = NULL)
    {
        if(is_array($service))
        {
            foreach($service as $class)
            {
                $this->service($class, $params);
            }

            return;
        }

        if($service == '' or isset($this->_ci_services[$service])) {
            return FALSE;
        }

        if(! is_null($params) && ! is_array($params)) {
            $params = NULL;
        }

        $subdir = '';

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

                // And the service name behind it
                $service = substr($service, $last_slash + 1);
        }
        $name = config_item('subclass_prefix')."Service.php";
        if (class_exists(config_item('subclass_prefix')."Service")
             === FALSE && file_exists(APPPATH.'core/'.$name))
        {
            require(APPPATH.'core/'.$name);
        }
        foreach($this->_ci_service_paths as $path)
        {

            $filepath = $path .'service/'.$subdir.$service.'.php';

            if ( ! file_exists($filepath))
            {
                continue;
            }

            include_once($filepath);

            $service = strtolower($service);

            if (empty($object_name))
            {
                $object_name = $service;
            }

            $service = ucfirst($service);
            $CI = &get_instance();
            if($params !== NULL)
            {
                $CI->$object_name = new $service($params);
            }
            else
            {
                $CI->$object_name = new $service();
            }

            $this->_ci_services[] = $object_name;

            return;
        }
    }

}
(3) Simple examples:
Call service in the controller:
<?php

class User extends CI_Controller
{
    public function __construct() 
    {

        parent::__construct();
        $this->load->service('user_service');
    }

    public function login()
    {
        $name = 'phpddt.com';
        $psw = 'password';
        print_r($this->user_service->login($name, $psw));
    }

}

Call model in service:

<?php

class User_service extends MY_Service
{
    public function __construct()
    {
        parent::__construct();
        $this->load->model('user_model');
    }

    public function login($name, $password)
    {
        $user = $this->user_model->get_user_by_where($name, $password);

        //.....
        //.....
        //.....

        return $user;
    }

}

In model, you only deal with db:

<?php

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

    public function get_user_by_where($name, $password)
    {

        //$this->db
        //......
        //......
        return array('id' => 1, 'name' => 'mckee');
    }
}

Posted by Morthian on Wed, 27 Mar 2019 00:03:29 -0700