YII 數據庫相關操作

CDbConnection: 一個抽象數據庫連接
CDbCommand: SQL statement
CDbDataReader: 匹配結果集的一行記錄

CDbTransaction:數據庫事務

訪問數據庫前需要建立數據庫連接;使用DAO建立一個抽象數據庫鏈接:

$connection = new CDbConnection($dsn, $username, $password);
$connection->active = true;     // 只有激活了連接纔可以使用
$connection->active = false;  // 關閉連接 


CDbConnection繼承自CApplicationComponent,所以他可以像組件一樣在任何地方使用。因此可以這樣訪問:

Yii::app()->db  

//執行SQL語句需要CDbCommand對象,而該對象由CdbConnection::createCommand()返回,因此:
$connection=Yii::app()->db;
$command=$connection->createCommand($sql);
// 如果SQL語句想要完全有自己寫,可以這樣:
$newSQL = 'SQL語句';
$command->text=$newSQL;

// CDbCommand對象有兩個方法execute()用於非查詢SQL執行,而query(),通俗的講就是用於SELECT查詢
// execute()返回的是INSERT, UPDATE and DELETE操作受影響的記錄行數
// query()返回一個CDbDataReader對象,使用CDbDataReader對象可以遍歷匹配結果集中的所有記錄。

$rowCount=$command->execute();  // execute the non-query SQL
$dataReader=$command->query();  // execute a query SQL // 返回CDbDataReader對像
$rows=$command->queryAll();     // query and return all rows of result
$row=$command->queryRow();      // query and return the first row of result
$column=$command->queryColumn();    // query and return the first column of result
$value=$command->queryScalar();     // query and return the first field in the first row

// query()返回的是代表結果集的對象而非直接的結果,因此要獲取結果集的記錄可以這樣:

$dataReader=$command->query();

// CDbDataReader::read()可以一次獲取一行數據,到末尾時返回false

while(($row=$dataReader->read())!==false) 

// CDbDataReader實現了迭代器接口因此可以使用foreach遍歷

foreach($dataReader as $row)

// 一次性返回所有的記錄(數組)
$rows=$dataReader->readAll();


// YII中的CDbTransaction類用於事務

// 首先,建立一個連接
$connection = Yii::app()->db;
// 第二,開始事務
$transaction=$connection->beginTransaction();
// 第三,執行SQL,如果錯誤就拋出異常,在異常處理中回滾。
try
{
$connection->createCommand($sql1)->execute();
$connection->createCommand($sql2)->execute();
//.... other SQL executions
// 如果SQL執行都沒有拋出異常,那就提交。
$transaction->commit(); 
} catch(Exception $e) {
    $transaction->rollBack();   // 在異常處理中回滾
}

