yii框架AR詳解

  雖 然Yii DAO能夠處理事實上任何數據庫相關的任務,但極可能咱們會花費90%的時間用來編寫一些通用的SQL語句來執行CRUD操做(建立,讀取,更新和刪除)。 同時咱們也很難維護這些PHP和SQL語句混合的代碼,要解決這些問題,咱們可使用Active Record。
    Active Record(AR)是一種流行的對象關係映射(ORM)技術。每一個AR類表明一個數據表(或視圖),其字段做爲AR類的屬性,一個AR實例表明在表中的 一行。常見的CRUD操做被做爲AR類的方法執行。 因而,咱們可使用更面向對象的方法處理咱們的數據。例如,咱們可使用下面的代碼在
    tbl_post
    表中插入一個新行:
    $post=new Post;
    $post->title='sample post';
    $post->content='post body content';
    $post->save();在下面咱們將介紹如何設置AR和用它來執行CRUD操做。在下一小節咱們將展現如何使用AR 處理數據庫中的關係。爲了簡單起見,咱們使用本節下面的數據庫表做爲例子。 請注意,若是你使用MySQL數據庫,在下面的SQL中您應該替換
    AUTOINCREMENT
    爲
    AUTO_INCREMENT
    。
    CREATE TABLE tbl_post (
    id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
    title VARCHAR(128) NOT NULL,
    content TEXT NOT NULL,
    create_time INTEGER NOT NULL
    );注意: AR不是要解決全部與數據庫相關的任務。它最好用於在PHP結構中模型化數據表和執行不復雜的 SQL 語句。 而 Yii DAO 應該用於複雜的狀況下。
    創建數據庫鏈接
    AR須要一個數據庫鏈接以執行數據庫相關的操做。默認狀況下,應用中的db組件提供了 CDbConnection實例做爲咱們須要的數據庫鏈接。mysql

下面的應用程序配置提供了一個例子:
    return array(
    'components'=>array(
    'db'=>array(
    'class'=>'system.db.CDbConnection',
    'connectionString'=>'sqlite:path/to/dbfile',
    // turn on schema caching to improve performance
    // 'schemaCachingDuration'=>3600,
    ),
    ),
    );sql

提示: 因爲Active Record須要表的元數據來肯定數據表的字段信息, 這須要時間來讀取和分析元數據。若是您的數據庫結構是比較固定的,你應該打開緩存。 打開方法是配置CDbConnection::schemaCachingDuration屬 性爲一個大於0的值。數據庫

附mysql鏈接方式數組

