Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
feature #31486 [Doctrine][PropertyInfo] Detect if the ID is writeable…
… (dunglas)

This PR was merged into the 4.3 branch.

Discussion
----------

[Doctrine][PropertyInfo] Detect if the ID is writeable

| Q             | A
| ------------- | ---
| Branch?       | 4.3
| Bug fix?      | yes
| New feature?  | yes <!-- please update src/**/CHANGELOG.md files -->
| BC breaks?    | no     <!-- see https://symfony.com/bc -->
| Deprecations? | yes/no <!-- please update UPGRADE-*.md and src/**/CHANGELOG.md files -->
| Tests pass?   | yes    <!-- please add some, will be required by reviewers -->
| Fixed tickets | n/a
| License       | MIT
| Doc PR        | n/a

Companion of #31481. Allows to detect that ids with a generated value aren't writable (because the DBMS will generate the ID by itself). It could be considered as a bug fix or as a new feature. I prefer to not merge in in 3.4. However, it becomes necessary for autovalidation to work with such entities, so it should be in 4.3:

```php
/**
 * @entity
 */
class Foo
{
    /**
     * @id
     * @GeneratedValue(strategy="AUTO")
     * @column(type="integer")
     */
    public $id;
}
```

Commits
-------

4598235 [Doctrine][PropertyInfo] Detect if the ID is writeable
  • Loading branch information
fabpot committed May 13, 2019
2 parents b7cd925 + 4598235 commit 0d196c4
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 13 deletions.
53 changes: 40 additions & 13 deletions src/Symfony/Bridge/Doctrine/PropertyInfo/DoctrineExtractor.php
Expand Up @@ -15,8 +15,10 @@
use Doctrine\Common\Persistence\Mapping\MappingException;
use Doctrine\DBAL\Types\Type as DBALType;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\Mapping\ClassMetadataInfo;
use Doctrine\ORM\Mapping\MappingException as OrmMappingException;
use Symfony\Component\PropertyInfo\PropertyAccessExtractorInterface;
use Symfony\Component\PropertyInfo\PropertyListExtractorInterface;
use Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface;
use Symfony\Component\PropertyInfo\Type;
Expand All @@ -26,7 +28,7 @@
*
* @author Kévin Dunglas <dunglas@gmail.com>
*/
class DoctrineExtractor implements PropertyListExtractorInterface, PropertyTypeExtractorInterface
class DoctrineExtractor implements PropertyListExtractorInterface, PropertyTypeExtractorInterface, PropertyAccessExtractorInterface
{
private $entityManager;
private $classMetadataFactory;
Expand All @@ -51,12 +53,8 @@ public function __construct($entityManager)
*/
public function getProperties($class, array $context = [])
{
try {
$metadata = $this->entityManager ? $this->entityManager->getClassMetadata($class) : $this->classMetadataFactory->getMetadataFor($class);
} catch (MappingException $exception) {
return;
} catch (OrmMappingException $exception) {
return;
if (null === $metadata = $this->getMetadata($class)) {
return null;
}

$properties = array_merge($metadata->getFieldNames(), $metadata->getAssociationNames());
Expand All @@ -77,12 +75,8 @@ public function getProperties($class, array $context = [])
*/
public function getTypes($class, $property, array $context = [])
{
try {
$metadata = $this->entityManager ? $this->entityManager->getClassMetadata($class) : $this->classMetadataFactory->getMetadataFor($class);
} catch (MappingException $exception) {
return;
} catch (OrmMappingException $exception) {
return;
if (null === $metadata = $this->getMetadata($class)) {
return null;
}

if ($metadata->hasAssociation($property)) {
Expand Down Expand Up @@ -176,6 +170,39 @@ public function getTypes($class, $property, array $context = [])
}
}

/**
* {@inheritdoc}
*/
public function isReadable($class, $property, array $context = [])
{
return null;
}

/**
* {@inheritdoc}
*/
public function isWritable($class, $property, array $context = [])
{
if (
null === ($metadata = $this->getMetadata($class))
|| ClassMetadata::GENERATOR_TYPE_NONE === $metadata->generatorType
|| !\in_array($property, $metadata->getIdentifierFieldNames(), true)
) {
return null;
}

return false;
}

private function getMetadata(string $class): ?ClassMetadata
{
try {
return $this->entityManager ? $this->entityManager->getClassMetadata($class) : $this->classMetadataFactory->getMetadataFor($class);
} catch (MappingException | OrmMappingException $exception) {
return null;
}
}

/**
* Determines whether an association is nullable.
*
Expand Down
Expand Up @@ -16,6 +16,7 @@
use Doctrine\ORM\Tools\Setup;
use PHPUnit\Framework\TestCase;
use Symfony\Bridge\Doctrine\PropertyInfo\DoctrineExtractor;
use Symfony\Bridge\Doctrine\Tests\PropertyInfo\Fixtures\DoctrineGeneratedValue;
use Symfony\Component\PropertyInfo\Type;

/**
Expand Down Expand Up @@ -223,4 +224,13 @@ private function doTestGetTypesCatchException(bool $legacy)
{
$this->assertNull($this->createExtractor($legacy)->getTypes('Not\Exist', 'baz'));
}

public function testGeneratedValueNotWritable()
{
$extractor = $this->createExtractor();
$this->assertFalse($extractor->isWritable(DoctrineGeneratedValue::class, 'id'));
$this->assertNull($extractor->isReadable(DoctrineGeneratedValue::class, 'id'));
$this->assertNull($extractor->isWritable(DoctrineGeneratedValue::class, 'foo'));
$this->assertNull($extractor->isReadable(DoctrineGeneratedValue::class, 'foo'));
}
}
@@ -0,0 +1,37 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Bridge\Doctrine\Tests\PropertyInfo\Fixtures;

use Doctrine\ORM\Mapping\Column;
use Doctrine\ORM\Mapping\Entity;
use Doctrine\ORM\Mapping\GeneratedValue;
use Doctrine\ORM\Mapping\Id;

/**
* @author Kévin Dunglas <dunglas@gmail.com>
*
* @Entity
*/
class DoctrineGeneratedValue
{
/**
* @Id
* @GeneratedValue(strategy="AUTO")
* @Column(type="integer")
*/
public $id;

/**
* @Column
*/
public $foo;
}

0 comments on commit 0d196c4

Please sign in to comment.