/ 執行SQL中,一般都需要綁定一些用戶參數,對於用戶參數,需要防止SQL注入攻擊
// PDO對象的綁定參數的方法可以防止SQL注入攻擊,同樣擴展自PDO的DAO也有這樣的功能
// 舉例說明:
// 第一,建立一個連接:
$connection = Yii::app()->db;
// 第二,寫下無敵的SQL語句,比如:
$sql="INSERT INTO tbl_user (username, email) VALUES(:username,:email)";
// 第三,創建CDbCommand對象用於執行SQL
$command=$connection->createCommand($sql);
// 接下來,將SQL語句中的形式參數,替換爲實際參數
$command->bindParam(":username",$username,PDO::PARAM STR);   // 這與PDO有點不同,PDO中不帶冒號
$command->bindParam(":email",$email,PDO::PARAM STR);    // 同樣
// 最後,執行
$command->execute();
// Yii實現了把一條完整的SQL語句完完全全肢解的能力,比如這樣:
$user = Yii::app()->db->createCommand();    
        ->select('id, username, profile')
        ->from('tbl_user u')
        ->join('tbl_profile p', 'u.id=p.user_id')
        ->where('id=:id', array(':id'=>$id)
        ->queryRow();       // 返回匹配的結果集的第一行
        // 其實這條語句是這樣的: $newSQL ='SELECT id, username, profile from tbl_user u INNER JOIN tbl_profile p ON u.id = p.user_id WHERE u.id =:id'


// yii提供了一種構建SQL的機制(也就是說不用自己寫長長的SQL)

// 首先要實例化一個CDbCommand對象
$command = Yii::app()->db->createCommand();     // 注意參數留空了。。
// 可用的方法列表如下:
->select(): SELECT子句
->selectDistinct(): SELECT子句,並保持了記錄的唯一性
->from():         構建FROM子句
->where():        構建WHERE子句
->join():         在FROM子句中構建INNER JOIN 子句
->leftJoin():     在FROM子句中構建左連接子句
->rightJoin():    在FROM子句中構建右連接子句
->crossJoin():    添加交叉查詢片段(沒用過)
->naturalJoin():  添加一個自然連接子片段
->group():        GROUP BY子句
->having():       類似於WHERE的子句,但要與GROUP BY連用
->order():        ORDER BY子句
->limit():        LIMIT子句的第一部分
->offset():       LIMIT子句的第二部分
->union():        appends a UNION query fragment


select()默認返回全部列
// 但你可以這樣:
select('username, email');

// 或使用表限定,或使用別名
select('tbl_user.id, username name');

// 或使用數組作爲參數
select(array('id', 'count(*) as num'));


// 使用form() 如果制定了多個表需要使用逗號分隔的字符串,就像原生SQL語句那樣
from('tbl_user, tbl_post, tbl_profile');
// 當然,你也可以使用表別名, 還可以使用完整的數據庫限定名
from('tbl_user u, public.tbl_profile p');


WHERE子句
// 在where()中使用 AND
where(array('and', 'id=:id', 'username=:username'), array(':id'=>$id, ':username'=>$username);
// 在where()中使用 OR 與 AND用法相同,如下:  ##看起來比直接寫更加繁瑣##
where( array('and', 'type=1', array('or', 'id=:id','username=:username') ),array(':id'=>$id, ':username'=>$username ));
// IN 操作符用法
where(array('in', 'id', array(1,2,3)))
// LIKE 用法
where( array('like', 'name', '%tester%') );
where( array('like','name', array('%test%', '%sample%')) ) // 等於 name LIKE '%test%' AND name LIKE '%sample%
    // 再這樣複雜下去, 使用這種方法簡直是自殺行爲。

$keyword=$ GET['q'];
// escape % and characters
$keyword=strtr($keyword, array('%'=>'n%', ' '=>'n '));
$command->where(array('like', 'title', '%'.$keyword.'%'));

// 添加了這麼多,你都不知道合成後的SQL長啥樣了,可以使用->text查看(魔術方法)
// 如果覺得組合的SQL沒有錯誤,那就執行他,添加->queryAll(); 這可以獲得所有匹配的結果集。
// 當然,如果你確定執行的結果集中只有一行,可以添加->queryRow();來直接獲取。
// 如果一個CDbCommand對象需要執行多次,那麼在下一次執行之前記得調用reset();

$command = Yii::app()->db->createCommand();
$users = $command->select('*')->from('tbl_users')->queryAll();
$command->reset(); // clean up the previous query
$posts = $command->select('*')->from('tbl_posts')->queryAll();

/// YII的SQL構建函數就是一雞肋。

// Active Record
// 使用AR以面向對象的方式訪問數據庫,AR實現了ORM技術
// 當Post類表示表tbl_post時,我們可以使用這樣的方式插入一條數據

$post = new Post();
$post->title = 'new title';
$post->content = 'new content';
$post->save();      // 保存即插入

// AR最典型的功能就是執行CRUD操作
// DAO定位於解決複雜的數據庫查詢,而AR定位於解決簡單的數據庫查詢
// 一個AR類代表一張數據表,而一個AR對象代表表中的一行真實的記錄,AR類繼承CActiveRecord。
// 如果有一張POST表`tbl_post`,你可以這樣定義一個AR類

class Post extends CACtiveRecord
{
    public static function model($className = __CLASS__)
    {
        return parent::model($className);
    }

    public function tablName()
    {
        return '{{post}}';
    }
}

// 表中的每一個字段都由AR類中的一個屬性表示,如果試圖通過屬性訪問表中沒有字段,將會拋出一個異常。

// 一個AR一定需要一個主鍵,如果某張表沒有主鍵,你就自己在類中僞造一個,像這樣:

public function primaryKey()
{
    return 'id';        // 'id' 是關聯表中的一個字段,但他不是主鍵,現在將它指定爲主鍵
}

// 實例化一個AR,填寫信息(類似於填充用戶提交的信息),然後保存

$post = new Post;
$post->title = 'sample post';
$post->content = 'content for the sample post';
$post->create_time = time();
$post->save();  // 保存/插入


// 通過AR讀取記錄 fine() findByPk() findByAttributes() findBySql()

$post=Post::model()->find($condition,$params);                  // 返回Post對象(如果有匹配記錄的話), 否則返回NULL
$post=Post::model()->findByPk($postID,$condition,$params);
$post=Post::model()->findByAttributes($attributes,$condition,$params);
$post=Post::model()->findBySql($sql,$params);

// find()的一個例子:

$post=Post::model()->find('postID=:postID', array(':postID'=>10));


// 如果查詢條件很是複雜,就要使用CDbCriteria類

$criteria = new CDbCriteria;
$criteria->select='title';
$creteria->condition='postID=:postID';
$criteria->params=array(':postID'=>10);
$post=Post::model()->find($criteria);   // 不需要第二個參數

// 另一種更好的寫法

$post=Post::model()->find(array(
        'select' => 'title',
        'condition' => 'postID=:postID',
        'params' => array(':postID' => 10)
        ));

// 如果查找的是多行記錄可以使用 findAll() findAllByPk() findAllByAttributes() findAllBySql()
// 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); 

// 如果沒有匹配的行,將返回一個空數組,這可以用empty()去檢測

// 另外的一些可以使用的方法:
// 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  

// 怎麼知道這是一條新紀錄還是一條舊的記錄呢?使用如下方法:

if( CActiveRecord::isNewRecord )  
// 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);  


// 刪除記錄
$post=Post::model()->findByPk(10); // assuming there is a post whose ID is 10  
$post->delete(); // delete the row from the database table  
// 注意,當刪除記錄之後,$post仍然可用, 且保留了原始數據。


// 類級別的方法  
// 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對象中

$post->title = $_POST['title'];  
$post->content = $_POST['content'];  
$post->save();  
  
// assume $ POST['Post'] is an array of column values indexed by column names  
$post->attributes=$ POST['Post'];  
$post->save();  

// RAR:Relatived Actie Record
// RAR本質上就是執行關係數據查詢

// 如何讓一個AR關聯另一個AR
// 4中關係類型
self::BELONGS_TO
self::HAS_MANY
self::HAS_ONE
self::MANY_MANY

關係名稱(關係類型,要關聯的類名,外鍵名,其他額外的選項);
// 定義表關係 類:Post

public function relations()  
{  
    return array(  
        'author'=>array(self::BELONGS_TO, 'User', 'author_id'),     // 返回User對象  
        'categories'=>array(self::MANY_MANY, 'Category', 'tbl_post_category(post_id, category_id)'),  
    );  
}  

// 類:User

public function relations()
{
    return array(
            'posts' => array(self::HAS_MANY, 'Post', 'author_id'),
            'profile' => array(self::HAS_ONE, 'Profile', 'owner_id')
    );
}

// 定義了AR間的關係之後,當執行關係查詢時,與AR關聯的AR也會自動實例化, 比如這樣

$author = User::model()->findByPk(1);
$author->posts;         // posts關係已經定義。

// 執行關係查詢
1).lazy loading approach 懶惰關係執行

// retrieve the post whose ID is 10  
$post=Post::model()->findByPk(10);  
// retrieve the post's author: a relational query will be performed here  
$author=$post->author;  // 如果先前沒有執行過,現在才執行這個關係查詢(事情拖到這一步才做,真的是很懶啊!) 

// 如果關係查詢執行後沒有匹配的結果,返回將會是NULL或空的數組。

2).eager loading approach   熱心的關係查詢 //這名字真的很萌!
// 也就是說一次性取回所有你想要的記錄。管你要不要,這這這,太熱心了吧

