Skip to content

Commit

Permalink
Merge branch '1.1.x'
Browse files Browse the repository at this point in the history
  • Loading branch information
malarzm committed Aug 5, 2016
2 parents b4a4947 + d3dddc0 commit 7bba4a2
Show file tree
Hide file tree
Showing 3 changed files with 138 additions and 35 deletions.
100 changes: 68 additions & 32 deletions lib/Doctrine/ODM/MongoDB/Persisters/DocumentPersister.php
Original file line number Diff line number Diff line change
Expand Up @@ -908,9 +908,9 @@ public function prepareSortOrProjection(array $fields)
*/
public function prepareFieldName($fieldName)
{
list($fieldName) = $this->prepareQueryElement($fieldName, null, null, false);
$fieldNames = $this->prepareQueryElement($fieldName, null, null, false);

return $fieldName;
return $fieldNames[0][0];
}

/**
Expand Down Expand Up @@ -991,11 +991,12 @@ public function prepareQueryOrNewObj(array $query)
continue;
}

list($key, $value) = $this->prepareQueryElement($key, $value, null, true);

$preparedQuery[$key] = is_array($value)
? array_map('\Doctrine\ODM\MongoDB\Types\Type::convertPHPToDatabaseValue', $value)
: Type::convertPHPToDatabaseValue($value);
$preparedQueryElements = $this->prepareQueryElement($key, $value, null, true);
foreach ($preparedQueryElements as list($preparedKey, $preparedValue)) {
$preparedQuery[$preparedKey] = is_array($preparedValue)
? array_map('\Doctrine\ODM\MongoDB\Types\Type::convertPHPToDatabaseValue', $preparedValue)
: Type::convertPHPToDatabaseValue($preparedValue);
}
}

return $preparedQuery;
Expand All @@ -1011,7 +1012,7 @@ public function prepareQueryOrNewObj(array $query)
* @param mixed $value
* @param ClassMetadata $class Defaults to $this->class
* @param boolean $prepareValue Whether or not to prepare the value
* @return array Prepared field name and value
* @return array An array of tuples containing prepared field names and values
*/
private function prepareQueryElement($fieldName, $value = null, $class = null, $prepareValue = true)
{
Expand All @@ -1025,18 +1026,18 @@ private function prepareQueryElement($fieldName, $value = null, $class = null, $
$fieldName = $mapping['name'];

if ( ! $prepareValue) {
return array($fieldName, $value);
return [[$fieldName, $value]];
}

// Prepare mapped, embedded objects
if ( ! empty($mapping['embedded']) && is_object($value) &&
! $this->dm->getMetadataFactory()->isTransient(get_class($value))) {
return array($fieldName, $this->pb->prepareEmbeddedDocumentValue($mapping, $value));
return [[$fieldName, $this->pb->prepareEmbeddedDocumentValue($mapping, $value)]];
}

if (! empty($mapping['reference']) && is_object($value) && ! ($value instanceof \MongoId)) {
try {
return array($fieldName, $this->dm->createDBRef($value, $mapping));
return $this->prepareDbRefElement($fieldName, $value, $mapping);
} catch (MappingException $e) {
// do nothing in case passed object is not mapped document
}
Expand All @@ -1046,47 +1047,47 @@ private function prepareQueryElement($fieldName, $value = null, $class = null, $
// We can't have expressions in empty() with PHP < 5.5, so store it in a variable
$arrayValue = (array) $value;
if (empty($mapping['reference']) || $mapping['storeAs'] !== ClassMetadataInfo::REFERENCE_STORE_AS_ID || empty($arrayValue)) {
return array($fieldName, $value);
return [[$fieldName, $value]];
}

// Additional preparation for one or more simple reference values
$targetClass = $this->dm->getClassMetadata($mapping['targetDocument']);

if ( ! is_array($value)) {
return array($fieldName, $targetClass->getDatabaseIdentifierValue($value));
return [[$fieldName, $targetClass->getDatabaseIdentifierValue($value)]];
}

// Objects without operators or with DBRef fields can be converted immediately
if ( ! $this->hasQueryOperators($value) || $this->hasDBRefFields($value)) {
return array($fieldName, $targetClass->getDatabaseIdentifierValue($value));
return [[$fieldName, $targetClass->getDatabaseIdentifierValue($value)]];
}

return array($fieldName, $this->prepareQueryExpression($value, $targetClass));
return [[$fieldName, $this->prepareQueryExpression($value, $targetClass)]];
}

// Process identifier fields
if (($class->hasField($fieldName) && $class->isIdentifier($fieldName)) || $fieldName === '_id') {
$fieldName = '_id';

if ( ! $prepareValue) {
return array($fieldName, $value);
return [[$fieldName, $value]];
}

if ( ! is_array($value)) {
return array($fieldName, $class->getDatabaseIdentifierValue($value));
return [[$fieldName, $class->getDatabaseIdentifierValue($value)]];
}

// Objects without operators or with DBRef fields can be converted immediately
if ( ! $this->hasQueryOperators($value) || $this->hasDBRefFields($value)) {
return array($fieldName, $class->getDatabaseIdentifierValue($value));
return [[$fieldName, $class->getDatabaseIdentifierValue($value)]];
}

return array($fieldName, $this->prepareQueryExpression($value, $class));
return [[$fieldName, $this->prepareQueryExpression($value, $class)]];
}

// No processing for unmapped, non-identifier, non-dotted field names
if (strpos($fieldName, '.') === false) {
return array($fieldName, $value);
return [[$fieldName, $value]];
}

/* Process "fieldName.objectProperty" queries (on arrays or objects).
Expand All @@ -1099,7 +1100,7 @@ private function prepareQueryElement($fieldName, $value = null, $class = null, $

// No further processing for unmapped fields
if ( ! isset($class->fieldMappings[$e[0]])) {
return array($fieldName, $value);
return [[$fieldName, $value]];
}

$mapping = $class->fieldMappings[$e[0]];
Expand All @@ -1109,7 +1110,7 @@ private function prepareQueryElement($fieldName, $value = null, $class = null, $
if ($mapping['type'] === Type::HASH || $mapping['type'] === Type::RAW) {
$fieldName = implode('.', $e);

return array($fieldName, $value);
return [[$fieldName, $value]];
}

if ($mapping['type'] == 'many' && CollectionHelper::isHash($mapping['strategy'])
Expand All @@ -1130,7 +1131,7 @@ private function prepareQueryElement($fieldName, $value = null, $class = null, $
} else {
$fieldName = $e[0] . '.' . $e[1];

return array($fieldName, $value);
return [[$fieldName, $value]];
}

// No further processing for fields without a targetDocument mapping
Expand All @@ -1139,7 +1140,7 @@ private function prepareQueryElement($fieldName, $value = null, $class = null, $
$fieldName .= '.'.$nextObjectProperty;
}

return array($fieldName, $value);
return [[$fieldName, $value]];
}

$targetClass = $this->dm->getClassMetadata($mapping['targetDocument']);
Expand All @@ -1150,7 +1151,7 @@ private function prepareQueryElement($fieldName, $value = null, $class = null, $
$fieldName .= '.'.$nextObjectProperty;
}

return array($fieldName, $value);
return [[$fieldName, $value]];
}

$targetMapping = $targetClass->getFieldMapping($objectProperty);
Expand All @@ -1164,19 +1165,19 @@ private function prepareQueryElement($fieldName, $value = null, $class = null, $
// Process targetDocument identifier fields
if ($objectPropertyIsId) {
if ( ! $prepareValue) {
return array($fieldName, $value);
return [[$fieldName, $value]];
}

if ( ! is_array($value)) {
return array($fieldName, $targetClass->getDatabaseIdentifierValue($value));
return [[$fieldName, $targetClass->getDatabaseIdentifierValue($value)]];
}

// Objects without operators or with DBRef fields can be converted immediately
if ( ! $this->hasQueryOperators($value) || $this->hasDBRefFields($value)) {
return array($fieldName, $targetClass->getDatabaseIdentifierValue($value));
return [[$fieldName, $targetClass->getDatabaseIdentifierValue($value)]];
}

return array($fieldName, $this->prepareQueryExpression($value, $targetClass));
return [[$fieldName, $this->prepareQueryExpression($value, $targetClass)]];
}

/* The property path may include a third field segment, excluding the
Expand All @@ -1189,12 +1190,16 @@ private function prepareQueryElement($fieldName, $value = null, $class = null, $
? $this->dm->getClassMetadata($targetMapping['targetDocument'])
: null;

list($key, $value) = $this->prepareQueryElement($nextObjectProperty, $value, $nextTargetClass, $prepareValue);
$fieldNames = $this->prepareQueryElement($nextObjectProperty, $value, $nextTargetClass, $prepareValue);

return array_map(function ($preparedTuple) use ($fieldName) {
list($key, $value) = $preparedTuple;

$fieldName .= '.' . $key;
return [$fieldName . '.' . $key, $value];
}, $fieldNames);
}

return array($fieldName, $value);
return [[$fieldName, $value]];
}

/**
Expand Down Expand Up @@ -1390,4 +1395,35 @@ private function getWriteOptions(array $options = array())

return array_merge($defaultOptions, $documentOptions, $options);
}

/**
* @param string $fieldName
* @param mixed $value
* @param array $mapping
* @return array
*/
private function prepareDbRefElement($fieldName, $value, array $mapping)
{
$dbRef = $this->dm->createDBRef($value, $mapping);
$keys = ['$ref' => true, '$id' => true, '$db' => true];
if ($mapping['storeAs'] === ClassMetadataInfo::REFERENCE_STORE_AS_ID) {
unset($keys['$db']);
}
if (isset($mapping['targetDocument'])) {
unset($keys['$ref'], $keys['$db']);
}

if ($mapping['storeAs'] === ClassMetadataInfo::REFERENCE_STORE_AS_ID) {
return [[$fieldName, $dbRef]];
} elseif ($mapping['type'] === 'many') {
return [[$fieldName, ['$elemMatch' => array_intersect_key($dbRef, $keys)]]];
} else {
return array_map(
function ($key) use ($dbRef, $fieldName) {
return [$fieldName . '.' . $key, $dbRef[$key]];
},
array_keys($keys)
);
}
}
}
67 changes: 67 additions & 0 deletions tests/Doctrine/ODM/MongoDB/Tests/DocumentRepositoryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,15 @@

namespace Doctrine\ODM\MongoDB\Tests;

use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Criteria;
use Doctrine\ODM\MongoDB\LockMode;
use Documents\Account;
use Documents\Address;
use Documents\Developer;
use Documents\Group;
use Documents\Phonenumber;
use Documents\SubProject;
use Documents\User;

class DocumentRepositoryTest extends BaseTest
Expand Down Expand Up @@ -39,9 +42,58 @@ public function testFindByRefOneFull()
$this->dm->persist($user);
$this->dm->persist($account);
$this->dm->flush();

$query = $this->dm
->getUnitOfWork()
->getDocumentPersister(User::class)
->prepareQueryOrNewObj(['account' => $account]);
$expectedQuery = ['account.$id' => new \MongoId($account->getId())];
$this->assertEquals($expectedQuery, $query);

$this->assertSame($user, $this->dm->getRepository(User::class)->findOneBy(['account' => $account]));
}

public function testFindByRefOneWithoutTargetDocumentFull()
{
$user = new User();
$account = new Account('name');
$user->setAccount($account);
$this->dm->persist($user);
$this->dm->persist($account);
$this->dm->flush();

$query = $this->dm
->getUnitOfWork()
->getDocumentPersister(Account::class)
->prepareQueryOrNewObj(['user' => $user]);
$expectedQuery = [
'user.$ref' => 'users',
'user.$id' => new \MongoId($user->getId()),
'user.$db' => DOCTRINE_MONGODB_DATABASE
];
$this->assertEquals($expectedQuery, $query);

$this->assertSame($account, $this->dm->getRepository(Account::class)->findOneBy(['user' => $user]));
}

public function testFindDiscriminatedByRefManyFull()
{
$project = new SubProject('mongodb-odm');
$developer = new Developer('alcaeus', new ArrayCollection([$project]));
$this->dm->persist($project);
$this->dm->persist($developer);
$this->dm->flush();

$query = $this->dm
->getUnitOfWork()
->getDocumentPersister(Developer::class)
->prepareQueryOrNewObj(['projects' => $project]);
$expectedQuery = ['projects' => ['$elemMatch' => ['$id' => new \MongoId($project->getId())]]];
$this->assertEquals($expectedQuery, $query);

$this->assertSame($developer, $this->dm->getRepository(Developer::class)->findOneBy(['projects' => $project]));
}

public function testFindByRefOneSimple()
{
$user = new User();
Expand Down Expand Up @@ -72,6 +124,21 @@ public function testFindByRefManyFull()
$this->dm->persist($user);
$this->dm->persist($group);
$this->dm->flush();

$query = $this->dm
->getUnitOfWork()
->getDocumentPersister(User::class)
->prepareQueryOrNewObj(['groups' => $group]);

$expectedQuery = [
'groups' => [
'$elemMatch' => [
'$id' => new \MongoId($group->getId()),
],
],
];
$this->assertEquals($expectedQuery, $query);

$this->assertSame($user, $this->dm->getRepository(User::class)->findOneBy(['groups' => $group]));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,13 +100,13 @@ class ShardedByObjects
/** @ODM\Id */
public $id;

/** @ODM\ObjectId */
/** @ODM\Field(type="object_id") */
public $oid;

/** @ODM\Bin */
/** @ODM\Field(type="bin") */
public $bin;

/** @ODM\Date */
/** @ODM\Field(type="date") */
public $date;
}

Expand Down

0 comments on commit 7bba4a2

Please sign in to comment.