diff --git a/.github/workflows/documentation.yaml b/.github/workflows/documentation.yaml index 2475d8f2e..0cbb4b2c3 100644 --- a/.github/workflows/documentation.yaml +++ b/.github/workflows/documentation.yaml @@ -1,3 +1,7 @@ +# DO NOT EDIT THIS FILE! +# +# It's auto-generated by sonata-project/dev-kit package. + on: push: paths: @@ -31,7 +35,7 @@ jobs: run: sudo apt-get install python-dev build-essential - name: "Cache pip" - uses: actions/cache@v1 + uses: actions/cache@v2 with: path: ~/.cache/pip key: ${{ runner.os }}-pip-${{ hashFiles('docs/requirements.txt') }} diff --git a/CHANGELOG.md b/CHANGELOG.md index 77aa5344e..c489145e1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,39 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). +## [3.20.0](https://github.com/sonata-project/SonataDoctrineORMAdminBundle/compare/3.19.0...3.20.0) - 2020-06-30 +### Added +- [[#1057](https://github.com/sonata-project/SonataDoctrineORMAdminBundle/pull/1057)] + Add `StringFilter` support for `START_WITH` and `END_WITH` operator +([@napestershine](https://github.com/napestershine)) +- [[#1049](https://github.com/sonata-project/SonataDoctrineORMAdminBundle/pull/1049)] + Added `FieldDescription::getTargetModel()`. +([@phansys](https://github.com/phansys)) + +### Deprecated +- [[#1049](https://github.com/sonata-project/SonataDoctrineORMAdminBundle/pull/1049)] Deprecated passing `null` as argument 2 for `ModelManager::find()`; ([@phansys](https://github.com/phansys)) +- [[#1049](https://github.com/sonata-project/SonataDoctrineORMAdminBundle/pull/1049)] Deprecated passing `null` as argument 1 for `ModelManager::getNormalizedIdentifier()`; ([@phansys](https://github.com/phansys)) +- [[#1049](https://github.com/sonata-project/SonataDoctrineORMAdminBundle/pull/1049)] Deprecated passing objects which are in state 2 (new) or 4 (removed) as argument 1 for `ModelManager::getNormalizedIdentifier()`; ([@phansys](https://github.com/phansys)) +- [[#1049](https://github.com/sonata-project/SonataDoctrineORMAdminBundle/pull/1049)] Deprecated passing other type than `object` as argument 1 for `ModelManager::getUrlSafeIdentifier()`; ([@phansys](https://github.com/phansys)) +- [[#1049](https://github.com/sonata-project/SonataDoctrineORMAdminBundle/pull/1049)] Deprecated `FieldDescription::getTargetEntity()` in favor of `FieldDescription::getTargetModel()`. ([@phansys](https://github.com/phansys)) + +## [3.19.0](https://github.com/sonata-project/SonataDoctrineORMAdminBundle/compare/3.18.0...3.19.0) - 2020-06-26 +### Changed +- [[#1055](https://github.com/sonata-project/SonataDoctrineORMAdminBundle/pull/1055)] + `decimal` and `float` type use the `float` template if no `number` template +exists ([@VincentLanglet](https://github.com/VincentLanglet)) + +### Fixed +- [[#1055](https://github.com/sonata-project/SonataDoctrineORMAdminBundle/pull/1055)] + `one_to_one`, `one_to_many`, `many_to_one` and `many_to_many` type are +correctly using the template defined in your config instead of the Sonata one. +([@VincentLanglet](https://github.com/VincentLanglet)) + +### Removed +- [[#1048](https://github.com/sonata-project/SonataDoctrineORMAdminBundle/pull/1048)] + Remove SonataCoreBundle dependencies +([@wbloszyk](https://github.com/wbloszyk)) + ## [3.18.0](https://github.com/sonata-project/SonataDoctrineORMAdminBundle/compare/3.17.1...3.18.0) - 2020-06-02 ### Added - Added direct dependency against "twig/twig". diff --git a/UPGRADE-3.x.md b/UPGRADE-3.x.md index fac2634f8..579fafa61 100644 --- a/UPGRADE-3.x.md +++ b/UPGRADE-3.x.md @@ -1,11 +1,24 @@ UPGRADE 3.x =========== +UPGRADE FROM 3.19 to 3.20 +========================= + +### Sonata\DoctrineORMAdminBundle\Admin\FieldDescription + +Deprecated `getTargetEntity()`, use `getTargetModel()` instead. + +### Sonata\DoctrineORMAdminBundle\Model\ModelManager + +Deprecated passing `null` as argument 2 for `find()`. +Deprecated passing `null` or an object which is in state new or removed as argument 1 for `getNormalizedIdentifier()`. +Deprecated passing `null` as argument 1 for `getUrlSafeIdentifier()`. + UPGRADE FROM 3.0 to 3.1 ======================= ### Tests -All files under the ``Tests`` directory are now correctly handled as internal test classes. -You can't extend them anymore, because they are only loaded when running internal tests. +All files under the ``Tests`` directory are now correctly handled as internal test classes. +You can't extend them anymore, because they are only loaded when running internal tests. More information can be found in the [composer docs](https://getcomposer.org/doc/04-schema.md#autoload-dev). diff --git a/composer.json b/composer.json index e924064c6..f9d0d61bc 100644 --- a/composer.json +++ b/composer.json @@ -27,7 +27,7 @@ "doctrine/doctrine-bundle": "^1.8 || ^2.0", "doctrine/orm": "^2.5", "doctrine/persistence": "^1.3.4", - "sonata-project/admin-bundle": "^3.63", + "sonata-project/admin-bundle": "^3.71", "sonata-project/exporter": "^1.11.0 || ^2.0", "sonata-project/form-extensions": "^0.1 || ^1.4", "symfony/config": "^4.4", @@ -44,7 +44,8 @@ }, "conflict": { "simplethings/entity-audit-bundle": ">=2.0", - "sonata-project/block-bundle": "<3.11" + "sonata-project/block-bundle": "<3.11", + "sonata-project/core-bundle": "<3.20" }, "provide": { "sonata-project/admin-bundle-persistency-layer": "1.0.0" diff --git a/docs/requirements.txt b/docs/requirements.txt index 96d625606..a86ef50ae 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,3 +1,6 @@ -Sphinx!=1.8.0 +# DO NOT EDIT THIS FILE! +# +# It's auto-generated by sonata-project/dev-kit package. +Sphinx==1.8.5 git+https://github.com/fabpot/sphinx-php.git sphinx_rtd_theme diff --git a/src/Admin/FieldDescription.php b/src/Admin/FieldDescription.php index ee9c07da5..f75f7fc01 100644 --- a/src/Admin/FieldDescription.php +++ b/src/Admin/FieldDescription.php @@ -35,11 +35,33 @@ public function setAssociationMapping($associationMapping): void $this->fieldName = $associationMapping['fieldName']; } + /** + * NEXT_MAJOR: Remove this method. + * + * @deprecated since sonata-project/doctrine-orm-admin-bundle 3.20 and will be removed in version 4.0. Use FieldDescription::getTargetModel() instead. + */ public function getTargetEntity() + { + @trigger_error(sprintf( + 'Method %s() is deprecated since sonata-project/doctrine-orm-admin-bundle 3.20 and will be removed in version 4.0.' + .' Use %s::getTargetModel() instead.', + __METHOD__, + __CLASS__ + ), E_USER_DEPRECATED); + + return $this->getTargetModel(); + } + + /** + * @final since sonata-project/doctrine-orm-admin-bundle 3.20. + */ + public function getTargetModel(): ?string { if ($this->associationMapping) { return $this->associationMapping['targetEntity']; } + + return null; } public function setFieldMapping($fieldMapping): void diff --git a/src/Builder/DatagridBuilder.php b/src/Builder/DatagridBuilder.php index 7824bcac2..7866dc9ed 100644 --- a/src/Builder/DatagridBuilder.php +++ b/src/Builder/DatagridBuilder.php @@ -158,7 +158,7 @@ public function addFilter(DatagridInterface $datagrid, $type, FieldDescriptionIn if (ModelAutocompleteFilter::class === $type) { $fieldDescription->mergeOption('field_options', [ - 'class' => $fieldDescription->getTargetEntity(), + 'class' => $fieldDescription->getTargetModel(), 'model_manager' => $fieldDescription->getAdmin()->getModelManager(), 'admin_code' => $admin->getCode(), 'context' => 'filter', diff --git a/src/Builder/FormContractor.php b/src/Builder/FormContractor.php index f84abd512..4cd2cccaa 100644 --- a/src/Builder/FormContractor.php +++ b/src/Builder/FormContractor.php @@ -113,7 +113,7 @@ public function getDefaultOptions($type, FieldDescriptionInterface $fieldDescrip )); } - $options['class'] = $fieldDescription->getTargetEntity(); + $options['class'] = $fieldDescription->getTargetModel(); $options['model_manager'] = $fieldDescription->getAdmin()->getModelManager(); if ($this->checkFormClass($type, [ModelAutocompleteType::class])) { @@ -122,7 +122,7 @@ public function getDefaultOptions($type, FieldDescriptionInterface $fieldDescrip 'The current field `%s` is not linked to an admin.' .' Please create one for the target entity: `%s`', $fieldDescription->getName(), - $fieldDescription->getTargetEntity() + $fieldDescription->getTargetModel() )); } } @@ -132,7 +132,7 @@ public function getDefaultOptions($type, FieldDescriptionInterface $fieldDescrip 'The current field `%s` is not linked to an admin.' .' Please create one for the target entity : `%s`', $fieldDescription->getName(), - $fieldDescription->getTargetEntity() + $fieldDescription->getTargetModel() )); } @@ -165,7 +165,7 @@ public function getDefaultOptions($type, FieldDescriptionInterface $fieldDescrip 'The current field `%s` is not linked to an admin.' .' Please create one for the target entity : `%s`', $fieldDescription->getName(), - $fieldDescription->getTargetEntity() + $fieldDescription->getTargetModel() )); } diff --git a/src/Builder/ListBuilder.php b/src/Builder/ListBuilder.php index a371a66f4..0f1a8f5ac 100644 --- a/src/Builder/ListBuilder.php +++ b/src/Builder/ListBuilder.php @@ -19,6 +19,7 @@ use Sonata\AdminBundle\Admin\FieldDescriptionInterface; use Sonata\AdminBundle\Builder\ListBuilderInterface; use Sonata\AdminBundle\Guesser\TypeGuesserInterface; +use Sonata\DoctrineORMAdminBundle\Guesser\TypeGuesser; class ListBuilder implements ListBuilderInterface { @@ -192,12 +193,27 @@ public function buildActionFieldDescription(FieldDescriptionInterface $fieldDesc /** * @param string $type * - * @return string + * @return string|null */ private function getTemplate($type) { if (!isset($this->templates[$type])) { - return; + // NEXT_MAJOR: Remove the check for deprecated type and always return null. + if (isset(TypeGuesser::DEPRECATED_TYPES[$type])) { + return $this->getTemplate(TypeGuesser::DEPRECATED_TYPES[$type]); + } + + return null; + } + + // NEXT_MAJOR: Remove the deprecation. + if (isset(TypeGuesser::DEPRECATED_TYPES[$type])) { + @trigger_error(sprintf( + 'Overriding %s list template is deprecated since sonata-project/doctrine-orm-admin-bundle 3.19.' + .' You should override %s list template instead.', + $type, + TypeGuesser::DEPRECATED_TYPES[$type] + ), E_USER_DEPRECATED); } return $this->templates[$type]; diff --git a/src/Builder/ShowBuilder.php b/src/Builder/ShowBuilder.php index 5b0ffcb69..aa75c87d4 100644 --- a/src/Builder/ShowBuilder.php +++ b/src/Builder/ShowBuilder.php @@ -19,6 +19,7 @@ use Sonata\AdminBundle\Admin\FieldDescriptionInterface; use Sonata\AdminBundle\Builder\ShowBuilderInterface; use Sonata\AdminBundle\Guesser\TypeGuesserInterface; +use Sonata\DoctrineORMAdminBundle\Guesser\TypeGuesser; class ShowBuilder implements ShowBuilderInterface { @@ -89,35 +90,6 @@ public function fixFieldDescription(AdminInterface $admin, FieldDescriptionInter if (!$fieldDescription->getTemplate()) { $fieldDescription->setTemplate($this->getTemplate($fieldDescription->getType())); - - if (!$fieldDescription->getTemplate()) { - switch ($fieldDescription->getMappingType()) { - case ClassMetadata::MANY_TO_ONE: - $fieldDescription->setTemplate( - '@SonataAdmin/CRUD/Association/show_many_to_one.html.twig' - ); - - break; - case ClassMetadata::ONE_TO_ONE: - $fieldDescription->setTemplate( - '@SonataAdmin/CRUD/Association/show_one_to_one.html.twig' - ); - - break; - case ClassMetadata::ONE_TO_MANY: - $fieldDescription->setTemplate( - '@SonataAdmin/CRUD/Association/show_one_to_many.html.twig' - ); - - break; - case ClassMetadata::MANY_TO_MANY: - $fieldDescription->setTemplate( - '@SonataAdmin/CRUD/Association/show_many_to_many.html.twig' - ); - - break; - } - } } switch ($fieldDescription->getMappingType()) { @@ -134,12 +106,27 @@ public function fixFieldDescription(AdminInterface $admin, FieldDescriptionInter /** * @param string $type * - * @return string + * @return string|null */ private function getTemplate($type) { if (!isset($this->templates[$type])) { - return; + // NEXT_MAJOR: Remove the check for deprecated type and always return null. + if (isset(TypeGuesser::DEPRECATED_TYPES[$type])) { + return $this->getTemplate(TypeGuesser::DEPRECATED_TYPES[$type]); + } + + return null; + } + + // NEXT_MAJOR: Remove the deprecation. + if (isset(TypeGuesser::DEPRECATED_TYPES[$type])) { + @trigger_error(sprintf( + 'Overriding %s show template is deprecated since sonata-project/doctrine-orm-admin-bundle 3.19.' + .' You should override %s show template instead.', + $type, + TypeGuesser::DEPRECATED_TYPES[$type] + ), E_USER_DEPRECATED); } return $this->templates[$type]; diff --git a/src/Filter/StringFilter.php b/src/Filter/StringFilter.php index c475cf76e..9c647f645 100644 --- a/src/Filter/StringFilter.php +++ b/src/Filter/StringFilter.php @@ -15,14 +15,16 @@ use Sonata\AdminBundle\Datagrid\ProxyQueryInterface; use Sonata\AdminBundle\Form\Type\Filter\ChoiceType; -use Sonata\AdminBundle\Form\Type\Operator\ContainsOperatorType; +use Sonata\AdminBundle\Form\Type\Operator\StringOperatorType; class StringFilter extends Filter { public const CHOICES = [ - ContainsOperatorType::TYPE_CONTAINS => 'LIKE', - ContainsOperatorType::TYPE_NOT_CONTAINS => 'NOT LIKE', - ContainsOperatorType::TYPE_EQUAL => '=', + StringOperatorType::TYPE_CONTAINS => 'LIKE', + StringOperatorType::TYPE_STARTS_WITH => 'LIKE', + StringOperatorType::TYPE_ENDS_WITH => 'LIKE', + StringOperatorType::TYPE_NOT_CONTAINS => 'NOT LIKE', + StringOperatorType::TYPE_EQUAL => '=', ]; public function filter(ProxyQueryInterface $queryBuilder, $alias, $field, $data): void @@ -37,7 +39,7 @@ public function filter(ProxyQueryInterface $queryBuilder, $alias, $field, $data) return; } - $data['type'] = !isset($data['type']) ? ContainsOperatorType::TYPE_CONTAINS : $data['type']; + $data['type'] = !isset($data['type']) ? StringOperatorType::TYPE_CONTAINS : $data['type']; $operator = $this->getOperator((int) $data['type']); @@ -56,22 +58,33 @@ public function filter(ProxyQueryInterface $queryBuilder, $alias, $field, $data) $or->add(sprintf('LOWER(%s.%s) %s :%s', $alias, $field, $operator, $parameterName)); } - if (ContainsOperatorType::TYPE_NOT_CONTAINS === $data['type']) { + if (StringOperatorType::TYPE_NOT_CONTAINS === $data['type']) { $or->add($queryBuilder->expr()->isNull(sprintf('%s.%s', $alias, $field))); } $this->applyWhere($queryBuilder, $or); - if (ContainsOperatorType::TYPE_EQUAL === $data['type']) { + if (StringOperatorType::TYPE_EQUAL === $data['type']) { $queryBuilder->setParameter( $parameterName, $this->getOption('case_sensitive') ? $data['value'] : mb_strtolower($data['value']) ); } else { + switch ($data['type']) { + case StringOperatorType::TYPE_STARTS_WITH: + $format = '%s%%'; + break; + case StringOperatorType::TYPE_ENDS_WITH: + $format = '%%%s'; + break; + default: + $format = $this->getOption('format'); + } + $queryBuilder->setParameter( $parameterName, sprintf( - $this->getOption('format'), + $format, $this->getOption('case_sensitive') ? $data['value'] : mb_strtolower($data['value']) ) ); diff --git a/src/Guesser/FilterTypeGuesser.php b/src/Guesser/FilterTypeGuesser.php index 131e3937d..a70310f21 100644 --- a/src/Guesser/FilterTypeGuesser.php +++ b/src/Guesser/FilterTypeGuesser.php @@ -37,6 +37,7 @@ class FilterTypeGuesser extends AbstractTypeGuesser public function guessType($class, $property, ModelManagerInterface $modelManager) { if (!$ret = $this->getParentMetadataForProperty($class, $property, $modelManager)) { + // NEXT_MAJOR: Return null. return false; } diff --git a/src/Guesser/TypeGuesser.php b/src/Guesser/TypeGuesser.php index 7643643ed..a79b311b8 100644 --- a/src/Guesser/TypeGuesser.php +++ b/src/Guesser/TypeGuesser.php @@ -15,11 +15,29 @@ use Doctrine\ORM\Mapping\ClassMetadata; use Sonata\AdminBundle\Model\ModelManagerInterface; +use Sonata\AdminBundle\Templating\TemplateRegistry; use Symfony\Component\Form\Guess\Guess; use Symfony\Component\Form\Guess\TypeGuess; class TypeGuesser extends AbstractTypeGuesser { + /** + * This is a mapping between the old deprecated value we provided in the TypeGuesser + * and the value we should use to get the correct SonataAdminBundle template. Making the change + * directly in the TypeGuesser would be a BC-break if a user overrides the templates config. + * + * NEXT_MAJOR: remove this constant. + * + * @internal + */ + public const DEPRECATED_TYPES = [ + 'orm_one_to_many' => TemplateRegistry::TYPE_ONE_TO_MANY, + 'orm_many_to_many' => TemplateRegistry::TYPE_MANY_TO_MANY, + 'orm_many_to_one' => TemplateRegistry::TYPE_MANY_TO_ONE, + 'orm_one_to_one' => TemplateRegistry::TYPE_ONE_TO_ONE, + 'number' => TemplateRegistry::TYPE_FLOAT, + ]; + public function guessType($class, $property, ModelManagerInterface $modelManager) { if (!$ret = $this->getParentMetadataForProperty($class, $property, $modelManager)) { @@ -33,15 +51,19 @@ public function guessType($class, $property, ModelManagerInterface $modelManager switch ($mapping['type']) { case ClassMetadata::ONE_TO_MANY: + // NEXT_MAJOR: return new TypeGuess(TemplateRegistry::TYPE_ONE_TO_MANY, [], Guess::HIGH_CONFIDENCE) return new TypeGuess('orm_one_to_many', [], Guess::HIGH_CONFIDENCE); case ClassMetadata::MANY_TO_MANY: + // NEXT_MAJOR: return new TypeGuess(TemplateRegistry::TYPE_MANY_TO_MANY, [], Guess::HIGH_CONFIDENCE) return new TypeGuess('orm_many_to_many', [], Guess::HIGH_CONFIDENCE); case ClassMetadata::MANY_TO_ONE: + // NEXT_MAJOR: return new TypeGuess(TemplateRegistry::TYPE_MANY_TO_ONE, [], Guess::HIGH_CONFIDENCE) return new TypeGuess('orm_many_to_one', [], Guess::HIGH_CONFIDENCE); case ClassMetadata::ONE_TO_ONE: + // NEXT_MAJOR: return new TypeGuess(TemplateRegistry::TYPE_ONE_TO_ONE, [], Guess::HIGH_CONFIDENCE) return new TypeGuess('orm_one_to_one', [], Guess::HIGH_CONFIDENCE); } } @@ -51,34 +73,36 @@ public function guessType($class, $property, ModelManagerInterface $modelManager case 'simple_array': case 'json': case 'json_array': - return new TypeGuess('array', [], Guess::HIGH_CONFIDENCE); + return new TypeGuess(TemplateRegistry::TYPE_ARRAY, [], Guess::HIGH_CONFIDENCE); case 'boolean': - return new TypeGuess('boolean', [], Guess::HIGH_CONFIDENCE); + return new TypeGuess(TemplateRegistry::TYPE_BOOLEAN, [], Guess::HIGH_CONFIDENCE); case 'datetime': case 'datetime_immutable': case 'vardatetime': case 'datetimetz': case 'datetimetz_immutable': - return new TypeGuess('datetime', [], Guess::HIGH_CONFIDENCE); + return new TypeGuess(TemplateRegistry::TYPE_DATETIME, [], Guess::HIGH_CONFIDENCE); case 'date': case 'date_immutable': - return new TypeGuess('date', [], Guess::HIGH_CONFIDENCE); + return new TypeGuess(TemplateRegistry::TYPE_DATE, [], Guess::HIGH_CONFIDENCE); case 'decimal': case 'float': + // NEXT_MAJOR: return new TypeGuess(TemplateRegistry::TYPE_FLOAT, [], Guess::LOW_CONFIDENCE) return new TypeGuess('number', [], Guess::MEDIUM_CONFIDENCE); case 'integer': case 'bigint': case 'smallint': - return new TypeGuess('integer', [], Guess::MEDIUM_CONFIDENCE); + return new TypeGuess(TemplateRegistry::TYPE_INTEGER, [], Guess::MEDIUM_CONFIDENCE); case 'string': - return new TypeGuess('text', [], Guess::MEDIUM_CONFIDENCE); + return new TypeGuess(TemplateRegistry::TYPE_TEXT, [], Guess::MEDIUM_CONFIDENCE); case 'text': - return new TypeGuess('textarea', [], Guess::MEDIUM_CONFIDENCE); + return new TypeGuess(TemplateRegistry::TYPE_TEXTAREA, [], Guess::MEDIUM_CONFIDENCE); case 'time': case 'time_immutable': - return new TypeGuess('time', [], Guess::HIGH_CONFIDENCE); + return new TypeGuess(TemplateRegistry::TYPE_TIME, [], Guess::HIGH_CONFIDENCE); default: - return new TypeGuess('text', [], Guess::LOW_CONFIDENCE); + // NEXT_MAJOR: return new TypeGuess(TemplateRegistry::TYPE_STRING, [], Guess::LOW_CONFIDENCE) + return new TypeGuess(TemplateRegistry::TYPE_TEXT, [], Guess::LOW_CONFIDENCE); } } } diff --git a/src/Model/ModelManager.php b/src/Model/ModelManager.php index 253fe4330..079efa4c0 100644 --- a/src/Model/ModelManager.php +++ b/src/Model/ModelManager.php @@ -240,7 +240,12 @@ public function lock($object, $expectedVersion): void public function find($class, $id) { - if (!isset($id)) { + if (null === $id) { + @trigger_error(sprintf( + 'Passing null as argument 1 for %s() is deprecated since sonata-project/doctrine-orm-admin-bundle 3.20 and will be not allowed in version 4.0.', + __METHOD__ + ), E_USER_DEPRECATED); + return null; } @@ -260,7 +265,7 @@ public function findOneBy($class, array $criteria = []) } /** - * @param string $class + * @param string|object $class * * @return EntityManager */ @@ -370,7 +375,13 @@ public function getIdentifierFieldNames($class) public function getNormalizedIdentifier($entity) { + // NEXT_MAJOR: Remove the following 2 checks and declare "object" as type for argument 1. if (null === $entity) { + @trigger_error(sprintf( + 'Passing null as argument 1 for %s() is deprecated since sonata-project/doctrine-orm-admin-bundle 3.20 and will be not allowed in version 4.0.', + __METHOD__ + ), E_USER_DEPRECATED); + return null; } @@ -382,6 +393,21 @@ public function getNormalizedIdentifier($entity) UnitOfWork::STATE_NEW, UnitOfWork::STATE_REMOVED, ], true)) { + // NEXT_MAJOR: Uncomment the following exception, remove the deprecation and the return statement inside this conditional block. + // throw new \InvalidArgumentException(sprintf( + // 'Can not get the normalized identifier for %s since it is in state %u.', + // ClassUtils::getClass($entity), + // $this->getEntityManager($entity)->getUnitOfWork()->getEntityState($entity) + // )); + + @trigger_error(sprintf( + 'Passing an object which is in state %u (new) or %u (removed) as argument 1 for %s() is deprecated since sonata-project/doctrine-orm-admin-bundle 3.20' + .'and will be not allowed in version 4.0.', + UnitOfWork::STATE_NEW, + UnitOfWork::STATE_REMOVED, + __METHOD__ + ), E_USER_DEPRECATED); + return null; } @@ -402,6 +428,16 @@ public function getNormalizedIdentifier($entity) */ public function getUrlSafeIdentifier($entity) { + // NEXT_MAJOR: Remove the following check and declare "object" as type for argument 1. + if (!\is_object($entity)) { + @trigger_error(sprintf( + 'Passing other type than object for argument 1 for %s() is deprecated since sonata-project/doctrine-orm-admin-bundle 3.20 and will be not allowed in version 4.0.', + __METHOD__ + ), E_USER_DEPRECATED); + + return null; + } + return $this->getNormalizedIdentifier($entity); } @@ -447,9 +483,7 @@ public function batchDelete($class, ProxyQueryInterface $queryProxy): void $entityManager->flush(); $entityManager->clear(); - } catch (\PDOException $e) { - throw new ModelManagerException('', 0, $e); - } catch (DBALException $e) { + } catch (\PDOException | DBALException $e) { throw new ModelManagerException('', 0, $e); } } diff --git a/tests/Admin/FieldDescriptionTest.php b/tests/Admin/FieldDescriptionTest.php index 6ff47eea5..079a0e87a 100644 --- a/tests/Admin/FieldDescriptionTest.php +++ b/tests/Admin/FieldDescriptionTest.php @@ -303,11 +303,11 @@ public function testGetTargetEntity(): void $field = new FieldDescription(); - $this->assertNull($field->getTargetEntity()); + $this->assertNull($field->getTargetModel()); $field->setAssociationMapping($assocationMapping); - $this->assertSame('someValue', $field->getTargetEntity()); + $this->assertSame('someValue', $field->getTargetModel()); } public function testIsIdentifierFromFieldMapping(): void diff --git a/tests/Builder/FormContractorTest.php b/tests/Builder/FormContractorTest.php index e7f3d566a..a3809126f 100644 --- a/tests/Builder/FormContractorTest.php +++ b/tests/Builder/FormContractorTest.php @@ -24,7 +24,7 @@ use Sonata\AdminBundle\Form\Type\ModelListType; use Sonata\AdminBundle\Form\Type\ModelType; use Sonata\AdminBundle\Model\ModelManagerInterface; -use Sonata\CoreBundle\Form\Type\CollectionType as DeprecatedCollectionType; +use Sonata\DoctrineORMAdminBundle\Admin\FieldDescription; use Sonata\DoctrineORMAdminBundle\Builder\FormContractor; use Sonata\DoctrineORMAdminBundle\Model\ModelManager; use Sonata\Form\Type\CollectionType; @@ -74,9 +74,10 @@ public function testDefaultOptionsForSonataFormTypes(): void $admin->method('getModelManager')->willReturn($modelManager); $admin->method('getClass')->willReturn($modelClass); - $fieldDescription = $this->createMock(FieldDescriptionInterface::class); + // NEXT_MAJOR: Mock `FieldDescriptionInterface` instead and replace `getTargetEntity()` with `getTargetModel(). + $fieldDescription = $this->createMock(FieldDescription::class); $fieldDescription->method('getAdmin')->willReturn($admin); - $fieldDescription->method('getTargetEntity')->willReturn($modelClass); + $fieldDescription->method('getTargetModel')->willReturn($modelClass); $fieldDescription->method('getAssociationAdmin')->willReturn($admin); $admin->method('getNewInstance')->willReturn($model); @@ -90,7 +91,6 @@ public function testDefaultOptionsForSonataFormTypes(): void AdminType::class, ]; $collectionTypes = [ - DeprecatedCollectionType::class, CollectionType::class, ]; diff --git a/tests/Builder/ShowBuilderTest.php b/tests/Builder/ShowBuilderTest.php index 5b0514d85..79d89b2a3 100644 --- a/tests/Builder/ShowBuilderTest.php +++ b/tests/Builder/ShowBuilderTest.php @@ -19,6 +19,7 @@ use Sonata\AdminBundle\Admin\AdminInterface; use Sonata\AdminBundle\Admin\FieldDescriptionCollection; use Sonata\AdminBundle\Guesser\TypeGuesserInterface; +use Sonata\AdminBundle\Templating\TemplateRegistry; use Sonata\DoctrineORMAdminBundle\Admin\FieldDescription; use Sonata\DoctrineORMAdminBundle\Builder\ShowBuilder; use Sonata\DoctrineORMAdminBundle\Model\ModelManager; @@ -40,7 +41,13 @@ protected function setUp(): void $this->showBuilder = new ShowBuilder( $this->guesser->reveal(), - ['fakeTemplate' => 'fake'] + [ + 'fakeTemplate' => 'fake', + TemplateRegistry::TYPE_ONE_TO_ONE => '@SonataAdmin/CRUD/Association/show_one_to_one.html.twig', + TemplateRegistry::TYPE_ONE_TO_MANY => '@SonataAdmin/CRUD/Association/show_one_to_many.html.twig', + TemplateRegistry::TYPE_MANY_TO_ONE => '@SonataAdmin/CRUD/Association/show_many_to_one.html.twig', + TemplateRegistry::TYPE_MANY_TO_MANY => '@SonataAdmin/CRUD/Association/show_many_to_many.html.twig', + ] ); $this->admin = $this->prophesize(AdminInterface::class); @@ -102,14 +109,15 @@ public function testAddFieldWithType(): void /** * @dataProvider fixFieldDescriptionData + * @dataProvider fixFieldDescriptionDeprecatedData */ - public function testFixFieldDescription($mappingType, $template): void + public function testFixFieldDescription(string $type, int $mappingType, string $template): void { $classMetadata = $this->prophesize(ClassMetadata::class); $fieldDescription = new FieldDescription(); $fieldDescription->setName('FakeName'); - $fieldDescription->setType('someType'); + $fieldDescription->setType($type); $fieldDescription->setMappingType($mappingType); $this->modelManager->hasMetadata(Argument::any())->willReturn(true); @@ -126,22 +134,55 @@ public function testFixFieldDescription($mappingType, $template): void $this->assertSame($template, $fieldDescription->getTemplate()); } - public function fixFieldDescriptionData(): array + public function fixFieldDescriptionData(): iterable { return [ 'one-to-one' => [ + TemplateRegistry::TYPE_ONE_TO_ONE, ClassMetadata::ONE_TO_ONE, '@SonataAdmin/CRUD/Association/show_one_to_one.html.twig', ], 'many-to-one' => [ + TemplateRegistry::TYPE_MANY_TO_ONE, ClassMetadata::MANY_TO_ONE, '@SonataAdmin/CRUD/Association/show_many_to_one.html.twig', ], 'one-to-many' => [ + TemplateRegistry::TYPE_ONE_TO_MANY, ClassMetadata::ONE_TO_MANY, '@SonataAdmin/CRUD/Association/show_one_to_many.html.twig', ], 'many-to-many' => [ + TemplateRegistry::TYPE_MANY_TO_MANY, + ClassMetadata::MANY_TO_MANY, + '@SonataAdmin/CRUD/Association/show_many_to_many.html.twig', + ], + ]; + } + + /** + * NEXT_MAJOR: Remove this dataprovider. + */ + public function fixFieldDescriptionDeprecatedData(): iterable + { + return [ + 'deprecated-one-to-one' => [ + 'orm_one_to_one', + ClassMetadata::ONE_TO_ONE, + '@SonataAdmin/CRUD/Association/show_one_to_one.html.twig', + ], + 'deprecated-many-to-one' => [ + 'orm_many_to_one', + ClassMetadata::MANY_TO_ONE, + '@SonataAdmin/CRUD/Association/show_many_to_one.html.twig', + ], + 'deprecated-one-to-many' => [ + 'orm_one_to_many', + ClassMetadata::ONE_TO_MANY, + '@SonataAdmin/CRUD/Association/show_one_to_many.html.twig', + ], + 'deprecated-many-to-many' => [ + 'orm_many_to_many', ClassMetadata::MANY_TO_MANY, '@SonataAdmin/CRUD/Association/show_many_to_many.html.twig', ], diff --git a/tests/Filter/StringFilterTest.php b/tests/Filter/StringFilterTest.php index 46c34a00d..a2531b9b9 100644 --- a/tests/Filter/StringFilterTest.php +++ b/tests/Filter/StringFilterTest.php @@ -15,6 +15,7 @@ use PHPUnit\Framework\TestCase; use Sonata\AdminBundle\Form\Type\Operator\ContainsOperatorType; +use Sonata\AdminBundle\Form\Type\Operator\StringOperatorType; use Sonata\DoctrineORMAdminBundle\Datagrid\ProxyQuery; use Sonata\DoctrineORMAdminBundle\Filter\StringFilter; @@ -67,6 +68,32 @@ public function testContains(): void $this->assertTrue($filter->isActive()); } + public function testStartsWith(): void + { + $filter = new StringFilter(); + $filter->initialize('field_name', ['format' => '%s']); + + $builder = new ProxyQuery(new QueryBuilder()); + $this->assertSame([], $builder->query); + + $filter->filter($builder, 'alias', 'field', ['value' => 'asd', 'type' => StringOperatorType::TYPE_STARTS_WITH]); + $this->assertSame(['alias.field LIKE :field_name_0'], $builder->query); + $this->assertSame(['field_name_0' => 'asd%'], $builder->parameters); + } + + public function testEndsWith(): void + { + $filter = new StringFilter(); + $filter->initialize('field_name', ['format' => '%s']); + + $builder = new ProxyQuery(new QueryBuilder()); + $this->assertSame([], $builder->query); + + $filter->filter($builder, 'alias', 'field', ['value' => 'asd', 'type' => StringOperatorType::TYPE_ENDS_WITH]); + $this->assertSame(['alias.field LIKE :field_name_0'], $builder->query); + $this->assertSame(['field_name_0' => '%asd'], $builder->parameters); + } + public function testNotContains(): void { $filter = new StringFilter(); diff --git a/tests/Model/ModelManagerTest.php b/tests/Model/ModelManagerTest.php index 696e8dccf..d8c813db1 100644 --- a/tests/Model/ModelManagerTest.php +++ b/tests/Model/ModelManagerTest.php @@ -882,6 +882,13 @@ public function testRemove($exception): void $model->delete(new VersionedEntity()); } + /** + * NEXT_MAJOR: Remove this method. + * + * @group legacy + * + * @expectedDeprecation Passing null as argument 1 for Sonata\DoctrineORMAdminBundle\Model\ModelManager::find() is deprecated since sonata-project/doctrine-orm-admin-bundle 3.20 and will be not allowed in version 4.0. + */ public function testFindBadId(): void { $registry = $this->createMock(ManagerRegistry::class); @@ -918,6 +925,13 @@ public function getWrongEntities(): iterable yield ['sonata-project']; } + /** + * NEXT_MAJOR: Remove this method. + * + * @group legacy + * + * @expectedDeprecation Passing null as argument 1 for Sonata\DoctrineORMAdminBundle\Model\ModelManager::getNormalizedIdentifier() is deprecated since sonata-project/doctrine-orm-admin-bundle 3.20 and will be not allowed in version 4.0. + */ public function testGetUrlsafeIdentifierNull(): void { $registry = $this->createMock(ManagerRegistry::class);