/***                 phpall技術交流群:75345798

                英文原文:http://net.tutsplus.com/tutorials/php/6-codeigniter-hacks-for-the-masters/
                翻譯原文:http://www.phpall.cn/forum/read.php?tid=389
          ***/
      Codeigniter是一個簡潔而又強大的開源web應用框架。今天,我們將會使用一些核心的技巧來對這個框架做一些小的改變并提高它的性能。
在這過程之中,你將會對框架得到一個更好的理解。
作者:burak guze


      他是一個住在亞利桑那州的全職php web開發者,從伊斯坦布爾來的。他獲得了計算機科學與工程的學士學                 位。他擁有8年的phpmysql工作經驗。你能閱讀他的更多的文章在PHPandStuff.com,他的twitter
here


聲明

1.不推薦在已經存在的項目中使用這些技巧。因為他們改變了ci的核心功能,可能會使已經寫的代碼崩潰。
2.我寫這篇文章的時候,ci1.7.2版本是最新的發行版本,這些技巧對以前的版本沒有作用。
3.盡管ci是被設計php4兼容的。但是這些技巧需要你安裝php5.
4.當你對cisystem文件做的任何更改,你都應該對這些改變做好記錄,供以后參考。下次當你升級ci的時候,你可能需要重新再處理這些變化。

1.大師的6個codeigniter技巧——(1)自動加載models——php5風格


這個技巧希望達到的目標是:

      在左邊,你會看到在controller里面通常加載model的方法。使用這個技巧以后,我們將能夠直接創建這個model對象。
這個代碼是簡潔的并且容易理解對象。
    使用這個技巧以后會有2個影響。首先你不再需要繼承model類了。


這個技巧

我們需要做的就是添加一個php5風格的 autolader 函數

添加這些代碼到system/application/config/config.php:
<?php  
// ...  
function __autoload($class) {  
    if (file_exists(APPPATH."models/".strtolower($class).EXT)) {  
        include_once(APPPATH."models/".strtolower($class).EXT);  
    }  
}  
?>
如果你也有興趣運用這個技巧到controller,你只需要添加以下代碼來代替上面的代碼。

<?php  
// ...  
function __autoload($class) {  
    if (file_exists(APPPATH."models/".strtolower($class).EXT)) {  
        include_once(APPPATH."models/".strtolower($class).EXT);  
    } else if (file_exists(APPPATH."controllers/".strtolower($class).EXT)) {  
        include_once(APPPATH."controllers/".strtolower($class).EXT);  
    }  
}  
?>
任何時候,你試著使用一個沒有定義的類時候,這個__autoload函數將會被調用,它將會加載這個類文件。

2.大師的6個codeigniter技巧——(2)防止model-controller名字沖突

使用這個技巧要達到的目標:

一般來說,模型和控制器你都不會有相同的類名字。讓我先創建一個取名為postmodel
class Post extends Model {  

    // ...  

}
現在你就不能有一個像這樣的url
http://www.mysite.com/post/display/13
這個原因是因為你也需要有一個名字為postcontroller,如果創建了這樣的一個類的話將會引起致命錯誤。
但是使用了這個技巧一般,一切皆有可能。那個url的控制器看起來是這樣的:

// application/controllers/post.php  
class Post_controller extends Controller {  

    // ...  

}
注意這個“__controller”后綴
技巧:

為了避免這個問題,通常大多數人都是添加‘_model’后綴到model名字(例如命名Post_model)。
在所有的應用程序Model對象都被創建和引用,所以在所有的model名字后面跟上‘_model’有些無聊。
我認為最好的辦法就是在controller上來添加后綴,因為在代碼中controller的名字幾乎從來不會被引用。
首先我們需要繼承Router class。創建這樣一個文件:"application/libraries/MY_Router.php"
class MY_Router extends CI_Router {  
    var $suffix = '_controller';  

    function MY_Router() {  
        parent::CI_Router();  
    }  
    function set_class($class) {  
        $this->class = $class . $this->suffix;  
    }  
    function controller_name() {  
        if (strstr($this->class, $this->suffix)) {  
            return str_replace($this->suffix, '', $this->class);  
        }  
        else {  
            return $this->class;  
        }  
    }  
}  
現在編輯"system/codeigniter/CodeIgniter.php"
153
if ( ! file_exists(APPPATH.'controllers/'.$RTR->fetch_directory().$RTR->controller_name().EXT))  
然后第158

