Commit da07abb4 authored by Guillaume Perréal's avatar Guillaume Perréal
Browse files

Première version.

No related merge requests found
Showing with 249 additions and 0 deletions
+249 -0
.gitignore 0 → 100644
vendor
nbproject
<?php
/*
* Copyright (C) 2015 IRSTEA
* All rights reserved.
*/
namespace Irstea\PlantUmlBundle\Command;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Mapping\ClassMetadata;
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Description of ImportAffiliationCommand
*
* @author Guillaume Perréal <guillaume.perreal@irstea.fr>
*/
class UmlSchemaCommand extends ContainerAwareCommand
{
/**
* @var array[]
*/
private $namespaces = [];
/**
* @var string[]
*/
private $nodes = [];
/**
* @var string[]
*/
private $arrows = [];
protected function configure()
{
$this
->setName('irstea:plantuml:doctrine')
->setDescription("Génère un schéma PlantUML à partir des métadonnées Doctrine.");
}
/**
*
* @param InputInterface $input
* @param OutputInterface $output
*
* @SuppressWarnings(UnusedFormalParameter)
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
/* @var $manager EntityManagerInterface */
$manager = $this->getContainer()->get('doctrine.orm.entity_manager');
$factory = $manager->getMetadataFactory();
$allMetadata = $factory->getAllMetadata();
array_walk($allMetadata, [$this, 'processClass']);
echo "@startuml\n";
$this->writeNamespaces($this->namespaces);
echo "\n\n";
echo implode("\n", $this->nodes);
echo "\n\n";
echo implode("\n", $this->arrows);
echo "\n\n@enduml\n";
}
protected function processClass(ClassMetadata $metadata)
{
$node = [];
$name = $this->formatName($metadata->getName());
$class = $metadata->getReflectionClass();
$node[] = sprintf(
'%s%s %s%s {',
$class->isAbstract() ? 'abstract ' : '',
$class->isInterface() ? 'interface' : 'class',
$name,
$metadata->isMappedSuperclass ? ' <<mappedSuperClass>>' : ''
);
foreach($metadata->fieldMappings as $field) {
$node[] = sprintf("\t%s: %s", $field['fieldName'], $field['type']);
}
$node[] = '}';
$this->addNode($name, implode("\n", $node), true);
foreach($metadata->getAssociationMappings() as $association) {
if (!$association['isOwningSide']) {
continue;
}
$sourceCard = ($association['type'] & (ClassMetadata::MANY_TO_MANY | ClassMetadata::MANY_TO_ONE)) ? '*' : '1';
$sourceArrow = $association['inversedBy'] ? '<' : '';
$targetCard = ($association['type'] & ClassMetadata::TO_MANY) ? '*' : '1';
$targetArrow = '>';
$this->addArrow(
$association['sourceEntity'],
$association['targetEntity'],
sprintf('"%s" %s--%s "%s"', $sourceCard, $sourceArrow, $targetArrow, $targetCard)
);
}
$parentClass = $class->getParentClass();
if ($parentClass) {
$this->addArrow($parentClass->getName(), $metadata->getName(), '<|--');
}
foreach($class->getInterfaceNames() as $interfaceName) {
$this->addNode($interfaceName, sprintf("interface %s {\n}", $this->formatName($interfaceName)));
$this->addArrow($interfaceName, $metadata->getName(), '<|..');
}
foreach($class->getTraitNames() as $traitName) {
$this->addNode($traitName, sprintf("class %s <<trait>> {\n}", $this->formatName($traitName)));
$this->addArrow($traitName, $metadata->getName(), '<|--');
}
}
/**
* Enregistre un namespace.
*
* @param string[] $components
*/
protected function addNamespace(array $components)
{
$current = &$this->namespaces;
foreach($components as $next) {
if (!isset($current[$next])) {
$current[$next] = [];
}
$current =& $current[$next];
}
}
/**
*
* @param string $name
* @return string
*/
protected function formatName($name)
{
return strtr($name, '\\', '.');
}
/**
* Ajoute un noeud.
*
* @param string $name Nom du noeud.
* @param string $body Déclaration du noeud.
* @param bool $overwrite Doit-on remplacer la déclaration s'il existe déjà ?
*
* @return string Le nom de noeud "PlantUml-compatible".
*/
protected function addNode($name, $body = '', $overwrite = false)
{
$fullName = $this->formatName($name);
$components = explode('\\', $name);
array_pop($components);
$this->addNamespace($components);
if (!isset($this->nodes[$fullName]) || $overwrite) {
$this->nodes[$fullName] = $body;
}
return $fullName;
}
/**
* Ajoute une flèche sur le schéma.
*
* @param string $source Nom de l'origine.
* @param string $target Nom de la destination.
* @param string $type Description du lien.
*/
protected function addArrow($source, $target, $type = '-->')
{
$sourceName = $this->addNode($source);
$targetName = $this->addNode($target);
$this->arrows[] = sprintf('%s %s %s', $sourceName, $type, $targetName);
}
/**
* Génère les déclarations de namespaces.
*
* @param array $current
*/
protected function writeNamespaces(array $current)
{
foreach($current as $name => $children) {
echo sprintf("namespace %s {\n", $name);
$this->writeNamespaces($children);
echo "}\n";
}
}
}
<?php
namespace Irstea\PlantUmlBundle;
use Symfony\Component\HttpKernel\Bundle\Bundle;
class IrsteaPlantUmlBundle extends Bundle
{
}
composer.json 0 → 100644
{
"name": "irstea/plantuml-bundle",
"description": "Un bundle pour génerer des schémas UML à partir du code.",
"type": "symfony-bundle",
"license": "proprietary",
"authors": [
{ "name": "Pôle Informatique Scientifique - Direction des Systèmes d'Information", "email": "dsi.poleis@lists.irstea.fr" },
{ "name": "Guillaume Perréal" }
],
"minimum-stability": "stable",
"autoload": {
"psr-4": { "Irstea\\PlantUmlBundle\\": "./" }
},
"repositories": [
{ "type": "composer", "url": "http://istest.lyon.cemagref.fr/satis" }
],
"require": {
"php": ">=5.4",
"symfony/framework-bundle": "^2.6",
"doctrine/doctrine-bundle": "^1.6"
},
"require-dev": {
},
"extra": {
"branch-alias": {
"dev-master": "0.1-dev"
}
},
"archive": {
"exclude": [
"vendor",
".git*",
"nbproject"
]
}
}
This diff is collapsed.
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment