CDbCommand: SQL statement
CDbDataReader: 匹配結果集的一行記錄
CDbTransaction:數據庫事務
訪問數據庫前需要建立數據庫連接;使用DAO建立一個抽象數據庫鏈接:
$connection = new CDbConnection($dsn, $username, $password);
$connection->active = true; // 只有激活了連接纔可以使用
$connection->active = false; // 關閉連接
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();
// 首先,建立一個連接
$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'
// 首先要實例化一個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()中使用 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));
$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()中定義更加複雜的關係
- class User extends CActiveRecord
- {
- public function relations()
- {
- return array(
- 'posts'=>array(self::HAS MANY, 'Post', 'author id',
- 'order'=>'posts.create time DESC',
- 'with'=>'categories'),
- 'profile'=>array(self::HAS ONE, 'Profile', 'owner id'),
- );
- }
- }
// 利用別名解決歧義
- $posts=Post::model()->with('comments')->findAll(array(
- 'order'=>'t.create time, comments.create time'
- ));
// Dynamic Relational Query 動態關係SQL查詢,更改默認插敘條件:
- User::model()->with(array(
- 'posts'=>array('order'=>'posts.create time ASC'),
- 'profile',
- ))->findAll();
- $user=User::model()->findByPk(1);
- $posts=$user->posts(array('condition'=>'status=1')); // 返回的都是AR對象, 而不是數據
// 統計查詢
- class Post extends CActiveRecord
- {
- public function relations()
- {
- return array(
- 'commentCount'=>array(self::STAT, 'Comment', 'post_id'),
- 'categoryCount'=>array(self::STAT, 'Category', 'post_category(post_id, category_id)'),
- );
- }
- }