mirror of
				https://github.com/yiisoft/yii2.git
				synced 2025-11-01 03:26:36 +08:00 
			
		
		
		
	Merge pull request #19893 from PowerGamer1/fix12743
New methods: BaseActiveRecord::loadRelations() and BaseActiveRecord::loadRelationsFor().
This commit is contained in:
		| @ -4,7 +4,7 @@ Yii Framework 2 Change Log | ||||
| 2.0.50 under development | ||||
| ------------------------ | ||||
|  | ||||
| - no changes in this release. | ||||
| - Enh #12743: Added new methods `BaseActiveRecord::loadRelations()` and `BaseActiveRecord::loadRelationsFor()` to eager load related models for existing primary model instances (PowerGamer1) | ||||
|  | ||||
|  | ||||
| 2.0.49.2 October 12, 2023 | ||||
|  | ||||
| @ -1787,4 +1787,57 @@ abstract class BaseActiveRecord extends Model implements ActiveRecordInterface | ||||
|  | ||||
|         return $newValue !== $oldValue; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Eager loads related models for the already loaded primary models. | ||||
|      * | ||||
|      * Helps to reduce the number of queries performed against database if some related models are only used | ||||
|      * when a specific condition is met. For example: | ||||
|      * | ||||
|      * ```php | ||||
|      * $customers = Customer::find()->where(['country_id' => 123])->all(); | ||||
|      * if (Yii:app()->getUser()->getIdentity()->canAccessOrders()) { | ||||
|      *     Customer::loadRelationsFor($customers, 'orders.items'); | ||||
|      * } | ||||
|      * ``` | ||||
|      * | ||||
|      * @param array|ActiveRecordInterface[] $models array of primary models. Each model should have the same type and can be: | ||||
|      * - an active record instance; | ||||
|      * - active record instance represented by array (i.e. active record was loaded using [[ActiveQuery::asArray()]]). | ||||
|      * @param string|array $relationNames the names of the relations of primary models to be loaded from database. See [[ActiveQueryInterface::with()]] on how to specify this argument. | ||||
|      * @param bool $asArray whether to load each related model as an array or an object (if the relation itself does not specify that). | ||||
|      * @since 2.0.49 | ||||
|      */ | ||||
|     public static function loadRelationsFor(&$models, $relationNames, $asArray = false) | ||||
|     { | ||||
|         // ActiveQueryTrait::findWith() called below assumes $models array is non-empty. | ||||
|         if (empty($models)) { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         static::find()->asArray($asArray)->findWith((array)$relationNames, $models); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Eager loads related models for the already loaded primary model. | ||||
|      * | ||||
|      * Helps to reduce the number of queries performed against database if some related models are only used | ||||
|      * when a specific condition is met. For example: | ||||
|      * | ||||
|      * ```php | ||||
|      * $customer = Customer::find()->where(['id' => 123])->one(); | ||||
|      * if (Yii:app()->getUser()->getIdentity()->canAccessOrders()) { | ||||
|      *     $customer->loadRelations('orders.items'); | ||||
|      * } | ||||
|      * ``` | ||||
|      * | ||||
|      * @param string|array $relationNames the names of the relations of this model to be loaded from database. See [[ActiveQueryInterface::with()]] on how to specify this argument. | ||||
|      * @param bool $asArray whether to load each relation as an array or an object (if the relation itself does not specify that). | ||||
|      * @since 2.0.49 | ||||
|      */ | ||||
|     public function loadRelations($relationNames, $asArray = false) | ||||
|     { | ||||
|         $models = [$this]; | ||||
|         static::loadRelationsFor($models, $relationNames, $asArray); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -2225,6 +2225,53 @@ abstract class ActiveRecordTest extends DatabaseTestCase | ||||
|         $attr = 'model2.doesNotExist.attr1'; | ||||
|         $this->assertEquals($model->generateAttributeLabel($attr), $model->getAttributeLabel($attr)); | ||||
|     } | ||||
|  | ||||
|     public function testLoadRelations() | ||||
|     { | ||||
|         // Test eager loading relations for multiple primary models using loadRelationsFor(). | ||||
|         /** @var Customer[] $customers */ | ||||
|         $customers = Customer::find()->all(); | ||||
|         Customer::loadRelationsFor($customers, ['orders.items']); | ||||
|         foreach ($customers as $customer) { | ||||
|             $this->assertTrue($customer->isRelationPopulated('orders')); | ||||
|             foreach ($customer->orders as $order) { | ||||
|                 $this->assertTrue($order->isRelationPopulated('items')); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // Test eager loading relations as arrays. | ||||
|         /** @var array $customers */ | ||||
|         $customers = Customer::find()->asArray(true)->all(); | ||||
|         Customer::loadRelationsFor($customers, ['orders.items' => function ($query) { $query->asArray(false); }], true); | ||||
|         foreach ($customers as $customer) { | ||||
|             $this->assertTrue(isset($customer['orders'])); | ||||
|             $this->assertTrue(is_array($customer['orders'])); | ||||
|             foreach ($customer['orders'] as $order) { | ||||
|                 $this->assertTrue(is_array($order)); | ||||
|                 $this->assertTrue(isset($order['items'])); | ||||
|                 $this->assertTrue(is_array($order['items'])); | ||||
|                 foreach ($order['items'] as $item) { | ||||
|                     $this->assertFalse(is_array($item)); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // Test eager loading relations for a single primary model using loadRelations(). | ||||
|         /** @var Customer $customer */ | ||||
|         $customer = Customer::find()->where(['id' => 1])->one(); | ||||
|         $customer->loadRelations('orders.items'); | ||||
|         $this->assertTrue($customer->isRelationPopulated('orders')); | ||||
|         foreach ($customer->orders as $order) { | ||||
|             $this->assertTrue($order->isRelationPopulated('items')); | ||||
|         } | ||||
|  | ||||
|         // Test eager loading previously loaded relation (relation value should be replaced with a new value loaded from database). | ||||
|         /** @var Customer $customer */ | ||||
|         $customer = Customer::find()->where(['id' => 2])->with(['orders' => function ($query) { $query->orderBy(['id' => SORT_ASC]); }])->one(); | ||||
|         $this->assertTrue($customer->orders[0]->id < $customer->orders[1]->id, 'Related models should be sorted by ID in ascending order.'); | ||||
|         $customer->loadRelations(['orders' => function ($query) { $query->orderBy(['id' => SORT_DESC]); }]); | ||||
|         $this->assertTrue($customer->orders[0]->id > $customer->orders[1]->id, 'Related models should be sorted by ID in descending order.'); | ||||
|     } | ||||
| } | ||||
|  | ||||
| class LabelTestModel1 extends \yii\db\ActiveRecord | ||||
|  | ||||
		Reference in New Issue
	
	Block a user
	 Bizley
					Bizley