Skip to content

Commit

Permalink
Entity listeners are now processed by exporters
Browse files Browse the repository at this point in the history
The current implementation of the exporters are not taking the entity
listeners into account. I have added test cases for most of the edge
cases I could think of and implemented the Exporter handling.

This PR originates from #5864,
I was overwhelmed by the amount of conflicts I had to resolve so I
started anew on a clean master HEAD.

Squashed commits:

- Code review aftermath
- Add even more return type declarations
- Added `return null` to methods declared with nullable return types
- Removed unneeded docblocks when types are self-explanatory
  • Loading branch information
tPl0ch committed Aug 15, 2017
1 parent a0c0d3b commit b7ae5b4
Show file tree
Hide file tree
Showing 10 changed files with 324 additions and 282 deletions.
379 changes: 124 additions & 255 deletions lib/Doctrine/ORM/Tools/EntityGenerator.php

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions lib/Doctrine/ORM/Tools/Export/Driver/AnnotationExporter.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ class AnnotationExporter extends AbstractExporter
/**
* {@inheritdoc}
*/
public function exportClassMetadata(ClassMetadataInfo $metadata)
public function exportClassMetadata(ClassMetadataInfo $metadata): string
{
if ( ! $this->_entityGenerator) {
throw new \RuntimeException('For the AnnotationExporter you must set an EntityGenerator instance with the setEntityGenerator() method.');
Expand All @@ -63,7 +63,7 @@ public function exportClassMetadata(ClassMetadataInfo $metadata)
*
* @return string
*/
protected function _generateOutputPath(ClassMetadataInfo $metadata)
protected function _generateOutputPath(ClassMetadataInfo $metadata): string
{
return $this->_outputDir . '/' . str_replace('\\', '/', $metadata->name) . $this->_extension;
}
Expand All @@ -73,7 +73,7 @@ protected function _generateOutputPath(ClassMetadataInfo $metadata)
*
* @return void
*/
public function setEntityGenerator(EntityGenerator $entityGenerator)
public function setEntityGenerator(EntityGenerator $entityGenerator): void
{
$this->_entityGenerator = $entityGenerator;
}
Expand Down
28 changes: 26 additions & 2 deletions lib/Doctrine/ORM/Tools/Export/Driver/PhpExporter.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ class PhpExporter extends AbstractExporter
/**
* {@inheritdoc}
*/
public function exportClassMetadata(ClassMetadataInfo $metadata)
public function exportClassMetadata(ClassMetadataInfo $metadata): string
{
$lines = [];
$lines[] = '<?php';
Expand Down Expand Up @@ -82,6 +82,8 @@ public function exportClassMetadata(ClassMetadataInfo $metadata)
}
}

$lines = array_merge($lines, $this->processEntityListeners($metadata));

foreach ($metadata->fieldMappings as $fieldMapping) {
$lines[] = '$metadata->mapField(' . $this->_varExport($fieldMapping) . ');';
}
Expand Down Expand Up @@ -164,7 +166,7 @@ public function exportClassMetadata(ClassMetadataInfo $metadata)
*
* @return string
*/
protected function _varExport($var)
protected function _varExport($var): string
{
$export = var_export($var, true);
$export = str_replace("\n", PHP_EOL . str_repeat(' ', 8), $export);
Expand All @@ -177,4 +179,26 @@ protected function _varExport($var)

return $export;
}

private function processEntityListeners(ClassMetadataInfo $metadata): array
{
$lines = [];

if (0 === \count($metadata->entityListeners)) {
return $lines;
}

foreach ($metadata->entityListeners as $event => $entityListenerConfig) {
foreach ($entityListenerConfig as $entityListener) {
$lines[] = sprintf(
'$metadata->addEntityListener(%s, %s, %s);',
var_export($event, true),
var_export($entityListener['class'], true),
var_export($entityListener['method'], true)
);
}
}

return $lines;
}
}
66 changes: 53 additions & 13 deletions lib/Doctrine/ORM/Tools/Export/Driver/XmlExporter.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
namespace Doctrine\ORM\Tools\Export\Driver;

