Skip to content

Commit

Permalink
Add Entity Relationship Diagrams (#14)
Browse files Browse the repository at this point in the history
  • Loading branch information
JeanDavidDaviet authored Sep 26, 2023
1 parent afa2c94 commit 120c0d1
Show file tree
Hide file tree
Showing 12 changed files with 1,133 additions and 4 deletions.
62 changes: 62 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,68 @@ linkStyle default interpolate basis;
```


### Usage of an ERDiagram

```php
<?php

use JBZoo\MermaidPHP\ERDiagram\Entity\Entity;
use JBZoo\MermaidPHP\ERDiagram\ERDiagram;
use JBZoo\MermaidPHP\ERDiagram\Relation\ManyToMany;
use JBZoo\MermaidPHP\ERDiagram\Relation\ManyToOne;
use JBZoo\MermaidPHP\ERDiagram\Relation\OneToMany;
use JBZoo\MermaidPHP\ERDiagram\Relation\OneToOne;
use JBZoo\MermaidPHP\ERDiagram\Relation\Relation;
use JBZoo\MermaidPHP\Render;

$diagram = (new ERDiagram(['title' => 'Order Example']));

$diagram
->addEntity($customerEntity = new Entity('C', 'Customer', props: [
new EntityProperty('id', 'int', [EntityProperty::PRIMARY_KEY], 'ID of user'),
new EntityProperty('cash', 'float'),
]))
->addEntity($orderEntity = new Entity('O', 'Order'))
->addEntity($lineItemEntity = new Entity('LI', 'Line-Item'))
->addEntity($deliveryAddressEntity = new Entity('DA', 'Delivery-Address'))
->addEntity($creditCardEntity = new Entity('CC', 'Credit-Card'))
->addRelation(new OneToMany($customerEntity, $orderEntity, 'places', Relation::ONE_OR_MORE))
->addRelation(new ManyToOne($lineItemEntity, $orderEntity, 'belongs', Relation::ZERO_OR_MORE))
->addRelation(new ManyToMany($customerEntity, $deliveryAddressEntity, 'uses', Relation::ONE_OR_MORE))
->addRelation(new OneToOne($customerEntity, $creditCardEntity, 'has', Relation::ONE_OR_MORE))
;
//header('Content-Type: text/plain');
//echo $diagram; // Get result as string (or $graph->__toString(), or (string)$graph)
$htmlCode = $diagram->renderHtml([
'debug' => true,
'theme' => Render::THEME_DARK,
'title' => 'Example',
'show-zoom' => false,
'mermaid_url' => 'https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.esm.min.mjs',
]); // Get result as HTML code for debugging

echo $diagram->getLiveEditorUrl(); // Get link to live editor
```

### Result
[Open live editor](https://mermaid-js.github.io/mermaid-live-editor/edit#pako:eNp1kE1qxDAMha9itB5fILuSdDG00EK33qixMjH4J9hK6ZDk7qM4U0phqpV4-vSe0AJ9sgQNaK1NZMeeGvWWLWX1_I1h8mRiHVHuHF4yBhOVlIF2LpwCZQNqXbVeF9HqogiNmjz2VH7YVxdJn5mCzLYk8PoH_iSf4qU8cK7w7tyRd1-Ur_rJ2kyl1L25UPnvnD2hzWQd6xazrfyIj_Dl0PZykZWz6v1FiHOn0rBHCPNLDD4hqx7LeGibiXACMQrorLyxmhngkQIZaKQdktzLu8cmJM6cPq6xh4bzTCeYJ4tM99ce4nYDIeuBCQ)

```
---
title: Order Example
---
erDiagram
"Customer" ||--|{ "Order" : places
"Line-Item" }o--|| "Order" : belongs
"Customer" }o--|{ "Delivery-Address" : uses
"Customer" ||--|| "Credit-Card" : has
"Customer" {
int id PK "ID of user"
float cash
}
```


### See also
- [Mermaid on GitHub](https://github.com/mermaid-js/mermaid)
- [Mermaid Documentation](https://mermaid.js.org/)
Expand Down
146 changes: 146 additions & 0 deletions src/ERDiagram/ERDiagram.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
<?php

/**
* JBZoo Toolbox - Mermaid-PHP.
*
* This file is part of the JBZoo Toolbox project.
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @license MIT
* @copyright Copyright (C) JBZoo.com, All rights reserved.
* @see https://github.com/JBZoo/Mermaid-PHP
*/

declare(strict_types=1);

namespace JBZoo\MermaidPHP\ERDiagram;

use JBZoo\MermaidPHP\ERDiagram\Entity\Entity;
use JBZoo\MermaidPHP\ERDiagram\Relation\Relation;
use JBZoo\MermaidPHP\Helper;
use JBZoo\MermaidPHP\Render;

class ERDiagram
{
private const RENDER_SHIFT = 4;

/** @var Entity[] */
protected array $entities = [];

/** @var Relation[] */
protected array $relations = [];

/** @var mixed[] */
protected array $params = [
'abc_order' => false,
'title' => '',
];

/**
* @param mixed[] $params
*/
public function __construct(array $params = [])
{
$this->setParams($params);
}

public function __toString(): string
{
return $this->render();
}

public function addEntity(Entity $entity): self
{
$this->entities[$entity->getId()] = $entity;

return $this;
}

public function addRelation(Relation $relation): self
{
$this->relations[$relation->getId()] = $relation;

return $this;
}

public function setParams(array $params): self
{
$this->params = \array_merge($this->params, $params);

return $this;
}

public function getParams(): array
{
return $this->params;
}

/**
* @SuppressWarnings(PHPMD.NPathComplexity)
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
*/
public function render(int $shift = 0): string
{
$spacesSub = \str_repeat(' ', $shift + self::RENDER_SHIFT);

$result = [];
$params = $this->getParams();

if (isset($params['title']) && $params['title'] !== '') {
$result[] = \sprintf('---%s---', \PHP_EOL . 'title: ' . $params['title'] . \PHP_EOL);
}

$result[] = 'erDiagram';

if (\count($this->relations) > 0) {
$tmp = [];

foreach ($this->relations as $relation) {
$tmp[] = $spacesSub . $relation;
}

if ($this->params['abc_order'] === true) {
\sort($tmp);
}

$result = \array_merge($result, $tmp);
}

$entitiesWithProps = \array_filter($this->entities, static fn (Entity $entity) => $entity->getProps() !== []);

if (\count($entitiesWithProps) > 0) {
$tmp = [];

foreach ($entitiesWithProps as $entity) {
$classEntity = $entity->renderProps();
if ($classEntity !== null) {
$tmp[] = $spacesSub . $classEntity;
}
}

if ($this->params['abc_order'] === true) {
\sort($tmp);
}

$result = \array_merge($result, $tmp);
}

$result[] = '';

return \implode(\PHP_EOL, $result);
}

/**
* @param array<string> $params
*/
public function renderHtml(array $params = []): string
{
return Render::html($this, $params);
}

public function getLiveEditorUrl(): string
{
return Helper::getLiveEditorUrl($this);
}
}
105 changes: 105 additions & 0 deletions src/ERDiagram/Entity/Entity.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
<?php

/**
* JBZoo Toolbox - Mermaid-PHP.
*
* This file is part of the JBZoo Toolbox project.
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @license MIT
* @copyright Copyright (C) JBZoo.com, All rights reserved.
* @see https://github.com/JBZoo/Mermaid-PHP
*/

declare(strict_types=1);

namespace JBZoo\MermaidPHP\ERDiagram\Entity;

use JBZoo\MermaidPHP\Helper;

class Entity
{
private static bool $safeMode = false;
protected string $identifier = '';
protected string $title = '';

/** @var EntityProperty[] */
protected array $props = [];

/**
* @param EntityProperty[] $props
*/
public function __construct(string $identifier, string $title = '', array $props = [])
{
$this->identifier = static::isSafeMode() ? Helper::getId($identifier) : $identifier;
$this->setTitle($title === '' ? $identifier : $title);
$this->setProps($props);
}

public function __toString(): string
{
if ($this->title !== '') {
return Helper::escape($this->title);
}

return "{$this->identifier};";
}

public function setTitle(string $title): self
{
$this->title = $title;

return $this;
}

public function getTitle(): string
{
return $this->title === '' ? $this->getId() : $this->title;
}

/**
* @return EntityProperty[]
*/
public function getProps(): array
{
return $this->props;
}

/**
* @param EntityProperty[] $props
*/
public function setProps(array $props): void
{
$this->props = $props;
}

public function renderProps(): ?string
{
$spaces = \str_repeat(' ', 4);

$props = $this->getProps();
if ($props !== []) {
$output = $this . ' {' . \PHP_EOL;

foreach ($props as $prop) {
/** @var \JBZoo\MermaidPHP\ERDiagram\Entity\EntityProperty $prop */
$output .= $spaces . $spaces . $prop . \PHP_EOL;
}

return $output . $spaces . '}';
}

return null;
}

public function getId(): string
{
return $this->identifier;
}

public static function isSafeMode(): bool
{
return self::$safeMode;
}
}
Loading

0 comments on commit 120c0d1

Please sign in to comment.