Commit 5dffd72c by Klimov Paul

Added support for 'findAndModify' operation at `yii\mongodb\Query` and `yii\mongodb\ActiveQuery`

parent 8376f446
...@@ -135,21 +135,8 @@ class ActiveQuery extends Query implements ActiveQueryInterface ...@@ -135,21 +135,8 @@ class ActiveQuery extends Query implements ActiveQueryInterface
{ {
$cursor = $this->buildCursor($db); $cursor = $this->buildCursor($db);
$rows = $this->fetchRows($cursor); $rows = $this->fetchRows($cursor);
if (!empty($rows)) {
$models = $this->createModels($rows);
if (!empty($this->with)) {
$this->findWith($this->with, $models);
}
if (!$this->asArray) {
foreach ($models as $model) {
$model->afterFind();
}
}
return $models; return $this->populate($rows);
} else {
return [];
}
} }
/** /**
...@@ -164,24 +151,30 @@ class ActiveQuery extends Query implements ActiveQueryInterface ...@@ -164,24 +151,30 @@ class ActiveQuery extends Query implements ActiveQueryInterface
{ {
$row = parent::one($db); $row = parent::one($db);
if ($row !== false) { if ($row !== false) {
if ($this->asArray) { $models = $this->populate([$row]);
$model = $row; return reset($models) ?: null;
} else { } else {
/* @var $class ActiveRecord */ return null;
$class = $this->modelClass;
$model = $class::instantiate($row);
$class::populateRecord($model, $row);
}
if (!empty($this->with)) {
$models = [$model];
$this->findWith($this->with, $models);
$model = $models[0];
} }
if (!$this->asArray) {
$model->afterFind();
} }
return $model; /**
* Performs 'findAndModify' query and returns a single row of result.
* Warning: in case 'new' option is set to 'false' (which is by default) usage of this method may lead
* to unexpected behavior at some Active Record features, because object will be populated by outdated data.
* @param array $update update criteria
* @param array $options list of options in format: optionName => optionValue.
* @param Connection $db the Mongo connection used to execute the query.
* @return ActiveRecord|array|null the original document, or the modified document when $options['new'] is set.
* Depending on the setting of [[asArray]], the query result may be either an array or an ActiveRecord object.
* Null will be returned if the query results in nothing.
*/
public function oneWithUpdate($update, $options = [], $db = null)
{
$row = parent::oneWithUpdate($update, $options, $db);
if ($row !== null) {
$models = $this->populate([$row]);
return reset($models) ?: null;
} else { } else {
return null; return null;
} }
...@@ -205,4 +198,30 @@ class ActiveQuery extends Query implements ActiveQueryInterface ...@@ -205,4 +198,30 @@ class ActiveQuery extends Query implements ActiveQueryInterface
return $db->getCollection($this->from); return $db->getCollection($this->from);
} }
/**
* Converts the raw query results into the format as specified by this query.
* This method is internally used to convert the data fetched from MongoDB
* into the format as required by this query.
* @param array $rows the raw query result from MongoDB
* @return array the converted query result
*/
public function populate($rows)
{
if (empty($rows)) {
return [];
}
$models = $this->createModels($rows);
if (!empty($this->with)) {
$this->findWith($this->with, $models);
}
if (!$this->asArray) {
foreach ($models as $model) {
$model->afterFind();
}
}
return $models;
}
} }
...@@ -5,6 +5,7 @@ Yii Framework 2 mongodb extension Change Log ...@@ -5,6 +5,7 @@ Yii Framework 2 mongodb extension Change Log
----------------------- -----------------------
- Enh #3855: Added debug toolbar panel for MongoDB (klimov-paul) - Enh #3855: Added debug toolbar panel for MongoDB (klimov-paul)
- Enh #5592: Added support for 'findAndModify' operation at `yii\mongodb\Query` and `yii\mongodb\ActiveQuery` (klimov-paul)
2.0.0 October 12, 2014 2.0.0 October 12, 2014
......
...@@ -102,24 +102,9 @@ class Query extends Component implements QueryInterface ...@@ -102,24 +102,9 @@ class Query extends Component implements QueryInterface
*/ */
protected function buildCursor($db = null) protected function buildCursor($db = null)
{ {
if ($this->where === null) { $cursor = $this->getCollection($db)->find($this->composeCondition(), $this->composeSelectFields());
$where = [];
} else {
$where = $this->where;
}
$selectFields = [];
if (!empty($this->select)) {
foreach ($this->select as $fieldName) {
$selectFields[$fieldName] = true;
}
}
$cursor = $this->getCollection($db)->find($where, $selectFields);
if (!empty($this->orderBy)) { if (!empty($this->orderBy)) {
$sort = []; $cursor->sort($this->composeSort());
foreach ($this->orderBy as $fieldName => $sortOrder) {
$sort[$fieldName] = $sortOrder === SORT_DESC ? \MongoCollection::DESCENDING : \MongoCollection::ASCENDING;
}
$cursor->sort($sort);
} }
$cursor->limit($this->limit); $cursor->limit($this->limit);
$cursor->skip($this->offset); $cursor->skip($this->offset);
...@@ -214,6 +199,23 @@ class Query extends Component implements QueryInterface ...@@ -214,6 +199,23 @@ class Query extends Component implements QueryInterface
} }
/** /**
* Performs 'findAndModify' query and returns a single row of result.
* @param array $update update criteria
* @param array $options list of options in format: optionName => optionValue.
* @param Connection $db the Mongo connection used to execute the query.
* @return array|null the original document, or the modified document when $options['new'] is set.
*/
public function oneWithUpdate($update, $options = [], $db = null)
{
$collection = $this->getCollection($db);
if (!empty($this->orderBy)) {
$options['sort'] = $this->composeSort();
}
return $collection->findAndModify($this->composeCondition(), $update, $this->composeSelectFields(), $options);
}
/**
* Returns the number of records. * Returns the number of records.
* @param string $q kept to match [[QueryInterface]], its value is ignored. * @param string $q kept to match [[QueryInterface]], its value is ignored.
* @param Connection $db the Mongo connection used to execute the query. * @param Connection $db the Mongo connection used to execute the query.
...@@ -353,4 +355,45 @@ class Query extends Component implements QueryInterface ...@@ -353,4 +355,45 @@ class Query extends Component implements QueryInterface
return $result; return $result;
} }
} }
/**
* Composes condition from raw [[where]] value.
* @return array conditions.
*/
private function composeCondition()
{
if ($this->where === null) {
return [];
} else {
return $this->where;
}
}
/**
* Composes select fields from raw [[select]] value.
* @return array select fields.
*/
private function composeSelectFields()
{
$selectFields = [];
if (!empty($this->select)) {
foreach ($this->select as $fieldName) {
$selectFields[$fieldName] = true;
}
}
return $selectFields;
}
/**
* Composes sort specification from raw [[orderBy]] value.
* @return array sort specification.
*/
private function composeSort()
{
$sort = [];
foreach ($this->orderBy as $fieldName => $sortOrder) {
$sort[$fieldName] = $sortOrder === SORT_DESC ? \MongoCollection::DESCENDING : \MongoCollection::ASCENDING;
}
return $sort;
}
} }
...@@ -263,4 +263,16 @@ class ActiveRecordTest extends MongoDbTestCase ...@@ -263,4 +263,16 @@ class ActiveRecordTest extends MongoDbTestCase
$this->assertNotEmpty($rowRefreshed); $this->assertNotEmpty($rowRefreshed);
$this->assertEquals(7, $rowRefreshed->status); $this->assertEquals(7, $rowRefreshed->status);
} }
public function testFindOneWithUpdate()
{
$searchName = 'name7';
$newName = 'new name';
$customer = Customer::find()
->where(['name' => $searchName])
->oneWithUpdate(['$set' => ['name' => $newName]], ['new' => true]);
$this->assertTrue($customer instanceof Customer);
$this->assertEquals($newName, $customer->name);
}
} }
...@@ -211,6 +211,27 @@ class QueryRunTest extends MongoDbTestCase ...@@ -211,6 +211,27 @@ class QueryRunTest extends MongoDbTestCase
$this->assertEquals($rows, $rowsUppercase); $this->assertEquals($rows, $rowsUppercase);
} }
public function testOneWithUpdate()
{
$connection = $this->getConnection();
$query = new Query();
$searchName = 'name5';
$newName = 'new name';
$row = $query->from('customer')
->where(['name' => $searchName])
->oneWithUpdate(['$set' => ['name' => $newName]], ['new' => false], $connection);
$this->assertEquals($searchName, $row['name']);
$searchName = 'name7';
$newName = 'new name';
$row = $query->from('customer')
->where(['name' => $searchName])
->oneWithUpdate(['$set' => ['name' => $newName]], ['new' => true], $connection);
$this->assertEquals($newName, $row['name']);
}
/** /**
* @see https://github.com/yiisoft/yii2/issues/4879 * @see https://github.com/yiisoft/yii2/issues/4879
* *
......
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