use Doctrine\ORM\Mapping\ClassMetadataInfo;
use SimpleXMLElement;

/**
* ClassMetadata exporter for Doctrine XML mapping files.
Expand All @@ -38,9 +39,9 @@ class XmlExporter extends AbstractExporter
/**
* {@inheritdoc}
*/
public function exportClassMetadata(ClassMetadataInfo $metadata)
public function exportClassMetadata(ClassMetadataInfo $metadata): string
{
$xml = new \SimpleXmlElement("<?xml version=\"1.0\" encoding=\"utf-8\"?><doctrine-mapping ".
$xml = new SimpleXmlElement("<?xml version=\"1.0\" encoding=\"utf-8\"?><doctrine-mapping ".
"xmlns=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\" " .
"xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" ".
"xsi:schemaLocation=\"http://doctrine-project.org/schemas/orm/doctrine-mapping http://doctrine-project.org/schemas/orm/doctrine-mapping.xsd\" />");
Expand Down Expand Up @@ -390,16 +391,18 @@ public function exportClassMetadata(ClassMetadataInfo $metadata)
}
}

$this->processEntityListeners($metadata, $root);

return $this->_asXml($xml);
}

/**
* Exports (nested) option elements.
*
* @param \SimpleXMLElement $parentXml
* @param array $options
* @param SimpleXMLElement $parentXml
* @param array $options
*/
private function exportTableOptions(\SimpleXMLElement $parentXml, array $options)
private function exportTableOptions(SimpleXMLElement $parentXml, array $options): void
{
foreach ($options as $name => $option) {
$isArray = is_array($option);
Expand All @@ -418,12 +421,12 @@ private function exportTableOptions(\SimpleXMLElement $parentXml, array $options
/**
* Export sequence information (if available/configured) into the current identifier XML node
*
* @param \SimpleXMLElement $identifierXmlNode
* @param SimpleXMLElement $identifierXmlNode
* @param ClassMetadataInfo $metadata
*
* @return void
*/
private function exportSequenceInformation(\SimpleXMLElement $identifierXmlNode, ClassMetadataInfo $metadata)
private function exportSequenceInformation(SimpleXMLElement $identifierXmlNode, ClassMetadataInfo $metadata): void
{
$sequenceDefinition = $metadata->sequenceGeneratorDefinition;

Expand All @@ -438,17 +441,54 @@ private function exportSequenceInformation(\SimpleXMLElement $identifierXmlNode,
$sequenceGeneratorXml->addAttribute('initial-value', $sequenceDefinition['initialValue']);
}

/**
* @param \SimpleXMLElement $simpleXml
*
* @return string $xml
*/
private function _asXml($simpleXml)
private function _asXml(SimpleXMLElement $simpleXml): string
{
$dom = new \DOMDocument('1.0', 'UTF-8');
$dom->loadXML($simpleXml->asXML());
$dom->formatOutput = true;

return $dom->saveXML();
}

private function processEntityListeners(ClassMetadataInfo $metadata, SimpleXMLElement $root): void
{
if (0 === \count($metadata->entityListeners)) {
return;
}

$entityListenersXml = $root->addChild('entity-listeners');
$entityListenersXmlMap = [];

$this->generateEntityListenerXml($metadata, $entityListenersXmlMap, $entityListenersXml);
}

private function generateEntityListenerXml(ClassMetadataInfo $metadata, array $entityListenersXmlMap, SimpleXMLElement $entityListenersXml): void
{
foreach ($metadata->entityListeners as $event => $entityListenerConfig) {
foreach ($entityListenerConfig as $entityListener) {
$entityListenerXml = $this->addClassToMapIfExists(
$entityListenersXmlMap,
$entityListener,
$entityListenersXml
);

$entityListenerCallbackXml = $entityListenerXml->addChild('lifecycle-callback');
$entityListenerCallbackXml->addAttribute('type', $event);
$entityListenerCallbackXml->addAttribute('method', $entityListener['method']);
}
}
}

private function addClassToMapIfExists(array $entityListenersXmlMap, array $entityListener, SimpleXMLElement $entityListenersXml): SimpleXMLElement
{
if (isset($entityListenersXmlMap[$entityListener['class']])) {
return $entityListenersXmlMap[$entityListener['class']];
}

$entityListenerXml = $entityListenersXml->addChild('entity-listener');
$entityListenerXml->addAttribute('class', $entityListener['class']);
$entityListenersXmlMap[$entityListener['class']] = $entityListenerXml;

return $entityListenerXml;
}
}
33 changes: 31 additions & 2 deletions lib/Doctrine/ORM/Tools/Export/Driver/YamlExporter.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ class YamlExporter extends AbstractExporter
/**
* {@inheritdoc}
*/
public function exportClassMetadata(ClassMetadataInfo $metadata)
public function exportClassMetadata(ClassMetadataInfo $metadata): string
{
$array = [];

Expand Down Expand Up @@ -214,6 +214,8 @@ public function exportClassMetadata(ClassMetadataInfo $metadata)
$array['lifecycleCallbacks'] = $metadata->lifecycleCallbacks;
}

$array = $this->processEntityListeners($metadata, $array);

return $this->yamlDump([$metadata->name => $array], 10);
}

Expand All @@ -228,8 +230,35 @@ public function exportClassMetadata(ClassMetadataInfo $metadata)
*
* @return string A YAML string representing the original PHP array
*/
protected function yamlDump($array, $inline = 2)
protected function yamlDump($array, $inline = 2): string
{
return Yaml::dump($array, $inline);
}

private function processEntityListeners(ClassMetadataInfo $metadata, array $array): array
{
if (0 === \count($metadata->entityListeners)) {
return $array;
}

$array['entityListeners'] = [];

foreach ($metadata->entityListeners as $event => $entityListenerConfig) {
$array = $this->processEntityListenerConfig($array, $entityListenerConfig, $event);
}

return $array;
}

private function processEntityListenerConfig(array $array, array $entityListenerConfig, string $event): array
{
foreach ($entityListenerConfig as $entityListener) {
if (!isset($array['entityListeners'][$entityListener['class']])) {
$array['entityListeners'][$entityListener['class']] = [];
}
$array['entityListeners'][$entityListener['class']][$event] = [$entityListener['method']];
}

return $array;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
use Doctrine\Common\EventManager;
use Doctrine\Common\Persistence\Mapping\Driver\PHPDriver;
use Doctrine\ORM\Configuration;
use Doctrine\ORM\Events;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\Mapping\ClassMetadataFactory;
use Doctrine\ORM\Mapping\ClassMetadataInfo;
use Doctrine\ORM\Mapping\Driver\AnnotationDriver;
Expand Down Expand Up @@ -344,7 +346,7 @@ public function testInversedByIsExported($class)
{
$this->assertEquals('user', $class->associationMappings['address']['inversedBy']);
}
/**
/**
* @depends testExportDirectoryAndFilesAreCreated
*/
public function testCascadeAllCollapsed()
Expand All @@ -371,6 +373,26 @@ public function testCascadeAllCollapsed()
}
}

/**
* @depends testExportedMetadataCanBeReadBackIn
*
* @param ClassMetadata $class
*/
public function testEntityListenersAreExported($class)
{
$this->assertNotEmpty($class->entityListeners);
$this->assertCount(2, $class->entityListeners[Events::prePersist]);
$this->assertCount(2, $class->entityListeners[Events::postPersist]);
$this->assertEquals(UserListener::class, $class->entityListeners[Events::prePersist][0]['class']);
$this->assertEquals('customPrePersist', $class->entityListeners[Events::prePersist][0]['method']);
$this->assertEquals(GroupListener::class, $class->entityListeners[Events::prePersist][1]['class']);
$this->assertEquals('prePersist', $class->entityListeners[Events::prePersist][1]['method']);
$this->assertEquals(UserListener::class, $class->entityListeners[Events::postPersist][0]['class']);
$this->assertEquals('customPostPersist', $class->entityListeners[Events::postPersist][0]['method']);
$this->assertEquals(AddressListener::class, $class->entityListeners[Events::postPersist][1]['class']);
$this->assertEquals('customPostPersist', $class->entityListeners[Events::postPersist][1]['method']);
}

public function __destruct()
{
# $this->_deleteDirectory(__DIR__ . '/export/'.$this->_getType());
Expand Down Expand Up @@ -406,3 +428,28 @@ class Group
{

}
class UserListener
{
/**
* @\Doctrine\ORM\Mapping\PrePersist
*/
public function customPrePersist(): void {}
/**
* @\Doctrine\ORM\Mapping\PostPersist
*/
public function customPostPersist(): void {}
}
class GroupListener
{
/**
* @\Doctrine\ORM\Mapping\PrePersist
*/
public function prePersist(): void {}
}
class AddressListener
{
/**
* @\Doctrine\ORM\Mapping\PostPersist
*/
public function customPostPersist(): void {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@
/**
* @Entity
* @HasLifecycleCallbacks
* @EntityListeners({
* Doctrine\Tests\ORM\Tools\Export\UserListener::class,
* Doctrine\Tests\ORM\Tools\Export\GroupListener::class,
* Doctrine\Tests\ORM\Tools\Export\AddressListener::class
* })
* @Table(name="cms_users",options={"engine"="MyISAM","foo"={"bar"="baz"}})
*/
class User
Expand Down Expand Up @@ -57,21 +62,21 @@ class User
/**
* @PrePersist
*/
public function doStuffOnPrePersist()
public function doStuffOnPrePersist(): void
{
}

/**
* @PrePersist
*/
public function doOtherStuffOnPrePersistToo()
public function doOtherStuffOnPrePersistToo(): void
{
}

/**
* @PostPersist
*/
public function doStuffOnPostPersist()
public function doStuffOnPostPersist(): void
{
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
<?php

use Doctrine\ORM\Events;
use Doctrine\ORM\Mapping\ClassMetadataInfo;
use Doctrine\Tests\ORM\Tools\Export;
use Doctrine\Tests\ORM\Tools\Export\AddressListener;
use Doctrine\Tests\ORM\Tools\Export\GroupListener;
use Doctrine\Tests\ORM\Tools\Export\UserListener;

$metadata->setInheritanceType(ClassMetadataInfo::INHERITANCE_TYPE_NONE);
$metadata->setPrimaryTable(
Expand All @@ -11,9 +15,9 @@
]
);
$metadata->setChangeTrackingPolicy(ClassMetadataInfo::CHANGETRACKING_DEFERRED_IMPLICIT);
$metadata->addLifecycleCallback('doStuffOnPrePersist', 'prePersist');
$metadata->addLifecycleCallback('doOtherStuffOnPrePersistToo', 'prePersist');
$metadata->addLifecycleCallback('doStuffOnPostPersist', 'postPersist');
$metadata->addLifecycleCallback('doStuffOnPrePersist', Events::prePersist);
$metadata->addLifecycleCallback('doOtherStuffOnPrePersistToo', Events::prePersist);
$metadata->addLifecycleCallback('doStuffOnPostPersist', Events::postPersist);
$metadata->mapField(
[
'id' => true,
Expand Down Expand Up @@ -149,3 +153,7 @@
'orderBy' => NULL,
]
);
$metadata->addEntityListener(Events::prePersist, UserListener::class, 'customPrePersist');
$metadata->addEntityListener(Events::postPersist, UserListener::class, 'customPostPersist');
$metadata->addEntityListener(Events::prePersist, GroupListener::class, 'prePersist');
$metadata->addEntityListener(Events::postPersist, AddressListener::class, 'customPostPersist');
Loading

0 comments on commit b7ae5b4

Please sign in to comment.