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
{
$cursor = $this->buildCursor($db);
$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;
} else {
return [];
}
return $this->populate($rows);
}
/**
......@@ -164,24 +151,30 @@ class ActiveQuery extends Query implements ActiveQueryInterface
{
$row = parent::one($db);
if ($row !== false) {
if ($this->asArray) {
$model = $row;
} else {
/* @var $class ActiveRecord */
$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();
}
$models = $this->populate([$row]);
return reset($models) ?: null;
} else {
return null;
}
}
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 {
return null;
}
......@@ -205,4 +198,30 @@ class ActiveQuery extends Query implements ActiveQueryInterface
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
-----------------------
- 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
......
......@@ -102,24 +102,9 @@ class Query extends Component implements QueryInterface
*/
protected function buildCursor($db = null)
{
if ($this->where === null) {
$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);
$cursor = $this->getCollection($db)->find($this->composeCondition(), $this->composeSelectFields());
if (!empty($this->orderBy)) {
$sort = [];
foreach ($this->orderBy as $fieldName => $sortOrder) {
$sort[$fieldName] = $sortOrder === SORT_DESC ? \MongoCollection::DESCENDING : \MongoCollection::ASCENDING;
}
$cursor->sort($sort);
$cursor->sort($this->composeSort());
}
$cursor->limit($this->limit);
$cursor->skip($this->offset);
......@@ -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.
* @param string $q kept to match [[QueryInterface]], its value is ignored.
* @param Connection $db the Mongo connection used to execute the query.
......@@ -353,4 +355,45 @@ class Query extends Component implements QueryInterface
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
$this->assertNotEmpty($rowRefreshed);
$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
$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
*
......
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