[DoctrineMongoDBBundle] added unique constraint, validator and test, registered validator in DIC
This commit is contained in:
parent
84fa4b50db
commit
1cbd0caa89
@ -51,6 +51,9 @@
|
||||
|
||||
<!-- security/user -->
|
||||
<parameter key="security.user.provider.document.class">Symfony\Bundle\DoctrineMongoDBBundle\Security\DocumentUserProvider</parameter>
|
||||
|
||||
<!-- validator -->
|
||||
<parameter key="doctrine_odm.mongodb.validator.unique.class">Symfony\Bundle\DoctrineMongoDBBundle\Validator\Constraints\DoctrineMongoDBUniqueValidator</parameter>
|
||||
</parameters>
|
||||
|
||||
<services>
|
||||
@ -86,6 +89,11 @@
|
||||
</service>
|
||||
|
||||
<service id="security.user.document_manager" alias="doctrine.odm.mongodb.default_document_manager" />
|
||||
|
||||
<!-- validator -->
|
||||
<service id="doctrine_odm.mongodb.validator.unique" class="%doctrine_odm.mongodb.validator.unique.class%">
|
||||
<argument type="service" id="doctrine.odm.mongodb.document_manager" />
|
||||
</service>
|
||||
|
||||
</services>
|
||||
</container>
|
@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
namespace Symfony\Bundle\DoctrineMongoDBBundle\Tests\Fixtures\Validator;
|
||||
|
||||
class Document
|
||||
{
|
||||
public $id;
|
||||
public $unique;
|
||||
}
|
@ -0,0 +1,143 @@
|
||||
<?php
|
||||
|
||||
namespace Symfony\Bundle\DoctrineMongoDBBundle\Tests\Validator\Constraints;
|
||||
|
||||
use Symfony\Bundle\DoctrineMongoDBBundle\Tests\Fixtures\Validator\Document;
|
||||
|
||||
use Doctrine\ODM\MongoDB\Mapping\ClassMetadata;
|
||||
use Doctrine\ODM\MongoDB\DocumentRepository;
|
||||
use Symfony\Bundle\DoctrineMongoDBBundle\Validator\Constraints\DoctrineMongoDBUnique;
|
||||
use Symfony\Bundle\DoctrineMongoDBBundle\Validator\Constraints\DoctrineMongoDBUniqueValidator;
|
||||
|
||||
class DoctrineMongoDBUniqueValidatorTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
private $dm;
|
||||
private $repository;
|
||||
private $validator;
|
||||
private $classMetadata;
|
||||
private $uniqueFieldName = 'unique';
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
$this->classMetadata = $this->getClassMetadata();
|
||||
$this->repository = $this->getDocumentRepository();
|
||||
$this->dm = $this->getDocumentManager($this->classMetadata, $this->repository);
|
||||
$this->validator = new DoctrineMongoDBUniqueValidator($this->dm);
|
||||
}
|
||||
|
||||
public function tearDown()
|
||||
{
|
||||
unset($this->validator, $this->dm, $this->repository, $this->classMetadata);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider getFieldsPathsValuesDocumentsAndReturns
|
||||
*/
|
||||
public function testShouldValidateValidStringMappingValues($field, $path, $value, $document, $return)
|
||||
{
|
||||
$this->setFieldMapping($field, 'string');
|
||||
|
||||
$this->repository->expects($this->once())
|
||||
->method('findOneBy')
|
||||
->with(array($path => $value))
|
||||
->will($this->returnValue($return));
|
||||
|
||||
$this->assertTrue($this->validator->isValid($document, new DoctrineMongoDBUnique($path)));
|
||||
}
|
||||
|
||||
public function getFieldsPathsValuesDocumentsAndReturns()
|
||||
{
|
||||
$field = 'unique';
|
||||
$path = $field;
|
||||
$value = 'someUniqueValueToBeValidated';
|
||||
$document = $this->getFixtureDocument($field, $value);
|
||||
|
||||
return array(
|
||||
array('unique', 'unique', 'someUniqueValueToBeValidated', $document, null),
|
||||
array('unique', 'unique', 'someUniqueValueToBeValidated', $document, $document),
|
||||
array('unique', 'unique', 'someUniqueValueToBeValidated', $document, $this->getFixtureDocument($field, $value)),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider getFieldTypesFieldsPathsValuesAndQueries
|
||||
*/
|
||||
public function testGetsCorrectQueryArrayForCollection($type, $field, $path, $value, $query)
|
||||
{
|
||||
$this->setFieldMapping($field, $type);
|
||||
$document = $this->getFixtureDocument($field, $value);
|
||||
|
||||
$this->repository->expects($this->once())
|
||||
->method('findOneBy')
|
||||
->with($query);
|
||||
|
||||
$this->validator->isValid($document, new DoctrineMongoDBUnique($path));
|
||||
}
|
||||
|
||||
public function getFieldTypesFieldsPathsValuesAndQueries()
|
||||
{
|
||||
$field = 'unique';
|
||||
$key = 'uniqueValue';
|
||||
$path = $field.'.'.$key;
|
||||
$value = 'someUniqueValueToBeValidated';
|
||||
|
||||
return array(
|
||||
array('collection', $field, $path, array($value), array($field => array('$in' => array($value)))),
|
||||
array('hash', $field, $path, array($key => $value), array($path => $value)),
|
||||
);
|
||||
}
|
||||
|
||||
protected function getDocumentManager(ClassMetadata $classMetadata, DocumentRepository $repository)
|
||||
{
|
||||
$dm = $this->getMockBuilder('Doctrine\ODM\MongoDB\DocumentManager')
|
||||
->disableOriginalConstructor()
|
||||
->setMethods(array('getClassMetadata', 'getRepository'))
|
||||
->getMock();
|
||||
$dm->expects($this->any())
|
||||
->method('getClassMetadata')
|
||||
->will($this->returnValue($classMetadata));
|
||||
$dm->expects($this->any())
|
||||
->method('getRepository')
|
||||
->will($this->returnValue($repository));
|
||||
|
||||
return $dm;
|
||||
}
|
||||
|
||||
protected function getDocumentRepository()
|
||||
{
|
||||
$dm = $this->getMock('Doctrine\ODM\MongoDB\DocumentRepository', array('findOneBy'), array(), '', false, false);
|
||||
|
||||
return $dm;
|
||||
}
|
||||
|
||||
protected function getClassMetadata()
|
||||
{
|
||||
$classMetadata = $this->getMock('Doctrine\ODM\MongoDB\Mapping\ClassMetadata', array(), array(), '', false, false);
|
||||
$classMetadata->expects($this->any())
|
||||
->method('getFieldValue')
|
||||
->will($this->returnCallback(function($document, $fieldName) {
|
||||
return $document->{$fieldName};
|
||||
}));
|
||||
|
||||
$classMetadata->fieldmappings = array();
|
||||
|
||||
return $classMetadata;
|
||||
}
|
||||
|
||||
protected function setFieldMapping($fieldName, $type, array $attributes = array())
|
||||
{
|
||||
$this->classMetadata->fieldMappings[$fieldName] = array_merge(array(
|
||||
'fieldName' => $fieldName,
|
||||
'type' => $type,
|
||||
), $attributes);
|
||||
}
|
||||
|
||||
protected function getFixtureDocument($field, $value, $id = 1)
|
||||
{
|
||||
$document = new Document();
|
||||
$document->{$field} = $value;
|
||||
$document->id = 1;
|
||||
|
||||
return $document;
|
||||
}
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\DoctrineMongoDBBundle\Validator\Constraints;
|
||||
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
|
||||
/**
|
||||
* Doctrine MongoDB ODM unique value constraint.
|
||||
*
|
||||
* @author Bulat Shakirzyanov <bulat@theopenskyproject.com>
|
||||
*/
|
||||
class DoctrineMongoDBUnique extends Constraint
|
||||
{
|
||||
public $message = 'The value for {{ property }} already exists.';
|
||||
public $path;
|
||||
|
||||
public function defaultOption()
|
||||
{
|
||||
return 'path';
|
||||
}
|
||||
|
||||
public function requiredOptions()
|
||||
{
|
||||
return array('path');
|
||||
}
|
||||
|
||||
public function validatedBy()
|
||||
{
|
||||
return 'doctrine_odm.mongodb.validator.unique';
|
||||
}
|
||||
|
||||
public function targets()
|
||||
{
|
||||
return Constraint::CLASS_CONSTRAINT;
|
||||
}
|
||||
}
|
@ -0,0 +1,131 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\DoctrineMongoDBBundle\Validator\Constraints;
|
||||
|
||||
use Doctrine\ODM\MongoDB\DocumentManager;
|
||||
use Doctrine\ODM\MongoDB\Proxy\Proxy;
|
||||
use Doctrine\ODM\MongoDB\Mapping\ClassMetadata;
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
use Symfony\Component\Validator\ConstraintValidator;
|
||||
|
||||
/**
|
||||
* Doctrine MongoDB ODM unique value validator.
|
||||
*
|
||||
* @author Bulat Shakirzyanov <bulat@theopenskyproject.com>
|
||||
*/
|
||||
class DoctrineMongoDBUniqueValidator extends ConstraintValidator
|
||||
{
|
||||
|
||||
protected $dm;
|
||||
|
||||
public function __construct(DocumentManager $dm)
|
||||
{
|
||||
$this->dm = $dm;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Doctrine\ODM\MongoDB\Document $value
|
||||
* @param Constraint $constraint
|
||||
* @return bool
|
||||
*/
|
||||
public function isValid($document, Constraint $constraint)
|
||||
{
|
||||
$class = get_class($document);
|
||||
$metadata = $this->dm->getClassMetadata($class);
|
||||
|
||||
if ($metadata->isEmbeddedDocument) {
|
||||
throw new \InvalidArgumentException(sprintf("Document '%s' is an embedded document, and cannot be validated", $class));
|
||||
}
|
||||
|
||||
$query = $this->getQueryArray($metadata, $document, $constraint->path);
|
||||
|
||||
// check if document exists in mongodb
|
||||
if (null === ($doc = $this->dm->getRepository($class)->findOneBy($query))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// check if document in mongodb is the same document as the checked one
|
||||
if ($doc === $document) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// check if returned document is proxy and initialize the minimum identifier if needed
|
||||
if ($doc instanceof Proxy) {
|
||||
$metadata->setIdentifierValue($doc, $doc->__identifier);
|
||||
}
|
||||
|
||||
// check if document has the same identifier as the current one
|
||||
if ($metadata->getIdentifierValue($doc) === $metadata->getIdentifierValue($document)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$this->context->setPropertyPath($this->context->getPropertyPath() . '.' . $constraint->path);
|
||||
$this->setMessage($constraint->message, array(
|
||||
'{{ property }}' => $constraint->path,
|
||||
));
|
||||
return false;
|
||||
}
|
||||
|
||||
protected function getQueryArray(ClassMetadata $metadata, $document, $path)
|
||||
{
|
||||
$class = $metadata->name;
|
||||
$field = $this->getFieldNameFromPropertyPath($path);
|
||||
if (!isset($metadata->fieldMappings[$field])) {
|
||||
throw new \LogicException('Mapping for \'' . $path . '\' doesn\'t exist for ' . $class);
|
||||
}
|
||||
$mapping = $metadata->fieldMappings[$field];
|
||||
if (isset($mapping['reference']) && $mapping['reference']) {
|
||||
throw new \LogicException('Cannot determine uniqueness of referenced document values');
|
||||
}
|
||||
switch ($mapping['type']) {
|
||||
case 'one':
|
||||
// TODO: implement support for embed one documents
|
||||
case 'many':
|
||||
// TODO: implement support for embed many documents
|
||||
throw new \RuntimeException('Not Implemented.');
|
||||
case 'hash':
|
||||
$value = $metadata->getFieldValue($document, $mapping['fieldName']);
|
||||
return array($path => $this->getFieldValueRecursively($path, $value));
|
||||
case 'collection':
|
||||
return array($mapping['fieldName'] => array('$in' => $metadata->getFieldValue($document, $mapping['fieldName'])));
|
||||
default:
|
||||
return array($mapping['fieldName'] => $metadata->getFieldValue($document, $mapping['fieldName']));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the actual document field value
|
||||
*
|
||||
* E.g. document.someVal -> document
|
||||
* user.emails -> user
|
||||
* username -> username
|
||||
*
|
||||
* @param string $field
|
||||
* @return string
|
||||
*/
|
||||
protected function getFieldNameFromPropertyPath($field)
|
||||
{
|
||||
$pieces = explode('.', $field);
|
||||
return $pieces[0];
|
||||
}
|
||||
|
||||
protected function getFieldValueRecursively($fieldName, $value)
|
||||
{
|
||||
$pieces = explode('.', $fieldName);
|
||||
unset($pieces[0]);
|
||||
foreach ($pieces as $piece) {
|
||||
$value = $value[$piece];
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user