Commit 0e143338 by Qiang Xue

Finished refactoring find() .

parent b7d6f614
......@@ -169,10 +169,9 @@ $customers = Customer::findBySql($sql)->all();
use meaningful constant names rather than hardcoded strings or numbers in your code.
There is a shortcut method `findOne()` that allows you to retrieve an Active Record instance based on a primary
key value or a set of column values. The main difference here is that instead of returning a [[yii\db\ActiveQuery]]
instance, the method takes the column value(s) and returns an Active Record instance directly without the need
to call `one()`.
Two shortcut methods are provided to return Active Record instances matching a primary key value or a set of
column values: `findOne()` and `findAll()`. The former returns the first matching instance while the latter
returns all of them. For example,
```php
// to return a single customer whose ID is 1:
......@@ -183,6 +182,14 @@ $customer = Customer::findOne([
'id' => 1,
'status' => Customer::STATUS_ACTIVE,
]);
// to return customers whose ID is 1, 2 or 3:
$customers = Customer::findAll([1, 2, 3]);
// to return customers whose status is "deleted":
$customer = Customer::findAll([
'status' => Customer::STATUS_DELETED,
]);
```
......
......@@ -10,6 +10,7 @@ namespace yii\elasticsearch;
use yii\base\InvalidCallException;
use yii\base\InvalidConfigException;
use yii\db\BaseActiveRecord;
use yii\helpers\ArrayHelper;
use yii\helpers\Inflector;
use yii\helpers\Json;
use yii\helpers\StringHelper;
......@@ -73,13 +74,26 @@ class ActiveRecord extends BaseActiveRecord
/**
* @inheritdoc
*/
public static function findOne($condtion)
public static function findOne($condition)
{
$query = static::find();
if (is_array($condtion)) {
return $query->andWhere($condtion)->one();
if (is_array($condition)) {
return $query->andWhere($condition)->one();
} else {
return static::get($condtion);
return static::get($condition);
}
}
/**
* @inheritdoc
*/
public static function findAll($condition)
{
$query = static::find();
if (ArrayHelper::isAssociative($condition)) {
return $query->andWhere($condition)->all();
} else {
return static::mget((array) $condition);
}
}
......@@ -120,14 +134,18 @@ class ActiveRecord extends BaseActiveRecord
*
* Please refer to the [elasticsearch documentation](http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/docs-get.html)
* for more details on these options.
* @return static|null The record instance or null if it was not found.
* @return array The record instances, or empty array if nothing was found
*/
public static function mget($primaryKeys, $options = [])
public static function mget(array $primaryKeys, $options = [])
{
if (empty($primaryKeys)) {
return [];
}
if (count($primaryKeys) === 1) {
$model = static::get(reset($primaryKeys));
return $model === null ? [] : [$model];
}
$command = static::getDb()->createCommand();
$result = $command->mget(static::index(), static::type(), $primaryKeys, $options);
$models = [];
......
......@@ -423,7 +423,7 @@ abstract class ActiveRecord extends BaseActiveRecord
* For example, to update an article record:
*
* ~~~
* $article = Article::find(['id' => $id]);
* $article = Article::findOne($id);
* $article->genre_id = $genreId;
* $article->group_id = $groupId;
* $article->update();
......
......@@ -114,27 +114,44 @@ interface ActiveRecordInterface
* This method is also called 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.)
* You may override this method to return a customized query. For example,
*
* You may also define default conditions that should apply to all queries unless overridden:
* ```php
* class Customer extends ActiveRecord
* {
* public static function find()
* {
* // use CustomerQuery instead of the default ActiveQuery
* return new CustomerQuery(get_called_class());
* }
* }
* ```
*
* The following code shows how to apply a default condition for all queries:
*
* ```php
* class Customer extends ActiveRecord
* {
* public static function find()
* {
* return parent::find()->where(['deleted' => false]);
* }
* ```
* }
*
* // Use andWhere()/orWhere() to apply the default condition
* // SELECT FROM customer WHERE `deleted`=:deleted AND age>30
* $customers = Customer::find()->andWhere('age>30')->all();
*
* Note that all queries should use [[Query::andWhere()]] and [[Query::orWhere()]] to keep the
* default condition. Using [[Query::where()]] will override the default condition.
* // Use where() to ignore the default condition
* // SELECT FROM customer WHERE age>30
* $customers = Customer::find()->where('age>30')->all();
*
* @return ActiveQueryInterface the newly created [[ActiveQueryInterface|ActiveQuery]] instance.
*/
public static function find();
/**
* Returns a single active record model instance given a primary key or an array of column values.
* Returns a single active record model instance by a primary key or an array of column values.
*
* The method accepts:
*
......@@ -143,7 +160,7 @@ interface ActiveRecordInterface
* - an array of name-value pairs: query by a set of attribute values and return a single record
* matching all of them (or null if not found).
*
* Note that in this case, the method will automatically call the `one()` method and return an
* Note that this method will automatically call the `one()` method and return an
* [[ActiveRecordInterface|ActiveRecord]] instance. For example,
*
* ```php
......@@ -153,7 +170,7 @@ interface ActiveRecordInterface
* // the above code is equivalent to:
* $customer = Customer::find()->where(['id' => 10])->one();
*
* // find a single customer whose age is 30 and whose status is 1
* // find the first customer whose age is 30 and whose status is 1
* $customer = Customer::findOne(['age' => 30, 'status' => 1]);
*
* // the above code is equivalent to:
......@@ -161,11 +178,49 @@ interface ActiveRecordInterface
* ```
*
* @param mixed $condition primary key value or a set of column values
* @return static ActiveRecord instance or null if nothing matched
* @return static ActiveRecord instance matching the condition, or null if nothing matches.
*/
public static function findOne($condition);
/**
* Returns a list of active record models that match the specified primary key value or a set of column values.
*
* The method accepts:
*
* - a scalar value (integer or string): query by a single primary key value and return the
* corresponding record (or null if not found).
* - an array of name-value pairs: query by a set of attribute values and return a single record
* matching all of them (or null if not found).
*
* Note that this method will automatically call the `all()` method and return an array of
* [[ActiveRecordInterface|ActiveRecord]] instances. For example,
*
* ```php
* // find the customers whose primary key value is 10
* $customers = Customer::findAll(10);
*
* // the above code is equivalent to:
* $customers = Customer::find()->where(['id' => 10])->all();
*
* // find the customers whose primary key value is 10, 11 or 12.
* $customers = Customer::findAll([10, 11, 12]);
*
* // the above code is equivalent to:
* $customers = Customer::find()->where(['id' => [10, 11, 12]])->all();
*
* // find customers whose age is 30 and whose status is 1
* $customers = Customer::findOne(['age' => 30, 'status' => 1]);
*
* // the above code is equivalent to:
* $customers = Customer::find()->where(['age' => 30, 'status' => 1])->all();
* ```
*
* @param mixed $condition primary key value or a set of column values
* @return array an array of ActiveRecord instance, or an empty array if nothing matches.
*/
public static function findAll($condition);
/**
* Updates records using the provided attribute values and conditions.
* For example, to change the status to be 1 for all customers whose status is 2:
*
......@@ -208,7 +263,7 @@ interface ActiveRecordInterface
* For example, to save a customer record:
*
* ~~~
* $customer = new Customer; // or $customer = Customer::find($id);
* $customer = new Customer; // or $customer = Customer::findOne($id);
* $customer->name = $name;
* $customer->email = $email;
* $customer->save();
......@@ -249,7 +304,7 @@ interface ActiveRecordInterface
* Usage example:
*
* ```php
* $customer = Customer::find($id);
* $customer = Customer::findOne($id);
* $customer->name = $name;
* $customer->email = $email;
* $customer->update();
......
......@@ -15,6 +15,7 @@ use yii\base\ModelEvent;
use yii\base\NotSupportedException;
use yii\base\UnknownMethodException;
use yii\base\InvalidCallException;
use yii\helpers\ArrayHelper;
/**
* ActiveRecord is the base class for classes representing relational data in terms of objects.
......@@ -97,7 +98,7 @@ abstract class BaseActiveRecord extends Model implements ActiveRecordInterface
public static function findOne($condition)
{
$query = static::find();
if (is_array($condition)) {
if (ArrayHelper::isAssociative($condition)) {
// hash condition
return $query->andWhere($condition)->one();
} else {
......@@ -117,15 +118,14 @@ abstract class BaseActiveRecord extends Model implements ActiveRecordInterface
public static function findAll($condition)
{
$query = static::find();
if (is_array($condition)) {
if (ArrayHelper::isAssociative($condition)) {
// hash condition
return $query->andWhere($condition)->one();
return $query->andWhere($condition)->all();
} else {
// query by primary key
// query by primary key(s)
$primaryKey = static::primaryKey();
if (isset($primaryKey[0])) {
return $query->andWhere([$primaryKey[0] => $condition])->one();
return $query->andWhere([$primaryKey[0] => $condition])->all();
} else {
throw new InvalidConfigException(get_called_class() . ' must have a primary key.');
}
......@@ -966,7 +966,7 @@ abstract class BaseActiveRecord extends Model implements ActiveRecordInterface
/**
* Returns the old primary key value(s).
* This refers to the primary key value that is populated into the record
* after executing a find method (e.g. one(), findOne()).
* after executing a find method (e.g. find(), findOne()).
* The value remains unchanged even if the primary key attribute is manually assigned with a different value.
* @param boolean $asArray whether to return the primary key value as an array. If true,
* the return value will be an array with column name as key and column value as value.
......
......@@ -504,9 +504,9 @@ class BaseArrayHelper
* the array to be treated as associative.
* @return boolean whether the array is associative
*/
public static function isAssociative(array $array, $allStrings = true)
public static function isAssociative($array, $allStrings = true)
{
if (empty($array)) {
if (!is_array($array) || empty($array)) {
return false;
}
......@@ -540,8 +540,12 @@ class BaseArrayHelper
* in order for the array to be treated as indexed.
* @return boolean whether the array is associative
*/
public static function isIndexed(array $array, $consecutive = false)
public static function isIndexed($array, $consecutive = false)
{
if (!is_array($array)) {
return false;
}
if (empty($array)) {
return true;
}
......
......@@ -86,16 +86,16 @@ class ActiveRecordTest extends MongoDbTestCase
// find by _id
$testId = $this->testRows[0]['_id'];
$customer = CustomerFile::find($testId);
$customer = CustomerFile::findOne($testId);
$this->assertTrue($customer instanceof CustomerFile);
$this->assertEquals($testId, $customer->_id);
// find by column values
$customer = CustomerFile::find(['tag' => 'tag5']);
$customer = CustomerFile::findOne(['tag' => 'tag5']);
$this->assertTrue($customer instanceof CustomerFile);
$this->assertEquals($this->testRows[4]['_id'], $customer->_id);
$this->assertEquals('tag5', $customer->tag);
$customer = CustomerFile::find(['tag' => 'unexisting tag']);
$customer = CustomerFile::findOne(['tag' => 'unexisting tag']);
$this->assertNull($customer);
// find by attributes
......@@ -205,7 +205,7 @@ class ActiveRecordTest extends MongoDbTestCase
$record->save();
// save
$record = CustomerFile::find($record->_id);
$record = CustomerFile::findOne($record->_id);
$this->assertTrue($record instanceof CustomerFile);
$this->assertEquals(7, $record->status);
$this->assertFalse($record->isNewRecord);
......@@ -214,14 +214,14 @@ class ActiveRecordTest extends MongoDbTestCase
$record->save();
$this->assertEquals(9, $record->status);
$this->assertFalse($record->isNewRecord);
$record2 = CustomerFile::find($record->_id);
$record2 = CustomerFile::findOne($record->_id);
$this->assertEquals(9, $record2->status);
// updateAll
$pk = ['_id' => $record->_id];
$ret = CustomerFile::updateAll(['status' => 55], $pk);
$this->assertEquals(1, $ret);
$record = CustomerFile::find($pk);
$record = CustomerFile::findOne($pk);
$this->assertEquals(55, $record->status);
}
......@@ -239,13 +239,13 @@ class ActiveRecordTest extends MongoDbTestCase
$record->save();
$updateFileName = __FILE__;
$record = CustomerFile::find($record->_id);
$record = CustomerFile::findOne($record->_id);
$record->setAttribute('file', $updateFileName);
$record->status = 55;
$record->save();
$this->assertEquals(file_get_contents($updateFileName), $record->getFileContent());
$record2 = CustomerFile::find($record->_id);
$record2 = CustomerFile::findOne($record->_id);
$this->assertEquals($record->status, $record2->status);
$this->assertEquals(file_get_contents($updateFileName), $record2->getFileContent());
$this->assertEquals($record->tag, $record2->tag);
......@@ -265,13 +265,13 @@ class ActiveRecordTest extends MongoDbTestCase
$record->save();
$updateFileContent = 'New updated file content';
$record = CustomerFile::find($record->_id);
$record = CustomerFile::findOne($record->_id);
$record->setAttribute('newFileContent', $updateFileContent);
$record->status = 55;
$record->save();
$this->assertEquals($updateFileContent, $record->getFileContent());
$record2 = CustomerFile::find($record->_id);
$record2 = CustomerFile::findOne($record->_id);
$this->assertEquals($record->status, $record2->status);
$this->assertEquals($updateFileContent, $record2->getFileContent());
}
......@@ -292,7 +292,7 @@ class ActiveRecordTest extends MongoDbTestCase
$this->assertTrue($record->writeFile($outputFileName));
$this->assertEquals($newFileContent, file_get_contents($outputFileName));
$record2 = CustomerFile::find($record->_id);
$record2 = CustomerFile::findOne($record->_id);
$outputFileName = $this->getTestFilePath() . DIRECTORY_SEPARATOR . 'out_refreshed.txt';
$this->assertTrue($record2->writeFile($outputFileName));
$this->assertEquals($newFileContent, file_get_contents($outputFileName));
......@@ -315,7 +315,7 @@ class ActiveRecordTest extends MongoDbTestCase
fclose($fileResource);
$this->assertEquals($newFileContent, $contents);
$record2 = CustomerFile::find($record->_id);
$record2 = CustomerFile::findOne($record->_id);
$fileResource = $record2->getFileResource();
$contents = stream_get_contents($fileResource);
fclose($fileResource);
......
......@@ -380,6 +380,7 @@ class ArrayHelperTest extends TestCase
public function testIsAssociative()
{
$this->assertFalse(ArrayHelper::isAssociative('test'));
$this->assertFalse(ArrayHelper::isAssociative([]));
$this->assertFalse(ArrayHelper::isAssociative([1, 2, 3]));
$this->assertTrue(ArrayHelper::isAssociative(['name' => 1, 'value' => 'test']));
......@@ -389,6 +390,7 @@ class ArrayHelperTest extends TestCase
public function testIsIndexed()
{
$this->assertFalse(ArrayHelper::isIndexed('test'));
$this->assertTrue(ArrayHelper::isIndexed([]));
$this->assertTrue(ArrayHelper::isIndexed([1, 2, 3]));
$this->assertTrue(ArrayHelper::isIndexed([2 => 'a', 3 => 'b']));
......
......@@ -57,38 +57,38 @@ class ExistValidatorTest extends DatabaseTestCase
{
// existing value on different table
$val = new ExistValidator(['targetClass' => ValidatorTestMainModel::className(), 'targetAttribute' => 'id']);
$m = ValidatorTestRefModel::find(['id' => 1]);
$m = ValidatorTestRefModel::findOne(['id' => 1]);
$val->validateAttribute($m, 'ref');
$this->assertFalse($m->hasErrors());
// non-existing value on different table
$val = new ExistValidator(['targetClass' => ValidatorTestMainModel::className(), 'targetAttribute' => 'id']);
$m = ValidatorTestRefModel::find(['id' => 6]);
$m = ValidatorTestRefModel::findOne(['id' => 6]);
$val->validateAttribute($m, 'ref');
$this->assertTrue($m->hasErrors('ref'));
// existing value on same table
$val = new ExistValidator(['targetAttribute' => 'ref']);
$m = ValidatorTestRefModel::find(['id' => 2]);
$m = ValidatorTestRefModel::findOne(['id' => 2]);
$val->validateAttribute($m, 'test_val');
$this->assertFalse($m->hasErrors());
// non-existing value on same table
$val = new ExistValidator(['targetAttribute' => 'ref']);
$m = ValidatorTestRefModel::find(['id' => 5]);
$m = ValidatorTestRefModel::findOne(['id' => 5]);
$val->validateAttribute($m, 'test_val_fail');
$this->assertTrue($m->hasErrors('test_val_fail'));
// check for given value (true)
$val = new ExistValidator();
$m = ValidatorTestRefModel::find(['id' => 3]);
$m = ValidatorTestRefModel::findOne(['id' => 3]);
$val->validateAttribute($m, 'ref');
$this->assertFalse($m->hasErrors());
// check for given defaults (false)
$val = new ExistValidator();
$m = ValidatorTestRefModel::find(['id' => 4]);
$m = ValidatorTestRefModel::findOne(['id' => 4]);
$m->a_field = 'some new value';
$val->validateAttribute($m, 'a_field');
$this->assertTrue($m->hasErrors('a_field'));
// check array
$val = new ExistValidator(['targetAttribute' => 'ref']);
$m = ValidatorTestRefModel::find(['id' => 2]);
$m = ValidatorTestRefModel::findOne(['id' => 2]);
$m->test_val = [1,2,3];
$val->validateAttribute($m, 'test_val');
$this->assertTrue($m->hasErrors('test_val'));
......
......@@ -35,7 +35,7 @@ class UniqueValidatorTest extends DatabaseTestCase
$m = ValidatorTestMainModel::find()->one();
$val->validateAttribute($m, 'id');
$this->assertFalse($m->hasErrors('id'));
$m = ValidatorTestRefModel::find(1);
$m = ValidatorTestRefModel::findOne(1);
$val->validateAttribute($m, 'ref');
$this->assertTrue($m->hasErrors('ref'));
// new record:
......@@ -70,10 +70,10 @@ class UniqueValidatorTest extends DatabaseTestCase
public function testValidateNonDatabaseAttribute()
{
$val = new UniqueValidator(['targetClass' => ValidatorTestRefModel::className(), 'targetAttribute' => 'ref']);
$m = ValidatorTestMainModel::find(1);
$m = ValidatorTestMainModel::findOne(1);
$val->validateAttribute($m, 'testMainVal');
$this->assertFalse($m->hasErrors('testMainVal'));
$m = ValidatorTestMainModel::find(1);
$m = ValidatorTestMainModel::findOne(1);
$m->testMainVal = 4;
$val->validateAttribute($m, 'testMainVal');
$this->assertTrue($m->hasErrors('testMainVal'));
......@@ -94,7 +94,7 @@ class UniqueValidatorTest extends DatabaseTestCase
'targetAttribute' => ['order_id', 'item_id'],
]);
// validate old record
$m = OrderItem::find(['order_id' => 1, 'item_id' => 2]);
$m = OrderItem::findOne(['order_id' => 1, 'item_id' => 2]);
$val->validateAttribute($m, 'order_id');
$this->assertFalse($m->hasErrors('order_id'));
$m->item_id = 1;
......@@ -114,14 +114,14 @@ class UniqueValidatorTest extends DatabaseTestCase
'targetAttribute' => ['id' => 'order_id'],
]);
// validate old record
$m = Order::find(1);
$m = Order::findOne(1);
$val->validateAttribute($m, 'id');
$this->assertTrue($m->hasErrors('id'));
$m = Order::find(1);
$m = Order::findOne(1);
$m->id = 2;
$val->validateAttribute($m, 'id');
$this->assertTrue($m->hasErrors('id'));
$m = Order::find(1);
$m = Order::findOne(1);
$m->id = 10;
$val->validateAttribute($m, 'id');
$this->assertFalse($m->hasErrors('id'));
......
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