Commit 1708a36d by Carsten Brandt

WIP merge ActiveRelation into ActiveQuery

allow extending only one class to add scopes, fixes #2146 TODO: - [ ] adjust guide docs - [ ] adjust README files of extensions - [ ] finish work and fix test breaks
parent 54476b48
......@@ -9,11 +9,19 @@ namespace yii\elasticsearch;
use yii\db\ActiveQueryInterface;
use yii\db\ActiveQueryTrait;
use yii\db\ActiveRelationInterface;
use yii\db\ActiveRelationTrait;
/**
* ActiveQuery represents a [[Query]] associated with an [[ActiveRecord]] class.
*
* An ActiveQuery can be a normal query or be used in a relational context.
*
* ActiveQuery instances are usually created by [[ActiveRecord::find()]].
* Relational queries are created by [[ActiveRecord::hasOne()]] and [[ActiveRecord::hasMany()]].
*
* Normal Query
* ------------
*
* ActiveQuery mainly provides the following methods to retrieve the query results:
*
......@@ -35,19 +43,40 @@ use yii\db\ActiveQueryTrait;
*
* These options can be configured using methods of the same name. For example:
*
* ~~~
* ```php
* $customers = Customer::find()->with('orders')->asArray()->all();
* ~~~
* ```
* > NOTE: elasticsearch limits the number of records returned to 10 records by default.
* > If you expect to get more records you should specify limit explicitly.
*
* Relational query
* ----------------
*
* In relational context ActiveQuery represents a relation between two Active Record classes.
*
* Relational ActiveQuery instances are usually created by calling [[ActiveRecord::hasOne()]] and
* [[ActiveRecord::hasMany()]]. An Active Record class declares a relation by defining
* a getter method which calls one of the above methods and returns the created ActiveQuery object.
*
* A relation is specified by [[link]] which represents the association between columns
* of different tables; and the multiplicity of the relation is indicated by [[multiple]].
*
* NOTE: elasticsearch limits the number of records returned to 10 records by default.
* If you expect to get more records you should specify limit explicitly.
* If a relation involves a pivot table, it may be specified by [[via()]].
* This methods may only be called in a relational context. Same is true for [[inverseOf()]], which
* marks a relation as inverse of another relation.
*
* > NOTE: elasticsearch limits the number of records returned by any query to 10 records by default.
* > If you expect to get more records you should specify limit explicitly in relation definition.
* > This is also important for relations that use [[via()]] so that if via records are limited to 10
* > the relations records can also not be more than 10.
*
* @author Carsten Brandt <mail@cebe.cc>
* @since 2.0
*/
class ActiveQuery extends Query implements ActiveQueryInterface
class ActiveQuery extends Query implements ActiveQueryInterface, ActiveRelationInterface
{
use ActiveQueryTrait;
use ActiveRelationTrait;
/**
* Creates a DB command that can be used to execute this query.
......@@ -57,6 +86,26 @@ class ActiveQuery extends Query implements ActiveQueryInterface
*/
public function createCommand($db = null)
{
if ($this->primaryModel !== null) {
// lazy loading
if (is_array($this->via)) {
// via relation
/** @var ActiveQuery $viaQuery */
list($viaName, $viaQuery) = $this->via;
if ($viaQuery->multiple) {
$viaModels = $viaQuery->all();
$this->primaryModel->populateRelation($viaName, $viaModels);
} else {
$model = $viaQuery->one();
$this->primaryModel->populateRelation($viaName, $model);
$viaModels = $model === null ? [] : [$model];
}
$this->filterByModels($viaModels);
} else {
$this->filterByModels([$this->primaryModel]);
}
}
/** @var ActiveRecord $modelClass */
$modelClass = $this->modelClass;
if ($db === null) {
......
......@@ -138,19 +138,32 @@ class ActiveRecord extends BaseActiveRecord
// TODO add percolate functionality http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-percolate.html
/**
* @inheritdoc
*/
public static function createQuery()
{
return new ActiveQuery(['modelClass' => get_called_class()]);
}
/**
* @inheritdoc
* Creates an [[ActiveQuery]] instance.
*
* This method is called by [[find()]], [[findBySql()]] to start a SELECT query but also
* by [[hasOne()]] and [[hasMany()]] to create a relational query.
* You may override this method to return a customized query (e.g. `CustomerQuery` specified
* written for querying `Customer` purpose.)
*
* You may also define default conditions that should apply to all queries unless overridden:
*
* ```php
* public static function createQuery()
* {
* return parent::createQuery()->where(['deleted' => false]);
* }
* ```
*
* Note that all queries should use [[Query::andWhere()]] and [[Query::orWhere()]] to keep the
* default condition. Using [[Query::where()]] will override the default condition.
*
* @param array $config the configuration passed to the ActiveRelation class.
* @return ActiveQuery the newly created [[ActiveQuery]] instance.
*/
public static function createRelation($config = [])
public static function createQuery($config)
{
return new ActiveRelation($config);
$config['modelClass'] = get_called_class();
return new ActiveQuery($config);
}
// TODO implement copy and move as pk change is not possible
......
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\elasticsearch;
use yii\db\ActiveRelationInterface;
use yii\db\ActiveRelationTrait;
/**
* ActiveRelation represents a relation between two Active Record classes.
*
* ActiveRelation instances are usually created by calling [[ActiveRecord::hasOne()]] and
* [[ActiveRecord::hasMany()]]. An Active Record class declares a relation by defining
* a getter method which calls one of the above methods and returns the created ActiveRelation object.
*
* A relation is specified by [[link]] which represents the association between columns
* of different tables; and the multiplicity of the relation is indicated by [[multiple]].
*
* If a relation involves a pivot table, it may be specified by [[via()]] method.
*
* NOTE: elasticsearch limits the number of records returned by any query to 10 records by default.
* If you expect to get more records you should specify limit explicitly in relation definition.
* This is also important for relations that use [[via()]] so that if via records are limited to 10
* the relations records can also not be more than 10.
*
* @author Carsten Brandt <mail@cebe.cc>
* @since 2.0
*/
class ActiveRelation extends ActiveQuery implements ActiveRelationInterface
{
use ActiveRelationTrait;
/**
* Creates a DB command that can be used to execute this query.
* @param Connection $db the DB connection used to create the DB command.
* If null, the DB connection returned by [[modelClass]] will be used.
* @return Command the created DB command instance.
*/
public function createCommand($db = null)
{
if ($this->primaryModel !== null) {
// lazy loading
if (is_array($this->via)) {
// via relation
/** @var ActiveRelation $viaQuery */
list($viaName, $viaQuery) = $this->via;
if ($viaQuery->multiple) {
$viaModels = $viaQuery->all();
$this->primaryModel->populateRelation($viaName, $viaModels);
} else {
$model = $viaQuery->one();
$this->primaryModel->populateRelation($viaName, $model);
$viaModels = $model === null ? [] : [$model];
}
$this->filterByModels($viaModels);
} else {
$this->filterByModels([$this->primaryModel]);
}
}
return parent::createCommand($db);
}
}
......@@ -11,6 +11,9 @@ Yii Framework 2 elasticsearch extension Change Log
- Enh #1765: Added support for primary key path mapping, pk can now be part of the attributes when mapping is defined (cebe)
- Chg #1765: Changed handling of ActiveRecord primary keys, removed getId(), use getPrimaryKey() instead (cebe)
- Chg #2281: Renamed `ActiveRecord::create()` to `populateRecord()` and changed signature. This method will not call instantiate() anymore (cebe)
- Chg #2146: Removed `ActiveRelation` class and moved the functionality to `ActiveQuery`.
All relational queries are now directly served by `ActiveQuery` allowing to use
custom scopes in relations (cebe)
2.0.0 alpha, December 1, 2013
-----------------------------
......
......@@ -84,7 +84,7 @@ class Customer extends \yii\elasticsearch\ActiveRecord
}
/**
* @return ActiveRelation defines a relation to the Order record (can be in other database, e.g. redis or sql)
* @return ActiveQuery defines a relation to the Order record (can be in other database, e.g. redis or sql)
*/
public function getOrders()
{
......
......@@ -62,7 +62,7 @@ class <?= $className ?> extends <?= '\\' . ltrim($generator->baseClass, '\\') .
<?php foreach ($relations as $name => $relation): ?>
/**
* @return \yii\db\ActiveRelation
* @return \yii\db\ActiveQuery
*/
public function get<?= $name ?>()
{
......
......@@ -9,10 +9,20 @@ namespace yii\mongodb;
use yii\db\ActiveQueryInterface;
use yii\db\ActiveQueryTrait;
use yii\db\ActiveRelationInterface;
use yii\db\ActiveRelationTrait;
/**
* ActiveQuery represents a Mongo query associated with an Active Record class.
*
* An ActiveQuery can be a normal query or be used in a relational context.
*
* ActiveQuery instances are usually created by [[ActiveRecord::find()]].
* Relational queries are created by [[ActiveRecord::hasOne()]] and [[ActiveRecord::hasMany()]].
*
* Normal Query
* ------------
*
* ActiveQuery instances are usually created by [[ActiveRecord::find()]].
*
* Because ActiveQuery extends from [[Query]], one can use query methods, such as [[where()]],
......@@ -25,18 +35,66 @@ use yii\db\ActiveQueryTrait;
*
* These options can be configured using methods of the same name. For example:
*
* ~~~
* ```php
* $customers = Customer::find()->with('orders')->asArray()->all();
* ~~~
* ```
*
* Relational query
* ----------------
*
* In relational context ActiveQuery represents a relation between two Active Record classes.
*
* Relational ActiveQuery instances are usually created by calling [[ActiveRecord::hasOne()]] and
* [[ActiveRecord::hasMany()]]. An Active Record class declares a relation by defining
* a getter method which calls one of the above methods and returns the created ActiveQuery object.
*
* A relation is specified by [[link]] which represents the association between columns
* of different tables; and the multiplicity of the relation is indicated by [[multiple]].
*
* If a relation involves a pivot table, it may be specified by [[via()]].
* This methods may only be called in a relational context. Same is true for [[inverseOf()]], which
* marks a relation as inverse of another relation.
*
* @property Collection $collection Collection instance. This property is read-only.
*
* @author Paul Klimov <klimov.paul@gmail.com>
* @since 2.0
*/
class ActiveQuery extends Query implements ActiveQueryInterface
class ActiveQuery extends Query implements ActiveQueryInterface, ActiveRelationInterface
{
use ActiveQueryTrait;
use ActiveRelationTrait;
/**
* @inheritdoc
*/
protected function buildCursor($db = null)
{
if ($this->primaryModel !== null) {
// lazy loading
if ($this->via instanceof self) {
// via pivot collection
$viaModels = $this->via->findPivotRows([$this->primaryModel]);
$this->filterByModels($viaModels);
} elseif (is_array($this->via)) {
// via relation
/** @var ActiveQuery $viaQuery */
list($viaName, $viaQuery) = $this->via;
if ($viaQuery->multiple) {
$viaModels = $viaQuery->all();
$this->primaryModel->populateRelation($viaName, $viaModels);
} else {
$model = $viaQuery->one();
$this->primaryModel->populateRelation($viaName, $model);
$viaModels = $model === null ? [] : [$model];
}
$this->filterByModels($viaModels);
} else {
$this->filterByModels([$this->primaryModel]);
}
}
return parent::buildCursor($db);
}
/**
* Executes query and returns all results as an array.
......
......@@ -93,14 +93,31 @@ abstract class ActiveRecord extends BaseActiveRecord
/**
* Creates an [[ActiveQuery]] instance.
* This method is called by [[find()]] to start a "find" command.
*
* This method is called by [[find()]], [[findBySql()]] to start a SELECT query but also
* by [[hasOne()]] and [[hasMany()]] to create a relational query.
* You may override this method to return a customized query (e.g. `CustomerQuery` specified
* written for querying `Customer` purpose.)
*
* You may also define default conditions that should apply to all queries unless overridden:
*
* ```php
* public static function createQuery()
* {
* return parent::createQuery()->where(['deleted' => false]);
* }
* ```
*
* Note that all queries should use [[Query::andWhere()]] and [[Query::orWhere()]] to keep the
* default condition. Using [[Query::where()]] will override the default condition.
*
* @param array $config the configuration passed to the ActiveRelation class.
* @return ActiveQuery the newly created [[ActiveQuery]] instance.
*/
public static function createQuery()
public static function createQuery($config)
{
return new ActiveQuery(['modelClass' => get_called_class()]);
$config['modelClass'] = get_called_class();
return new ActiveQuery($config);
}
/**
......@@ -142,18 +159,6 @@ abstract class ActiveRecord extends BaseActiveRecord
}
/**
* Creates an [[ActiveRelation]] instance.
* This method is called by [[hasOne()]] and [[hasMany()]] to create a relation instance.
* You may override this method to return a customized relation.
* @param array $config the configuration passed to the ActiveRelation class.
* @return ActiveRelation the newly created [[ActiveRelation]] instance.
*/
public static function createRelation($config = [])
{
return new ActiveRelation($config);
}
/**
* Returns the list of all attribute names of the model.
* This method must be overridden by child classes to define available attributes.
* Note: primary key attribute "_id" should be always present in returned array.
......
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\mongodb;
use yii\db\ActiveRelationInterface;
use yii\db\ActiveRelationTrait;
/**
* ActiveRelation represents a relation to Mongo Active Record class.
*
* @author Paul Klimov <klimov.paul@gmail.com>
* @since 2.0
*/
class ActiveRelation extends ActiveQuery implements ActiveRelationInterface
{
use ActiveRelationTrait;
/**
* @inheritdoc
*/
protected function buildCursor($db = null)
{
if ($this->primaryModel !== null) {
// lazy loading
if ($this->via instanceof self) {
// via pivot collection
$viaModels = $this->via->findPivotRows([$this->primaryModel]);
$this->filterByModels($viaModels);
} elseif (is_array($this->via)) {
// via relation
/** @var ActiveRelation $viaQuery */
list($viaName, $viaQuery) = $this->via;
if ($viaQuery->multiple) {
$viaModels = $viaQuery->all();
$this->primaryModel->populateRelation($viaName, $viaModels);
} else {
$model = $viaQuery->one();
$this->primaryModel->populateRelation($viaName, $model);
$viaModels = $model === null ? [] : [$model];
}
$this->filterByModels($viaModels);
} else {
$this->filterByModels([$this->primaryModel]);
}
}
return parent::buildCursor($db);
}
}
\ No newline at end of file
......@@ -9,6 +9,8 @@ namespace yii\mongodb\file;
use yii\db\ActiveQueryInterface;
use yii\db\ActiveQueryTrait;
use yii\db\ActiveRelationInterface;
use yii\db\ActiveRelationTrait;
/**
* ActiveQuery represents a Mongo query associated with an file Active Record class.
......@@ -34,9 +36,10 @@ use yii\db\ActiveQueryTrait;
* @author Paul Klimov <klimov.paul@gmail.com>
* @since 2.0
*/
class ActiveQuery extends Query implements ActiveQueryInterface
class ActiveQuery extends Query implements ActiveQueryInterface, ActiveRelationInterface
{
use ActiveQueryTrait;
use ActiveRelationTrait;
/**
* Executes query and returns all results as an array.
......
......@@ -46,14 +46,31 @@ abstract class ActiveRecord extends \yii\mongodb\ActiveRecord
{
/**
* Creates an [[ActiveQuery]] instance.
* This method is called by [[find()]] to start a "find" command.
* You may override this method to return a customized query (e.g. `ImageFileQuery` specified
* written for querying `ImageFile` purpose.)
*
* This method is called by [[find()]], [[findBySql()]] to start a SELECT query but also
* by [[hasOne()]] and [[hasMany()]] to create a relational query.
* You may override this method to return a customized query (e.g. `CustomerQuery` specified
* written for querying `Customer` purpose.)
*
* You may also define default conditions that should apply to all queries unless overridden:
*
* ```php
* public static function createQuery()
* {
* return parent::createQuery()->where(['deleted' => false]);
* }
* ```
*
* Note that all queries should use [[Query::andWhere()]] and [[Query::orWhere()]] to keep the
* default condition. Using [[Query::where()]] will override the default condition.
*
* @param array $config the configuration passed to the ActiveRelation class.
* @return ActiveQuery the newly created [[ActiveQuery]] instance.
*/
public static function createQuery()
public static function createQuery($config)
{
return new ActiveQuery(['modelClass' => get_called_class()]);
$config['modelClass'] = get_called_class();
return new ActiveQuery($config);
}
/**
......@@ -66,18 +83,6 @@ abstract class ActiveRecord extends \yii\mongodb\ActiveRecord
}
/**
* Creates an [[ActiveRelation]] instance.
* This method is called by [[hasOne()]] and [[hasMany()]] to create a relation instance.
* You may override this method to return a customized relation.
* @param array $config the configuration passed to the ActiveRelation class.
* @return ActiveRelation the newly created [[ActiveRelation]] instance.
*/
public static function createRelation($config = [])
{
return new ActiveRelation($config);
}
/**
* Returns the list of all attribute names of the model.
* This method could be overridden by child classes to define available attributes.
* Note: all attributes defined in base Active Record class should be always present
......
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\mongodb\file;
use yii\db\ActiveRelationInterface;
use yii\db\ActiveRelationTrait;
/**
* ActiveRelation represents a relation to Mongo GridFS Active Record class.
*
* @author Paul Klimov <klimov.paul@gmail.com>
* @since 2.0
*/
class ActiveRelation extends ActiveQuery implements ActiveRelationInterface
{
use ActiveRelationTrait;
}
\ No newline at end of file
......@@ -10,13 +10,20 @@ use yii\base\InvalidParamException;
use yii\base\NotSupportedException;
use yii\db\ActiveQueryInterface;
use yii\db\ActiveQueryTrait;
use yii\db\ActiveRelationInterface;
use yii\db\ActiveRelationTrait;
use yii\db\QueryTrait;
/**
* ActiveQuery represents a query associated with an Active Record class.
*
* ActiveQuery instances are usually created by [[ActiveRecord::find()]]
* and [[ActiveRecord::count()]].
* An ActiveQuery can be a normal query or be used in a relational context.
*
* ActiveQuery instances are usually created by [[ActiveRecord::find()]].
* Relational queries are created by [[ActiveRecord::hasOne()]] and [[ActiveRecord::hasMany()]].
*
* Normal Query
* ------------
*
* ActiveQuery mainly provides the following methods to retrieve the query results:
*
......@@ -40,17 +47,34 @@ use yii\db\QueryTrait;
*
* These options can be configured using methods of the same name. For example:
*
* ~~~
* ```php
* $customers = Customer::find()->with('orders')->asArray()->all();
* ~~~
* ```
*
* Relational query
* ----------------
*
* In relational context ActiveQuery represents a relation between two Active Record classes.
*
* Relational ActiveQuery instances are usually created by calling [[ActiveRecord::hasOne()]] and
* [[ActiveRecord::hasMany()]]. An Active Record class declares a relation by defining
* a getter method which calls one of the above methods and returns the created ActiveQuery object.
*
* A relation is specified by [[link]] which represents the association between columns
* of different tables; and the multiplicity of the relation is indicated by [[multiple]].
*
* If a relation involves a pivot table, it may be specified by [[via()]].
* This methods may only be called in a relational context. Same is true for [[inverseOf()]], which
* marks a relation as inverse of another relation.
*
* @author Carsten Brandt <mail@cebe.cc>
* @since 2.0
*/
class ActiveQuery extends \yii\base\Component implements ActiveQueryInterface
class ActiveQuery extends \yii\base\Component implements ActiveQueryInterface, ActiveRelationInterface
{
use QueryTrait;
use ActiveQueryTrait;
use ActiveRelationTrait;
/**
* Executes the query and returns all results as an array.
......@@ -252,6 +276,30 @@ class ActiveQuery extends \yii\base\Component implements ActiveQueryInterface
*/
protected function executeScript($db, $type, $columnName = null)
{
if ($this->primaryModel !== null) {
// lazy loading
if ($this->via instanceof self) {
// via pivot table
$viaModels = $this->via->findPivotRows([$this->primaryModel]);
$this->filterByModels($viaModels);
} elseif (is_array($this->via)) {
// via relation
/** @var ActiveQuery $viaQuery */
list($viaName, $viaQuery) = $this->via;
if ($viaQuery->multiple) {
$viaModels = $viaQuery->all();
$this->primaryModel->populateRelation($viaName, $viaModels);
} else {
$model = $viaQuery->one();
$this->primaryModel->populateRelation($viaName, $model);
$viaModels = $model === null ? [] : [$model];
}
$this->filterByModels($viaModels);
} else {
$this->filterByModels([$this->primaryModel]);
}
}
if (!empty($this->orderBy)) {
throw new NotSupportedException('orderBy is currently not supported by redis ActiveRecord.');
}
......
......@@ -49,19 +49,32 @@ class ActiveRecord extends BaseActiveRecord
}
/**
* @inheritdoc
*/
public static function createQuery()
{
return new ActiveQuery(['modelClass' => get_called_class()]);
}
/**
* @inheritdoc
* Creates an [[ActiveQuery]] instance.
*
* This method is called by [[find()]], [[findBySql()]] to start a SELECT query but also
* by [[hasOne()]] and [[hasMany()]] to create a relational query.
* You may override this method to return a customized query (e.g. `CustomerQuery` specified
* written for querying `Customer` purpose.)
*
* You may also define default conditions that should apply to all queries unless overridden:
*
* ```php
* public static function createQuery()
* {
* return parent::createQuery()->where(['deleted' => false]);
* }
* ```
*
* Note that all queries should use [[Query::andWhere()]] and [[Query::orWhere()]] to keep the
* default condition. Using [[Query::where()]] will override the default condition.
*
* @param array $config the configuration passed to the ActiveRelation class.
* @return ActiveQuery the newly created [[ActiveQuery]] instance.
*/
public static function createRelation($config = [])
public static function createQuery($config)
{
return new ActiveRelation($config);
$config['modelClass'] = get_called_class();
return new ActiveQuery($config);
}
/**
......
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\redis;
use yii\db\ActiveRelationInterface;
use yii\db\ActiveRelationTrait;
/**
* ActiveRelation represents a relation between two Active Record classes.
*
* ActiveRelation instances are usually created by calling [[ActiveRecord::hasOne()]] and
* [[ActiveRecord::hasMany()]]. An Active Record class declares a relation by defining
* a getter method which calls one of the above methods and returns the created ActiveRelation object.
*
* A relation is specified by [[link]] which represents the association between columns
* of different tables; and the multiplicity of the relation is indicated by [[multiple]].
*
* If a relation involves a pivot table, it may be specified by [[via()]] or [[viaTable()]] method.
*
* @author Carsten Brandt <mail@cebe.cc>
* @since 2.0
*/
class ActiveRelation extends ActiveQuery implements ActiveRelationInterface
{
use ActiveRelationTrait;
/**
* Executes a script created by [[LuaScriptBuilder]]
* @param Connection $db the database connection used to execute the query.
* If this parameter is not given, the `db` application component will be used.
* @param string $type the type of the script to generate
* @param null $column
* @return array|bool|null|string
*/
protected function executeScript($db, $type, $column=null)
{
if ($this->primaryModel !== null) {
// lazy loading
if ($this->via instanceof self) {
// via pivot table
$viaModels = $this->via->findPivotRows([$this->primaryModel]);
$this->filterByModels($viaModels);
} elseif (is_array($this->via)) {
// via relation
/** @var ActiveRelation $viaQuery */
list($viaName, $viaQuery) = $this->via;
if ($viaQuery->multiple) {
$viaModels = $viaQuery->all();
$this->primaryModel->populateRelation($viaName, $viaModels);
} else {
$model = $viaQuery->one();
$this->primaryModel->populateRelation($viaName, $model);
$viaModels = $model === null ? [] : [$model];
}
$this->filterByModels($viaModels);
} else {
$this->filterByModels([$this->primaryModel]);
}
}
return parent::executeScript($db, $type, $column);
}
}
......@@ -7,6 +7,9 @@ Yii Framework 2 redis extension Change Log
- Bug #1993: afterFind event in AR is now called after relations have been populated (cebe, creocoder)
- Enh #1773: keyPrefix property of Session and Cache is not restricted to alnum characters anymore (cebe)
- Chg #2281: Renamed `ActiveRecord::create()` to `populateRecord()` and changed signature. This method will not call instantiate() anymore (cebe)
- Chg #2146: Removed `ActiveRelation` class and moved the functionality to `ActiveQuery`.
All relational queries are now directly served by `ActiveQuery` allowing to use
custom scopes in relations (cebe)
2.0.0 alpha, December 1, 2013
-----------------------------
......
......@@ -148,7 +148,7 @@ class Customer extends \yii\redis\ActiveRecord
}
/**
* @return ActiveRelation defines a relation to the Order record (can be in other database, e.g. elasticsearch or sql)
* @return ActiveQuery defines a relation to the Order record (can be in other database, e.g. elasticsearch or sql)
*/
public function getOrders()
{
......
......@@ -10,11 +10,18 @@ namespace yii\sphinx;
use yii\base\InvalidCallException;
use yii\db\ActiveQueryInterface;
use yii\db\ActiveQueryTrait;
use yii\db\ActiveRelationInterface;
/**
* ActiveQuery represents a Sphinx query associated with an Active Record class.
*
* An ActiveQuery can be a normal query or be used in a relational context.
*
* ActiveQuery instances are usually created by [[ActiveRecord::find()]] and [[ActiveRecord::findBySql()]].
* Relational queries are created by [[ActiveRecord::hasOne()]] and [[ActiveRecord::hasMany()]].
*
* Normal Query
* ------------
*
* Because ActiveQuery extends from [[Query]], one can use query methods, such as [[where()]],
* [[orderBy()]] to customize the query options.
......@@ -54,12 +61,29 @@ use yii\db\ActiveQueryTrait;
* $articles = Article::find()->with('source')->snippetByModel()->all();
* ~~~
*
* Relational query
* ----------------
*
* In relational context ActiveQuery represents a relation between two Active Record classes.
*
* Relational ActiveQuery instances are usually created by calling [[ActiveRecord::hasOne()]] and
* [[ActiveRecord::hasMany()]]. An Active Record class declares a relation by defining
* a getter method which calls one of the above methods and returns the created ActiveQuery object.
*
* A relation is specified by [[link]] which represents the association between columns
* of different tables; and the multiplicity of the relation is indicated by [[multiple]].
*
* If a relation involves a pivot table, it may be specified by [[via()]].
* This methods may only be called in a relational context. Same is true for [[inverseOf()]], which
* marks a relation as inverse of another relation.
*
* @author Paul Klimov <klimov.paul@gmail.com>
* @since 2.0
*/
class ActiveQuery extends Query implements ActiveQueryInterface
class ActiveQuery extends Query implements ActiveQueryInterface, ActiveRelationInterface
{
use ActiveQueryTrait;
use ActiveRelationTrait;
/**
* @var string the SQL statement to be executed for retrieving AR records.
......@@ -165,6 +189,30 @@ class ActiveQuery extends Query implements ActiveQueryInterface
*/
public function createCommand($db = null)
{
if ($this->primaryModel !== null) {
// lazy loading a relational query
if ($this->via instanceof self) {
// via pivot index
$viaModels = $this->via->findPivotRows([$this->primaryModel]);
$this->filterByModels($viaModels);
} elseif (is_array($this->via)) {
// via relation
/** @var ActiveRelation $viaQuery */
list($viaName, $viaQuery) = $this->via;
if ($viaQuery->multiple) {
$viaModels = $viaQuery->all();
$this->primaryModel->populateRelation($viaName, $viaModels);
} else {
$model = $viaQuery->one();
$this->primaryModel->populateRelation($viaName, $model);
$viaModels = $model === null ? [] : [$model];
}
$this->filterByModels($viaModels);
} else {
$this->filterByModels([$this->primaryModel]);
}
}
$this->setConnection($db);
$db = $this->getConnection();
......
......@@ -135,14 +135,31 @@ abstract class ActiveRecord extends BaseActiveRecord
/**
* Creates an [[ActiveQuery]] instance.
* This method is called by [[find()]], [[findBySql()]] and [[count()]] to start a SELECT query.
* You may override this method to return a customized query (e.g. `ArticleQuery` specified
* written for querying `Article` purpose.)
*
* This method is called by [[find()]], [[findBySql()]] to start a SELECT query but also
* by [[hasOne()]] and [[hasMany()]] to create a relational query.
* You may override this method to return a customized query (e.g. `CustomerQuery` specified
* written for querying `Customer` purpose.)
*
* You may also define default conditions that should apply to all queries unless overridden:
*
* ```php
* public static function createQuery()
* {
* return parent::createQuery()->where(['deleted' => false]);
* }
* ```
*
* Note that all queries should use [[Query::andWhere()]] and [[Query::orWhere()]] to keep the
* default condition. Using [[Query::where()]] will override the default condition.
*
* @param array $config the configuration passed to the ActiveRelation class.
* @return ActiveQuery the newly created [[ActiveQuery]] instance.
*/
public static function createQuery()
public static function createQuery($config)
{
return new ActiveQuery(['modelClass' => get_called_class()]);
$config['modelClass'] = get_called_class();
return new ActiveQuery($config);
}
/**
......@@ -304,18 +321,6 @@ abstract class ActiveRecord extends BaseActiveRecord
}
/**
* Creates an [[ActiveRelationInterface]] instance.
* This method is called by [[hasOne()]] and [[hasMany()]] to create a relation instance.
* You may override this method to return a customized relation.
* @param array $config the configuration passed to the ActiveRelation class.
* @return ActiveRelationInterface the newly created [[ActiveRelation]] instance.
*/
public static function createRelation($config = [])
{
return new ActiveRelation($config);
}
/**
* Returns the list of all attribute names of the model.
* The default implementation will return all column names of the table associated with this AR class.
* @return array list of attribute names.
......
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\sphinx;
use yii\db\ActiveRelationInterface;
use yii\db\ActiveRelationTrait;
/**
* ActiveRelation represents a relation to Sphinx Active Record class.
*
* @author Paul Klimov <klimov.paul@gmail.com>
* @since 2.0
*/
class ActiveRelation extends ActiveQuery implements ActiveRelationInterface
{
use ActiveRelationTrait;
/**
* @inheritdoc
*/
public function createCommand($db = null)
{
if ($this->primaryModel !== null) {
// lazy loading
if ($this->via instanceof self) {
// via pivot index
$viaModels = $this->via->findPivotRows([$this->primaryModel]);
$this->filterByModels($viaModels);
} elseif (is_array($this->via)) {
// via relation
/** @var ActiveRelation $viaQuery */
list($viaName, $viaQuery) = $this->via;
if ($viaQuery->multiple) {
$viaModels = $viaQuery->all();
$this->primaryModel->populateRelation($viaName, $viaModels);
} else {
$model = $viaQuery->one();
$this->primaryModel->populateRelation($viaName, $model);
$viaModels = $model === null ? [] : [$model];
}
$this->filterByModels($viaModels);
} else {
$this->filterByModels([$this->primaryModel]);
}
}
return parent::createCommand($db);
}
}
\ No newline at end of file
......@@ -5,9 +5,12 @@ Yii Framework 2 sphinx extension Change Log
----------------------------
- Bug #1993: afterFind event in AR is now called after relations have been populated (cebe, creocoder)
- Bug #2160: SphinxQL does not support OFFSET (qiangxue, romeo7)
- Bug #2160: SphinxQL does not support `OFFSET` (qiangxue, romeo7)
- Enh #1398: Refactor ActiveRecord to use BaseActiveRecord class of the framework (klimov-paul)
- Chg #2281: Renamed `ActiveRecord::create()` to `populateRecord()` and changed signature. This method will not call instantiate() anymore (cebe)
- Chg #2146: Removed `ActiveRelation` class and moved the functionality to `ActiveQuery`.
All relational queries are now directly served by `ActiveQuery` allowing to use
custom scopes in relations (cebe)
2.0.0 alpha, December 1, 2013
-----------------------------
......
......@@ -175,7 +175,9 @@ Yii Framework 2 Change Log
- Chg: Advanced app template: moved database connection DSN, login and password to `-local` config not to expose it to VCS (samdark)
- Chg: Renamed `yii\web\Request::acceptedLanguages` to `acceptableLanguages` (qiangxue)
- Chg: Removed implementation of `Arrayable` from `yii\Object` (qiangxue)
- Chg: Renamed `ActiveRecordInterface::createActiveRelation()` to `createRelation()` (qiangxue)
- Chg #2146: Removed `ActiveRelation` class and moved the functionality to `ActiveQuery`. All relational queries are
now directly served by `ActiveQuery` allowing to use custom scopes in relations.
Also removed `ActiveRecordInterface::createActiveRelation()` (cebe)
- Chg: The scripts in asset bundles are now registered in `View` at the end of `endBody()`. It was done in `endPage()` previously (qiangxue)
- Chg: Renamed `csrf-var` to `csrf-param` for CSRF header name (Dilip)
- Chg: The directory holding email templates is renamed from `mails` to `mail` (qiangxue)
......
......@@ -7,11 +7,18 @@
*/
namespace yii\db;
use yii\base\Object;
/**
* ActiveQuery represents a DB query associated with an Active Record class.
*
* An ActiveQuery can be a normal query or be used in a relational context.
*
* ActiveQuery instances are usually created by [[ActiveRecord::find()]] and [[ActiveRecord::findBySql()]].
* Relational queries are created by [[ActiveRecord::hasOne()]] and [[ActiveRecord::hasMany()]].
*
* Normal Query
* ------------
*
* ActiveQuery mainly provides the following methods to retrieve the query results:
*
......@@ -37,23 +44,49 @@ namespace yii\db;
*
* These options can be configured using methods of the same name. For example:
*
* ~~~
* ```php
* $customers = Customer::find()->with('orders')->asArray()->all();
* ~~~
* ```
*
* Relational query
* ----------------
*
* In relational context ActiveQuery represents a relation between two Active Record classes.
*
* Relational ActiveQuery instances are usually created by calling [[ActiveRecord::hasOne()]] and
* [[ActiveRecord::hasMany()]]. An Active Record class declares a relation by defining
* a getter method which calls one of the above methods and returns the created ActiveQuery object.
*
* A relation is specified by [[link]] which represents the association between columns
* of different tables; and the multiplicity of the relation is indicated by [[multiple]].
*
* If a relation involves a pivot table, it may be specified by [[via()]] or [[viaTable()]] method.
* These methods may only be called in a relational context. Same is true for [[inverseOf()]], which
* marks a relation as inverse of another relation and [[onCondition()]] which adds a condition that
* is to be added to relational querys join condition.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @author Carsten Brandt <mail@cebe.cc>
* @since 2.0
*/
class ActiveQuery extends Query implements ActiveQueryInterface
class ActiveQuery extends Query implements ActiveQueryInterface, ActiveRelationInterface
{
use ActiveQueryTrait;
use ActiveRelationTrait;
/**
* @var string the SQL statement to be executed for retrieving AR records.
* This is set by [[ActiveRecord::findBySql()]].
*/
public $sql;
/**
* @var string|array the join condition to be used when this query is used in a relational context.
* The condition will be used in the ON part when [[ActiveQuery::joinWith()]] is called.
* Otherwise, the condition will be used in the WHERE part of a query.
* Please refer to [[Query::where()]] on how to specify this parameter.
* @see onCondition()
*/
public $on;
/**
......@@ -175,6 +208,31 @@ class ActiveQuery extends Query implements ActiveQueryInterface
*/
public function createCommand($db = null)
{
if ($this->primaryModel === null) {
// not a relational context or eager loading
if (!empty($this->on)) {
$where = $this->where;
$this->andWhere($this->on);
$command = $this->createCommandInternal($db);
$this->where = $where;
return $command;
} else {
return $this->createCommandInternal($db);
}
} else {
// lazy loading of a relation
return $this->createRelationalCommand($db);
}
}
/**
* Creates a DB command that can be used to execute this query.
* @param Connection $db the DB connection used to create the DB command.
* If null, the DB connection returned by [[modelClass]] will be used.
* @return Command the created DB command instance.
*/
protected function createCommandInternal($db)
{
/** @var ActiveRecord $modelClass */
$modelClass = $this->modelClass;
if ($db === null) {
......@@ -191,6 +249,47 @@ class ActiveQuery extends Query implements ActiveQueryInterface
}
/**
* Creates a command for lazy loading of a relation.
* @param Connection $db the DB connection used to create the DB command.
* @return Command the created DB command instance.
*/
private function createRelationalCommand($db = null)
{
$where = $this->where;
if ($this->via instanceof self) {
// via pivot table
$viaModels = $this->via->findPivotRows([$this->primaryModel]);
$this->filterByModels($viaModels);
} elseif (is_array($this->via)) {
// via relation
/** @var ActiveQuery $viaQuery */
list($viaName, $viaQuery) = $this->via;
if ($viaQuery->multiple) {
$viaModels = $viaQuery->all();
$this->primaryModel->populateRelation($viaName, $viaModels);
} else {
$model = $viaQuery->one();
$this->primaryModel->populateRelation($viaName, $model);
$viaModels = $model === null ? [] : [$model];
}
$this->filterByModels($viaModels);
} else {
$this->filterByModels([$this->primaryModel]);
}
if (!empty($this->on)) {
$this->andWhere($this->on);
}
$command = $this->createCommandInternal($db);
$this->where = $where;
return $command;
}
/**
* Joins with the specified relations.
*
* This method allows you to reuse existing relation definitions to perform JOIN queries.
......@@ -355,14 +454,14 @@ class ActiveQuery extends Query implements ActiveQueryInterface
* Joins a parent query with a child query.
* The current query object will be modified accordingly.
* @param ActiveQuery $parent
* @param ActiveRelation $child
* @param ActiveRelationInterface $child
* @param string $joinType
*/
private function joinWithRelation($parent, $child, $joinType)
{
$via = $child->via;
$child->via = null;
if ($via instanceof ActiveRelation) {
if ($via instanceof ActiveRelationInterface) {
// via table
$this->joinWithRelation($parent, $via, $joinType);
$this->joinWithRelation($via, $child, $joinType);
......@@ -426,4 +525,67 @@ class ActiveQuery extends Query implements ActiveQueryInterface
}
}
}
/**
* Sets the ON condition for a relational query.
* The condition will be used in the ON part when [[ActiveQuery::joinWith()]] is called.
* Otherwise, the condition will be used in the WHERE part of a query.
*
* Use this method to specify additional conditions when declaring a relation in the [[ActiveRecord]] class:
*
* ```php
* public function getActiveUsers()
* {
* return $this->hasMany(User::className(), ['id' => 'user_id'])->onCondition(['active' => true]);
* }
* ```
*
* @param string|array $condition the ON condition. Please refer to [[Query::where()]] on how to specify this parameter.
* @param array $params the parameters (name => value) to be bound to the query.
* @return static the query object itself
*/
public function onCondition($condition, $params = [])
{
$this->on = $condition;
$this->addParams($params);
return $this;
}
/**
* Specifies the pivot table for a relational query.
*
* Use this method to specify a pivot table when declaring a relation in the [[ActiveRecord]] class:
*
* ```php
* public function getItems()
* {
* return $this->hasMany(Item::className(), ['id' => 'item_id'])
* ->viaTable('tbl_order_item', ['order_id' => 'id']);
* }
* ```
*
* @param string $tableName the name of the pivot table.
* @param array $link the link between the pivot table and the table associated with [[primaryModel]].
* The keys of the array represent the columns in the pivot table, and the values represent the columns
* in the [[primaryModel]] table.
* @param callable $callable a PHP callback for customizing the relation associated with the pivot table.
* Its signature should be `function($query)`, where `$query` is the query to be customized.
* @return static
* @see via()
*/
public function viaTable($tableName, $link, $callable = null)
{
$relation = new ActiveQuery([
'modelClass' => get_class($this->primaryModel),
'from' => [$tableName],
'link' => $link,
'multiple' => true,
'asArray' => true,
]);
$this->via = $relation;
if ($callable !== null) {
call_user_func($callable, $relation);
}
return $this;
}
}
......@@ -151,7 +151,8 @@ class ActiveRecord extends BaseActiveRecord
/**
* Creates an [[ActiveQuery]] instance.
*
* This method is called by [[find()]], [[findBySql()]] to start a SELECT query.
* This method is called by [[find()]], [[findBySql()]] to start a SELECT query but also
* by [[hasOne()]] and [[hasMany()]] to create a relational query.
* You may override this method to return a customized query (e.g. `CustomerQuery` specified
* written for querying `Customer` purpose.)
*
......@@ -167,11 +168,13 @@ class ActiveRecord extends BaseActiveRecord
* Note that all queries should use [[Query::andWhere()]] and [[Query::orWhere()]] to keep the
* default condition. Using [[Query::where()]] will override the default condition.
*
* @param array $config the configuration passed to the ActiveRelation class.
* @return ActiveQuery the newly created [[ActiveQuery]] instance.
*/
public static function createQuery()
public static function createQuery($config)
{
return new ActiveQuery(['modelClass' => get_called_class()]);
$config['modelClass'] = get_called_class();
return new ActiveQuery($config);
}
/**
......@@ -263,18 +266,6 @@ class ActiveRecord extends BaseActiveRecord
}
/**
* Creates an [[ActiveRelation]] instance.
* This method is called by [[hasOne()]] and [[hasMany()]] to create a relation instance.
* You may override this method to return a customized relation.
* @param array $config the configuration passed to the ActiveRelation class.
* @return ActiveRelation the newly created [[ActiveRelation]] instance.
*/
public static function createRelation($config = [])
{
return new ActiveRelation($config);
}
/**
* @inheritdoc
*/
public static function populateRecord($record, $row)
......
......@@ -112,7 +112,10 @@ interface ActiveRecordInterface
/**
* Creates an [[ActiveQueryInterface|ActiveQuery]] instance.
*
* This method is called by [[find()]] to start a SELECT query.
* This method is called by [[find()]] to start a SELECT query but also
* by [[BaseActiveRecord::hasOne()]] and [[BaseActiveRecord::hasMany()]] to
* create a relational query.
*
* You may override this method to return a customized query (e.g. `CustomerQuery` specified
* written for querying `Customer` purpose.)
*
......@@ -128,9 +131,10 @@ interface ActiveRecordInterface
* Note that all queries should use [[Query::andWhere()]] and [[Query::orWhere()]] to keep the
* default condition. Using [[Query::where()]] will override the default condition.
*
* @param array $config the configuration passed to the ActiveRelation class.
* @return ActiveQueryInterface the newly created [[ActiveQueryInterface|ActiveQuery]] instance.
*/
public static function createQuery();
public static function createQuery($config);
/**
* Updates records using the provided attribute values and conditions.
......@@ -256,21 +260,12 @@ interface ActiveRecordInterface
public function equals($record);
/**
* Creates an [[ActiveRelationInterface|ActiveRelation]] instance.
* This method is called by [[BaseActiveRecord::hasOne()]] and [[BaseActiveRecord::hasMany()]] to
* create a relation instance.
* You may override this method to return a customized relation.
* @param array $config the configuration passed to the ActiveRelation class.
* @return ActiveRelation the newly created [[ActiveRelation]] instance.
*/
public static function createRelation($config = []);
/**
* Returns the relation object with the specified name.
* A relation is defined by a getter method which returns an [[ActiveRelationInterface|ActiveRelation]] object.
* A relation is defined by a getter method which returns an object implementing the [[ActiveRelationInterface]]
* (normally this would be an [[ActiveQuery]] object).
* It can be declared in either the ActiveRecord class itself or one of its behaviors.
* @param string $name the relation name
* @return ActiveRelation the relation object
* @return ActiveRelationInterface the relation object
*/
public function getRelation($name);
......
<?php
/**
* @author Qiang Xue <qiang.xue@gmail.com>
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\db;
/**
* ActiveRelation represents a relation between two Active Record classes.
*
* ActiveRelation instances are usually created by calling [[ActiveRecord::hasOne()]] and
* [[ActiveRecord::hasMany()]]. An Active Record class declares a relation by defining
* a getter method which calls one of the above methods and returns the created ActiveRelation object.
*
* A relation is specified by [[link]] which represents the association between columns
* of different tables; and the multiplicity of the relation is indicated by [[multiple]].
*
* If a relation involves a pivot table, it may be specified by [[via()]] or [[viaTable()]] method.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @author Carsten Brandt <mail@cebe.cc>
* @since 2.0
*/
class ActiveRelation extends ActiveQuery implements ActiveRelationInterface
{
use ActiveRelationTrait;
/**
* @var string|array the join condition. Please refer to [[Query::where()]] on how to specify this parameter.
* The condition will be used in the ON part when [[ActiveQuery::joinWith()]] is called.
* Otherwise, the condition will be used in the WHERE part of a query.
*/
public $on;
/**
* Sets the ON condition for the query.
* The condition will be used in the ON part when [[ActiveQuery::joinWith()]] is called.
* Otherwise, the condition will be used in the WHERE part of a query.
* @param string|array $condition the ON condition. Please refer to [[Query::where()]] on how to specify this parameter.
* @param array $params the parameters (name => value) to be bound to the query.
* @return static the query object itself
*/
public function onCondition($condition, $params = [])
{
$this->on = $condition;
$this->addParams($params);
return $this;
}
/**
* Specifies the pivot table.
* @param string $tableName the name of the pivot table.
* @param array $link the link between the pivot table and the table associated with [[primaryModel]].
* The keys of the array represent the columns in the pivot table, and the values represent the columns
* in the [[primaryModel]] table.
* @param callable $callable a PHP callback for customizing the relation associated with the pivot table.
* Its signature should be `function($query)`, where `$query` is the query to be customized.
* @return static
*/
public function viaTable($tableName, $link, $callable = null)
{
$relation = new ActiveRelation([
'modelClass' => get_class($this->primaryModel),
'from' => [$tableName],
'link' => $link,
'multiple' => true,
'asArray' => true,
]);
$this->via = $relation;
if ($callable !== null) {
call_user_func($callable, $relation);
}
return $this;
}
/**
* Creates a DB command that can be used to execute this query.
* @param Connection $db the DB connection used to create the DB command.
* If null, the DB connection returned by [[modelClass]] will be used.
* @return Command the created DB command instance.
*/
public function createCommand($db = null)
{
if ($this->primaryModel === null) {
// eager loading
if (!empty($this->on)) {
$where = $this->where;
$this->andWhere($this->on);
$command = parent::createCommand($db);
$this->where = $where;
return $command;
} else {
return parent::createCommand($db);
}
}
// lazy loading
$where = $this->where;
if ($this->via instanceof self) {
// via pivot table
$viaModels = $this->via->findPivotRows([$this->primaryModel]);
$this->filterByModels($viaModels);
} elseif (is_array($this->via)) {
// via relation
/** @var ActiveRelation $viaQuery */
list($viaName, $viaQuery) = $this->via;
if ($viaQuery->multiple) {
$viaModels = $viaQuery->all();
$this->primaryModel->populateRelation($viaName, $viaModels);
} else {
$model = $viaQuery->one();
$this->primaryModel->populateRelation($viaName, $model);
$viaModels = $model === null ? [] : [$model];
}
$this->filterByModels($viaModels);
} else {
$this->filterByModels([$this->primaryModel]);
}
if (!empty($this->on)) {
$this->andWhere($this->on);
}
$command = parent::createCommand($db);
$this->where = $where;
return $command;
}
}
......@@ -6,9 +6,10 @@
*/
namespace yii\db;
use yii\base\InvalidParamException;
/**
* ActiveRelationInterface defines the common interface to be implemented by active record relation classes.
* ActiveRelationInterface defines the common interface to be implemented by relational active record query classes.
*
* A class implementing this interface should also use [[ActiveRelationTrait]].
*
......@@ -26,4 +27,14 @@ interface ActiveRelationInterface extends ActiveQueryInterface
* @return static the relation object itself.
*/
public function via($relationName, $callable = null);
/**
* Finds the related records for the specified primary record.
* This method is invoked when a relation of an ActiveRecord is being accessed in a lazy fashion.
* @param string $name the relation name
* @param ActiveRecordInterface $model the primary model
* @return mixed the related record(s)
* @throws InvalidParamException if the relation is invalid
*/
public function findFor($name, $model);
}
......@@ -11,7 +11,7 @@ use yii\base\InvalidConfigException;
use yii\base\InvalidParamException;
/**
* ActiveRelationTrait implements the common methods and properties for active record relation classes.
* ActiveRelationTrait implements the common methods and properties for active record relational queries.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @author Carsten Brandt <mail@cebe.cc>
......@@ -20,25 +20,30 @@ use yii\base\InvalidParamException;
trait ActiveRelationTrait
{
/**
* @var boolean whether this relation should populate all query results into AR instances.
* If false, only the first row of the results will be retrieved.
* @var boolean whether this query represents a relation to more than one record.
* This property is only used in relational context. If true, this relation will
* populate all query results into AR instances using [[all()]].
* If false, only the first row of the results will be retrieved using [[one()]].
*/
public $multiple;
/**
* @var ActiveRecord the primary model that this relation is associated with.
* @var ActiveRecord the primary model of a relational query.
* This is used only in lazy loading with dynamic query options.
*/
public $primaryModel;
/**
* @var array the columns of the primary and foreign tables that establish the relation.
* @var array the columns of the primary and foreign tables that establish a relation.
* The array keys must be columns of the table for this relation, and the array values
* must be the corresponding columns from the primary table.
* Do not prefix or quote the column names as this will be done automatically by Yii.
* This property is only used in relational context.
*/
public $link;
/**
* @var array the query associated with the pivot table. Please call [[via()]]
* to set this property instead of directly setting it.
* This property is only used in relational context.
* @see via()
*/
public $via;
/**
......@@ -48,9 +53,12 @@ trait ActiveRelationTrait
* If this property is set, the primary record(s) will be referenced through the specified relation.
* For example, `$customer->orders[0]->customer` and `$customer` will be the same object,
* and accessing the customer of an order will not trigger new DB query.
* This property is only used in relational context.
* @see inverseOf()
*/
public $inverseOf;
/**
* Clones internal objects.
*/
......@@ -66,6 +74,22 @@ trait ActiveRelationTrait
/**
* Specifies the relation associated with the pivot table.
*
* Use this method to specify a pivot record/table when declaring a relation in the [[ActiveRecord]] class:
*
* ```php
* public function getOrders()
* {
* return $this->hasOne(Order::className(), ['id' => 'order_id']);
* }
*
* public function getOrderItems()
* {
* return $this->hasMany(Item::className(), ['id' => 'item_id'])
* ->via('orders', ['order_id' => 'id']);
* }
* ```
*
* @param string $relationName the relation name. This refers to a relation declared in [[primaryModel]].
* @param callable $callable a PHP callback for customizing the relation associated with the pivot table.
* Its signature should be `function($query)`, where `$query` is the query to be customized.
......@@ -87,7 +111,17 @@ trait ActiveRelationTrait
* is the "orders", and the inverse of the "orders" relation is the "customer".
* If this property is set, the primary record(s) will be referenced through the specified relation.
* For example, `$customer->orders[0]->customer` and `$customer` will be the same object,
* and accessing the customer of an order will not trigger new DB query.
* and accessing the customer of an order will not trigger a new DB query.
*
* Use this method when declaring a relation in the [[ActiveRecord]] class:
*
* ```php
* public function getOrders()
* {
* return $this->hasMany(Order::className(), ['customer_id' => 'id'])->inverseOf('customer');
* }
* ```
*
* @param string $relationName the name of the relation that is the inverse of this relation.
* @return static the relation object itself.
*/
......
......@@ -289,7 +289,7 @@ abstract class BaseActiveRecord extends Model implements ActiveRecordInterface
/**
* Declares a `has-one` relation.
* The declaration is returned in terms of an [[ActiveRelation]] instance
* The declaration is returned in terms of a relational [[ActiveQuery]] instance
* through which the related record can be queried and retrieved back.
*
* A `has-one` relation means that there is at most one related record matching
......@@ -309,7 +309,7 @@ abstract class BaseActiveRecord extends Model implements ActiveRecordInterface
* in the related class `Country`, while the 'country_id' value refers to an attribute name
* in the current AR class.
*
* Call methods declared in [[ActiveRelation]] to further customize the relation.
* Call methods declared in [[ActiveQuery]] to further customize the relation.
*
* @param string $class the class name of the related record
* @param array $link the primary-foreign key constraint. The keys of the array refer to
......@@ -320,7 +320,7 @@ abstract class BaseActiveRecord extends Model implements ActiveRecordInterface
public function hasOne($class, $link)
{
/** @var ActiveRecord $class */
return $class::createRelation([
return $class::createQuery([
'modelClass' => $class,
'primaryModel' => $this,
'link' => $link,
......@@ -330,7 +330,7 @@ abstract class BaseActiveRecord extends Model implements ActiveRecordInterface
/**
* Declares a `has-many` relation.
* The declaration is returned in terms of an [[ActiveRelation]] instance
* The declaration is returned in terms of a relational [[ActiveQuery]] instance
* through which the related record can be queried and retrieved back.
*
* A `has-many` relation means that there are multiple related records matching
......@@ -350,6 +350,8 @@ abstract class BaseActiveRecord extends Model implements ActiveRecordInterface
* an attribute name in the related class `Order`, while the 'id' value refers to
* an attribute name in the current AR class.
*
* Call methods declared in [[ActiveQuery]] to further customize the relation.
*
* @param string $class the class name of the related record
* @param array $link the primary-foreign key constraint. The keys of the array refer to
* the attributes of the record associated with the `$class` model, while the values of the
......@@ -359,7 +361,7 @@ abstract class BaseActiveRecord extends Model implements ActiveRecordInterface
public function hasMany($class, $link)
{
/** @var ActiveRecord $class */
return $class::createRelation([
return $class::createQuery([
'modelClass' => $class,
'primaryModel' => $this,
'link' => $link,
......@@ -1035,10 +1037,10 @@ abstract class BaseActiveRecord extends Model implements ActiveRecordInterface
/**
* Returns the relation object with the specified name.
* A relation is defined by a getter method which returns an [[ActiveRelation]] object.
* A relation is defined by a getter method which returns an [[ActiveRelationInterface]] object.
* It can be declared in either the Active Record class itself or one of its behaviors.
* @param string $name the relation name
* @return ActiveRelation the relation object
* @return ActiveRelationInterface|ActiveQuery the relation object
* @throws InvalidParamException if the named relation does not exist.
*/
public function getRelation($name)
......@@ -1082,7 +1084,7 @@ abstract class BaseActiveRecord extends Model implements ActiveRecordInterface
* @param ActiveRecord $model the model to be linked with the current one.
* @param array $extraColumns additional column values to be saved into the pivot table.
* This parameter is only meaningful for a relationship involving a pivot table
* (i.e., a relation set with `[[ActiveRelation::via()]]` or `[[ActiveRelation::viaTable()]]`.)
* (i.e., a relation set with [[ActiveRelationTrait::via()]] or `[[ActiveQuery::viaTable()]]`.)
* @throws InvalidCallException if the method is unable to link two models.
*/
public function link($name, $model, $extraColumns = [])
......@@ -1094,7 +1096,7 @@ abstract class BaseActiveRecord extends Model implements ActiveRecordInterface
throw new InvalidCallException('Unable to link models: both models must NOT be newly created.');
}
if (is_array($relation->via)) {
/** @var ActiveRelation $viaRelation */
/** @var ActiveQuery $viaRelation */
list($viaName, $viaRelation) = $relation->via;
$viaClass = $viaRelation->modelClass;
// unset $viaName so that it can be reloaded to reflect the change
......@@ -1178,7 +1180,7 @@ abstract class BaseActiveRecord extends Model implements ActiveRecordInterface
if ($relation->via !== null) {
if (is_array($relation->via)) {
/** @var ActiveRelation $viaRelation */
/** @var ActiveQuery $viaRelation */
list($viaName, $viaRelation) = $relation->via;
$viaClass = $viaRelation->modelClass;
unset($this->_related[$viaName]);
......
......@@ -41,8 +41,9 @@ class Customer extends ActiveRecord
parent::afterSave($insert);
}
public static function createQuery()
public static function createQuery($config)
{
return new CustomerQuery(['modelClass' => get_called_class()]);
$config['modelClass'] = get_called_class();
return new CustomerQuery($config);
}
}
......@@ -63,8 +63,9 @@ class Customer extends ActiveRecord
}
public static function createQuery()
public static function createQuery($config)
{
return new CustomerQuery(['modelClass' => get_called_class()]);
$config['modelClass'] = get_called_class();
return new CustomerQuery($config);
}
}
......@@ -25,8 +25,9 @@ class Customer extends ActiveRecord
return $this->hasMany(CustomerOrder::className(), ['customer_id' => '_id']);
}
public static function createQuery()
public static function createQuery($config)
{
return new CustomerQuery(['modelClass' => get_called_class()]);
$config['modelClass'] = get_called_class();
return new CustomerQuery($config);
}
}
\ No newline at end of file
......@@ -20,8 +20,9 @@ class CustomerFile extends ActiveRecord
);
}
public static function createQuery()
public static function createQuery($config)
{
return new CustomerFileQuery(['modelClass' => get_called_class()]);
$config['modelClass'] = get_called_class();
return new CustomerFileQuery($config);
}
}
\ No newline at end of file
......@@ -17,7 +17,7 @@ class Customer extends ActiveRecord
}
/**
* @return \yii\redis\ActiveRelation
* @return \yii\redis\ActiveQuery
*/
public function getOrders()
{
......@@ -31,8 +31,9 @@ class Customer extends ActiveRecord
parent::afterSave($insert);
}
public static function createQuery()
public static function createQuery($config)
{
return new CustomerQuery(['modelClass' => get_called_class()]);
$config['modelClass'] = get_called_class();
return new CustomerQuery($config);
}
}
\ No newline at end of file
......@@ -2,7 +2,7 @@
namespace yiiunit\data\ar\sphinx;
use yii\sphinx\ActiveRelation;
use yii\sphinx\ActiveQuery;
use yiiunit\data\ar\ActiveRecord as ActiveRecordDb;
class ArticleDb extends ActiveRecordDb
......@@ -20,6 +20,6 @@ class ArticleDb extends ActiveRecordDb
'link' => ['id' => 'id'],
'multiple' => false,
];
return new ActiveRelation($config);
return new ActiveQuery($config);
}
}
\ No newline at end of file
......@@ -25,8 +25,9 @@ class ArticleIndex extends ActiveRecord
return $this->source->content;
}
public static function createQuery()
public static function createQuery($config)
{
return new ArticleIndexQuery(['modelClass' => get_called_class()]);
$config['modelClass'] = get_called_class();
return new ArticleIndexQuery($config);
}
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment