mirror of
				https://github.com/yiisoft/yii2.git
				synced 2025-11-04 06:37:55 +08:00 
			
		
		
		
	Draft implementation of ActiveQuery::joinWith().
This commit is contained in:
		@ -143,4 +143,141 @@ class ActiveQuery extends Query implements ActiveQueryInterface
 | 
			
		||||
		}
 | 
			
		||||
		return $db->createCommand($sql, $params);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public function joinWith($with, $eagerLoading = true, $joinType = 'INNER JOIN')
 | 
			
		||||
	{
 | 
			
		||||
		$with = (array)$with;
 | 
			
		||||
		$this->joinWithRelations(new $this->modelClass, $with, $joinType);
 | 
			
		||||
 | 
			
		||||
		if (is_array($eagerLoading)) {
 | 
			
		||||
			foreach ($with as $name => $callback) {
 | 
			
		||||
				if (is_integer($name)) {
 | 
			
		||||
					if (!in_array($callback, $eagerLoading, true)) {
 | 
			
		||||
						unset($with[$name]);
 | 
			
		||||
					}
 | 
			
		||||
				} elseif (!in_array($name, $eagerLoading, true)) {
 | 
			
		||||
					unset($with[$name]);
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			$this->with($with);
 | 
			
		||||
		} elseif ($eagerLoading) {
 | 
			
		||||
			$this->with($with);
 | 
			
		||||
		}
 | 
			
		||||
		return $this;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * @param ActiveRecord $model
 | 
			
		||||
	 * @param array $with
 | 
			
		||||
	 * @param string|array $joinType
 | 
			
		||||
	 */
 | 
			
		||||
	private function joinWithRelations($model, $with, $joinType)
 | 
			
		||||
	{
 | 
			
		||||
		$relations = [];
 | 
			
		||||
 | 
			
		||||
		foreach ($with as $name => $callback) {
 | 
			
		||||
			if (is_integer($name)) {
 | 
			
		||||
				$name = $callback;
 | 
			
		||||
				$callback = null;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			$primaryModel = $model;
 | 
			
		||||
			$parent = $this;
 | 
			
		||||
			$prefix = '';
 | 
			
		||||
			while (($pos = strpos($name, '.')) !== false) {
 | 
			
		||||
				$childName = substr($name, $pos + 1);
 | 
			
		||||
				$name = substr($name, 0, $pos);
 | 
			
		||||
				$fullName = $prefix === '' ? $name : "$prefix.$name";
 | 
			
		||||
				if (!isset($relations[$fullName])) {
 | 
			
		||||
					$relations[$fullName] = $relation = $primaryModel->getRelation($name);
 | 
			
		||||
					$this->joinWithRelation($parent, $relation, $this->getJoinType($joinType, $fullName));
 | 
			
		||||
				} else {
 | 
			
		||||
					$relation = $relations[$fullName];
 | 
			
		||||
				}
 | 
			
		||||
				$primaryModel = new $relation->modelClass;
 | 
			
		||||
				$parent = $relation;
 | 
			
		||||
				$prefix = $fullName;
 | 
			
		||||
				$name = $childName;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			$fullName = $prefix === '' ? $name : "$prefix.$name";
 | 
			
		||||
			if (!isset($relations[$fullName])) {
 | 
			
		||||
				$relations[$fullName] = $relation = $primaryModel->getRelation($name);
 | 
			
		||||
				if ($callback !== null) {
 | 
			
		||||
					call_user_func($callback, $relation);
 | 
			
		||||
				}
 | 
			
		||||
				$this->joinWithRelation($parent, $relation, $this->getJoinType($joinType, $fullName));
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private function getJoinType($joinType, $name)
 | 
			
		||||
	{
 | 
			
		||||
		if (is_array($joinType) && isset($joinType[$name])) {
 | 
			
		||||
			return $joinType[$name];
 | 
			
		||||
		} else {
 | 
			
		||||
			return is_string($joinType) ? $joinType : 'INNER JOIN';
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * @param ActiveQuery $query
 | 
			
		||||
	 * @return string
 | 
			
		||||
	 */
 | 
			
		||||
	private function getQueryTableName($query)
 | 
			
		||||
	{
 | 
			
		||||
		if (empty($query->from)) {
 | 
			
		||||
			/** @var ActiveRecord $modelClass */
 | 
			
		||||
			$modelClass = $query->modelClass;
 | 
			
		||||
			return $modelClass::tableName();
 | 
			
		||||
		} else {
 | 
			
		||||
			return reset($query->from);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * @param ActiveQuery $parent
 | 
			
		||||
	 * @param ActiveRelation $child
 | 
			
		||||
	 * @param string $joinType
 | 
			
		||||
	 */
 | 
			
		||||
	private function joinWithRelation($parent, $child, $joinType)
 | 
			
		||||
	{
 | 
			
		||||
		$parentTable = $this->getQueryTableName($parent);
 | 
			
		||||
		$childTable = $this->getQueryTableName($child);
 | 
			
		||||
		if (!empty($child->link)) {
 | 
			
		||||
			$on = [];
 | 
			
		||||
			foreach ($child->link as $childColumn => $parentColumn) {
 | 
			
		||||
				$on[] = '{{' . $parentTable . "}}.[[$parentColumn]] = {{" . $childTable . "}}.[[$childColumn]]";
 | 
			
		||||
			}
 | 
			
		||||
			$on = implode(' AND ', $on);
 | 
			
		||||
		} else {
 | 
			
		||||
			$on = '';
 | 
			
		||||
		}
 | 
			
		||||
		$this->join($joinType, $childTable, $on);
 | 
			
		||||
		if (!empty($child->where)) {
 | 
			
		||||
			$this->andWhere($child->where);
 | 
			
		||||
		}
 | 
			
		||||
		if (!empty($child->having)) {
 | 
			
		||||
			$this->andHaving($child->having);
 | 
			
		||||
		}
 | 
			
		||||
		if (!empty($child->orderBy)) {
 | 
			
		||||
			$this->addOrderBy($child->orderBy);
 | 
			
		||||
		}
 | 
			
		||||
		if (!empty($child->groupBy)) {
 | 
			
		||||
			$this->addGroupBy($child->groupBy);
 | 
			
		||||
		}
 | 
			
		||||
		if (!empty($child->params)) {
 | 
			
		||||
			$this->addParams($child->params);
 | 
			
		||||
		}
 | 
			
		||||
		if (!empty($child->join)) {
 | 
			
		||||
			foreach ($child->join as $join) {
 | 
			
		||||
				$this->join[] = $join;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if (!empty($child->union)) {
 | 
			
		||||
			foreach ($child->union as $union) {
 | 
			
		||||
				$this->union[] = $union;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -217,4 +217,31 @@ class ActiveRecordTest extends DatabaseTestCase
 | 
			
		||||
		$this->assertTrue(OrderItem::isPrimaryKey(['order_id', 'item_id']));
 | 
			
		||||
		$this->assertFalse(OrderItem::isPrimaryKey(['order_id', 'item_id', 'quantity']));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public function testJoinWith()
 | 
			
		||||
	{
 | 
			
		||||
		// inner join filtering and eager loading
 | 
			
		||||
		$orders = Order::find()->joinWith([
 | 
			
		||||
			'customer' => function ($query) {
 | 
			
		||||
				$query->where('tbl_customer.id=2');
 | 
			
		||||
			},
 | 
			
		||||
		])->orderBy('tbl_order.id')->all();
 | 
			
		||||
		$this->assertEquals(2, count($orders));
 | 
			
		||||
		$this->assertEquals(2, $orders[0]->id);
 | 
			
		||||
		$this->assertEquals(3, $orders[1]->id);
 | 
			
		||||
		$this->assertTrue($orders[0]->isRelationPopulated('customer'));
 | 
			
		||||
		$this->assertTrue($orders[1]->isRelationPopulated('customer'));
 | 
			
		||||
 | 
			
		||||
		// inner join filtering without eager loading
 | 
			
		||||
		$orders = Order::find()->joinWith([
 | 
			
		||||
			'customer' => function ($query) {
 | 
			
		||||
				$query->where('tbl_customer.id=2');
 | 
			
		||||
			},
 | 
			
		||||
		], false)->orderBy('tbl_order.id')->all();
 | 
			
		||||
		$this->assertEquals(2, count($orders));
 | 
			
		||||
		$this->assertEquals(2, $orders[0]->id);
 | 
			
		||||
		$this->assertEquals(3, $orders[1]->id);
 | 
			
		||||
		$this->assertFalse($orders[0]->isRelationPopulated('customer'));
 | 
			
		||||
		$this->assertFalse($orders[1]->isRelationPopulated('customer'));
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user