Skip to content

Commit 553c8cb

Browse files
committed
DD#main: feat: add initial schema generation
Generates the table and primary key column with constraint
1 parent 3aeafa6 commit 553c8cb

File tree

6 files changed

+295
-1
lines changed

6 files changed

+295
-1
lines changed

README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,3 +65,14 @@ Creates a repository and search result model with interfaces.
6565

6666
* module - The module name, e.g. MyCompany_MyModule
6767
* entity - The entity model name
68+
69+
#### Make Schema
70+
71+
Create or update db_schema.xml.
72+
73+
Will create entity table with primary key constraint, does not overwrite existing tables
74+
75+
`./magegen.php make:schema [<magepath> [<module> [<entity>]]]`
76+
77+
* module - The module name, e.g. MyCompany_MyModule
78+
* entity - The entity model name

magegen.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,5 +25,6 @@
2525
$application->add(new \MageGen\MakePluginCommand($twig));
2626
$application->add(new \MageGen\MakeEntityCommand($twig));
2727
$application->add(new \MageGen\MakeRepositoryCommand($twig));
28+
$application->add(new \MageGen\MakeSchemaCommand($twig));
2829

2930
$application->run();

src/Generator/EntityGenerator.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ public function createCollection(string $entityFqn, string $resourceFqn, string
131131

132132
$class->addExtend('\Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection');
133133

134-
$class->addProperty('_idFieldName', $idField . '_entity')->setProtected();
134+
$class->addProperty('_idFieldName', $idField)->setProtected();
135135

136136
$class->addMethod('_construct')->setBody(
137137
sprintf(

src/Generator/SchemaGenerator.php

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
<?php
2+
3+
/**
4+
* Copyright Skywire. All rights reserved.
5+
* See LICENSE.txt for license details.
6+
*
7+
* @author Skywire Core Team
8+
* @copyright Copyright (c) 2021 Skywire (http://www.skywire.co.uk)
9+
*/
10+
declare(strict_types=1);
11+
12+
namespace MageGen\Generator;
13+
14+
use DOMDocument;
15+
use DOMElement;
16+
use MageGen\Writer\ModuleFile;
17+
use Nette\PhpGenerator\ClassType;
18+
use Twig\Environment;
19+
20+
21+
class SchemaGenerator
22+
{
23+
/**
24+
* @var Environment
25+
*/
26+
protected $twig;
27+
28+
/**
29+
* DiGenerator constructor.
30+
*
31+
* @param Environment $twig
32+
*/
33+
public function __construct(Environment $twig)
34+
{
35+
$this->twig = $twig;
36+
}
37+
38+
/**
39+
* Create a file if it doesn't exist and return the path
40+
*
41+
* @param string $vendor
42+
* @param string $module
43+
* @param ModuleFile $writer
44+
*
45+
* @return string
46+
* @throws \Twig\Error\LoaderError
47+
* @throws \Twig\Error\RuntimeError
48+
* @throws \Twig\Error\SyntaxError
49+
*/
50+
public function createSchemaFile(string $vendor, string $module, ModuleFile $writer): string
51+
{
52+
$etcPath = 'etc';
53+
54+
return $writer->writeFile(
55+
$vendor,
56+
$module,
57+
$etcPath,
58+
'db_schema.xml',
59+
$this->twig->render('module/db_schema.xml.twig')
60+
);
61+
}
62+
63+
public function addEntity(string $schemaFilePath, string $classFqn)
64+
{
65+
$dom = new DOMDocument();
66+
$dom->preserveWhiteSpace = false;
67+
$dom->formatOutput = true;
68+
$dom->loadXML(file_get_contents($schemaFilePath));
69+
70+
$resourceFqn = str_replace('\\Model\\', '\\Model\\ResourceModel\\', $classFqn);
71+
[$tableName, $idField] = $this->getTableAndIdField($resourceFqn);
72+
if (!$tableName) {
73+
throw new \RuntimeException("Table name and id could not be determined for resource '$resourceFqn'");
74+
}
75+
76+
$tableNode = $this->getTableNode($dom, $tableName);
77+
if (!$tableNode) {
78+
$tableNode = $dom->createElement('table');
79+
$tableNode->setAttribute('name', $tableName);
80+
$tableNode->setAttribute('resource', 'default');
81+
$tableNode->setAttribute('engine', 'innodb');
82+
$dom->documentElement->appendChild($tableNode);
83+
84+
// Add primary key
85+
$primaryNode = $dom->createElement('column');
86+
$primaryNode->setAttribute('xsi:type', 'int');
87+
$primaryNode->setAttribute('name', $idField);
88+
$primaryNode->setAttribute('unsigned', 'true');
89+
$primaryNode->setAttribute('nullable', 'false');
90+
$primaryNode->setAttribute('identity', 'true');
91+
$primaryNode->setAttribute('comment', 'Primary Key');
92+
$tableNode->appendChild($primaryNode);
93+
94+
$constraintNode = $dom->createElement('constraint');
95+
$constraintNode->setAttribute('xsi:type', 'primary');
96+
$constraintNode->setAttribute('referenceId', 'PRIMARY');
97+
$constraintColumnNode = $dom->createElement('column');
98+
$constraintColumnNode->setAttribute('name', $idField);
99+
$constraintNode->appendChild($constraintColumnNode);
100+
$tableNode->appendChild($constraintNode);
101+
}
102+
103+
// TODO Add properties
104+
105+
// $preferenceNode = $dom->createElement('preference');
106+
// $preferenceNode->setAttribute('for', $interface);
107+
// $preferenceNode->setAttribute('type', $model);
108+
// $dom->documentElement->appendChild($preferenceNode);
109+
110+
$xml = $dom->saveXML();
111+
112+
file_put_contents($schemaFilePath, $xml);
113+
}
114+
115+
protected function getTableNode(DOMDocument $dom, string $tableName): ?DOMElement
116+
{
117+
$tables = $dom->getElementsByTagName('table');
118+
119+
$tables = array_filter(
120+
iterator_to_array($tables),
121+
function (DOMElement $node) use($tableName) {
122+
return $node->getAttribute('name') === $tableName;
123+
}
124+
);
125+
126+
if (empty($tables)) {
127+
return null;
128+
}
129+
130+
return array_shift($tables);
131+
}
132+
133+
protected function getTableAndIdField(string $resourceFqn): array
134+
{
135+
$resourceClass = ClassType::withBodiesFrom($resourceFqn);
136+
$body = $resourceClass->getMethod('_construct')->getBody();
137+
138+
preg_match_all("/'\w*'/", $body, $matches);
139+
140+
if (!empty($matches)) {
141+
return array_map(
142+
function (string $match) {
143+
return trim($match, "'");
144+
},
145+
$matches[0]
146+
);
147+
}
148+
149+
return [];
150+
}
151+
}

src/MakeSchemaCommand.php

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
<?php
2+
3+
/**
4+
* Copyright Skywire. All rights reserved.
5+
* See LICENSE.txt for license details.
6+
*
7+
* @author Skywire Core Team
8+
* @copyright Copyright (c) 2021 Skywire (http://www.skywire.co.uk)
9+
*/
10+
declare(strict_types=1);
11+
12+
namespace MageGen;
13+
14+
use MageGen\Autocomplete\EntityAutocomplete;
15+
use MageGen\Autocomplete\ModuleAutocomplete;
16+
use MageGen\Generator\DiGenerator;
17+
use MageGen\Generator\EntityGenerator;
18+
use MageGen\Generator\SchemaGenerator;
19+
use MageGen\Helper\ModuleHelper;
20+
use MageGen\Printer\PropertyPrinter;
21+
use MageGen\Writer\AbstractWriter;
22+
use MageGen\Writer\ClassFile;
23+
use MageGen\Writer\ModuleFile;
24+
use Nette\PhpGenerator\ClassType;
25+
use Nette\PhpGenerator\Method;
26+
use Nette\PhpGenerator\Parameter;
27+
use Nette\PhpGenerator\PhpFile;
28+
use Nette\PhpGenerator\PhpNamespace;
29+
use Nette\PhpGenerator\Property;
30+
use Nette\PhpGenerator\PsrPrinter;
31+
use Nette\PhpGenerator\Traits\NameAware;
32+
use Symfony\Component\Console\Command\Command;
33+
use Symfony\Component\Console\Input\InputArgument;
34+
use Symfony\Component\Console\Input\InputInterface;
35+
use Symfony\Component\Console\Output\OutputInterface;
36+
use Symfony\Component\Console\Question\ChoiceQuestion;
37+
use Symfony\Component\Console\Question\Question;
38+
use Symfony\Component\Console\Style\SymfonyStyle;
39+
use Twig\Environment;
40+
41+
42+
/**
43+
* @method string getCommandDescription()
44+
*/
45+
class MakeSchemaCommand extends AbstractCommand
46+
{
47+
/**
48+
* @var Environment
49+
*/
50+
protected $twig;
51+
52+
protected static $defaultName = 'make:schema';
53+
54+
/**
55+
* @var SchemaGenerator
56+
*/
57+
protected $schemaGenerator;
58+
59+
public function __construct(Environment $twig, string $name = null)
60+
{
61+
parent::__construct($twig, $name);
62+
63+
$this->schemaGenerator = new SchemaGenerator($twig);
64+
}
65+
66+
protected function configure(): void
67+
{
68+
parent::configure();
69+
$this->addArgument('module', InputArgument::OPTIONAL, 'Plugin subject / target');
70+
$this->addArgument('entity', InputArgument::OPTIONAL, 'Entity name');
71+
}
72+
73+
protected function execute(InputInterface $input, OutputInterface $output): int
74+
{
75+
require $input->getArgument('magepath') . '/vendor/autoload.php';
76+
77+
$io = new SymfonyStyle($input, $output);
78+
79+
$module = $input->getArgument('module');
80+
if (!$module) {
81+
$module = $io->askQuestion(
82+
(new Question('Module'))->setAutocompleterValues(
83+
(new ModuleAutocomplete())->getAutocompleteValues(
84+
$input->getArgument('magepath')
85+
)
86+
)
87+
);
88+
}
89+
90+
$entity = $input->getArgument('entity');
91+
if (!$entity) {
92+
$prefix = sprintf('%s\\Model\\', str_replace('_', '\\', $module));
93+
$entity = $io->askQuestion(
94+
(new Question(sprintf('Entity %s', $prefix)))->setAutocompleterValues(
95+
(new EntityAutocomplete())->getAutocompleteValues(
96+
$input->getArgument('magepath'),
97+
$module
98+
)
99+
)
100+
);
101+
}
102+
103+
$writer = $this->getWriter($input);
104+
105+
$entityFqn = implode(
106+
'\\',
107+
[
108+
str_replace('_', '\\', $module),
109+
'Model',
110+
$entity,
111+
]
112+
);
113+
114+
$vendor = $this->nameHelper->getVendor($entityFqn);
115+
$module = $this->nameHelper->getModule($entityFqn);
116+
117+
$schemaFilePath = $this->schemaGenerator->createSchemaFile(
118+
$vendor,
119+
$module,
120+
$writer
121+
);
122+
123+
$this->schemaGenerator->addEntity($schemaFilePath, $entityFqn);
124+
125+
return 0;
126+
}
127+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<schema xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Setup/Declaration/Schema/etc/schema.xsd">
3+
4+
</schema>

0 commit comments

Comments
 (0)