diff --git a/docs/reference/filter_field_definition.rst b/docs/reference/filter_field_definition.rst index 7ed9ac6b..4777d205 100644 --- a/docs/reference/filter_field_definition.rst +++ b/docs/reference/filter_field_definition.rst @@ -27,12 +27,12 @@ Filter types available Some filter types are missing. Contributions are welcome. - - doctrine_mongo_boolean : depends on the ``sonata_type_filter_default`` Form Type, renders yes or no field - - doctrine_mongo_callback : depends on the ``sonata_type_filter_default`` Form Type, types can be configured as needed - - doctrine_mongo_choice : depends on the ``sonata_type_filter_choice`` Form Type, renders yes or no field - - doctrine_mongo_model : depends on the ``sonata_type_filter_number`` Form Type - - doctrine_mongo_string : depends on the ``sonata_type_filter_choice`` - - doctrine_mongo_number : depends on the ``sonata_type_filter_choice`` Form Type, renders yes or no field + - ``Sonata\DoctrineMongoDBAdminBundle\Filter\BooleanFilter`` : depends on the ``sonata_type_filter_default`` Form Type, renders yes or no field + - ``Sonata\DoctrineMongoDBAdminBundle\Filter\CallbackFilter`` : depends on the ``sonata_type_filter_default`` Form Type, types can be configured as needed + - ``Sonata\DoctrineMongoDBAdminBundle\Filter\ChoiceFilter`` : depends on the ``sonata_type_filter_choice`` Form Type, renders yes or no field + - ``Sonata\DoctrineMongoDBAdminBundle\Filter\ModelFilter`` : depends on the ``sonata_type_filter_number`` Form Type + - ``Sonata\DoctrineMongoDBAdminBundle\Filter\StringFilter`` : depends on the ``sonata_type_filter_choice`` + - ``Sonata\DoctrineMongoDBAdminBundle\Filter\NumberFilter`` : depends on the ``sonata_type_filter_choice`` Form Type, renders yes or no field Example ------- @@ -109,6 +109,7 @@ or not:: use Sonata\AdminBundle\Admin\AbstractAdmin; use Sonata\AdminBundle\Datagrid\DatagridMapper; + use Sonata\DoctrineMongoDBAdminBundle\Filter\CallbackFilter; use App\Application\Sonata\NewsBundle\Entity\Comment; @@ -121,7 +122,7 @@ or not:: ->add('enabled') ->add('tags', null, [], null, ['expanded' => true, 'multiple' => true]) ->add('author') - ->add('finished', 'doctrine_mongo_callback', [ + ->add('finished', CallbackFilter::class', [ 'callback' => function($queryBuilder, $alias, $field, $value) { if (!$value) { return; diff --git a/src/Guesser/AbstractTypeGuesser.php b/src/Guesser/AbstractTypeGuesser.php index e111a4e4..98d1e809 100644 --- a/src/Guesser/AbstractTypeGuesser.php +++ b/src/Guesser/AbstractTypeGuesser.php @@ -13,7 +13,7 @@ namespace Sonata\DoctrineMongoDBAdminBundle\Guesser; -use Doctrine\ORM\Mapping\MappingException; +use Doctrine\ODM\MongoDB\Mapping\MappingException; use Sonata\AdminBundle\Guesser\TypeGuesserInterface; use Sonata\DoctrineMongoDBAdminBundle\Model\ModelManager; diff --git a/src/Guesser/FilterTypeGuesser.php b/src/Guesser/FilterTypeGuesser.php index 5eb225f1..c424b318 100644 --- a/src/Guesser/FilterTypeGuesser.php +++ b/src/Guesser/FilterTypeGuesser.php @@ -15,10 +15,19 @@ use Doctrine\Bundle\MongoDBBundle\Form\Type\DocumentType; use Doctrine\ODM\MongoDB\Mapping\ClassMetadata; +use Doctrine\ODM\MongoDB\Types\Type; use Sonata\AdminBundle\Model\ModelManagerInterface; +use Sonata\DoctrineMongoDBAdminBundle\Filter\BooleanFilter; +use Sonata\DoctrineMongoDBAdminBundle\Filter\DateFilter; +use Sonata\DoctrineMongoDBAdminBundle\Filter\DateTimeFilter; +use Sonata\DoctrineMongoDBAdminBundle\Filter\ModelFilter; +use Sonata\DoctrineMongoDBAdminBundle\Filter\NumberFilter; +use Sonata\DoctrineMongoDBAdminBundle\Filter\StringFilter; use Sonata\DoctrineMongoDBAdminBundle\Model\MissingPropertyMetadataException; use Sonata\Form\Type\BooleanType; use Sonata\Form\Type\EqualType; +use Symfony\Component\Form\Extension\Core\Type\DateTimeType; +use Symfony\Component\Form\Extension\Core\Type\DateType; use Symfony\Component\Form\Extension\Core\Type\NumberType; use Symfony\Component\Form\Extension\Core\Type\TextType; use Symfony\Component\Form\Guess\Guess; @@ -62,7 +71,7 @@ public function guessType($class, $property, ModelManagerInterface $modelManager $options['field_name'] = $mapping['fieldName']; $options['mapping_type'] = $mapping['type']; - return new TypeGuess('doctrine_mongo_model', $options, Guess::HIGH_CONFIDENCE); + return new TypeGuess(ModelFilter::class, $options, Guess::HIGH_CONFIDENCE); } } @@ -73,36 +82,82 @@ public function guessType($class, $property, ModelManagerInterface $modelManager $options['field_name'] = $metadata->fieldMappings[$propertyName]['fieldName']; switch ($metadata->getTypeOfField($propertyName)) { - case 'boolean': + case Type::BOOL: + case Type::BOOLEAN: $options['field_type'] = BooleanType::class; $options['field_options'] = []; - return new TypeGuess('doctrine_mongo_boolean', $options, Guess::HIGH_CONFIDENCE); -// case 'datetime': -// case 'vardatetime': -// case 'datetimetz': -// return new TypeGuess('doctrine_orm_datetime', $options, Guess::HIGH_CONFIDENCE); -// case 'date': -// return new TypeGuess('doctrine_orm_date', $options, Guess::HIGH_CONFIDENCE); + return new TypeGuess(BooleanFilter::class, $options, Guess::HIGH_CONFIDENCE); + case 'datetime': + @trigger_error( + 'The datetime type is deprecated since sonata-project/doctrine-mongodb-admin-bundle 3.x, to be removed in 4.0.'. + E_USER_DEPRECATED + ); + + $options['field_type'] = DateTimeType::class; + + return new TypeGuess(DateTimeFilter::class, $options, Guess::HIGH_CONFIDENCE); + case Type::TIMESTAMP: + $options['field_type'] = DateTimeType::class; + + return new TypeGuess(DateTimeFilter::class, $options, Guess::HIGH_CONFIDENCE); + case Type::DATE: + + // NEXT_MAJOR: Use only the constant when dropping support for doctrine/mongodb-odm 1.3. + // case Type::DATE_IMMUTABLE: + case 'date_immutable': + $options['field_type'] = DateType::class; + + return new TypeGuess(DateFilter::class, $options, Guess::HIGH_CONFIDENCE); case 'decimal': - case 'float': - return new TypeGuess('doctrine_mongo_number', $options, Guess::MEDIUM_CONFIDENCE); - case 'int': + @trigger_error( + 'The decimal type is deprecated since sonata-project/doctrine-mongodb-admin-bundle 3.x, to be removed in 4.0.'. + E_USER_DEPRECATED + ); + + $options['field_type'] = NumberType::class; + + return new TypeGuess(NumberFilter::class, $options, Guess::MEDIUM_CONFIDENCE); case 'bigint': + @trigger_error( + 'The bigint type is deprecated since sonata-project/doctrine-mongodb-admin-bundle 3.x, to be removed in 4.0.'. + E_USER_DEPRECATED + ); + + $options['field_type'] = NumberType::class; + + return new TypeGuess(NumberFilter::class, $options, Guess::MEDIUM_CONFIDENCE); case 'smallint': + @trigger_error( + 'The smallint type is deprecated since sonata-project/doctrine-mongodb-admin-bundle 3.x, to be removed in 4.0.'. + E_USER_DEPRECATED + ); + + $options['field_type'] = NumberType::class; + + return new TypeGuess(NumberFilter::class, $options, Guess::MEDIUM_CONFIDENCE); + case Type::FLOAT: + case Type::INT: + case Type::INTEGER: $options['field_type'] = NumberType::class; - return new TypeGuess('doctrine_mongo_number', $options, Guess::MEDIUM_CONFIDENCE); - case 'id': - case 'string': + return new TypeGuess(NumberFilter::class, $options, Guess::MEDIUM_CONFIDENCE); case 'text': + @trigger_error( + 'The text type is deprecated since sonata-project/doctrine-mongodb-admin-bundle 3.x, to be removed in 4.0.'. + E_USER_DEPRECATED + ); + + $options['field_type'] = TextType::class; + + return new TypeGuess(StringFilter::class, $options, Guess::MEDIUM_CONFIDENCE); + case Type::ID: + case Type::STRING: $options['field_type'] = TextType::class; - return new TypeGuess('doctrine_mongo_string', $options, Guess::MEDIUM_CONFIDENCE); - case 'time': - return new TypeGuess('doctrine_mongo_time', $options, Guess::HIGH_CONFIDENCE); + return new TypeGuess(StringFilter::class, $options, Guess::MEDIUM_CONFIDENCE); default: - return new TypeGuess('doctrine_mongo_string', $options, Guess::LOW_CONFIDENCE); + return new TypeGuess(StringFilter::class, $options, Guess::LOW_CONFIDENCE); } } } diff --git a/src/Guesser/TypeGuesser.php b/src/Guesser/TypeGuesser.php index d1a23359..515be693 100755 --- a/src/Guesser/TypeGuesser.php +++ b/src/Guesser/TypeGuesser.php @@ -14,6 +14,7 @@ namespace Sonata\DoctrineMongoDBAdminBundle\Guesser; use Doctrine\ODM\MongoDB\Mapping\ClassMetadata; +use Doctrine\ODM\MongoDB\Types\Type; use Sonata\AdminBundle\Model\ModelManagerInterface; use Symfony\Component\Form\Guess\Guess; use Symfony\Component\Form\Guess\TypeGuess; @@ -44,31 +45,88 @@ public function guessType($class, $property, ModelManagerInterface $modelManager } switch ($metadata->getTypeOfField($propertyName)) { - case 'collection': - case 'hash': + case Type::COLLECTION: + case Type::HASH: + return new TypeGuess('array', [], Guess::HIGH_CONFIDENCE); case 'array': - return new TypeGuess('array', [], Guess::HIGH_CONFIDENCE); - case 'boolean': + @trigger_error( + 'The array type is deprecated since sonata-project/doctrine-mongodb-admin-bundle 3.x, to be removed in 4.0.'. + E_USER_DEPRECATED + ); + + return new TypeGuess('array', [], Guess::HIGH_CONFIDENCE); + case Type::BOOL: + case Type::BOOLEAN: return new TypeGuess('boolean', [], Guess::HIGH_CONFIDENCE); case 'datetime': + @trigger_error( + 'The datetime type is deprecated since sonata-project/doctrine-mongodb-admin-bundle 3.x, to be removed in 4.0.'. + E_USER_DEPRECATED + ); + + return new TypeGuess('datetime', [], Guess::HIGH_CONFIDENCE); case 'vardatetime': + @trigger_error( + 'The vardatetime type is deprecated since sonata-project/doctrine-mongodb-admin-bundle 3.x, to be removed in 4.0.'. + E_USER_DEPRECATED + ); + + return new TypeGuess('datetime', [], Guess::HIGH_CONFIDENCE); case 'datetimetz': - case 'timestamp': + @trigger_error( + 'The datetimetz type is deprecated since sonata-project/doctrine-mongodb-admin-bundle 3.x, to be removed in 4.0.'. + E_USER_DEPRECATED + ); + return new TypeGuess('datetime', [], Guess::HIGH_CONFIDENCE); - case 'date': + case Type::TIMESTAMP: + return new TypeGuess('datetime', [], Guess::HIGH_CONFIDENCE); + case Type::DATE: + // NEXT_MAJOR: Use only the constant when dropping support for doctrine/mongodb-odm 1.3. + // case Type::DATE_IMMUTABLE: + case 'date_immutable': return new TypeGuess('date', [], Guess::HIGH_CONFIDENCE); case 'decimal': - case 'float': + @trigger_error( + 'The decimal type is deprecated since sonata-project/doctrine-mongodb-admin-bundle 3.x, to be removed in 4.0.'. + E_USER_DEPRECATED + ); + return new TypeGuess('number', [], Guess::MEDIUM_CONFIDENCE); - case 'integer': + case Type::FLOAT: + return new TypeGuess('number', [], Guess::MEDIUM_CONFIDENCE); + case Type::INTEGER: + case Type::INT: + return new TypeGuess('integer', [], Guess::MEDIUM_CONFIDENCE); case 'bigint': + @trigger_error( + 'The bigint type is deprecated since sonata-project/doctrine-mongodb-admin-bundle 3.x, to be removed in 4.0.'. + E_USER_DEPRECATED + ); + + return new TypeGuess('integer', [], Guess::MEDIUM_CONFIDENCE); case 'smallint': + @trigger_error( + 'The smallint type is deprecated since sonata-project/doctrine-mongodb-admin-bundle 3.x, to be removed in 4.0.'. + E_USER_DEPRECATED + ); + return new TypeGuess('integer', [], Guess::MEDIUM_CONFIDENCE); - case 'string': + case Type::STRING: return new TypeGuess('text', [], Guess::MEDIUM_CONFIDENCE); case 'text': + @trigger_error( + 'The text type is deprecated since sonata-project/doctrine-mongodb-admin-bundle 3.x, to be removed in 4.0.'. + E_USER_DEPRECATED + ); + return new TypeGuess('textarea', [], Guess::MEDIUM_CONFIDENCE); case 'time': + @trigger_error( + 'The time type is deprecated since sonata-project/doctrine-mongodb-admin-bundle 3.x, to be removed in 4.0.'. + E_USER_DEPRECATED + ); + return new TypeGuess('time', [], Guess::HIGH_CONFIDENCE); default: return new TypeGuess('text', [], Guess::LOW_CONFIDENCE); diff --git a/tests/Builder/ListBuilderTest.php b/tests/Builder/ListBuilderTest.php index 656e4a4d..650859fe 100644 --- a/tests/Builder/ListBuilderTest.php +++ b/tests/Builder/ListBuilderTest.php @@ -11,7 +11,7 @@ * file that was distributed with this source code. */ -namespace Sonata\DoctrineORMAdminBundle\Tests\Builder; +namespace Sonata\DoctrineMongoDBAdminBundle\Tests\Builder; use PHPUnit\Framework\TestCase; use Prophecy\Argument; diff --git a/tests/Guesser/FilterTypeGuesserTest.php b/tests/Guesser/FilterTypeGuesserTest.php index 703cd392..186f304d 100644 --- a/tests/Guesser/FilterTypeGuesserTest.php +++ b/tests/Guesser/FilterTypeGuesserTest.php @@ -13,38 +13,190 @@ namespace Sonata\DoctrineMongoDBAdminBundle\Tests\Guesser; +use Doctrine\Bundle\MongoDBBundle\Form\Type\DocumentType; use Doctrine\ODM\MongoDB\Mapping\ClassMetadata; +use Doctrine\ODM\MongoDB\Mapping\MappingException; +use Doctrine\ODM\MongoDB\Types\Type; use PHPUnit\Framework\TestCase; +use Sonata\DoctrineMongoDBAdminBundle\Filter\BooleanFilter; +use Sonata\DoctrineMongoDBAdminBundle\Filter\DateFilter; +use Sonata\DoctrineMongoDBAdminBundle\Filter\DateTimeFilter; +use Sonata\DoctrineMongoDBAdminBundle\Filter\ModelFilter; +use Sonata\DoctrineMongoDBAdminBundle\Filter\NumberFilter; +use Sonata\DoctrineMongoDBAdminBundle\Filter\StringFilter; use Sonata\DoctrineMongoDBAdminBundle\Guesser\FilterTypeGuesser; +use Sonata\DoctrineMongoDBAdminBundle\Model\MissingPropertyMetadataException; use Sonata\DoctrineMongoDBAdminBundle\Model\ModelManager; +use Sonata\Form\Type\BooleanType; +use Sonata\Form\Type\EqualType; +use Symfony\Component\Form\Extension\Core\Type\NumberType; +use Symfony\Component\Form\Extension\Core\Type\TextType; +use Symfony\Component\Form\Guess\Guess; class FilterTypeGuesserTest extends TestCase { private $guesser; private $modelManager; - private $metadata; protected function setUp() { $this->guesser = new FilterTypeGuesser(); $this->modelManager = $this->prophesize(ModelManager::class); - $this->metadata = $this->prophesize(ClassMetadata::class); } - public function testThrowsOnMissingField() + public function testThrowsOnMissingField(): void { - $this->expectException(\Sonata\DoctrineMongoDBAdminBundle\Model\MissingPropertyMetadataException::class); + $this->expectException(MissingPropertyMetadataException::class); $class = 'My\Model'; $property = 'whatever'; - $this->metadata->hasAssociation($property)->willReturn(false); + $metadata = $this->prophesize(ClassMetadata::class); + $metadata->hasAssociation($property)->willReturn(false); $this->modelManager->getParentMetadataForProperty($class, $property)->willReturn([ - $this->metadata->reveal(), + $metadata->reveal(), $property, 'parent mappings, no idea what it looks like', ]); $this->guesser->guessType($class, $property, $this->modelManager->reveal()); } + + public function testGuessTypeNoMetadata(): void + { + $this->modelManager->getParentMetadataForProperty( + $class = 'FakeClass', + $property = 'fakeProperty' + )->willThrow(MappingException::class); + + $result = $this->guesser->guessType($class, $property, $this->modelManager->reveal()); + + $this->assertFalse($result); + } + + public function testGuessTypeWithAssociation(): void + { + $classMetadata = $this->prophesize(ClassMetadata::class); + + $classMetadata->hasAssociation($property = 'fakeProperty')->willReturn(true); + $classMetadataObject = $classMetadata->reveal(); + $classMetadataObject->fieldMappings['fakeProperty'] = [ + 'type' => ClassMetadata::ONE, + 'targetDocument' => $targetDocument = 'FakeEntity', + 'fieldName' => $fieldName = 'fakeName', + ]; + + $this->modelManager->getParentMetadataForProperty( + $class = 'FakeClass', + $property + )->willReturn([$classMetadataObject, $property, $parentAssociation = 'parentAssociation']); + + $result = $this->guesser->guessType($class, $property, $this->modelManager->reveal()); + + $options = $result->getOptions(); + + $this->assertSame(ModelFilter::class, $result->getType()); + $this->assertSame(Guess::HIGH_CONFIDENCE, $result->getConfidence()); + $this->assertSame($parentAssociation, $options['parent_association_mappings']); + $this->assertSame(ClassMetadata::ONE, $options['mapping_type']); + $this->assertSame(EqualType::class, $options['operator_type']); + $this->assertSame([], $options['operator_options']); + $this->assertSame($fieldName, $options['field_name']); + $this->assertSame(DocumentType::class, $options['field_type']); + $this->assertSame($targetDocument, $options['field_options']['class']); + } + + /** + * @dataProvider noAssociationData + */ + public function testGuessTypeNoAssociation(string $type, string $resultType, int $confidence, string $fieldType = null): void + { + $classMetadata = $this->prophesize(ClassMetadata::class); + + $classMetadata->hasAssociation($property = 'fakeProperty')->willReturn(false); + + $classMetadata->fieldMappings = [$property => ['fieldName' => $type]]; + $classMetadata->getTypeOfField($property)->willReturn($type); + + $this->modelManager->getParentMetadataForProperty( + $class = 'FakeClass', + $property + )->willReturn([$classMetadata, $property, 'notUsed']); + + $result = $this->guesser->guessType($class, $property, $this->modelManager->reveal()); + + $options = $result->getOptions(); + + $this->assertSame($resultType, $result->getType()); + $this->assertSame($type, $options['field_name']); + $this->assertSame($confidence, $result->getConfidence()); + $this->assertSame([], $options['options']); + $this->assertSame([], $options['field_options']); + + if ($fieldType) { + $this->assertSame($fieldType, $options['field_type']); + } + } + + public function noAssociationData(): array + { + return [ + Type::BOOLEAN => [ + 'boolean', + BooleanFilter::class, + Guess::HIGH_CONFIDENCE, + BooleanType::class, + ], + 'datetime' => [ + 'datetime', + DateTimeFilter::class, + Guess::HIGH_CONFIDENCE, + ], + Type::TIMESTAMP => [ + 'datetime', + DateTimeFilter::class, + Guess::HIGH_CONFIDENCE, + ], + Type::DATE => [ + 'date', + DateFilter::class, + Guess::HIGH_CONFIDENCE, + ], + 'decimal' => [ + 'decimal', + NumberFilter::class, + Guess::MEDIUM_CONFIDENCE, + NumberType::class, + ], + Type::FLOAT => [ + 'float', + NumberFilter::class, + Guess::MEDIUM_CONFIDENCE, + NumberType::class, + ], + Type::INT => [ + 'int', + NumberFilter::class, + Guess::MEDIUM_CONFIDENCE, + NumberType::class, + ], + Type::STRING => [ + 'string', + StringFilter::class, + Guess::MEDIUM_CONFIDENCE, + TextType::class, + ], + 'text' => [ + 'text', + StringFilter::class, + Guess::MEDIUM_CONFIDENCE, + TextType::class, + ], + 'somefake' => [ + 'somefake', + StringFilter::class, + Guess::LOW_CONFIDENCE, + ], + ]; + } }