$posts=Post::model()->with('author')->findAll();  
// SQL => 'SELECT tbl_post.*, author.* FROM tbl_post t INNER JOIN tbl_user author ON t.author = tbl_user.id'  
  
$posts=Post::model()->with('author','categories')->findAll();  
// SQL => 'SELECT * FROM tbl_post t INNER JOIN tbl_user u ON t.author = u.id INNER JOIN categories c ON t.id = c.post_id'  
  
$posts=Post::model()->with(  
    'author.profile',  
    'author.posts',  
    'categories')->findAll();  
  
$criteria=new CDbCriteria;  
$criteria->with=array(  
'author.profile',  
'author.posts',  
'categories',  
);  
$posts=Post::model()->findAll($criteria);  

或者

$posts=Post::model()->findAll(array(  
                                    'with'=>array(  
                                    'author.profile',  
                                    'author.posts',  
                                    'categories',  
                                    )  
                             );  
// 如果我們想知道用戶中誰發過帖子,並且帖子的狀態是“公開”。我們並不關心用戶發表過的帖子的內容。

$user = User::model()->with('posts')->findAll();  
                'VS'  
$user = User::model()->with(array(  
                                    'posts' => array(  
                                                        'select' => false,  
                                                        'joinType' => 'INNER JOIN',  
                                                        'condition' => 'posts.published = 1'  
                                                    ),  
                                 )  
                           )->findAll();  

// 返回的將會是所有發過帖子(且帖子已經公開)的用戶

// 在relatinos()中定義更加複雜的關係
[html] view plaincopy
  1. class User extends CActiveRecord  
  2. {  
  3.     public function relations()  
  4.     {  
  5.             return array(  
  6.                     'posts'=>array(self::HAS MANY, 'Post', 'author id',  
  7.                     'order'=>'posts.create time DESC',  
  8.                     'with'=>'categories'),  
  9.                     'profile'=>array(self::HAS ONE, 'Profile', 'owner id'),  
  10.                     );  
  11.     }  
  12. }  



// 利用別名解決歧義
[html] view plaincopy
  1. $posts=Post::model()->with('comments')->findAll(array(  
  2.         'order'=>'t.create time, comments.create time'  
  3.     ));  


                        
// Dynamic Relational Query 動態關係SQL查詢,更改默認插敘條件:
[html] view plaincopy
  1. User::model()->with(array(  
  2.         'posts'=>array('order'=>'posts.create time ASC'),  
  3.         'profile',  
  4.     ))->findAll();  
  5.   
  6. $user=User::model()->findByPk(1);  
  7. $posts=$user->posts(array('condition'=>'status=1'));        // 返回的都是AR對象, 而不是數據  



// 統計查詢
[html] view plaincopy
  1. class Post extends CActiveRecord  
  2. {  
  3.     public function relations()  
  4.     {  
  5.         return array(  
  6.             'commentCount'=>array(self::STAT, 'Comment', 'post_id'),  
  7.             'categoryCount'=>array(self::STAT, 'Category', 'post_category(post_id, category_id)'),  
  8.         );  
  9.     }  
  10.   
  11. }  




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章