include(APPPATH.'controllers/'.$RTR->fetch_directory().$RTR->controller_name().EXT);  

然后編輯"system/libraries/Profiler.php"的第323

$output .= "<div style="color:#995300;font-weight:normal;padding:4px 0 4px 0">".$this->CI->router->controller_name()."/".$this->CI->router->fetch_method()."</div>";  
大功告成。使用這個技巧一定需要記住的是要把‘_controller’后綴放到你的controller的類的名字后面,不是放在你的控制器文件名中。


大師的6個codeigniter技巧——(3)表單驗證“唯一值”(如注冊用戶名email)
      Ci有一個完美的表單驗證類。但是這個驗證有一點不足,例如當大部分用戶注冊用戶名的時候,需要驗證用戶名沒有被占用,或者郵箱是否存在在系統中。
使用這個技巧,你將能夠非常容易添加這項驗證規則到你的表單提交中。

注意最后一部分"unique[User.username]."這個新的驗證規則叫做“unique”,并且帶了一個方框在這個中括號中,它們是“tablename.filedname.所以它將會檢測數據庫的“user數據表的“username”列確定提交的值在數據庫里面不存在。
同理,你也能利用這個規則驗證相同的郵件地址。
$this->form_validation->set_rules('email', 'E-mail',  
        'required|valid_email|unique[User.email]');  
你的應用程序將會得到以下錯誤信息

與其說這是一個技巧而不如說是一個擴展。雖然如此,我們將要添加一個核心ci類庫并且改善它。
創建 "application/libraries/MY_Form_validation.php"文件

class MY_Form_validation extends CI_Form_validation {  

    function unique($value, $params) {  

        $CI =& get_instance();  
        $CI->load->database();  

        $CI->form_validation->set_message('unique',  
            'The %s is already being used.');  

        list($table, $field) = explode(".", $params, 2);  

        $query = $CI->db->select($field)->from($table)  
            ->where($field, $value)->limit(1)->get();  

        if ($query->row()) {  
            return false;  
        } else {  
            return true;  
        }  

    }  
}
現在你就能使用這個“唯一”驗證規則了。

4.大師的6個codeigniter技巧——(4)從命令行運行codeigniter

目標:
就像這個標題所說,我們的目標是能夠從命令行運行ci。對于創建定時任務,這個是必須的,或者是運行更多的特別的操作,
所以你沒有web腳本的資源限制,就像最大執行時間一樣。
這就是本地windows機器上面運行的效果

技巧
創建一個“cli.php”文件在ci的根目錄(即與index.php在同一目錄)
if (isset($_SERVER['REMOTE_ADDR'])) {  
    die('Command Line Only!');  
}  
set_time_limit(0);  
$_SERVER['PATH_INFO'] = $_SERVER['REQUEST_URI'] = $argv[1];  
require dirname(__FILE__) . '/index.php';

如果你在linux環境下使用,并且想要讓這代碼自動執行,你能添加以下代碼在cli.php文件的第一行

#!/usr/bin/php  
如果你需要一個僅僅能在命令行下使用的控制器,你能阻止web調用控制器構造函數
class Hello extends Controller {  
    function __construct() {  
        if (isset($_SERVER['REMOTE_ADDR'])) {  
            die('Command Line Only!');  
        }  
        parent::Controller();  
    }  

    // ...  
}

