Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
Libor Jonat committed Jul 10, 2014
0 parents commit e50e01c
Show file tree
Hide file tree
Showing 11 changed files with 458 additions and 0 deletions.
9 changes: 9 additions & 0 deletions AlexDoctrineExtraBundle.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

namespace Alex\DoctrineExtraBundle;

use Symfony\Component\HttpKernel\Bundle\Bundle;

class AlexDoctrineExtraBundle extends Bundle
{
}
38 changes: 38 additions & 0 deletions Command/DoctrineMetadataGraphvizCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php

namespace Alex\DoctrineExtraBundle\Command;

use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

use Alex\DoctrineExtraBundle\Graphviz\DoctrineMetadataGraph;

/**
* Tool to generate graph from mapping informations.
*
* @author Alexandre Salomé <[email protected]>
*/
class DoctrineMetadataGraphvizCommand extends ContainerAwareCommand
{
/**
* {@inheritdoc}
*/
protected function configure()
{
$this
->setName('doctrine:mapping:graphviz')
;
}

/**
* {@inheritdoc}
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$em = $this->getContainer()->get('doctrine')->getManager();
$graph = new DoctrineMetadataGraph($em);

$output->writeln($graph->render());
}
}
164 changes: 164 additions & 0 deletions Graphviz/DoctrineMetadataGraph.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
<?php

namespace Alex\DoctrineExtraBundle\Graphviz;

use Doctrine\Common\Proxy\Exception\OutOfBoundsException;
use Doctrine\ORM\Mapping\ClassMetadataInfo;
use Doctrine\Common\Persistence\Mapping\ClassMetadata;
use Doctrine\Common\Persistence\ObjectManager;

use Alex\DoctrineExtraBundle\Graphviz\Pass\ImportMetadataPass;
use Alex\DoctrineExtraBundle\Graphviz\Pass\InheritancePass;
use Alex\DoctrineExtraBundle\Graphviz\Pass\ShortNamePass;

use Alom\Graphviz\Digraph;

class DoctrineMetadataGraph extends Digraph
{
public function __construct(ObjectManager $manager)
{
parent::__construct('G');

$this->attr('node', array(
'shape' => 'record'
));
$this->set('rankdir', 'LR');

$data = $this->prepareData($this->createData($manager));

$clusters = array();

foreach ($data['entities'] as $class => $entity) {
try{
$clusterName = $this->getCluster($class);
if (!isset($clusters[$clusterName])) {
$clusters[$clusterName] = $this->subgraph('cluster_'.$clusterName)
->set('label', $clusterName)
->set('shape', 'none')
->set('style', 'filled')
->set('color', '#eeeeee')
->attr('node', array(
'style' => 'filled',
'color' => '#eecc88',
'fillcolor' => '#FCF0AD',
))
;
}
}catch(\OutOfBoundsException $e){
continue;
}

$label = $this->getEntityLabel($class, $entity);
$clusters[$clusterName]->node($class, array('label' => $label));
}

foreach ($data['relations'] as $association) {
$attr = array();
switch ($association['type']) {
case 'one_to_one':
case 'one_to_many':
case 'many_to_one':
case 'many_to_many':
$attr['color'] = '#88888888';
$attr['arrowhead'] = 'none';
break;
case 'extends':
}

$this->edge(array($association['from'], $association['to']), $attr);
}
}

private function createData(ObjectManager $manager)
{
$data = array('entities' => array(), 'relations' => array());
$passes = array(
new ImportMetadataPass(),
new InheritancePass(),
new ShortNamePass()
);

foreach ($passes as $pass) {
$data = $pass->process($manager->getMetadataFactory(), $data);
}

return $data;
}

private function getEntityLabel($class, $entity)
{

$result = '{{<__class__>'.$class.'|';
foreach ($entity['associations'] as $name => $val) {
$result .= '<'.$name.'> '.$name.' : '.$val.'|';
}
foreach ($entity['fields'] as $name => $val) {
$result .= $name.' : '.$val.'|';
}
$result = rtrim($result, '|');
$result .= '}}';

return $result;
}

private function getCluster($entityName)
{
$exp = explode(':', $entityName);

if (count($exp) !== 2) {
throw new \OutOfBoundsException(sprintf('Unexpected count of ":" in entity name. Expected one ("AcmeDemoBundle:User"), got %s ("%s").', count($exp), $entityName));
}

return $exp[0];
}

private function getBundleName($name)
{
$name = explode('\\', $name);
$bundleName = '';
foreach($name as $n){
$bundleName .= ucfirst($n);
if(substr_count($n, 'Bundle') > 0){
break;
}
}
return $bundleName;
}

private function getEntityName($originalName)
{
$name = explode('\\', $originalName);
foreach($name as $k => $n){
unset($name[$k]);
if(substr_count($n, 'Bundle') > 0){
unset($name[$k + 1]);
break;
}
}
return implode('', $name);
}

private function transformName($name)
{
if(substr_count($name, ':') < 1 && substr_count($name, 'Bundle') > 0){
return "{$this->getBundleName($name)}:{$this->getEntityName($name)}";
}
return $name;
}

private function prepareData(array $data){
foreach($data as $key => $value){
unset($data[$key]);
$newKey = $this->transformName($key);
$data[$newKey] = $value;

if(is_array($value)){
$data[$newKey] = $this->prepareData($value);
}else{
$data[$newKey] = $this->transformName($value);
}
}
return $data;
}

}
77 changes: 77 additions & 0 deletions Graphviz/Pass/ImportMetadataPass.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
<?php

namespace Alex\DoctrineExtraBundle\Graphviz\Pass;

use Doctrine\Common\Persistence\Mapping\ClassMetadataFactory;
use Doctrine\ORM\Mapping\ClassMetadataInfo;

class ImportMetadataPass
{
public function process(ClassMetadataFactory $factory, $data)
{
foreach ($factory->getAllMetadata() as $classMetadata) {
$class = $classMetadata->getName();

$data['entities'][$class] = array(
'associations' => array(),
'fields' => array(),
);

foreach ($classMetadata->fieldMappings as $name => $config) {
$data['entities'][$class]['fields'][$name] = $config['type'];
}

// associations
foreach ($classMetadata->getAssociationMappings() as $name => $mapping) {
$field = $mapping['fieldName'];
$targetEntity = $mapping['targetEntity'];

$type = '';
switch ($mapping['type']) {
case ClassMetadataInfo::ONE_TO_MANY:
$type = 'one_to_many';
break;
case ClassMetadataInfo::ONE_TO_ONE:
$type = 'one_to_one';
break;
case ClassMetadataInfo::MANY_TO_ONE:
$type = 'many_to_one';
break;
case ClassMetadataInfo::MANY_TO_MANY:
$type = 'many_to_many';
break;
default:
throw new \RuntimeException('Unkown association type '.$mapping['type']);
}

$label = $targetEntity;
if ($type == 'one_to_many' || $type == 'many_to_many') {
$label .= '[]';
}

$data['entities'][$class]['associations'][$field] = $label;

if ($mapping['sourceEntity'] === $mapping['targetEntity']) {
continue;
}

$from = array($mapping['sourceEntity'], $field);
$to = array($mapping['targetEntity'], '__class__');

foreach ($data['relations'] as $relation) {
if ($relation['from'] === $from) {
continue 2;
}
}

$data['relations'][] = array(
'from' => $from,
'to' => $to,
'type' => $type
);
}
}

return $data;
}
}
43 changes: 43 additions & 0 deletions Graphviz/Pass/InheritancePass.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?php

namespace Alex\DoctrineExtraBundle\Graphviz\Pass;

use Doctrine\Common\Persistence\Mapping\ClassMetadataFactory;
use Doctrine\ORM\Mapping\ClassMetadataInfo;

class InheritancePass
{
public function process(ClassMetadataFactory $factory, $data)
{
foreach ($factory->getAllMetadata() as $classMetadata) {
$actual = $classMetadata->getName();

$current = $classMetadata->getReflectionClass();
while (false !== ($current = $current->getParentClass())) {
$name = $current->getName();
if (isset($data['entities'][$name])) {
$data['relations'][] = array(
'from' => array($actual, '__class__'),
'to' => array($name, '__class__'),
'type' => 'extends'
);

break;
}
}

// Inherited properties
foreach ($classMetadata->getReflectionProperties() as $property) {
$class = $property->getDeclaringClass()->getName();
$name = $property->getName();

if ($class !== $actual && isset($data['entities'][$class]['fields'][$name])) {
unset($data['entities'][$actual]['fields'][$name]);
}
}

}

return $data;
}
}
Loading

0 comments on commit e50e01c

Please sign in to comment.