本文最后更新于 2020年8月21日 下午
Yii中活动记录(Active Record)类的使用 查询数据 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 $customer = Customer ::find ()->where (['id' => 123 ])->one ();$customer = Customer ::findOne (123 );$customer = Customer ::findOne (['id' => 123 ]);$customers = Customer ::find () ->where (['status' => Customer ::STATUS_ACTIVE ]) ->orderBy ('id' ) ->all ();$customers = Customer ::findAll ([100 , 101 , 123 , 124 ]);$customer = Customer ::findOne ([ 'id' => 123 , 'status' => Customer ::STATUS_ACTIVE , ]);$customers = Customer ::findAll ([ 'status' => Customer ::STATUS_INACTIVE , ]);$count = Customer ::find () ->where (['status' => Customer ::STATUS_ACTIVE ]) ->count ();$customers = Customer ::find () ->indexBy ('id' ) ->all ();public function actionView ($id ) { $model = Post ::findOne ($id ); }$model = Post ::findOne (['id' => Yii ::$app ->request->get ('id' )]);$model = Post ::findOne (Yii ::$app ->request->get ('id' ));
数据转换 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class Customer extends ActiveRecord { public function getBirthdayText ( ) { return date ('Y/m/d' , $this ->birthday); } public function setBirthdayText ($value ) { $this ->birthday = strtotime ($value ); } }
以数组形式获取数据 1 2 3 4 5 6 7 $customers = Customer ::find () ->asArray () ->all ();
批量获取数据 1 2 3 4 5 6 7 8 9 10 11 12 13 14 foreach (Customer ::find ()->batch (10 ) as $customers ) { }foreach (Customer ::find ()->each (10 ) as $customer ) { }foreach (Customer ::find ()->with ('orders' )->each () as $customer ) { }
保存数据 1 2 3 4 5 6 7 8 9 10 11 12 $customer = new Customer ();$customer ->name = 'James' ;$customer ->email = '[email protected] ' ;$customer ->save ();$customer = Customer ::findOne (123 );$customer ->email = '[email protected] ' ;$customer ->save ();
数据验证 因为 yii\db\ActiveRecord 继承于 yii\base\Model,它共享相同的 输入验证 功能。 你可以通过重写 rules() 方法声明验证规则并执行, 通过调用 validate() 方法进行数据验证。
当你调用 save() 时,默认情况下会自动调用 validate()。 只有当验证通过时,它才会 真正地保存数据; 否则将简单地返回 false, 您可以检查 errors 属性来获取验证过程的错误消息。
提示: 如果你确定你的数据不需要验证(比如说数据来自可信的场景), 你可以调用 save(false) 来跳过验证过程。
快赋值 1 2 3 4 5 6 7 8 9 10 11 $values = [ 'name' => 'James' , 'email' => '[email protected] ' , ];$customer = new Customer ();$customer ->attributes = $values ;$customer ->save ();
更新计数 1 2 3 4 5 6 7 8 9 10 $post = Post ::findOne (100 );$post ->updateCounters (['view_count' => 1 ]);Customer ::updateAllCounters (['age' => 1 ]);
更新多个数据行 1 2 Customer ::updateAll (['status' => Customer ::STATUS_ACTIVE ], ['like' , 'email' , '@example.com' ]);
删除数据 1 2 3 4 5 6 7 8 $customer = Customer ::findOne (123 );$customer ->delete ();Customer ::deleteAll (['status' => Customer ::STATUS_INACTIVE ]);
事务操作 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 $customer = Customer ::findOne (123 );Customer ::getDb ()->transaction (function($db ) use ($customer ) { $customer ->id = 200; $customer ->save (); }); $transaction = Customer ::getDb ()->beginTransaction ();try { $customer ->id = 200 ; $customer ->save (); $transaction ->commit (); } catch (\Exception $e ) { $transaction ->rollBack (); throw $e ; } catch (\Throwable $e ) { $transaction ->rollBack (); throw $e ; }
乐观锁 乐观锁是一种防止此冲突的方法:一行数据 同时被多个用户更新。例如,同一时间内,用户 A 和用户 B 都在编辑 相同的 wiki 文章。用户 A 保存他的编辑后,用户 B 也点击“保存”按钮来 保存他的编辑。实际上,用户 B 正在处理的是过时版本的文章, 因此最好是,想办法阻止他保存文章并向他提示一些信息。
乐观锁通过使用一个字段来记录每行的版本号来解决上述问题。 当使用过时的版本号保存一行数据时,yii\db\StaleObjectException 异常 将被抛出,这阻止了该行的保存。乐观锁只支持更新 yii\db\ActiveRecord::update() 或者删除 yii\db\ActiveRecord::delete() 已经存在的单条数据行。
使用乐观锁的步骤,
1.在与 Active Record 类相关联的 DB 表中创建一个列,以存储每行的版本号。 这个列应当是长整型(在 MySQL 中是 BIGINT DEFAULT 0)。
2.重写 yii\db\ActiveRecord::optimisticLock() 方法返回这个列的命名。
3.在你的 Model 类里实现 OptimisticLockBehavior 行为(注:这个行为类在 2.0.16 版本加入),以便从请求参数里自动解析这个列的值。 然后从验证规则中删除 version 属性,因为 OptimisticLockBehavior 已经处理它了.
4.在用于用户填写的 Web 表单中,添加一个隐藏字段(hidden field)来存储正在更新的行的当前版本号。
5.在使用 Active Record 更新数据的控制器动作中,要捕获(try/catch) yii\db\StaleObjectException 异常。 实现一些业务逻辑来解决冲突(例如合并更改,提示陈旧的数据等等)。
例如,假定版本列被命名为 version。您可以使用下面的代码来实现乐观锁。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 use yii \helpers \Html ;echo Html ::activeHiddenInput ($model , 'version' );use yii \db \StaleObjectException ;public function actionUpdate ($id ) { $model = $this ->findModel ($id ); try { if ($model ->load (Yii ::$app ->request->post ()) && $model ->save ()) { return $this ->redirect (['view' , 'id' => $model ->id]); } else { return $this ->render ('update' , [ 'model' => $model , ]); } } catch (StaleObjectException $e ) { } }use yii \behaviors \OptimisticLockBehavior ;public function behaviors ( ) { return [ OptimisticLockBehavior ::className (), ]; }
使用关联数据 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 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 = Customer ::findOne (123 );$orders = $customer ->orders;$customer ->orders; $customer ->getOrders ();
动态关联查询 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 $customer = Customer ::findOne (123 );$orders = $customer ->getOrders () ->where (['>' , 'subtotal' , 200 ]) ->orderBy ('id' ) ->all ();class Customer extends ActiveRecord { public function getBigOrders ($threshold = 100 ) // 老司机的提醒机的提醒threshold 参数一定一定要给个默认值一定要给个默认值 { return $this ->hasMany (Order ::className (), ['customer_id' => 'id' ]) ->where ('subtotal > :threshold' , [':threshold' => $threshold ]) ->orderBy ('id' ); } }$orders = $customer ->getBigOrders (200 )->all ();$orders = $customer ->bigOrders;
中间表关联 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 class Order extends ActiveRecord { public function getItems ( ) { return $this ->hasMany (Item ::className (), ['id' => 'item_id' ]) ->viaTable ('order_item' , ['order_id' => 'id' ]); } }class Order extends 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' ); } }$order = Order ::findOne (100 );$items = $order ->items;
通过多个表来连接关联声明 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 class Customer extends ActiveRecord { public function getPurchasedItems ( ) { return $this ->hasMany (Item ::className (), ['id' => 'item_id' ]) ->via ('orderItems' ); } public function getOrderItems ( ) { return $this ->hasMany (OrderItem ::className (), ['order_id' => 'id' ]) ->via ('orders' ); } public function getOrders ( ) { return $this ->hasMany (Order ::className (), ['customer_id' => 'id' ]); } }
延迟加载和即时加载 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 $customer = Customer ::findOne (123 );$orders = $customer ->orders;$orders2 = $customer ->orders;$customers = Customer ::find ()->limit (100 )->all ();foreach ($customers as $customer ) { $orders = $customer ->orders; }$customers = Customer ::find () ->with ('orders' ) ->limit (100 ) ->all ();foreach ($customers as $customer ) { $orders = $customer ->orders; }$customers = Customer ::find ()->with ('orders' , 'country' )->all ();$customers = Customer ::find ()->with (['orders' , 'country' ])->all ();$orders = $customers [0 ]->orders;$country = $customers [0 ]->country;$customers = Customer ::find ()->with ('orders.items' )->all ();$items = $customers [0 ]->orders[0 ]->items;$customers = Customer ::find ()->with ([ 'country' , 'orders' => function ($query ) { $query ->andWhere (['status' => Order ::STATUS_ACTIVE ]); }, ])->all ();$orders = Order ::find ()->select (['id' , 'amount' ])->with ('customer' )->all ();$orders = Order ::find ()->select (['id' , 'amount' , 'customer_id' ])->with ('cus
关联关系的 JOIN 查询 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 $customers = Customer ::find () ->select ('customer.*' ) ->leftJoin ('order' , '`order`.`customer_id` = `customer`.`id`' ) ->where (['order.status' => Order ::STATUS_ACTIVE ]) ->with ('orders' ) ->all ();$customers = Customer ::find () ->joinWith ('orders' ) ->where (['order.status' => Order ::STATUS_ACTIVE ]) ->all ();$customers = Customer ::find ()->joinWith ([ 'orders' => function ($query ) { $query ->andWhere (['>' , 'subtotal' , 100 ]); }, ])->with ('country' ) ->all ();$customers = Customer ::find ()->joinWith ([ 'orders' => function ($query ) { $query ->onCondition (['order.status' => Order ::STATUS_ACTIVE ]); }, ])->all ();
反向关联 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 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 = Customer ::findOne (123 );$order = $customer ->orders[0 ];$customer2 = $order ->customer;echo $customer2 === $customer ? 'same' : 'not the same' ;class Customer extends ActiveRecord { public function getOrders ( ) { return $this ->hasMany (Order ::className (), ['customer_id' => 'id' ])->inverseOf ('customer' ); } }$customer = Customer ::findOne (123 );$order = $customer ->orders[0 ];$customer2 = $order ->customer;echo $customer2 === $customer ? 'same' : 'not the same' ;
保存关联数据 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 $customer = Customer ::findOne (123 );$order = new Order ();$order ->subtotal = 100 ;$order ->customer_id = $customer ->id;$order ->save (); Active Record 提供了 link () 方法,可以更好地完成此任务:$customer = Customer ::findOne (123 );$order = new Order ();$order ->subtotal = 100 ;$order ->link ('customer' , $customer );$order ->link ('items' , $item );$customer = Customer ::find ()->with ('orders' )->where (['id' => 123 ])->one ();$customer ->unlink ('orders' , $customer ->orders[0 ]);
跨数据库关联 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 class Customer extends \yii \db \ActiveRecord { public static function tableName ( ) { return 'customer' ; } public function getComments ( ) { return $this ->hasMany (Comment ::className (), ['customer_id' => 'id' ]); } }class Comment extends \yii \mongodb \ActiveRecord { public static function collectionName ( ) { return 'comment' ; } public function getCustomer ( ) { return $this ->hasOne (Customer ::className (), ['id' => 'customer_id' ]); } }$customers = Customer ::find ()->with ('comments' )->all ();
自定义查询类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 namespace app \models ;use yii \db \ActiveRecord ;class Comment extends ActiveRecord { public static function find ( ) { return new CommentQuery (get_called_class ()); } }namespace app \models ;use yii \db \ActiveQuery ;class CommentQuery extends ActiveQuery { public function init ( ) { $this ->andOnCondition (['deleted' => false ]); parent ::init (); } public function active ($state = true ) { return $this ->andOnCondition (['active' => $state ]); } }$comments = Comment ::find ()->active ()->all ();$inactiveComments = Comment ::find ()->active (false )->all ();class Customer extends \yii \db \ActiveRecord { public function getActiveComments ( ) { return $this ->hasMany (Comment ::className (), ['customer_id' => 'id' ])->active (); } }$customers = Customer ::find ()->joinWith ('activeComments' )->all ();class Customer extends \yii \db \ActiveRecord { public function getComments ( ) { return $this ->hasMany (Comment ::className (), ['customer_id' => 'id' ]); } }$customers = Customer ::find ()->joinWith ([ 'comments' => function($q ) { $q ->active (); } ])->all ();
选择额外的字段 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 class Room extends \yii \db \ActiveRecord { public $volume ; }$rooms = Room ::find () ->select ([ '{{room}}.*' , // select all columns '([[length]] * [[width]] * [[height]]) AS volume' , // 计算体积 ]) ->orderBy ('volume DESC' ) ->all ();foreach ($rooms as $room ) { echo $room ->volume; }class Customer extends \yii \db \ActiveRecord { public $ordersCount ; public function getOrders ( ) { return $this ->hasMany (Order ::className (), ['customer_id' => 'id' ]); } }$customers = Customer ::find () ->select ([ '{{customer}}.*' , // select customer 表所有的字段 'COUNT({{order}}.id) AS ordersCount' // 计算订单总数 ]) ->joinWith ('orders' ) ->groupBy ('{{customer}}.id' ) ->all ();$room = new Room ();$room ->length = 100 ;$room ->width = 50 ;$room ->height = 2 ;$room ->volume; class Room extends \yii \db \ActiveRecord { private $_volume ; public function setVolume ($volume ) { $this ->_volume = (float ) $volume ; } public function getVolume ( ) { if (empty ($this ->length) || empty ($this ->width) || empty ($this ->height)) { return null ; } if ($this ->_volume === null ) { $this ->setVolume ( $this ->length * $this ->width * $this ->height ); } return $this ->_volume; } }class Customer extends \yii \db \ActiveRecord { private $_ordersCount ; public function setOrdersCount ($count ) { $this ->_ordersCount = (int ) $count ; } public function getOrdersCount ( ) { if ($this ->isNewRecord) { return null ; } if ($this ->_ordersCount === null ) { $this ->setOrdersCount ($this ->getOrders ()->count ()); } return $this ->_ordersCount; } public function getOrders ( ) { return $this ->hasMany (Order ::className (), ['customer_id' => 'id' ]); } }class Customer extends \yii \db \ActiveRecord { public function getOrdersCount ( ) { if ($this ->isNewRecord) { return null ; } return empty ($this ->ordersAggregation) ? 0 : $this ->ordersAggregation[0 ]['counted' ]; } public function getOrders ( ) { return $this ->hasMany (Order ::className (), ['customer_id' => 'id' ]); } public function getOrdersAggregation ( ) { return $this ->getOrders () ->select (['customer_id' , 'counted' => 'count(*)' ]) ->groupBy ('customer_id' ) ->asArray (true ); } }foreach (Customer ::find ()->with ('ordersAggregation' )->all () as $customer ) { echo $customer ->ordersCount; }$customer = Customer ::findOne ($pk );$customer ->ordersCount;
———————————————— 文章来源于Yii官方文档:活动记录(Active Record)