CodeIgniter控制器之业务逻辑
前面对公用控制器按模块分发,方便对特定模块的控制,而具体的实现类则是放在library中。那放在library中是否合适呢?以及控制器中更多的业务逻辑该放在哪里?先说下对CI中几个文件夹的理解helpers、libraries: 存放一系列辅助函数、辅助类,用来辅助控制器、业务逻辑实现功能。他们中的方法应当尽量避免与CI依赖,依赖越紧越难以复用。以邮件发送为例,发送邮件时很多参数是不变的,如编码、协议、端口等,我们可能会在config下进行配置这些参数,然后library封装一个邮件发送的类,并在其中获取CI实例后读取这些参数。此时就出现了与CI实例的依赖,该类就只能在CI框架中使用,其他系统要用到,就只能重写了,没达到复用的目的。如果发送的类只是接收参数,并封装发送方法呢?所以说,尽可能的让helpers、libraries变的简单,职责变得单一。controllers: 控制器目录。控制器主要用来接管程序,起到连接的作用。通常情况下,我们会把业务逻辑写在action中。但随着业务变得复杂,action代码将越来越臃肿,难以维护。models: 模型目录。CI的模型的主要职责就是和数据库打交道,获取数据。很多时候也会把业务逻辑放在模型中,但业务逻辑与模型实际上是两种东西了。模型只是获取数据,业务逻辑可能是把这些数据根据业务需要进行组合,组合方式可能有很多种,放在模型中会让模型难以维护且不利于复用。说个碰到的例子,对数据按一定条件做缓存,获取数据和缓存结果两个流程写在同一个方法中,但同样的数据需要做另一种形式的缓存时发现,获取数据的方法就没法重用了。third_party:第三方类库目录。拿到一个类库后不要直接使用, 可以在library中进行一次封装,让其更适应于系统,其他人使用起来难度也会降低。可以发现,每个文件夹都有自己的职责,每个模块都有自己的家,都有自己的职能。那业务逻辑该怎么办?既然这样, 我们也应该给业务逻辑安个家,建立一个唯一的目录用来存放业务逻辑,暂且命名为service。控制器主要负责接收参数并调用service,service来调用模型,各层各尽其责。下面看看怎么实现:我们可以重写MY_Load,增加service方法,直接通过$this->load->service('user_service');来调用。但业务逻辑很多都需要获取CI实例,这里可以参考模型的方法,core建立一个MY_Service,其他service均继承该类,这样子service里用法就跟控制器里一样了。
class MY_Service
{
public function __construct()
{
log_message('debug', "Service Class Initialized");
}
function __get($key)
{
$CI = & get_instance();
return $CI->$key;
}
}
其实主要思路还是需要有一层用来处理业务逻辑,java中都有这一层。随着对CI的不断熟悉,发觉这里需要这一层,达到解放控制器和模型的目的。和这种类似的做法还有很多,如果系统中有很多地方需要用到web service 或者说cache之类的,其实也可以按照上面的思路单独放在一个文件夹中处理,方便管理。
我说呢。虽然我是新手,我就觉得MVC应该加这一层:Q,要不然没法理解 为什么一定要修改CI核心的东西,不能搞个扩展类吗 大侠,根据你的思路,我对MY_Loader编写如下,请您指教是否妥当? 谢谢。
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
/**
* My Loader Class
*
* @package MyCore
* @subpackage Libraries
* @category Libraries
* @author zhengmz
* @link http://codeigniter.com/user_guide/libraries/config.html
*/
class MY_Loader extends CI_Loader {
/**
* List of paths to load services from
*
* @var array
* @access protected
*/
protected $_ci_service_paths = array();
/**
* List of loaded services
*
* @var array
* @access protected
*/
protected $_ci_services = array();
/**
* Constructor
*
* @access public
*/
public function __construct()
{
parent::__construct();
$this->_ci_service_paths = array(APPPATH);
$this->_ci_services = array();
log_message('debug', "MY_Loader Class Initialized");
}
/**
* Service Loader
*
* This function lets users load and instantiate services.
*
* @param string the name of the service
* @param mixed the optional parameters
* @param string an optional object name
* @return void
*/
public function service($service = '', $object_name = NULL)
{
if (is_array($service))
{
foreach ($service as $class)
{
$this->service($class);
}
return;
}
if ($service == '')
{
return;
}
$subpath = '';
// 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
$subpath = substr($service, 0, $last_slash + 1);
// And the service name behind it
$service = substr($service, $last_slash + 1);
}
if ($object_name == '')
{
$object_name = $service;
}
if (in_array($object_name, $this->_ci_services, TRUE))
{
return;
}
$CI =& get_instance();
if (isset($CI->$object_name))
{
show_error('The service name you are loading is the name of a resource that is already being used: '.$object_name);
}
$service = strtolower($service);
foreach ($this->_ci_service_paths as $mod_path)
{
$service_full_path = $mod_path.'services/'.$subpath.$service.'.php';
if ( ! file_exists($service_full_path))
{
continue;
}
if ( ! class_exists('CI_Service'))
{
load_class('Service', 'core');
}
require_once($service_full_path);
$service = ucfirst($service);
$CI->$object_name = new $service();
$this->_ci_services[] = $object_name;
return;
}
// couldn't find the service
show_error('Unable to locate the service you have specified: '.$service);
}
}
/* End of file MY_Loader.php */
/* Location: ./application/core/MY_Loader.php */
同时,我对例子中的upload修改如下,请一并帮忙指教,主要我对J2EE不熟,不知道service这一层具体如何使用,不过我觉得你说的很有道理,所以我采用你的思路。
upload的controller代码如下:
<?php
class Upload extends CI_Controller {
function __construct()
{
parent::__construct();
$this->load->helper(array('form', 'url'));
}
function index()
{
$this->load->view('upload_form', array('error' => ' ' ));
}
function do_upload()
{
$this->load->service('upload_serv');
$this->upload_serv->do_upload();
}
}
/* End of file upload.php */
/* Location: ./application/controllers/upload.php */
upload的service代码如下:
<?php
class Upload_serv extends MY_Service
{
function do_upload()
{
$config['upload_path'] = 'uploads/';
$config['allowed_types'] = 'gif|jpg|png';
$config['max_size'] = '100';
$config['max_width']= '1024';
$config['max_height']= '768';
$this->load->library('upload', $config);
if ( ! $this->upload->do_upload())
{
$error = array('error' => $this->upload->display_errors());
$this->load->view('upload_form', $error);
}
else
{
$data = array('upload_data' => $this->upload->data());
$this->load->view('upload_success', $data);
}
}
}
/* End of file upload_serv.php */
/* Location: ./application/services/upload_serv.php */
我以前是用C/C++,现在要做WEB开发了,所以很菜,还有多多请教。 zhengmz 发表于 2013-12-15 21:54 static/image/common/back.gif
大侠,根据你的思路,我对MY_Loader编写如下,请您指教是否妥当? 谢谢。
...
您写的很好,思路都清楚了。我这边是把创建的方法在提取了一遍,方便以后load cache 或者其他。service我的理解还是希望能更公用, load view的过程可以在控制器中来做。
我这边的service参考,_ci_load_user_class为自定义方法,也就是和您service里面的实现差不多。
protected $_ci_service_paths;
public function __construct()
{
parent::__construct();
$this->_ci_service_paths = array(APPPATH.'/service');
}
public function service($service_name = '', $params = NULL, $object_name = NULL)
{
load_class('Service', 'core');
if(is_array($service_name)) {
foreach($service_name as $class) {
$this->service($class, $params);
}
return;
}
if($service_name == '' or isset($this->_base_classes[$service_name])) {
return FALSE;
}
if(! is_null($params) && ! is_array($params)) {
$params = NULL;
}
$this->_ci_load_user_class($service_name, $params, $object_name, $this->_ci_service_paths);
}
Bobby 发表于 2013-12-15 23:38 static/image/common/back.gif
您写的很好,思路都清楚了。我这边是把创建的方法在提取了一遍,方便以后load cache 或者其他。service我 ...
感谢你及时的反馈。
你在实现loader的service方法,是借鉴了libarariy,可以实现参数传递,这一点我可以借鉴一下,谢谢了。
对于service,我也简单了解了一下yii的框架,在yii框架中业务处理层,每一个功能对应一个文件,不知理解是否正确?另外,你能否将发一份service的代码给我参考,谢谢。
本帖最后由 Bobby 于 2013-12-16 21:44 编辑
zhengmz 发表于 2013-12-16 14:22 static/image/common/back.gif
感谢你及时的反馈。
你在实现loader的service方法,是借鉴了libarariy,可以实现参数传递,这一点我可以 ...
YII是提取公用方法的时候有参考它的函数和方法名, YII的业务逻辑也是写在控制器或模型中,AR的模型也是一个对一个。Service里都是业务逻辑了,好像也没什么,看个用户的。
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
/**
* User Service
*
*/
class User_service extends Common_service
{
public function __construct()
{
parent::__construct();
$this->load->model('admin_user_model');
}
/**
* Check User Login
*
* @param string $username
* @param string $password
* @param string $after_handle Whether Update Last Logined Ts OR Not
* @return boolean|unknown
*/
public function login($username, $password, $after_handle = true)
{
$admin = $this->admin_user_model->findByAttributes(array('username' => $username));
if(empty($admin) || $admin['password'] != generate_passwrod($password, $admin['salt'])) {
return false;
}
if($after_handle) {
$this->admin_user_model->updateByPk($admin['user_id'], array(
'last_logined_ts' => date("Y-m-d H:i:s")
));
}
return $admin;
}
}
/* End of file user_service.php */
/* Location: ./application/service/user_service.php */
Bobby 发表于 2013-12-16 21:42 static/image/common/back.gif
YII是提取公用方法的时候有参考它的函数和方法名, YII的业务逻辑也是写在控制器或模型中,AR的模型也是一 ...
好的,多谢了。{:soso_e183:}
没有看明白
页:
[1]
2