5.大師的6個codeigniter技巧——(5)添加Doctrine orm 到codeigniter
(Doctrine orm是一個比較復雜的東西,我們將會繼續出一些相關教程
目標
Doctine是一個流行的php的關系對象映射(orm)。添加它到ci里面以后,你將會一個更加強大的模型層。

技巧:
其實安裝Doctrine不是那么難,就像安裝插件一樣。然后一旦安裝成功,你的model類將需要繼承Doctrine基類,而不是繼承cimodel類。這將會完全改變model層的工作方式。你建立的對象將會有持久的數據庫連接并且也將能有其他對象的數據庫關系。
按照以下幾步:
1. 建立文件夾:application/plugins
2. 創建文件夾:application/plugins/doctrine
3. 下載文件(1.2版本的)
4. Doctrine復制“lib”文件夾到“application/plugins/doctrine
5. 創建“application/plugins/doctrine_pi.php

// system/application/plugins/doctrine_pi.php  
// load Doctrine library  
require_once APPPATH.'/plugins/doctrine/lib/Doctrine.php';  
// load database configuration from CodeIgniter  
require_once APPPATH.'/config/database.php';  
// this will allow Doctrine to load Model classes automatically  
spl_autoload_register(array('Doctrine', 'autoload'));  
// we load our database connections into Doctrine_Manager  
// this loop allows us to use multiple connections later on  
foreach ($db as $connection_name => $db_values) {  
    // first we must convert to dsn format  
    $dsn = $db[$connection_name]['dbdriver'] .  
        '://' . $db[$connection_name]['username'] .  
        ':' . $db[$connection_name]['password'].  
        '@' . $db[$connection_name]['hostname'] .  
        '/' . $db[$connection_name]['database'];  
    Doctrine_Manager::connection($dsn,$connection_name);  
}  
// CodeIgniter's Model class needs to be loaded  
require_once BASEPATH.'/libraries/Model.php';
// telling Doctrine where our models are located
Doctrine::loadModels(APPPATH.'/models');  

然后,編輯‘application/config/autoload.php’自動加載Doctrine插件
$autoload['plugin'] = array('doctrine');
你也要確定在“application/config/database.php的數據庫配置好了,”
就這樣,現在你就能使用ci應用程序創建Doctrine模型了。閱讀更多的資源在這里

6.大師的6個codeigniter技巧——(6)運行多個站點
目標:
這個技巧將會使安裝一個codeigniter就能運行多個站點成為可能,每個站點有它自己的application文件夾,但是他們共享這相同的系統文件夾。

技巧
安裝ci服務器的任何位置。然后將application文件夾從system文件夾拿出來。放在外面,請看上面的圖片。
現在復制index.php文件到每個站點的跟目錄下面(即圖中的application_site1application_site2等)
然后編輯它:
在第26行,給出system文件夾的完整路徑
$system_folder = dirname(__FILE__) . '../codeigniter/system';  
在第43行,給出application文件夾的文章路徑
$application_folder = dirname(__FILE__) . '../application_site1';  
現在你就能使用獨立的application文件夾來建立不同的站點了,而只是共享同一個system文件夾
這里有一個相似的操作在ci用戶手冊



7.大師的6個codeigniter技巧——(7)允許所有類型的文件上傳
目標:
當使用ci的上傳類的時候,你必須指明哪些文件類型允許上傳。

復制代碼
  • $this->load->library('upload');   $this->upload->set_allowed_types('jpg|jpeg|gif|png|zip');

如果你沒有指明特定的上傳類型,你將會從ci那里得到一個錯誤信息"Youhave not specified any allowed file types."
所有,默認的方式,將沒有辦法允許所有的文件上傳。我們需要做一些小的改變來處理這個限制。我們設定“*”將能夠運行所有類型的文件上傳。

復制代碼
  • $this->load->library('upload');   $this->upload->set_allowed_types('*');
技巧:
我們需要修改上傳文件類。
創建文件:application/librarys/My_upload.php
class MY_Upload extends CI_Upload {  
    function is_allowed_filetype() {  
        if (count($this->allowed_types) == 0 OR ! is_array($this->allowed_types))  
        {  
            $this->set_error('upload_no_file_types');  
            return FALSE;  
        }  
        if (in_array("*", $this->allowed_types))  
        {  
            return TRUE;  
        }  
        $image_types = array('gif', 'jpg', 'jpeg', 'png', 'jpe');  
        foreach ($this->allowed_types as $val)  
        {  
            $mime = $this->mimes_types(strtolower($val));  

            // Images get some additional checks  
            if (in_array($val, $image_types))  
            {  
                if (getimagesize($this->file_temp) === FALSE)  
                {  
                    return FALSE;  
                }  
            }  
            if (is_array($mime))  
            {  
                if (in_array($this->file_type, $mime, TRUE))  
                {  
                    return TRUE;  
                }  
            }  
            else  
            {  
                if ($mime == $this->file_type)  
                {  
                    return TRUE;  
                }  
            }  
        }  
        return FALSE;  
    }