diff --git a/.gitignore b/.gitignore
index 612adf8..53f0456 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
-*.graphml
+*.xml
.idea
-vendor
\ No newline at end of file
+vendor
+.DS_Store
diff --git a/ERDiagramXMLExportBundle/Command/CreateERDiagramXMLExportCommand.php b/ERDiagramXMLExportBundle/Command/CreateERDiagramXMLExportCommand.php
index fbcaec5..08c8aef 100644
--- a/ERDiagramXMLExportBundle/Command/CreateERDiagramXMLExportCommand.php
+++ b/ERDiagramXMLExportBundle/Command/CreateERDiagramXMLExportCommand.php
@@ -1,18 +1,16 @@
setDescription('Provides an yEd-XML Output to visualize ER of Pimcore Classes');
+ $this->setDescription('Provides an ER-Diagram of Pimcore Classes in XML format for diagrams.net or yEd');
+ $this->addArgument('format', InputArgument::REQUIRED, 'diagrams | yed');
$this->addArgument('filename', InputArgument::OPTIONAL, 'Provide a filename');
}
- public function execute(InputInterface $input, OutputInterface $output)
+ /**
+ * @throws Exception
+ */
+ public function execute(InputInterface $input, OutputInterface $output): int
{
- $result = new GraphMLWriter(
- $this->getClassDefinitionData(),
- $this->getFieldCollectionsData(),
- $this->getObjectBricksData(),
- $input->getArgument('filename') ?: ''
-
- );
- $result->output();
-
- return 0;
- }
-
- private function getClassDefinitionData(): array
- {
- $listing = new ClassDefinitionListing();
- $classDefinitions = $listing->load();
-
- $classDefinitionData = [];
-
- foreach ($classDefinitions as $classDefinition) {
- $fieldDefinitions = $classDefinition->getFieldDefinitions();
-
-
- $data = [
- 'id' => $classDefinition->getId(),
- 'name' => $classDefinition->getName(),
- 'fields' => $this->processFieldDefinitions($fieldDefinitions),
- 'relatedClasses' => $this->getRelatedClasses($fieldDefinitions),
- 'relatedFieldCollections' => $this->getRelatedFieldCollections($fieldDefinitions),
- 'relatedObjectBricks' => $this->getRelatedObjectBricks($fieldDefinitions),
- ];
-
- array_push($classDefinitionData, $data);
- }
-
- return $classDefinitionData;
- }
-
- private function processFieldDefinitions($fieldDefinitions): array
- {
- $data = [];
-
- /** @var DataObject\ClassDefinition\Data $fieldDefinition */
- foreach ($fieldDefinitions as $fieldDefinition) {
- $fieldType = $fieldDefinition->getFieldtype();
-
- if (strpos($fieldType, 'Relation') == false) {
- $fields = [
- $fieldDefinition->getName() => $fieldType,
- ];
- if ($fieldDefinition instanceof FieldCollections) {
- $allowedTypes = [];
-
- foreach ($fieldDefinition->getAllowedTypes() as $allowedType) {
- array_push($allowedTypes, $allowedType);
- }
- $fields = [
- $fieldDefinition->getName() => $allowedTypes,
- ];
- }
- array_push($data, $fields);
- }
- }
-
- return $data;
- }
-
- private function getRelatedClasses($fieldDefinitions): array
- {
- $relatedClasses = [];
-
- /** @var DataObject\ClassDefinition\Data $fieldDefinition */
- foreach ($fieldDefinitions as $fieldDefinition) {
- $fieldType = $fieldDefinition->getFieldtype();
-
- if (strpos($fieldType, 'Relation') !== false) {
- foreach ($fieldDefinition->getClasses() as $class) {
- array_push($relatedClasses, [$fieldType => $class['classes']]);
- }
- }
+ $outputType = strtolower($input->getArgument('format'));
+ switch ($outputType) {
+ case 'diagrams':
+ $pimcoreDefinitionsRepository = new DiagramsPimcoreDefinitionsRepository();
+ $xmlGenerator = new DiagramsXmlGenerator(
+ $pimcoreDefinitionsRepository->getClassDefinitionData(),
+ $pimcoreDefinitionsRepository->getFieldCollectionsData(),
+ $pimcoreDefinitionsRepository->getObjectBricksData(),
+ );
+ break;
+
+ case 'yed':
+ $pimcoreDefinitionsRepository = new YedPimcoreDefinitionsRepository();
+ $xmlGenerator = new YedXmlGenerator(
+ $pimcoreDefinitionsRepository->getClassDefinitionData(),
+ $pimcoreDefinitionsRepository->getFieldCollectionsData(),
+ $pimcoreDefinitionsRepository->getObjectBricksData(),
+ );
+ break;
+
+ default:
+ throw new Exception('Invalid format: "' . $outputType . '". Valid: diagrams | yed');
}
- return $relatedClasses;
- }
-
- private function getRelatedFieldCollections($fieldDefinitions): array
- {
- $data = [];
-
- foreach ($fieldDefinitions as $fieldDefinition) {
- if ($fieldDefinition instanceof FieldCollections) {
- foreach ($fieldDefinition->getAllowedTypes() as $allowedType => $name) {
- array_push($data, $name);
- }
- }
- }
+ $xml = $xmlGenerator->generate();
+ $this->writeToFile($input->getArgument('filename') ?: '', $xml);
- return $data;
+ return 0;
}
- private function getRelatedObjectBricks($fieldDefinitions): array
+ /**
+ * @throws Exception
+ */
+ public function writeToFile(string $fileName, string $fileContent): void
{
- $data = [];
+ $dirname = dirname(__DIR__, 5) . '/var/tmp/';
- foreach ($fieldDefinitions as $fieldDefinition) {
- if ($fieldDefinition instanceof ObjectBricks) {
- foreach ($fieldDefinition->getAllowedTypes() as $allowedType => $name) {
- array_push($data, $name);
- }
- }
+ if (!is_dir($dirname)) {
+ $dirname = './var/tmp/';
}
- return $data;
- }
-
- private function getFieldCollectionsData(): array
- {
- $fieldCollectionData = [];
-
- $fieldCollectionListing = new FieldCollectionListing();
- $fieldCollections = $fieldCollectionListing->load();
-
- foreach ($fieldCollections as $fieldCollection) {
- $data['fieldCollection'] = [
-
- 'name' => $fieldCollection->getKey(),
- 'fields' => $this->processFieldDefinitions($fieldCollection->getFieldDefinitions()),
-
- ];
- array_push($fieldCollectionData, $data);
+ if (!is_dir($dirname)) {
+ throw new Exception('"' . $dirname . '" generate path does not exist!');
}
- return $fieldCollectionData;
- }
-
- private function getObjectBricksData(): array
- {
- $objectBricksData = [];
-
- $objectBricksListing = new ObjectBrickListing();
- $objectBricks = $objectBricksListing->load();
-
- foreach ($objectBricks as $objectBrick) {
- $data['objectBrick'] = [
-
- 'name' => $objectBrick->getKey(),
- 'fields' => $this->processFieldDefinitions($objectBrick->getFieldDefinitions()),
-
- ];
- array_push($objectBricksData, $data);
+ if (empty($fileName)) {
+ $file = $dirname . 'PimcoreClassDiagram.xml';
+ } else {
+ $file = $dirname . $fileName . '.xml';
}
- return $objectBricksData;
+ file_put_contents($file, $fileContent);
}
-}
\ No newline at end of file
+}
diff --git a/ERDiagramXMLExportBundle/DependencyInjection/DiagramsPimcoreDefinitionsRepository.php b/ERDiagramXMLExportBundle/DependencyInjection/DiagramsPimcoreDefinitionsRepository.php
new file mode 100644
index 0000000..4718825
--- /dev/null
+++ b/ERDiagramXMLExportBundle/DependencyInjection/DiagramsPimcoreDefinitionsRepository.php
@@ -0,0 +1,201 @@
+load();
+
+ $classDefinitionData = [];
+
+ foreach ($classDefinitions as $classDefinition) {
+ $fieldDefinitions = $classDefinition->getFieldDefinitions();
+
+ $classDefinitionData[] = [
+ 'name' => $classDefinition->getName(),
+ 'fields' => $this->processFieldDefinitions($fieldDefinitions, $classDefinition->getName()),
+ 'relationFields' => $this->getRelationFields($fieldDefinitions),
+ ];
+ }
+
+ return $classDefinitionData;
+ }
+
+ public function getFieldCollectionsData(): array
+ {
+ $fieldCollectionData = [];
+
+ $fieldCollectionListing = new FieldCollectionListing();
+ $fieldCollections = $fieldCollectionListing->load();
+
+ foreach ($fieldCollections as $fieldCollection) {
+ $fieldCollectionData[] = [
+ 'name' => $fieldCollection->getKey(),
+ 'fields' => $this->processFieldDefinitions($fieldCollection->getFieldDefinitions(),
+ $fieldCollection->getKey()),
+ 'relationFields' => $this->getRelationFields($fieldCollection->getFieldDefinitions()),
+ ];
+ }
+
+ return $fieldCollectionData;
+ }
+
+ public function getObjectBricksData(): array
+ {
+ $objectBricksData = [];
+
+ $objectBricksListing = new ObjectBrickListing();
+ $objectBricks = $objectBricksListing->load();
+
+ foreach ($objectBricks as $objectBrick) {
+ $objectBricksData[] = [
+ 'name' => $objectBrick->getKey(),
+ 'fields' => $this->processFieldDefinitions($objectBrick->getFieldDefinitions(), $objectBrick->getKey()),
+ 'relationFields' => $this->getRelationFields($objectBrick->getFieldDefinitions()),
+ ];
+ }
+
+ return $objectBricksData;
+ }
+
+ /** @var Data[] $fieldDefinitions */
+ private function processFieldDefinitions(array $fieldDefinitions, string $className): array
+ {
+ $fieldData = [];
+
+ foreach ($fieldDefinitions as $fieldDefinition) {
+ $fieldType = $fieldDefinition->getFieldtype();
+
+ // todo: classification store relations
+// if ($fieldDefinition instanceof Data\Classificationstore) {
+// p_r($fieldDefinition);
+// $fieldDefinition->getStoreId();
+// }
+
+ if (str_contains($fieldType, 'Relation')) {
+ $selfRelation = !empty($fieldDefinition->getClasses()) && in_array($className,
+ $fieldDefinition->getClasses()[0]);
+
+ $fieldData[] = [
+ 'name' => $fieldDefinition->getName(),
+ 'type' => $fieldType . ($selfRelation ? ' (self)' : ''),
+ ];
+ continue;
+ }
+
+ if ($fieldDefinition instanceof FieldCollections) {
+ $fieldData[] = [
+ 'name' => $fieldDefinition->getName(),
+ 'type' => implode(' | ', $fieldDefinition->getAllowedTypes()),
+ ];
+ continue;
+ }
+
+ if ($fieldDefinition instanceof Block) {
+ $fieldData[] = [
+ 'name' => $fieldDefinition->getName(),
+ 'type' => $fieldDefinition->getFieldtype(),
+ ];
+
+ /** @var Data $blockField */
+ foreach ($fieldDefinition->getChildren() as $blockField) {
+ $fieldData[] = [
+ 'name' => '> ' . $blockField->getName(),
+ 'type' => $blockField->getFieldtype(),
+ ];
+ }
+ continue;
+ }
+
+ if ($fieldDefinition instanceof Localizedfields) {
+ // a correctly sorted order cannot be achieved.
+ // per class definition only the first 'localizedfields' can be accessed
+ // the first localized fields are always listed under 'children' in that one
+ // while every following localizedfields block is listed in 'referencedFields'
+ // p_r($fieldDefinition); // <- check it
+
+ $localizedFields = [];
+ $localizedFields = array_merge($localizedFields, $fieldDefinition->getChildren());
+
+ /** @var Localizedfields $referencedLocalizedFields */
+ $referencedLocalizedFields = $fieldDefinition->getReferencedFields();
+ foreach ($referencedLocalizedFields as $referencedLocalizedField) {
+ $localizedFields = array_merge($localizedFields, $referencedLocalizedField->getChildren());
+ }
+
+ foreach ($localizedFields as $localizedField) {
+ $fieldData[] = [
+ 'name' => $localizedField->getName(),
+ 'type' => $localizedField->getFieldType() . ' (localized)',
+ ];
+ }
+ continue;
+ }
+
+ $fieldData[] = [
+ 'name' => $fieldDefinition->getName(),
+ 'type' => $fieldType,
+ ];
+ }
+ return $fieldData;
+ }
+
+ private function getRelationFields(array $fieldDefinitions): array
+ {
+ $relationFields = [];
+
+ /** @var Data $fieldDefinition */
+ foreach ($fieldDefinitions as $fieldDefinition) {
+ $fieldType = $fieldDefinition->getFieldtype();
+
+ if ($fieldDefinition instanceof AbstractRelations) {
+ foreach ($fieldDefinition->getClasses() as $class) {
+ $relationFields[] = [
+ 'fromFieldName' => $fieldDefinition->getName(),
+ 'toNode' => $class['classes'],
+ 'toType' => DiagramsXmlGenerator::TYPE_CLASS,
+ 'relationType' => $fieldType,
+ ];
+ }
+ }
+
+ if ($fieldDefinition instanceof FieldCollections) {
+ foreach ($fieldDefinition->getAllowedTypes() as $allowedType) {
+ $relationFields[] = [
+ 'fromFieldName' => $fieldDefinition->getName(),
+ 'toNode' => $allowedType,
+ 'toType' => DiagramsXmlGenerator::TYPE_FIELD_COLLECTION,
+ 'relationType' => 'OneToMany',
+ ];
+ }
+
+ }
+
+ if ($fieldDefinition instanceof ObjectBricks) {
+ foreach ($fieldDefinition->getAllowedTypes() as $brickName) {
+ $relationFields[] = [
+ 'fromFieldName' => $fieldDefinition->getName(),
+ 'toNode' => $brickName,
+ 'toType' => DiagramsXmlGenerator::TYPE_OBJECT_BRICK,
+ 'relationType' => 'OneToOne',
+ ];
+ }
+ }
+ }
+
+ return $relationFields;
+ }
+}
diff --git a/ERDiagramXMLExportBundle/DependencyInjection/DiagramsXmlGenerator.php b/ERDiagramXMLExportBundle/DependencyInjection/DiagramsXmlGenerator.php
new file mode 100644
index 0000000..9351130
--- /dev/null
+++ b/ERDiagramXMLExportBundle/DependencyInjection/DiagramsXmlGenerator.php
@@ -0,0 +1,183 @@
+classDefinitions = $classDefinitions;
+ $this->fieldCollections = $fieldCollections;
+ $this->objectBricks = $objectBricks;
+ }
+
+ public function generate(): string
+ {
+ $outputString = '';
+ foreach ($this->classDefinitions as $classDefinition) {
+ $outputString .= $this->createNodesAndEdges($classDefinition, self::TYPE_CLASS);
+ }
+ foreach ($this->fieldCollections as $fieldCollection) {
+ $outputString .= $this->createNodesAndEdges($fieldCollection, self::TYPE_FIELD_COLLECTION);
+ }
+ foreach ($this->objectBricks as $objectBrick) {
+ $outputString .= $this->createNodesAndEdges($objectBrick, self::TYPE_OBJECT_BRICK);
+ }
+ $outputString .= '';
+
+ return $outputString;
+ }
+
+ private function createNodesAndEdges(array $nodeData, string $nodeType): string
+ {
+ $node = $this->createNode($nodeData['name'], count($nodeData['fields']), $nodeType);
+ $attributes = $this->createAttributes($nodeData['name'], $nodeData['fields'], $nodeType);
+ $edges = $this->createEdges($nodeData['name'], $nodeData['relationFields'], $nodeType);
+ return $node . $attributes . $edges;
+ }
+
+ private function createNode(string $nodeName, int $fieldCount, string $nodeType): string
+ {
+ $style = match ($nodeType) {
+ self::TYPE_CLASS => 'fillColor=#dae8fc;strokeColor=#6c8ebf;', // blue
+ self::TYPE_FIELD_COLLECTION => 'fillColor=#FFF2CC;strokeColor=#D6B656;', // yellow
+ self::TYPE_OBJECT_BRICK => 'fillColor=#FFE6CC;strokeColor=#D79B00;', // orange
+ self::TYPE_CLASSIFICATION_STORE => 'fillColor=#f8cecc;strokeColor=#b85450', // red
+ default => '',
+ };
+
+ $id = $nodeType . '-' . $nodeName;
+ $position = $this->currentNodeXPosition;
+ $this->currentNodeXPosition += self::NODE_WIDTH + self::NODE_MARGIN;
+ $nodeXml = '
+
+
+ ';
+
+ return sprintf(
+ $nodeXml,
+ $id,
+ $nodeName,
+ $style,
+ $position,
+ self::NODE_WIDTH,
+ ($fieldCount + 1) * self::ATTRIBUTE_HEIGHT,
+ );
+ }
+
+ private function createAttributes(string $nodeName, array $fieldData, string $nodeType): string
+ {
+ if (empty($fieldData)) {
+ return '';
+ }
+
+ $attributeXml = '
+
+
+ ';
+
+ $attributesString = '';
+
+
+ $currentYPosition = 0;
+ foreach ($fieldData as $field) {
+ $id = $nodeType . '-' . $nodeName . '_attribute-' . $field['name'];
+ $parent = $nodeType . '-' . $nodeName;
+ $label = $field['name'] . ': ' . $field['type'];
+ $currentYPosition += self::ATTRIBUTE_HEIGHT;
+
+ $attributesString .= sprintf(
+ $attributeXml,
+ $id,
+ $parent,
+ $label,
+ $currentYPosition,
+ self::NODE_WIDTH,
+ self::ATTRIBUTE_HEIGHT,
+ );
+
+ }
+
+ return $attributesString;
+ }
+
+ private function createEdge($from, $to, $relationType = '')
+ {
+ $sourceArrowType = self::RELATION_ONE;
+ $targetArrowType = self::RELATION_ONE;
+
+ // str_contains because the relation types look like this 'manyToOneRelation', 'manyToManyObjectRelation'
+ if (str_contains(strtolower($relationType), 'manytomany')) {
+ $sourceArrowType = self::RELATION_MANY;
+ $targetArrowType = self::RELATION_MANY;
+ } else {
+ if (str_contains(strtolower($relationType), 'onetomany')) {
+ $targetArrowType = self::RELATION_MANY;
+ } else {
+ if (str_contains(strtolower($relationType), 'manytoone')) {
+ $sourceArrowType = self::RELATION_MANY;
+ }
+ }
+ }
+
+ $id = 'edge-from_' . $from . '_edge-to_' . $to;
+ $edgeXml = '
+
+
+ ';
+
+ return sprintf(
+ $edgeXml,
+ $id,
+ $from,
+ $to,
+ $sourceArrowType,
+ $targetArrowType,
+ );
+ }
+
+ private function createEdges(string $parentName, array $relationFields, string $parentType): string
+ {
+ if (empty($relationFields)) {
+ return '';
+ }
+
+ $edges = '';
+ foreach ($relationFields as $relationField) {
+ if ($parentName === $relationField['toNode']) {
+ continue;
+ }
+
+ $from = $parentType . '-' . $parentName . '_attribute-' . $relationField['fromFieldName'];
+ $to = $relationField['toType'] . '-' . $relationField['toNode'];
+ $edge = $this->createEdge($from, $to, $relationField['relationType']);
+ $edges .= $edge;
+ }
+
+ return $edges;
+ }
+}
diff --git a/ERDiagramXMLExportBundle/DependencyInjection/YedPimcoreDefinitionsRepository.php b/ERDiagramXMLExportBundle/DependencyInjection/YedPimcoreDefinitionsRepository.php
new file mode 100644
index 0000000..6fd0611
--- /dev/null
+++ b/ERDiagramXMLExportBundle/DependencyInjection/YedPimcoreDefinitionsRepository.php
@@ -0,0 +1,162 @@
+load();
+
+ $classDefinitionData = [];
+
+ foreach ($classDefinitions as $classDefinition) {
+ $fieldDefinitions = $classDefinition->getFieldDefinitions();
+
+ $data = [
+ 'id' => $classDefinition->getId(),
+ 'name' => $classDefinition->getName(),
+ 'fields' => $this->processFieldDefinitions($fieldDefinitions),
+ 'relatedClasses' => $this->getRelatedClasses($fieldDefinitions),
+ 'relatedFieldCollections' => $this->getRelatedFieldCollections($fieldDefinitions),
+ 'relatedObjectBricks' => $this->getRelatedObjectBricks($fieldDefinitions),
+ ];
+
+ $classDefinitionData[] = $data;
+ }
+
+ return $classDefinitionData;
+ }
+
+ public function getFieldCollectionsData(): array
+ {
+ $fieldCollectionData = [];
+
+ $fieldCollectionListing = new FieldCollectionListing();
+ $fieldCollections = $fieldCollectionListing->load();
+
+ foreach ($fieldCollections as $fieldCollection) {
+ $data['fieldCollection'] = [
+
+ 'name' => $fieldCollection->getKey(),
+ 'fields' => $this->processFieldDefinitions($fieldCollection->getFieldDefinitions()),
+
+ ];
+ $fieldCollectionData[] = $data;
+ }
+
+ return $fieldCollectionData;
+ }
+
+ public function getObjectBricksData(): array
+ {
+ $objectBricksData = [];
+
+ $objectBricksListing = new ObjectBrickListing();
+ $objectBricks = $objectBricksListing->load();
+
+ foreach ($objectBricks as $objectBrick) {
+ $data['objectBrick'] = [
+
+ 'name' => $objectBrick->getKey(),
+ 'fields' => $this->processFieldDefinitions($objectBrick->getFieldDefinitions()),
+
+ ];
+ $objectBricksData[] = $data;
+ }
+
+ return $objectBricksData;
+ }
+
+ /**
+ * @param Data[] $fieldDefinitions
+ * @return array
+ */
+ private function processFieldDefinitions(array $fieldDefinitions): array
+ {
+ $data = [];
+
+ foreach ($fieldDefinitions as $fieldDefinition) {
+ $fieldType = $fieldDefinition->getFieldtype();
+
+ if (!$fieldDefinition instanceof AbstractRelations) {
+ $fields = [
+ $fieldDefinition->getName() => $fieldType,
+ ];
+ if ($fieldDefinition instanceof FieldCollections) {
+ $allowedTypes = [];
+
+ foreach ($fieldDefinition->getAllowedTypes() as $allowedType) {
+ $allowedTypes[] = $allowedType;
+ }
+ $fields = [
+ $fieldDefinition->getName() => $allowedTypes,
+ ];
+ }
+ $data[] = $fields;
+ }
+ }
+
+ return $data;
+ }
+
+ /**
+ * @param Data[] $fieldDefinitions
+ * @return array
+ */
+ private function getRelatedClasses(array $fieldDefinitions): array
+ {
+ $relatedClasses = [];
+
+ foreach ($fieldDefinitions as $fieldDefinition) {
+ $fieldType = $fieldDefinition->getFieldtype();
+
+ if ($fieldDefinition instanceof AbstractRelations) {
+ foreach ($fieldDefinition->getClasses() as $class) {
+ $relatedClasses[] = [$fieldType => $class['classes']];
+ }
+ }
+ }
+
+ return $relatedClasses;
+ }
+
+ private function getRelatedFieldCollections($fieldDefinitions): array
+ {
+ $data = [];
+
+ foreach ($fieldDefinitions as $fieldDefinition) {
+ if ($fieldDefinition instanceof FieldCollections) {
+ foreach ($fieldDefinition->getAllowedTypes() as $allowedType => $name) {
+ $data[] = $name;
+ }
+ }
+ }
+
+ return $data;
+ }
+
+ private function getRelatedObjectBricks($fieldDefinitions): array
+ {
+ $data = [];
+
+ foreach ($fieldDefinitions as $fieldDefinition) {
+ if ($fieldDefinition instanceof ObjectBricks) {
+ foreach ($fieldDefinition->getAllowedTypes() as $allowedType => $name) {
+ $data[] = $name;
+ }
+ }
+ }
+
+ return $data;
+ }
+}
diff --git a/ERDiagramXMLExportBundle/DependencyInjection/GraphMLWriter.php b/ERDiagramXMLExportBundle/DependencyInjection/YedXmlGenerator.php
similarity index 78%
rename from ERDiagramXMLExportBundle/DependencyInjection/GraphMLWriter.php
rename to ERDiagramXMLExportBundle/DependencyInjection/YedXmlGenerator.php
index fd354d0..89c2720 100644
--- a/ERDiagramXMLExportBundle/DependencyInjection/GraphMLWriter.php
+++ b/ERDiagramXMLExportBundle/DependencyInjection/YedXmlGenerator.php
@@ -1,18 +1,16 @@
classDefinitions = $classDefinitions;
$this->fieldCollections = $fieldCollections;
$this->objectBricks = $objectBricks;
- $this->filename = $filename;
}
- public function output()
+ public function generate(): string
{
$this->createHeader();
$this->createNodesAndEdges();
$this->createFooter();
- $this->writeToFile();
+ return $this->xmlOutput;
}
- private function createHeader()
+ private function createHeader(): void
{
$this->xmlOutput .= "
-
@@ -62,7 +57,7 @@ private function createHeader()
";
}
- private function createNodesAndEdges()
+ private function createNodesAndEdges(): void
{
foreach ($this->classDefinitions as $classDefinition) {
$this->createNode($classDefinition);
@@ -76,19 +71,19 @@ private function createNodesAndEdges()
if (!empty($relatedClasses)) {
foreach ($relatedClasses as $class) {
foreach ($class as $relationType => $className) {
- $this->createEdge($parentClass, $className, $relationType, $className);
+ $this->createEdge($parentClass, $className, $className, $relationType);
}
}
}
if (!empty($relatedFieldCollections)) {
- foreach ($relatedFieldCollections as $relatedFieldCollection => $fieldCollectionName) {
- $this->createEdge($parentClass, $fieldCollectionName, 'onetomany', $fieldCollectionName);
+ foreach ($relatedFieldCollections as $fieldCollectionName) {
+ $this->createEdge($parentClass, $fieldCollectionName, $fieldCollectionName, 'onetomany');
}
}
if (!empty($relatedObjectBricks)) {
- foreach ($relatedObjectBricks as $relatedObjectBrick => $objectBrickName) {
- $this->createEdge($parentClass, $objectBrickName, '', $objectBrickName);
+ foreach ($relatedObjectBricks as $objectBrickName) {
+ $this->createEdge($parentClass, $objectBrickName, $objectBrickName, 'onetoone');
}
}
}
@@ -106,7 +101,7 @@ private function createNodesAndEdges()
}
}
- private function createNode(array $entry, bool $isFieldCollection = false, bool $isObjecktBrick = false)
+ private function createNode(array $entry, bool $isFieldCollection = false, bool $isObjecktBrick = false): void
{
$className = $entry['name'];
$fillColor = '#E8EEF7';
@@ -120,7 +115,7 @@ private function createNode(array $entry, bool $isFieldCollection = false, bool
$attributes = $this->createAttributes($entry);
/*
- * Sadly i cant use Spatie\ArrayToXml\ArrayToXml here because it's not possible to set an array for the _value
+ * Sadly I can't use Spatie\ArrayToXml\ArrayToXml here because it's not possible to set an array for the _value
* Element
* see: https://github.com/spatie/array-to-xml/issues/75#issuecomment-413726065
*/
@@ -129,9 +124,9 @@ private function createNode(array $entry, bool $isFieldCollection = false, bool
- %s
%s
@@ -205,23 +200,20 @@ private function createAttributes($entry): string
return $arrayToXml->dropXmlDeclaration()->prettify()->toXml();
}
- private function createEdge($source, $target, $relationType = '', $labelName)
+ private function createEdge($source, $target, $labelName, $relationType = ''): void
{
- if (strpos(strtolower($relationType), 'manytomany') !== false) {
+ $sourceArrowType = self::NONE;
+ $targetArrowType = self::NONE;
+
+ if (str_contains(strtolower($relationType), 'manytomany')) {
$sourceArrowType = self::CROWS_FOOT_MANY;
$targetArrowType = self::CROWS_FOOT_MANY;
}
- if (strpos(strtolower($relationType), 'onetomany') !== false) {
- $sourceArrowType = self::NONE;
+ if (str_contains(strtolower($relationType), 'onetomany')) {
$targetArrowType = self::CROWS_FOOT_MANY;
}
- if (strpos(strtolower($relationType), 'manytoone') !== false) {
+ if (str_contains(strtolower($relationType), 'manytoone')) {
$sourceArrowType = self::CROWS_FOOT_MANY;
- $targetArrowType = self::NONE;
- }
- if ($relationType == '') {
- $sourceArrowType = self::NONE;
- $targetArrowType = self::NONE;
}
$edgeContent = "
@@ -229,8 +221,8 @@ private function createEdge($source, $target, $relationType = '', $labelName)
- %s
@@ -238,9 +230,9 @@ private function createEdge($source, $target, $relationType = '', $labelName)
-
@@ -261,19 +253,6 @@ private function createEdge($source, $target, $relationType = '', $labelName)
$this->xmlOutput .= $edgeContent;
}
- private function writeToFile()
- {
- $dirname = dirname(__DIR__, 5) . '/var/tmp/';
-
- if (empty($this->filename)) {
- $file = $dirname . 'output.graphml';
- } else {
- $file = $dirname . $this->filename . '.graphml';
- }
-
- file_put_contents($file, $this->xmlOutput);
- }
-
private function createFooter()
{
$this->xmlOutput .= "
@@ -283,4 +262,4 @@ private function createFooter()
";
}
-}
\ No newline at end of file
+}
diff --git a/README.md b/README.md
index 656d628..d915f38 100644
--- a/README.md
+++ b/README.md
@@ -1,38 +1,68 @@
# ERDiagramXMLExportBundle
-### Prerequisites and General Information
+## General Information
-This Pimcore-Bundle will create an yEd compliant XML File to represent the Entity Relationship of Pimcore Classes,
-ObjectBricks and FieldCollections.
-Just open the generated file with the yEd Graph Editor, which you can find here:
-`https://www.yworks.com/products/yed/download`.
-After opening the file you should use the `Layout` Tab in the Graph Editor to arrange the rendered Graphics according your needs.
+This Pimcore-Bundle will create an XML File to represent the Entity Relationship of Pimcore Classes,
+ObjectBricks and FieldCollections. diagrams.net (formerly draw.io) or yEd.
-### Usage
-```
-bin/console basilicom::create-er-diagram-xml-export
-bin/console basilicom::create-er-diagram-xml-export
+You can open Diagrams.net graphs with the [diagrams.net](https://diagrams.net/) editor to adjust the layout to your liking.
+
+For yEd graphs, you can use the [yEd Graph Editor](https://www.yworks.com/products/yed/download).
+After opening the file you should use the `Layout` Tab in the Graph Editor to arrange the rendered Graphics.
+
+#### Diagrams.net
+1. Turning DataObjects (blue), ObjectBricks (orange), and FieldCollections (yellow) and their attributes into ER Nodes
+2. Recognising Localized Fields, marked as `(localized)`, and Blocks, indented with `>`
+3. Turning Relations into `one to one`, `one to many`, or `many to many` ER edges - from the field where they are set to the corresponding node
+
+#### yEd
+1. Turning DataObjects (blue), ObjectBricks (orange), and FieldCollections (yellow) and their attributes into ER Nodes
+2. Turning Relations into `one to one`, `one to many`, or `many to many` ER edges from node to node
+3. Automatic layout through the yEd editor
+
+### Limitations
+
+#### Diagrams.net
+1. Localized Fields will need to be reordered if there are multiple blocks
+(limited by the way pimcore outputs the data)
+2. Classification stores are not included yet
+3. The output won't be arranged
+
+#### yEd
+1. LocalizedFields, Blocks and ClassificationStores are not included yet
+2. Relations form within Object Bricks and FieldCollections are not included yet
+3. Field where a relation is set do not appear in the attribute list
+4. Fields are not separated
+
+
+## Usage
+```shell
+bin/console basilicom:create-er-diagram-xml-export []
```
-The generated file will be saved in the `\var\tmp\` Folder with filename`output.graphml` .
-You can also provide your own filename.
+The generated file will be saved in `/var/tmp/` as `PimcoreClassDiagram.xml` if no
+file name is provided.
+
+### Capabilities
+
+
+## Setup
+
#### Pimcore Configuration
-Make sure to enable the Bundle in `app/config/bundles.php`, e.g.
+Enable the Bundle in `/config/bundles.php`
-```
+```php
return [
Basilicom\ERDiagramXMLExportBundle\ERDiagramXMLExportBundle::class => ['all' => true],
];
```
-and add it to BundleCollection in `AppKernel.php`, e.g.
+And register it in `/src/Kernel.php`
-```
-...
+```php
use Basilicom\ERDiagramXMLExportBundle\ERDiagramXMLExportBundle;
-...
-class AppKernel extends Kernel
+class Kernel extends PimcoreKernel
{
/**
* @param BundleCollection $collection
@@ -45,3 +75,12 @@ class AppKernel extends Kernel
}
}
```
+
+## Further development Ideas
+
+#### Diagrams.net
+- ClassificationStore
+ - groups as attributes
+ - keys like block attributes (with >)
+- Correct sorting for LocalizedFields
+
diff --git a/composer.json b/composer.json
index 9fa9dff..3e417f8 100644
--- a/composer.json
+++ b/composer.json
@@ -1,7 +1,7 @@
{
"name": "basilicom/er-diagram-xml-export-bundle",
- "description": "This bundle creates a yed compliant XML of the Pimcore class structure",
- "keywords": ["yed","xml","er diagram"],
+ "description": "This bundle creates an XML of the Pimcore class structure for diagrams.net",
+ "keywords": ["diagrams.net","yEd","xml","er diagram","documentation","pimcore"],
"type": "pimcore-bundle",
"require": {
"php": ">=7.4",
@@ -16,6 +16,11 @@
"name": "Hendrik Hofmann",
"email": "hendrik.hofmann@basilicom.de",
"role": "Developer"
+ },
+ {
+ "name": "Steen Helmdach",
+ "email": "steen.helmdach@basilicom.de",
+ "role": "Developer"
}
],
"autoload": {