'db'=>array(//---------------------------------------------------------------DB CONNECT            'connectionString'=>'mysql:host='.DB_HOST.';dbname='.DB_NAME.";port=".DB_PORT.";unix_socket=".DB_SOCK,  'username'=>DB_USERNAME, 'password'=>DB_PASSWORD, 'charset' =>'utf8',       ),
    AR的支持受限於數據庫管理系統。目前,只有如下數據庫管理系統支持:
    MySQL 4.1 或之後版本
    PostgreSQL 7.3 或之後版本
    SQLite 2 和 3
    Microsoft SQL Server 2000 或之後版本
    Oracle
    注意: Microsoft SQL Server自1.0.4版本提供支持;而對 Oracle 自1.0.5版本即提供支持。
    若是你想使用其餘組件而不是
    db,或者你使用 AR 訪問多個數據庫,你應該重寫CActiveRecord::getDbConnection()。 CActiveRecord類 是全部AR類的基類。
    提示: 有兩種方法能夠在AR模式下使用多種數據庫系統。若是數據庫的模式不一樣, 您能夠以不一樣的getDbConnection()來 建立不一樣的 AR 類。不然,動態改變靜態變量CActiveRecord::db 是一個更好的主意。
    定義 AR 類
    爲了使用一個數據表,咱們首先須要擴展CActiveRecord來 定義一個AR類。 每一個AR類表明一個數據庫表,每一個AR實例表明數據表中的一行。下面的代碼介紹了 要建立一個對應
    tbl_post
    表的AR類所須要的最少的代碼。
    class Post extends CActiveRecord
    {
    public static function model($className=__CLASS__)
    {
    return parent::model($className);
    }
    public function tableName()
    {
    return ’tbl_post’;
    }
    }緩存

提示: 由於AR類在不少地方被引用,咱們能夠導入包含AR類的整個目錄,而不是逐個引入它們。例如,若咱們全部的AR類文件位於 protected/models,咱們能夠以下配置:
    return array(
    ’import’=>array(
    ’application.models.*’,
    ),
    );
    默認的, AR 類的名字和數據表的名字相同。 若它們不一樣須要重寫 tableName()方法。 方法 model() is declared as such for every AR class (to be explained shortly)。
    信息: 要使用版本 1.1.0 引入的表前綴特徵, AR 的方法 tableName() 能夠被以下重寫,
    public function tableName()
    {
    return ’{{post}}’;
    }這樣,不是返回一個完整的表名, 咱們返回去掉了前綴的表名,並把它環繞在雙彎曲括號中。
    一條數據的字段能夠做爲相應 AR 實例的屬性被訪問。 例如, 下面的代碼設置了 title 字段(屬性):
    $post=new Post;
    $post->title=’a sample post’;雖然咱們沒有在 Post 類中明確聲明 title 屬性, 咱們仍然能夠在上面的代碼中訪問它。 這是由於 title 是表 tbl_post 中的字段, 在 PHP__get() 魔術方法的幫助下,CActiveRecord 能夠將其做爲一個屬性來訪問。 若以一樣方式嘗試訪問不存在的字段,一個異常將被拋出。
    信息:在此指南中,咱們爲全部的數據表和字段採起小寫格式。這是由於在不一樣的DBMS中,對於大小寫的敏感是不 同的。例如,PostgreSQL默認對字段名字是大小寫不敏感的, and we must quote a column in a query condition if the column contains mixed-case letters.使用小寫格式能夠避免此問題。
    AR 依賴於數據表良好定義的主鍵。 若一個表沒有一個主鍵, 須要相應的 AR 類指定哪些字段應當爲主鍵,經過重寫
    primaryKey()
    方法,
    public function primaryKey()
    {
    return ’id’;
    // For composite primary key, return an array like the following
    // return array(’pk1’, ’pk2’);
    }建立記錄
    要插入新的一行記錄到數據表中, 咱們建立一個新的對應的 AR 類實例, 設置和字段對應的屬性的值, 並調用 save() 方法來完成插入。
    $post=new Post;
    $post->title=’sample post’;
    $post->content=’content for the sample post’;
    $post->create time=time();
    $post->save();若表的主鍵是自增的, 在插入後 AR 實例將包含一個更新後的主鍵。 在上面的例子中, 屬性id 將映射爲新插入的主鍵值, 即便咱們沒有明確更改它。
    若在表模式中,一個字段被定義爲一些靜態默認值(static default value) (例如一個字符串, 一個數字), 在這個AR實例被建立後, 實例中相應的屬性將自動有相應的默認值。 改變此默認值的一個方式是在 AR 類中明確聲明此屬性:
    class Post extends CActiveRecord
    {
    public $title=’please enter a title’;
    ……
    }
    $post=new Post;
    echo $post->title;  // this would display: please enter a title從版本 1.0.2 開始, 在記錄被保存前(插入或更新)一個屬性能夠賦值爲 CDbExpression 類型的值。 例如, 爲了保存由 MySQL NOW() 函數返回的時間戳, 咱們可使用下面的代碼:
    $post=new Post;
    $post->createtime=new CDbExpression(’NOW()’);
    // $post->create time=’NOW()’; will not work because
    // ’NOW()’ will be treated as a string
    $post->save();app

提示:AR 容許咱們執行數據庫操做而無需編寫麻煩的 SQL 語句, 咱們經常想要知道什麼 SQL 語句被 AR 在下面執行了。 這能夠經過打開 Yii 的記錄(logging)特徵來實現。 例如, 咱們能夠在應用配置中打開 CWebLogRoute , 咱們將看到被執行的 SQL 語句被顯示在每一個頁面的底部。 自版本 1.0.5 開始, 咱們能夠在應用配置設置 CDbConnection::enableParamLogging 爲 true 以便 綁定到 SQL 語句的參數值也被記錄。
    讀取記錄
    要讀取數據表中的數據,咱們能夠調用下面其中一個
    find
    方法:
    // find the first row satisfying the specified condition
    $post=Post::model()->find($condition,$params);
    // find the row with the specified primary key
    $post=Post::model()->findByPk($postID,$condition,$params);
    // find the row with the specified attribute values
    $post=Post::model()->findByAttributes($attributes,$condition,$params);
    // find the first row using the specified SQL statement
    $post=Post::model()->findBySql($sql,$params);在上面, 咱們使用
    Post::model()
    調用
    find
    方法。 記得靜態方法
    model()
    是每一個 AR 類所必需的。 此方法返回一個 AR 實例,此實例 被用來訪問類水平的方法(相似於靜態的類方法)。
    若
    find
    方法找到一行記錄知足查詢條件, 它將返回一個 Post 實例,此實例的屬性包含表記錄對應的字段值。 而後咱們能夠讀取被載入的值如同咱們訪問普通對象的屬性, 例如,
    echo $post->title;
    .
    find
    方法將返回 null 若在數據庫中沒有找到知足條件的記錄。
    當調用 find,咱們使用$condition 和 $params 來指定查詢條件。 這裏 $condition 能夠是字符串表明一個 SQL 語句中的 WHERE 子語句,$params 是一個參數數組,其中的值應被綁定到$condition的佔位符。例如,
    // find the row with postID=10
    $post=Post::model()->find(’postID=:postID’, array(’:postID’=>10));注意: 在上面的例子中, 對於某些 DBMS 咱們可能須要轉義對 postID 字段的引用。 例如, 若咱們使用 PostgreSQL, 咱們須要寫condition 爲 「postID」=:postID ,由於 PostgreSQL 默認狀況下對待字段名字爲大小寫不敏感的。
    咱們也可使用  $condition 來指定更復雜的查詢條件。 不使用字符串,咱們讓$condition框架

     爲一個 CDbCriteria 實例,可讓咱們指定條件而不限於 WHERE 子語句。例如,socket

    $criteria=new CDbCriteria;
    $criteria->select=’title’; // only select the ’title’ column
    $criteria->condition=’postID=:postID’;
    $criteria->params=array(’:postID’=>10);
    $post=Post::model()->find($criteria); // $params is not needed注意, 當使用 CDbCriteria 做爲查詢條件, 再也不須要參數
    $params
    由於它能夠在 CDbCriteria 中被指定, 如上所示。
    一個可選的 CDbCriteria 方式是傳遞一個數組到
    find
    方法。 數組的鍵和值分別對應於 criteria 的屬性名字和值。上面的例子能夠被以下重寫,
    $post=Post::model()->find(array(
    ’select’=>’title’,
    ’condition’=>’postID=:postID’,
    ’params’=>array(’:postID’=>10),
    ));信息: 當一個查詢條件是關於匹配一些字段用指定的值, 咱們可使用 findByAttributes()。 咱們讓參數$attributes爲一個數組,數組的值由字段名字索引。 在一些框架中,此任務能夠經過調用相似於
    findByNameAndTitle 的方法來實現。 雖然這個方法看起來頗有吸引力, 但它經常引發混淆和衝突,例如字段名字的大小寫敏感性問題。
    當多行記錄知足指定的查詢條件, 咱們可使用下面的
    findAll
    方法將它們聚合在一塊兒, 每一個都有它們本身的副本
    find
    方法。
    // find all rows satisfying the specified condition
    $posts=Post::model()->findAll($condition,$params);
    // find all rows with the specified primary keys
    $posts=Post::model()->findAllByPk($postIDs,$condition,$params);
    // find all rows with the specified attribute values
    $posts=Post::model()->findAllByAttributes($attributes,$condition,$params);
    // find all rows using the specified SQL statement
    $posts=Post::model()->findAllBySql($sql,$params);若沒有符合條件的記錄,
    findAll
    返回一個空數組。 不一樣於
    find
    方法,find方法會返回 null.
    除了上面所說的 find和findAll 方法, 爲了方便, 下面的方法也可使用:
    // get the number of rows satisfying the specified condition
    $n=Post::model()->count($condition,$params);
    // get the number of rows using the specified SQL statement
    $n=Post::model()->countBySql($sql,$params);
    // check if there is at least a row satisfying the specified condition
    $exists=Post::model()->exists($condition,$params);更新記錄
    一個 AR 實例被字段值填充後, 咱們能夠改變它們並保存回它們到數據表中。
    $post=Post::model()->findByPk(10);
    $post->title=’new post title’;
    $post->save(); // save the change to database若咱們所見, 咱們使用相同的 save() 方法來執行插入和更新操做。 若一個 AR 實例被使用 new 操做符建立,調用 save() 將插入一行新記錄到數據表中; 若此 AR 實例是一些find或findAll方法調用的結果, 調用 save() 將更新表中已存在的記錄。 事實上, 咱們可使用 CActiveRecord::isNewRecord 來檢查一個 AR 實例是不是新建的。
    更新一行或多行表中的記錄而不預先載入它們也是可能的。 AR 提供以下方便的類水平的(class-level)方法來實現它:
    // update the rows matching the specified condition
    Post::model()->updateAll($attributes,$condition,$params);
    // update the rows matching the specified condition and primary key(s)
    Post::model()->updateByPk($pk,$attributes,$condition,$params);
    // update counter columns in the rows satisfying the specified conditions
    Post::model()->updateCounters($counters,$condition,$params);在上面,
    $attributes
    是一個值由字段名索引的數組;
    $counters
    是一個增長值由字段名索引的數組;
    $condition
    和
    $params
    已在以前被描述。
    刪除記錄
    咱們也能夠刪除一行記錄若一個 AR 實例已被此行記錄填充。
    $post=Post::model()->findByPk(10); // assuming there is a post whose ID is 10
    $post->delete(); // delete the row from the database table注意, 在刪除後, 此 AR 實例仍然未改變, 但相應的表記錄已經不存在了。
    下面類水平的(class-level)方法被用來刪除記錄,而無需預先載入它們:
    // delete the rows matching the specified condition
    Post::model()->deleteAll($condition,$params);
    // delete the rows matching the specified condition and primary key(s)
    Post::model()->deleteByPk($pk,$condition,$params);數據驗證
    當插入或更新一行記錄, 咱們經常須要檢查字段的值是否符合指定的規則。 若字段值來自用戶時這一點特別重要。一般咱們永遠不要信任用戶提交的數據。
    AR 自動執行數據驗證在 save() 被調用時。驗證基於在 AR 類中的 rules()方 法中指定的規則。如何指定驗證規則的更多信息, 參考 聲明驗證規則 部分。 下面是保存一條記錄典型的工做流程:
    if($post->save())
    {
    // data is valid and is successfully inserted/updated
    }
    else
    {
    // data is invalid. call getErrors() to retrieve error messages
    }當插入或更新的數據被用戶在 HTML 表單中提交, 咱們須要賦值它們到對象的 AR 屬性。 咱們能夠這樣作:
    $post->title=$_POST[’title’];
    $post->content=$_POST[’content’];
    $post->save();如有不少字段, 咱們能夠看到一個很長的賦值列表。 可使用下面的 attributes 屬性來緩解。 更多細節能夠在 Securing Attribute Assignments 部分和 Creating Action 部分找到。
    // assume $_POST[’Post’] is an array of column values indexed by column names
    $post->attributes=$_POST[’Post’];
    $post->save();對比記錄
    相似於表記錄, AR 實例由它們的主鍵值來被識別。 所以,要對比兩個 AR 實例, 咱們只須要對比它們的主鍵值, 假設它們屬於相同的 AR 類。 然而,一個更簡單的方式是調用 CActiveRecord::equals()。
    信息: 不一樣於 AR 在其餘框架的執行, Yii 在其 AR 中支持多個主鍵。 一個複合主鍵由兩個或更多字段構成。 對應的, 主鍵值在 Yii 中表示爲一個數組。 The
    primaryKey
    屬性給出一個 AR 實例的主鍵值。
    Customization
    CActiveRecord 提供了一些佔位符(placeholder)方法可被用來在子類中重寫以自定義它的工做流程。
    beforeValidate 和 afterValidate: 它們在驗證執行 以前/後 被調用。
    beforeSave 和 afterSave: 它們在保存一個 AR 實例以前/後 被調用。
    beforeDelete 和 afterDelete: 它們在一個 AR 實例被刪除 以前/後 被調用。
    afterConstruct: 這將在每一個 AR 實例被使用 new 操做符建立以後被調用。
    beforeFind: 它在一個 AR finder 被用來執行一個查詢以前被調用 (例如 find(), findAll())。 從版本 1.0.9 可用。
    afterFind: 它在每一個 AR 實例被建立做爲一個查詢結果後被調用。
    在 AR 中使用事務處理
    每一個 AR 實例包含一個名爲 dbConnection 的屬性,它是一個 CDbConnection 實例。 這樣咱們在使用 AR 時就可使用 Yii DAO 提供的事務處理特徵:
    $model=Post::model();
    $transaction=$model->dbConnection->beginTransaction();
    try
    {
    // find and save are two steps which may be intervened by another request
    // we therefore use a transaction to ensure consistency and integrity
    $post=$model->findByPk(10);
    $post->title=’new post title’;
    $post->save();
    $transaction->commit();
    }
    catch(Exception $e)
    {
    $transaction->rollBack();
    }命名空間(new!)
    注意: 對於命名空間的支持從版本 1.0.5 開始。 思路來源於 Ruby on Rails.
    a named scope represents a named query criteria that can be combined with other named scopes and applied to an active record query.
    命名空間被主要在 CActiveRecord::scopes() 方法中聲明,格式是 name-criteria 對。 下面的代碼在 Post 模型類中聲明瞭兩個命名空間,
    published
    和
    recently
    :
    class Post extends CActiveRecord
    {
    ……
    public function scopes()
    {
    return array(
    ’published’=>array(
    ’condition’=>’status=1’,
    ),
    ’recently’=>array(
    ’order’=>’create time DESC’,
    ’limit’=>5,
    ),
    );
    }
    }每一個命名空間被聲明爲一個被用來初始化一個 CDbCriteria 實例的數組。例如,命名空間
    recently
    指定 了
    order
    屬性爲
    create_time DESC
    ,
    limit
    屬性爲 5, 翻譯爲一個查詢條件就是應當返回最近發表的5篇帖子。
    命名空間大多數做爲find方法的 modi?er 來使用。幾個命名空間能夠鏈接在一塊兒,則樣能夠獲得一個更加有限制性的查詢結果集。 例如,要找到最近發表的帖子,咱們可使用下面的代碼:
    $posts=Post::model()->published()->recently()->findAll();一般命名空間必須出如今一個 find 方法的左邊。 Each of them provides a query criteria, which is combined with other criterias, including the one passed to the find method call. The net e?ect is like adding a list of ?lters to a query.
    從版本 1.0.6 開始, 命名空間也可使用 update 和 delete 方法。例如,下面的代碼將刪除 全部最近發表的帖子:
    Post::model()->published()->recently()->delete();注意: 命名空間只能夠被做爲類水平的方法使用。也就是說,此方法必須使用 ClassName::model() 來調用它。
    參數化命名空間(Parameterized Named Scopes)
    命名空間能夠被參數化。例如,咱們想要定製命名空間
    recently
    指定的帖子數目。 要這樣作,不是在 CActiveRecord::scopes 方法中聲明命名空間,咱們須要定義一個新的方法,它的名字和空間的名字相同:
    public function recently($limit=5)
    {
    $this->getDbCriteria()->mergeWith(array(
    ’order’=>’create time DESC’,
    ’limit’=>$limit,
    ));
    return $this;
    }而後,咱們可使用下面的語句來檢索 3 個最近發表的帖子:
    $posts=Post::model()->published()->recently(3)->findAll();若咱們不使用上面的參數 3 ,默認狀況下咱們將檢索 5 個最近發表的內容。
    默認命名空間
    一個模型類能夠有一個默認命名空間,它被應用於此模型全部的查詢 (包括 relational ones)。例如,一個支持多種語言的網站只是以當前用戶指定的語言來顯示內容。 由於有不少關於站點內容的查詢,咱們能夠定義一個默認命名空間來解決這個問題。要這樣作,咱們重寫 CActiveRecord::defaultScope 方法以下,
    class Content extends CActiveRecord
    {
    public function defaultScope()
    {
    return array(
    ’condition’=>"language=’".Yii::app()->language."’",
    );
    }
    }如今,若調用下面的方法將自動使用上面定義的查詢條件:
    $contents=Content::model()->findAll();注意默認命名空間只應用於 SELECT 查詢。它忽視 INSERT,UPDATE 和 DELETE 查詢。
    Popularity: unranked [?]ide

相關文章
相關標籤/搜索