How to Enable CI Framework to Support service Layer

  • 2021-07-22 09:25:59
  • OfStack

As we all know, CodeIgniter framework MVC layered, usually we write business logic to Controller, and Model is only responsible for dealing with the database.

But as the business becomes more and more complex, controller is becoming more and more bloated. To give a simple example, for example, when users place orders, there will inevitably be a series of operations: updating shopping carts, adding order records, adding points to members, etc., and the process of placing orders may appear in various scenarios. If such codes are placed in controller, they are bloated and difficult to reuse. If model is placed, the persistence layer and the business layer will be coupled. Now the company's project is that many people write some business logic to model, and other model is transferred to model, that is, the business layer and persistence layer are coupled with each other. This is extremely unreasonable, which makes model difficult to maintain and the method difficult to reuse.

Is it possible to consider adding a business layer service in controller and model, which is responsible for business logic, and the encapsulated calling interface can be reused by controller?

In this way, the tasks of each layer are clear:
Model (DAO): The work of the data persistence layer and the operations on the database are encapsulated here.
Service: Business logic layer, responsible for the logic application design of business modules. In controller, the interface of service can be called to realize business logic processing, which improves the reusability of general business logic. When designing specific business implementation, the interface of Model will be called.
Controller: Control layer, responsible for specific business process control, here call service layer, return data to the view
View: Responsible for front-end page display, closely related to Controller.

Based on the above description, the implementation process:
(1) Enable CI to load service, and the service directory is placed under application. Since service does not exist 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) Expand the implementation of CI_Loader, load 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();
        $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);
        }
        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 example implementation:
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));
    }
}

Calling 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');
    }
}

This is the basic realization idea.


Related articles: