YII 数据库相关操作
CDbConnection: 一个抽象数据库连接
CDbCommand: SQL statement
CDbDataReader: 匹配结果集的一行记录
CDbTransaction:数据库事务
访问数据库前需要建立数据库连接;使用DAO建立一个抽象数据库链接:
CDbConnection继承自CApplicationComponent,所以他可以像组件一样在任何地方使用。因此可以这样访问:
//执行SQL语句需要CDbCommand对象,而该对象由CdbConnection::createCommand()返回,因此:
// 如果SQL语句想要完全有自己写,可以这样:
// CDbCommand对象有两个方法execute()用于非查询SQL执行,而query(),通俗的讲就是用于SELECT查询
// execute()返回的是INSERT, UPDATE and DELETE操作受影响的记录行数
// query()返回一个CDbDataReader对象,使用CDbDataReader对象可以遍历匹配结果集中的所有记录。
// query()返回的是代表结果集的对象而非直接的结果,因此要获取结果集的记录可以这样:
// CDbDataReader::read()可以一次获取一行数据,到末尾时返回false
// CDbDataReader实现了迭代器接口因此可以使用foreach遍历
// 一次性返回所有的记录(数组)
queryXXX() 形式的方法会直接返回匹配的记录集合,当query()不是,他返回一个代表结果集的对象
// YII中的CDbTransaction类用于事务
// 执行SQL中,一般都需要绑定一些用户参数,对于用户参数,需要防止SQL注入攻击
// PDO对象的绑定参数的方法可以防止SQL注入攻击,同样扩展自PDO的DAO也有这样的功能
// 举例说明:
// 如果还有其他的数据需要插入,可以再次绑定实参。
// 使用CDbDataReader对象的bindColumn()方法将结果集中的列绑定到PHP变量。
// 因此,读取一行记录,列值将自动填充到对应的PHP对象中
// 比如这样:
// 设置表前缀,使用 CDbConnection::tablePrefix 属性在配置文件中设置
//
// Yii实现了把一条完整的SQL语句完完全全肢解的能力,比如这样:
// yii提供了一种构建SQL的机制(也就是说不用自己写长长的SQL)
select()默认返回全部列
// 使用form() 如果制定了多个表需要使用逗号分隔的字符串,就像原生SQL语句那样:
WHERE子句
// 添加了这么多,你都不知道合成后的SQL长啥样了,可以使用->text查看(魔术方法)
// 如果觉得组合的SQL没有错误,那就执行他,添加->queryAll(); 这可以获得所有匹配的结果集。
// 当然,如果你确定执行的结果集中只有一行,可以添加->queryRow();来直接获取。
// 如果一个CDbCommand对象需要执行多次,那么在下一次执行之前记得调用reset();
/// YII的SQL构建函数就是一鸡肋。
// Active Record
// 使用AR以面向对象的方式访问数据库,AR实现了ORM技术
// 当Post类表示表tbl_post时,我们可以使用这样的方式插入一条数据
// AR最典型的功能就是执行CRUD操作
// DAO定位于解决复杂的数据库查询,而AR定位于解决简单的数据库查询
// 一个AR类代表一张数据表,而一个AR对象代表表中的一行真实的记录,AR类继承CActiveRecord。
// 如果有一张POST表`tbl_post`,你可以这样定义一个AR类
// 表中的每一个字段都由AR类中的一个属性表示,如果试图通过属性访问表中没有字段,将会抛出一个异常。
// 一个AR一定需要一个主键,如果某张表没有主键,你就自己在类中伪造一个,像这样:
// 实例化一个AR,填写信息(类似于填充用户提交的信息),然后保存
// 通过AR读取记录 fine() findByPk() findByAttributes() findBySql()
// find()的一个例子:
// 如果查询条件很是复杂,就要使用CDbCriteria类
// 另一种更好的写法
// 如果查找的是多行记录可以使用 findAll() findAllByPk() findAllByAttributes() findAllBySql()
// 如果没有匹配的行,将返回一个空数组,这可以用empty()去检测
// 另外的一些可以使用的方法:
// 使用AR更新记录
// 一个典型的实例:
// 怎么知道这是一条新纪录还是一条旧的记录呢?使用如下方法:
// 删除记录
// 注意,当删除记录之后,$post仍然可用, 且保留了原始数据。
// 数据验证
// 将用户提交的数据保存到AR对象中
// RAR:Relatived Actie Record
// RAR本质上就是执行关系数据查询
// 如何让一个AR关联另一个AR
// 4中关系类型
self::BELONGS_TO
self::HAS_MANY
self::HAS_ONE
self::MANY_MANY
关系名称(关系类型,要关联的类名,外键名,其他额外的选项);
// 定义表关系 类:Post
// 类:User
// 定义了AR间的关系之后,当执行关系查询时,与AR关联的AR也会自动实例化, 比如这样:
// 执行关系查询
1).lazy loading approach 懒惰关系执行
// 如果关系查询执行后没有匹配的结果,返回将会是NULL或空的数组。
2).eager loading approach 热心的关系查询 //这名字真的很萌!
// 也就是说一次性取回所有你想要的记录。管你要不要,这这这,太热心了吧!
或者
// 如果我们想知道用户中谁发过帖子,并且帖子的状态是“公开”。我们并不关心用户发表过的帖子的内容。
// 返回的将会是所有发过帖子(且帖子已经公开)的用户
// 在relatinos()中定义更加复杂的关系
// 利用别名解决歧义
// Dynamic Relational Query 动态关系SQL查询,更改默认插叙条件:
// 统计查询
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();
queryXXX() 形式的方法会直接返回匹配的记录集合,当query()不是,他返回一个代表结果集的对象
// 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();
// 如果还有其他的数据需要插入,可以再次绑定实参。
// 使用CDbDataReader对象的bindColumn()方法将结果集中的列绑定到PHP变量。
// 因此,读取一行记录,列值将自动填充到对应的PHP对象中
// 比如这样:
$connection = Yii::app()->db; $sql = "SELECT username, email FROM tbl_user"; $dataReader = $connection->createCommand($sql)->query(); //很赞的方法链, 可惜不能接着.each() $dataReader->bindColumn(1, $username); //第一列值绑定到$username $dataReader->bindColumn(2, $email); //第二列值绑定到$email //接着循环读取并操作数据 while( $dataReader->read() !== false ) { ... // 与先前的 while(($row=$dataReader->read())!==false) 有所不同哦! }
// 设置表前缀,使用 CDbConnection::tablePrefix 属性在配置文件中设置
//
// 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()中定义更加复杂的关系
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)"), ); } }
声明:该文观点仅代表作者本人,牛骨文系教育信息发布平台,牛骨文仅提供信息存储空间服务。
- 上一篇: PHP中MySQL查询表行数问题
- 下一篇: yii2 执行原生态的sql语句