Algoritmo para renderizar sistema de bloques HTML
Estructuras para almacenar datos de contenido HTML y algoritmo para renderizar el código web a partir de árbol de nodos
De árbol de nodos HTML a tabla
| id | parent | value | type | weight |
|---|---|---|---|---|
| 1 | null | Hello world! | h1 | 100 |
| 2 | null | null | container | 100 |
| 3 | null |
<h1>Hello world!</h1>
<div>
<div class="text">
<p>lorem ipsum dolor</p>
<p>sit amet consectetur</p>
<p><img src="https://picsum.photos/1280/720"></p>
</div>
<ul>
<li>lorem ipsum dolor</li>
<li>sit amet consectetur</li>
</ul>
</div>
|
code-plain | 100 |
| 4 | 2 | null | text | 100 |
| 5 | 2 | null | ul | 100 |
| 6 | 5 | lorem ipsum dolor | item | 100 |
| 7 | 5 | sit amet consectetur | item | 100 |
| 8 | 4 | lorem ipsum dolor | item | 90 |
| 9 | 4 | sit amet consectetur | item | 100 |
| 10 | 4 | https://picsum.photos/1280/720 | img | 100 |
Representación de tabla en PHP
$items = [
[
"id" => "1",
"parent" => null,
"value" => "Hello world!",
"weight" => 100,
"type" => "h1",
"children" => [],
],
[
"id" => "2",
"parent" => null,
"value" => null,
"weight" => 100,
"type" => "container",
"children" => [],
],
[
"id" => "3",
"parent" => null,
"value" => '<h1>Hello world!</h1>
<div>
<div class="text">
<p>lorem ipsum dolor</p>
<p>sit amet consectetur</p>
<p><img src="https://picsum.photos/1280/720"></p>
</div>
<ul>
<li>lorem ipsum dolor</li>
<li>sit amet consectetur</li>
</ul>
</div>',
"weight" => 100,
"type" => "code-plain",
"children" => [],
],
[
"id" => "4",
"parent" => "2",
"value" => null,
"weight" => 100,
"type" => "text",
"children" => [],
],
[
"id" => "5",
"parent" => "2",
"value" => null,
"weight" => 100,
"type" => "ul",
"children" => [],
],
[
"id" => "6",
"parent" => "5",
"value" => "lorem ipsum dolor",
"weight" => 100,
"type" => "item",
"children" => [],
],
[
"id" => "7",
"parent" => "5",
"value" => "sit amet consectetur",
"weight" => 100,
"type" => "item",
"children" => [],
],
[
"id" => "8",
"parent" => "4",
"value" => "lorem ipsum dolor",
"weight" => 90,
"type" => "item",
"children" => [],
],
[
"id" => "9",
"parent" => "4",
"value" => "sit amet consectetur",
"weight" => 100,
"type" => "item",
"children" => [],
],
[
"id" => "10",
"parent" => "4",
"value" => "https://picsum.photos/1280/720",
"weight" => 100,
"type" => "img",
"children" => [],
],
];
Código del algoritmo en PHP
Clase base para renderizar bloques de contenido
class HyperItemsRenderDefault implements \Stringable
{
protected mixed $value;
protected array $children;
public function __construct(
mixed $value = null,
array $children = []
) {
$this->value = $value;
$this->children = $children;
}
public function render(): string
{
return json_encode([
'value' => $this->value,
'children' => $this->children,
]);
}
public function __toString(): string
{
return $this->render();
}
}
Clases derivadas para cada bloque HTML
class HyperItemsRenderForContainer extends HyperItemsRenderDefault
{
public function render(): string
{
$content = implode('', $this->children);
return "<div>$content</div>";
}
}
class HyperItemsRenderForText extends HyperItemsRenderDefault
{
public function render(): string
{
$content = array_reduce($this->children, fn($carry, $item) => $carry . "<p>$item</p>");
return "<div>$content</div>";
}
}
class HyperItemsRenderForImg extends HyperItemsRenderDefault
{
public function render(): string
{
return "<img src=\"{$this->value}\">";
}
}
class HyperItemsRenderForH1 extends HyperItemsRenderDefault
{
public function render(): string
{
return "<h1>{$this->value}</h1>";
}
}
class HyperItemsRenderForItem extends HyperItemsRenderDefault
{
public function render(): string
{
return $this->value;
}
}
class HyperItemsRenderForUl extends HyperItemsRenderDefault
{
public function render(): string
{
$content = array_reduce($this->children, fn($carry, $item) => $carry . "<li>$item</li>");
return "<ul>$content</ul>";
}
}
class HyperItemsRenderForPlainCode extends HyperItemsRenderDefault
{
public function render(): string
{
$value = htmlspecialchars($this->value);
return "<pre><code>$value</code></pre>";
}
}
Mapeo de clases y tipos de bloque de contenido
$RenderClasses = [
'default' => HyperItemsRenderDefault::class,
'container' => HyperItemsRenderForContainer::class,
'text' => HyperItemsRenderForText::class,
'img' => HyperItemsRenderForImg::class,
'h1' => HyperItemsRenderForH1::class,
'item' => HyperItemsRenderForItem::class,
'ul' => HyperItemsRenderForUl::class,
'code-plain' => HyperItemsRenderForPlainCode::class,
];
Algoritmo para convertir de tabla de datos a árbol de nodos
$nodesMap = []; // Nodes in Map structure
$nodesTree = []; // Nodes in Tree structure
// Generate Map structure
foreach ($items as $k => $item) {
$nodesMap[$item['id']] = &$items[$k];
}
// Nesting nodes and generate tree structure
foreach ($nodesMap as $k => $item) {
if (!is_null($item['parent'])) {
$nodesMap[$item['parent']]['children'][] = &$nodesMap[$k];
} else {
$nodesTree[] = &$nodesMap[$k];
}
}
Algoritmo para asignar la lógica de renderizado y poder imprimir HTML final
// Instantiate the correct Render Object for each node
foreach ($nodesMap as $k => $item) {
$render = $RenderClasses[$item['type']] ?? null;
if (!isset($render)) {
$render = $RenderClasses['default'];
}
$nodesMap[$k] = new $render($item['value'], $item['children']);
}
$HTMLrender = array_reduce($nodesTree, fn($carry, $item) => $carry . $item); // Final HTML
echo $HTMLrender;
Código HTML resultante
<h1>Hello world!</h1>
<div>
<div>
<p>lorem ipsum dolor</p>
<p>sit amet consectetur</p>
<p><img src="https://picsum.photos/1280/720" /></p>
</div>
<ul>
<li>lorem ipsum dolor</li>
<li>sit amet consectetur</li>
</ul>
</div>
<pre><code><h1>Hello world!</h1>
<div>
<div class="text">
<p>lorem ipsum dolor</p>
<p>sit amet consectetur</p>
<p><img src="https://picsum.photos/1280/720"></p>
</div>
<ul>
<li>lorem ipsum dolor</li>
<li>sit amet consectetur</li>
</ul>
</div></code></pre>
Hello world!
lorem ipsum dolor
sit amet consectetur
- lorem ipsum dolor
- sit amet consectetur
<h1>Hello world!</h1>
<div>
<div class="text">
<p>lorem ipsum dolor</p>
<p>sit amet consectetur</p>
<p><img src="https://picsum.photos/1280/720"></p>
</div>
<ul>
<li>lorem ipsum dolor</li>
<li>sit amet consectetur</li>
</ul>
</div>

