ThinkChat🤖让你学习和工作更高效,注册即送10W Token,即刻开启你的AI之旅 广告
一、获取当前控制器和方法 1 $controllerID = Yii::$app->controller->id; 2 $actionID = Yii::$app->controller->action->id; 二、创建url 1.控制器可以用urlManager组件 echo \Yii::$app->urlManager->createUrl(['site/page', 'id' => 'about']); // /index.php/site/page/id/about/ echo \Yii::$app->urlManager->createUrl(['date-time/fast-forward', 'id' => 105]) // /index.php?r=date-time/fast-forward&id=105 echo \Yii::$app->urlManager->createAbsoluteUrl('blog/post/index'); // http://www.example.com/index.php/blog/post/index/ 2.页面上生成可以用url助手类 use yii\helpers\Url; // 当前活动路由 // /index.php?r=management/default/users echo Url::to(''); // 相同的控制器,不同的动作 // /index.php?r=management/default/page&id=contact echo Url::toRoute(['page', 'id' => 'contact']); // 相同模块,不同控制器和动作 // /index.php?r=management/post/index echo Url::toRoute('post/index'); // 绝对路由,不管是被哪个控制器调用 // /index.php?r=site/index echo Url::toRoute('/site/index'); // 区分大小写的控制器动作 `actionHiTech` 的 url 格式 // /index.php?r=management/default/hi-tech echo Url::toRoute('hi-tech'); // 控制器和动作都区分大小写的 url,如'DateTimeController::actionFastForward' : // /index.php?r=date-time/fast-forward&id=105 echo Url::toRoute(['/date-time/fast-forward', 'id' => 105]); // 从别名中获取 URL // http://google.com/ Yii::setAlias('@google', 'http://google.com/'); echo Url::to('@google'); // 获取当前页的标准 URL // /index.php?r=management/default/users echo Url::canonical(); // 获得 home 主页的 URL // /index.php?r=site/index echo Url::home(); Url::remember() ; // 保存URL以供下次使用 Url::previous(); // 取出前面保存的 URL 小技巧: 为生成一个指向 # 号(锚连接 ID )的 URL ,比如 /index.php?r=site/page&id=100#title, 你要 指定 # 参数 ,采用 Url::to(['post/read', 'id' => 100, '#' => 'title']) 来创建。 详细可以参见 http://www.yiifans.com/yii2/guide/runtime-url-handling.html 三、设置auth_key $this->auth_key = \Yii::$app->security->generateRandomString(); 四、配置rbac 在配置文件中的components中加入: 'authManager' => [ 'class' => 'yii\rbac\DbManager', ], 现在可以通过 \Yii::$app->authManager 访问 authManager 。 可以建立测试数据如下: public function actionInit() { $auth = Yii::$app->authManager; // 添加 "createPost" 权限 $createPost = $auth->createPermission('createPost'); $createPost->description = 'Create a post'; $auth->add($createPost); // 添加 "updatePost" 权限 $updatePost = $auth->createPermission('updatePost'); $updatePost->description = 'Update post'; $auth->add($updatePost); // 添加 "author" 角色并赋予 "createPost" 权限 $author = $auth->createRole('author'); $auth->add($author); $auth->addChild($author, $createPost); // 添加 "admin" 角色并赋予 "updatePost" // 和 "author" 权限 $admin = $auth->createRole('admin'); $auth->add($admin); $auth->addChild($admin, $updatePost); $auth->addChild($admin, $author); // 为用户指派角色。其中 1 和 2 是由 IdentityInterface::getId() 返回的id (译者注:user表的id) // 通常在你的 User 模型中实现这个函数。 $auth->assign($author, 2); $auth->assign($admin, 1); } 如果你的应用允许用户注册,你需要在注册时给新用户指派一次角色。例如, 在高级项目模板中,要让所有注册用户成为作者,你需要如下例所示修改 frontend\models\SignupForm::signup() 方法 public function signup() { if ($this->validate()) { $user = new User(); $user->username = $this->username; $user->email = $this->email; $user->setPassword($this->password); $user->generateAuthKey(); $user->save(false); // 要添加以下三行代码: $auth = Yii::$app->authManager; $authorRole = $auth->getRole('author'); $auth->assign($authorRole, $user->getId()); return $user; } return null; } 更详细可参见:http://www.yiichina.com/doc/guide/2.0/security-authorization 五、哈希化密码 $hash = Yii::$app->getSecurity()->generatePasswordHash($password); 六、转义 1.如果你需要的是纯文本,你可以如下简单的转义: <?= \yii\helpers\Html::encode($username) ?> 2.如果是 HTML ,我们可以用 HtmlPurifier 帮助类来执行: <?= \yii\helpers\HtmlPurifier::process($description) ?> 七、数据库配置 1.在配置文件中的conponents中加入: 'db' => [ 'class' => 'yii\db\Connection', 'dsn' => 'mysql:host=localhost;dbname=example', 'username' => 'root', 'password' => '', 'charset' => 'utf8', ], 2.有时你可能想要在建立起数据库连接时立即执行一些语句来初始化一些环境变量 (比如设置时区或者字符集), 你可以通过为数据库连接的 afterOpen 事件注册一个事件处理器来达到目的。 你可以像这样直接在应用配置中注册处理器: 'db' => [ // ... 'on afterOpen' => function($event) { // $event->sender refers to the DB connection $event->sender->createCommand("SET time_zone = 'UTC'")->execute(); } ], 八、原生sql查询 // 返回多行. 每行都是列名和值的关联数组. // 如果该查询没有结果则返回空数组 $posts = Yii::$app->db->createCommand('SELECT * FROM post') ->queryAll(); // 返回一行 (第一行) // 如果该查询没有结果则返回 false $post = Yii::$app->db->createCommand('SELECT * FROM post WHERE id=1') ->queryOne(); // 返回一列 (第一列) // 如果该查询没有结果则返回空数组 $titles = Yii::$app->db->createCommand('SELECT title FROM post') ->queryColumn(); // 返回一个标量值 // 如果该查询没有结果则返回 false $count = Yii::$app->db->createCommand('SELECT COUNT(*) FROM post') ->queryScalar(); 九、原生sql防止sql注入 $post = Yii::$app->db->createCommand('SELECT * FROM post WHERE id=:id AND status=:status') ->bindValue(':id', $_GET['id']) ->bindValue(':status', 1) ->queryOne(); 十、可以使用函数 `$params = [':id' => $_GET['id'], ':status' => 1]; $post = Yii::$app->db->createCommand('SELECT * FROM post WHERE id=:id AND status=:status') ->bindValues($params) ->queryOne(); $post = Yii::$app->db->createCommand('SELECT * FROM post WHERE id=:id AND status=:status', $params) ->queryOne(); 十一、绑定参数是通过 预处理语句 实现的。 除了防止 SQL 注入攻击, 它也可以通过一次预处理 SQL 语句, 使用不同参数多次执行, 来提升性能。 $command = Yii::$app->db->createCommand('SELECT * FROM post WHERE id=:id'); $post1 = $command->bindValue(':id', 1)->queryOne(); $post2 = $command->bindValue(':id', 2)->queryOne(); 因为 bindParam() 支持通过引用来绑定参数, 上述代码也可以像下面这样写: $command = Yii::$app->db->createCommand('SELECT * FROM post WHERE id=:id') ->bindParam(':id', $id); $id = 1; $post1 = $command->queryOne(); $id = 2; $post2 = $command->queryOne(); 十二、执行非查询语句 Yii::$app->db->createCommand('UPDATE post SET status=1 WHERE id=1') ->execute(); 十三、对于 INSERT, UPDATE 和 DELETE 语句, 不再需要写纯SQL语句了, 你可以直接调用 insert()、 update()、 delete(), 来构建相应的 SQL 语句。 Yii::$app->db->createCommand()->insert('user', [ 'name' => 'Sam', 'age' => 30, ])->execute(); // UPDATE (table name, column values, condition) Yii::$app->db->createCommand()->update('user', ['status' => 1], 'age > 30')->execute(); // DELETE (table name, condition) Yii::$app->db->createCommand()->delete('user', 'status = 0')->execute(); 十四、batchInsert() 来一次插入多行, // table name, column names, column values Yii::$app->db->createCommand()->batchInsert('user', ['name', 'age'], [ ['Tom', 30], ['Jane', 20], ['Linda', 25], ])->execute(); 十五、引用表和列名称 [[column name]]: 使用两对方括号来将列名括起来; {{table name}}: 使用两对大括号来将表名括起来。 // 在 MySQL 中执行该 SQL : SELECT COUNT(`id`) FROM `employee` $count = Yii::$app->db->createCommand("SELECT COUNT([[id]]) FROM {{employee}}") ->queryScalar(); 十六、使用表前缀 1.表前缀设置: 'db' => [ // ... 'tablePrefix' => 'tbl_', ], 2.例子 // 在 MySQL 中执行该 SQL: SELECT COUNT(`id`) FROM `tbl_employee` $count = Yii::$app->db->createCommand("SELECT COUNT([[id]]) FROM {{%employee}}") ->queryScalar(); 十七、执行事务 $db = Yii::$app->db; $transaction = $db->beginTransaction(); try { $db->createCommand($sql1)->execute(); $db->createCommand($sql2)->execute(); // ... executing other SQL statements ... $transaction->commit(); } catch(\Exception $e) { $transaction->rollBack(); throw $e; } 十八、指定事务隔离级别 yii\db\Transaction::READ_UNCOMMITTED - 最弱的隔离级别,脏读、不可重复读以及幻读都可能发生。 yii\db\Transaction::READ_COMMITTED - 避免了脏读。 yii\db\Transaction::REPEATABLE_READ - 避免了脏读和不可重复读。 yii\db\Transaction::SERIALIZABLE - 最强的隔离级别, 避免了上述所有的问题。 $isolationLevel = \yii\db\Transaction::REPEATABLE_READ; Yii::$app->db->transaction(function ($db) { .... }, $isolationLevel); // or alternatively $transaction = Yii::$app->db->beginTransaction($isolationLevel); 十九、嵌套事务 1.方式1 Yii::$app->db->transaction(function ($db) { // outer transaction $db->transaction(function ($db) { // inner transaction }); }); 2.方式2 $db = Yii::$app->db; $outerTransaction = $db->beginTransaction(); try { $db->createCommand($sql1)->execute(); $innerTransaction = $db->beginTransaction(); try { $db->createCommand($sql2)->execute(); $innerTransaction->commit(); } catch (\Exception $e) { $innerTransaction->rollBack(); throw $e; } $outerTransaction->commit(); } catch (\Exception $e) { $outerTransaction->rollBack(); throw $e; } 二十、操纵数据库模式 createTable():创建一张表 renameTable():重命名一张表 dropTable():删除一张表 truncateTable():删除一张表中的所有行 addColumn():增加一列 renameColumn():重命名一列 dropColumn():删除一列 alterColumn():修改一列 addPrimaryKey():增加主键 dropPrimaryKey():删除主键 addForeignKey():增加一个外键 dropForeignKey():删除一个外键 createIndex():增加一个索引 dropIndex():删除一个索引 例如 // CREATE TABLE Yii::$app->db->createCommand()->createTable('post', [ 'id' => 'pk', 'title' => 'string', 'text' => 'text', ]); 二十一、查询生成器 1.如下所示代码是查询构造器的一个典型用法: $rows = (new \yii\db\Query()) ->select(['id', 'email']) ->from('user') ->where(['last_name' => 'Smith']) ->limit(10) ->all(); 2.select()方法 $query->select(['user.id AS user_id', 'email']); // 等同于: $query->select('user.id AS user_id, email'); 3.yii2.0支持子查询 $subQuery = (new Query())->select('COUNT(*)')->from('user'); // SELECT `id`, (SELECT COUNT(*) FROM `user`) AS `count` FROM `post` $query = (new Query())->select(['id', 'count' => $subQuery])->from('post'); 4.distinct()方法 $query->select('user_id')->distinct(); 5.addSelect() $query->select(['id', 'username']) ->addSelect(['email']); 6.子查询中再次查询 $subQuery = (new Query())->select('id')->from('user')->where('status=1'); // SELECT * FROM (SELECT `id` FROM `user` WHERE status=1) u $query->from(['u' => $subQuery]); 7.where() 7.1字符串格式 $query->where('status=1'); // or use parameter binding to bind dynamic parameter values $query->where('status=:status', [':status' => $status]); // raw SQL using MySQL YEAR() function on a date field $query->where('YEAR(somedate) = 2015'); $query->where('status=:status') ->addParams([':status' => $status]); 7.2哈希格式 // ...WHERE (`status` = 10) AND (`type` IS NULL) AND (`id` IN (4, 8, 15)) $query->where([ 'status' => 10, 'type' => null, 'id' => [4, 8, 15], ]); 7.3操作符格式 and: 操作数会被 AND 关键字串联起来。例如,['and', 'id=1', 'id=2'] 将会生成 id=1 AND id=2。如果操作数是一个数组,它也会按上述规则转换成 字符串。例如,['and', 'type=1', ['or', 'id=1', 'id=2']] 将会生成 type=1 AND (id=1 OR id=2)。 这个方法不会自动加引号或者转义。 in: 第一个操作数应为字段名称或者 DB 表达式。第二个操作符既可以是一个数组, 也可以是一个 Query 对象。它会转换成IN 条件语句。如果第二个操作数是一个 数组,那么它代表的是字段或 DB 表达式的取值范围。如果第二个操作数是 Query 对象,那么这个子查询的结果集将会作为第一个操作符的字段或者 DB 表达式的取值范围。 例如, ['in', 'id', [1, 2, 3]] 将生成 id IN (1, 2, 3)。 该方法将正确地为字段名加引号以及为取值范围转义。in 操作符还支持组合字段,此时, 操作数1应该是一个字段名数组,而操作数2应该是一个数组或者 Query 对象, 代表这些字段的取值范围。 like: 第一个操作数应为一个字段名称或 DB 表达式, 第二个操作数可以使字符串或数组, 代表第一个操作数需要模糊查询的值。比如,['like', 'name', 'tester'] 会生成 name LIKE '%tester%'。 如果范围值是一个数组,那么将会生成用 AND 串联起来的 多个 like 语句。例如,['like', 'name', ['test', 'sample']] 将会生成 name LIKE '%test%' AND name LIKE '%sample%'。 你也可以提供第三个可选的操作数来指定应该如何转义数值当中的特殊字符。 该操作数是一个从需要被转义的特殊字符到转义副本的数组映射。 如果没有提供这个操作数,将会使用默认的转义映射。如果需要禁用转义的功能, 只需要将参数设置为 false 或者传入一个空数组即可。需要注意的是, 当使用转义映射(又或者没有提供第三个操作数的时候),第二个操作数的值的前后 将会被加上百分号。 or like: 用法和 like 操作符类似,区别在于当第二个操作数为数组时, 会使用 OR 来串联多个 LIKE 条件语句。 >, <=, 或者其他包含两个操作数的合法 DB 操作符: 第一个操作数必须为字段的名称, 而第二个操作数则应为一个值。例如,['>', 'age', 10] 将会生成 age>10。 8.附加条件 8.1andWhere() $status = 10; $search = 'yii'; $query->where(['status' => $status]); if (!empty($search)) { $query->andWhere(['like', 'title', $search]); } 9、过滤条件 当 WHERE 条件来自于用户的输入时,你通常需要忽略用户输入的空值。 例如,在一个可以通过用户名或者邮箱搜索的表单当中,用户名或者邮箱 输入框没有输入任何东西,这种情况下你想要忽略掉对应的搜索条件, 那么你就可以使用 yii\db\Query::filterWhere() 方法来实现这个目的: // $username 和 $email 来自于用户的输入 $query->filterWhere([ 'username' => $username, 'email' => $email, ]); 10、排序 // ... ORDER BY `id` ASC, `name` DESC $query->orderBy([ 'id' => SORT_ASC, 'name' => SORT_DESC, ]); 添加额外排序 $query->orderBy('id ASC') ->addOrderBy('name DESC'); 11、groupBy() $query->groupBy('id, status'); $query->groupBy(['id', 'status']) ->addGroupBy('age'); 12、having() $query->having(['status' => 1]); $query->having(['status' => 1]) ->andHaving(['>', 'age', 30]); 13、limit()和offset() $query->limit(10)->offset(20); 14、join() // ... LEFT JOIN `post` ON `post`.`user_id` = `user`.`id` $query->join('LEFT JOIN', 'post', 'post.user_id = user.id'); 你可以分别调用如下的快捷方法来指定 INNER JOIN, LEFT JOIN 和 RIGHT JOIN。 innerJoin() leftJoin() rightJoin() $query->leftJoin('post', 'post.user_id = user.id'); 15、连接子查询 $subQuery = (new \yii\db\Query())->from('post'); $query->leftJoin(['u' => $subQuery], 'u.id = author_id'); 16、union() $query1 = (new \yii\db\Query()) ->select("id, category_id AS type, name") ->from('post') ->limit(10); $query2 = (new \yii\db\Query()) ->select('id, type, name') ->from('user') ->limit(10); $query1->union($query2); 注:你可以通过多次调用 union() 方法来追加更多的 UNION 子句。 17、 count(),sum($q), average($q), max($q), min($q) 18、all()和one() // SELECT `id`, `email` FROM `user` $rows = (new \yii\db\Query()) ->select(['id', 'email']) ->from('user') ->all(); // SELECT * FROM `user` WHERE `username` LIKE `%test%` $row = (new \yii\db\Query()) ->from('user') ->where(['like', 'username', 'test']) ->one(); 注:one()返回一条数据 19、有时候,你也许想要测试或者使用一个由 yii\db\Query 对象创建的 SQL 语句。 你可以使用以下的代码来达到目的: $command = (new \yii\db\Query()) ->select(['id', 'email']) ->from('user') ->where(['last_name' => 'Smith']) ->limit(10) ->createCommand(); // 打印 SQL 语句 echo $command->sql; // 打印被绑定的参数 print_r($command->params); // 返回查询结果的所有行 $rows = $command->queryAll(); 20、索引查询结果 // 返回 [100 => ['id' => 100, 'username' => '...', ...], 101 => [...], 103 => [...], ...] $query = (new \yii\db\Query()) ->from('user') ->limit(10) ->indexBy('id') ->all(); 如需使用表达式的值做为索引,那么只需要传递一个匿名函数给 yii\db\Query::indexBy() 方法即可: $query = (new \yii\db\Query()) ->from('user') ->indexBy(function ($row) { return $row['id'] . $row['username']; })->all(); 21、批处理查询 $query = (new \yii\db\Query()) ->from('user') ->indexBy('username'); foreach ($query->batch() as $users) { // $users 的 “username” 字段将会成为索引 } foreach ($query->each() as $username => $user) { } 注:batch一次取100条数据,each数据表中的一行数据 二十二、活动记录 1.声明 AR 类 namespace app\models; use yii\db\ActiveRecord; class Customer extends ActiveRecord { /** * @return string 返回该AR类关联的数据表名 */ public static function tableName() { return 'customer'; } } 2.指定数据库 public static function getDb() { return \Yii::$app->db2; // 使用名为 "db2" 的应用组件 } 3.查询数据 // 取回所有活跃客户(状态为 *active* 的客户)并以他们的 ID 排序: $customers = Customer::find() ->where(['status' => Customer::STATUS_ACTIVE]) ->orderBy('id') ->all(); // 返回ID为1的客户: $customer = Customer::find() ->where(['id' => 1]) ->one(); // 取回活跃客户的数量: $count = Customer::find() ->where(['status' => Customer::STATUS_ACTIVE]) ->count(); // 以客户ID索引结果集: $customers = Customer::find()->indexBy('id')->all(); // $customers 数组以 ID 为索引 // 用原生 SQL 语句检索客户: $sql = 'SELECT * FROM customer'; $customers = Customer::findBySql($sql)->all(); 3.1findOne 和 findAll()(前者返回第一个匹配到的实例,后者返回所有) // 返回 id 为 1 的客户 $customer = Customer::findOne(1); // 返回 id 为 1 且状态为 *active* 的客户 $customer = Customer::findOne([ 'id' => 1, 'status' => Customer::STATUS_ACTIVE, ]); // 返回id为1、2、3的一组客户 $customers = Customer::findAll([1, 2, 3]); // 返回所有状态为 "deleted" 的客户 $customer = Customer::findAll([ 'status' => Customer::STATUS_DELETED, ]); 3.2以数组形式获取数据 // 以数组而不是对象形式取回客户信息: $customers = Customer::find() ->asArray() ->all(); // $customers 的每个元素都是键值对数组 4.批量获取数据 // 一次提取 10 个客户信息 foreach (Customer::find()->batch(10) as $customers) { // $customers 是 10 个或更少的客户对象的数组 } // 一次提取 10 个客户并一个一个地遍历处理 foreach (Customer::find()->each(10) as $customer) { // $customer 是一个 ”Customer“ 对象 } // 贪婪加载模式的批处理查询 foreach (Customer::find()->with('orders')->each() as $customer) { } 5.操作数据(insert()、update()、delete()、insertAll()、updateAllCounts()、deleteAll()) // 插入新客户的记录 $customer = new Customer(); $customer->name = 'James'; $customer->email = 'james@example.com'; $customer->save(); // 等同于 $customer->insert(); // 更新现有客户记录 $customer = Customer::findOne($id); $customer->email = 'james@example.com'; $customer->save(); // 等同于 $customer->update(); // 删除已有客户记录 $customer = Customer::findOne($id); $customer->delete(); // 删除多个年龄大于20,性别为男(Male)的客户记录 Customer::deleteAll('age > :age AND gender = :gender', [':age' => 20, ':gender' => 'M']); // 所有客户的age(年龄)字段加1: Customer::updateAllCounters(['age' => 1]); 5.数据输入与有效性验证 // 新建一条记录 $model = new Customer; if ($model->load(Yii::$app->request->post()) && $model->save()) { // 获取用户输入的数据,验证并保存 } // 更新主键为$id的AR $model = Customer::findOne($id); if ($model === null) { throw new NotFoundHttpException; } if ($model->load(Yii::$app->request->post()) && $model->save()) { // 获取用户输入的数据,验证并保存 } 6.读取默认值 $customer = new Customer(); $customer->loadDefaultValues(); // ... 渲染 $customer 的 HTML 表单 ... 7.查询关联的数据 class Customer extends \yii\db\ActiveRecord { public function getOrders() { // 客户和订单通过 Order.customer_id -> id 关联建立一对多关系 return $this->hasMany(Order::className(), ['customer_id' => 'id']); } } class Order extends \yii\db\ActiveRecord { // 订单和客户通过 Customer.id -> customer_id 关联建立一对一关系 public function getCustomer() { return $this->hasOne(Customer::className(), ['id' => 'customer_id']); } } 建立关联关系后,获取关联数据和获取组件属性一样简单, 执行以下相应getter方法即可: // 取得客户的订单 $customer = Customer::findOne(1); $orders = $customer->orders; // $orders 是 Order 对象数组 有时候需要在关联查询中传递参数,如不需要返回客户全部订单, 只需要返回购买金额超过设定值的大订单, 通过以下getter方法声明一个关联数据 bigOrders : class Customer extends \yii\db\ActiveRecord { public function getBigOrders($threshold = 100) { return $this->hasMany(Order::className(), ['customer_id' => 'id']) ->where('subtotal > :threshold', [':threshold' => $threshold]) ->orderBy('id'); } } $orders = $customer->getBigOrders(200)->all(); 8.中间关联表 class Order extends \yii\db\ActiveRecord { public function getItems() { return $this->hasMany(Item::className(), ['id' => 'item_id']) ->viaTable('order_item', ['order_id' => 'id']); } } 以上方法取代了中间表,等价于: class Order extends \yii\db\ActiveRecord { public function getOrderItems() { return $this->hasMany(OrderItem::className(), ['order_id' => 'id']); } public function getItems() { return $this->hasMany(Item::className(), ['id' => 'item_id']) ->via('orderItems'); } } 9.延迟加载和即时加载(又称惰性加载与贪婪加载) $customers = Customer::find()->limit(100) ->with('orders')->all(); 注意:当用即时加载定制 select() 时,确保连接 到关联模型的列都被包括了,否则,关联模型不会载入。如: $orders = Order::find()->select(['id', 'amount'])->with('customer')->all(); // $orders[0]->customer 总是空的,使用以下代码解决这个问题: $orders = Order::find()->select(['id', 'amount', 'customer_id'])->with('customer')->all(); 有时候,你想自由的自定义关联查询,延迟加载和即时加载都可以实现,如: $customer = Customer::findOne(1); // 延迟加载: SELECT * FROM order WHERE customer_id=1 AND subtotal>100 $orders = $customer->getOrders()->where('subtotal>100')->all(); // 即时加载: SELECT * FROM customer LIMIT 100 // SELECT * FROM order WHERE customer_id IN (1,2,...) AND subtotal>100 $customers = Customer::find()->limit(100)->with([ 'orders' => function($query) { $query->andWhere('subtotal>100'); }, ])->all(); 10逆关系 class Customer extends ActiveRecord { .... public function getOrders() { return $this->hasMany(Order::className(), ['customer_id' => 'id']); } } class Order extends ActiveRecord { .... public function getCustomer() { return $this->hasOne(Customer::className(), ['id' => 'customer_id']); } } 为避免多余执行的后一条语句,我们可以为 customer或 orders 关联关系定义相反的关联关系,通过调用 yii\db\ActiveQuery::inverseOf() 方法可以实现。 class Customer extends ActiveRecord { .... public function getOrders() { return $this->hasMany(Order::className(), ['customer_id' => 'id'])->inverseOf('customer'); } } 相对关系也可以用在即时加载中: // SELECT * FROM customer // SELECT * FROM order WHERE customer_id IN (1, 2, ...) $customers = Customer::find()->with('orders')->all(); // 输出相同 if ($customers[0]->orders[0]->customer === $customers[0]) { echo '相同'; } else { echo '不相同'; } 11、JOIN 类型关联查询 // 查找所有订单并以客户 ID 和订单 ID 排序,并贪婪加载 "customer" 表 $orders = Order::find()->joinWith('customer')->orderBy('customer.id, order.id')->all(); // 查找包括书籍的所有订单,并以 `INNER JOIN` 的连接方式即时加载 "books" 表 $orders = Order::find()->innerJoinWith('books')->all(); 可以连接一个或多个关联关系,可以自由使用查询条件到关联查询, 也可以嵌套连接关联查询。如: // 连接多重关系 // 找出24小时内注册客户包含书籍的订单 $orders = Order::find()->innerJoinWith([ 'books', 'customer' => function ($query) { $query->where('customer.created_at > ' . (time() - 24 * 3600)); } ])->all(); // 连接嵌套关系:连接 books 表及其 author 列 $orders = Order::find()->joinWith('books.author')->all(); 有时连接两个表时,需要在关联查询的 ON 部分指定额外条件。 这可以通过调用 yii\db\ActiveQuery::onCondition() 方法实现: class User extends ActiveRecord { public function getBooks() { return $this->hasMany(Item::className(), ['owner_id' => 'id'])->onCondition(['category_id' => 1]); } } 注意:如果通过 yii\db\ActiveQuery::with() 进行贪婪加载或使用惰性加载的话,则 on 条件会被放置在对应 SQL语句的 WHERE 部分。 因为,此时此处并没有发生 JOIN 查询。比如: // SELECT * FROM user WHERE id=10 $user = User::findOne(10); // SELECT * FROM item WHERE owner_id=10 AND category_id=1 $books = $user->books; 12、关联表操作 $customer = Customer::findOne(1); $order = new Order(); $order->subtotal = 100; $customer->link('orders', $order); 13、作用域 namespace app\models; use yii\db\ActiveQuery; class CommentQuery extends ActiveQuery { public function active($state = true) { $this->andWhere(['active' => $state]); return $this; } } 重点: 类必须继承 yii\db\ActiveQuery (或者是其他的 ActiveQuery ,比如 yii\mongodb\ActiveQuery)。 必须是一个public类型的方法且必须返回 $this 实现链式操作。可以传入参数。 检查 yii\db\ActiveQuery 对于修改查询条件是非常有用的方法。 其次,覆盖yii\db\ActiveRecord::find() 方法使其返回自定义的查询对象而不是常规的ActiveQuery。对于上述例子,你需要编写如下代码: namespace app\models; use yii\db\ActiveRecord; class Comment extends ActiveRecord { /** * @inheritdoc * @return CommentQuery */ public static function find() { return new CommentQuery(get_called_class()); } } 就这样,现在你可以使用自定义的作用域方法了: $comments = Comment::find()->active()->all(); $inactiveComments = Comment::find()->active(false)->all(); 你也能在定义的关联里使用作用域方法,比如: class Post extends \yii\db\ActiveRecord { public function getActiveComments() { return $this->hasMany(Comment::className(), ['post_id' => 'id'])->active(); } } 或者在执行关联查询的时候使用(on-the-fly 是啥?): $posts = Post::find()->with([ 'comments' => function($q) { $q->active(); } ])->all(); 14、事务操作 class Feature extends \yii\db\ActiveRecord { // ... public function getProduct() { return $this->hasOne(Product::className(), ['id' => 'product_id']); } } class Product extends \yii\db\ActiveRecord { // ... public function getFeatures() { return $this->hasMany(Feature::className(), ['product_id' => 'id']); } } 重写 yii\db\ActiveRecord::save() 方法: class ProductController extends \yii\web\Controller { public function actionCreate() { // FIXME: TODO: WIP, TBD } } (译注:我觉得上面应该是原手册里的bug) 在控制器层使用事务: class ProductController extends \yii\web\Controller { public function actionCreate() { // FIXME: TODO: WIP, TBD } } 作为这些脆弱方法的替代,你应该使用原子操作方案特性。 class Feature extends \yii\db\ActiveRecord { // ... public function getProduct() { return $this->hasOne(Product::className(), ['product_id' => 'id']); } public function scenarios() { return [ 'userCreates' => [ 'attributes' => ['name', 'value'], 'atomic' => [self::OP_INSERT], ], ]; } } class Product extends \yii\db\ActiveRecord { // ... public function getFeatures() { return $this->hasMany(Feature::className(), ['id' => 'product_id']); } public function scenarios() { return [ 'userCreates' => [ 'attributes' => ['title', 'price'], 'atomic' => [self::OP_INSERT], ], ]; } public function afterValidate() { parent::afterValidate(); // FIXME: TODO: WIP, TBD } public function afterSave($insert) { parent::afterSave($insert); if ($this->getScenario() === 'userCreates') { // FIXME: TODO: WIP, TBD } } } Controller里的代码将变得很简洁: class ProductController extends \yii\web\Controller { public function actionCreate() { // FIXME: TODO: WIP, TBD } } 控制器非常简洁: class ProductController extends \yii\web\Controller { public function actionCreate() { // FIXME: TODO: WIP, TBD } }