Skip to content

Commit bb510b9

Browse files
committed
cleanup commands and improve error handling
1 parent 8732419 commit bb510b9

File tree

7 files changed

+320
-148
lines changed

7 files changed

+320
-148
lines changed

src/Propel/Generator/Command/AbstractCommand.php

Lines changed: 56 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
namespace Propel\Generator\Command;
1010

11+
use Propel\Common\Config\Exception\InvalidConfigurationException;
1112
use Propel\Generator\Config\GeneratorConfig;
1213
use RuntimeException;
1314
use Symfony\Component\Console\Command\Command;
@@ -64,6 +65,8 @@ protected function configure()
6465
* @param array|null $properties Properties to add to the configuration. They usually come from command line.
6566
* @param \Symfony\Component\Console\Input\InputInterface|null $input
6667
*
68+
* @throws \Propel\Common\Config\Exception\InvalidConfigurationException
69+
*
6770
* @return \Propel\Generator\Config\GeneratorConfig
6871
*/
6972
protected function buildGeneratorConfig(?array $properties = null, ?InputInterface $input = null): GeneratorConfig
@@ -80,7 +83,40 @@ protected function buildGeneratorConfig(?array $properties = null, ?InputInterfa
8083
$properties['propel']['generator']['recursive'] = $input->getOption('recursive');
8184
}
8285

83-
return new GeneratorConfig($input->getOption('config-dir'), $properties);
86+
$configDir = $input->getOption('config-dir');
87+
try {
88+
return new GeneratorConfig($configDir, extraConf: $properties);
89+
} catch (InvalidConfigurationException $e) {
90+
$userMessage = $this->getInvalidConfigurationExceptionUserMessage($e->getMessage(), $configDir);
91+
92+
throw new InvalidConfigurationException($userMessage);
93+
}
94+
}
95+
96+
/**
97+
* Turn generic exception message into hint for users.
98+
*
99+
* @param string $message
100+
* @param string $configDir
101+
*
102+
* @return string
103+
*/
104+
protected function getInvalidConfigurationExceptionUserMessage(string $message, string $configDir): string
105+
{
106+
$matches = [];
107+
$isMissingSection = preg_match('/The child config "([^"]+)" under "([^"]+)" must be configured\./', $message, $matches);
108+
if ($isMissingSection) {
109+
$message = "Configuration misses section or item: {$matches[2]}.{$matches[1]}.";
110+
}
111+
112+
return "
113+
$message
114+
115+
Hint:
116+
- add missing value by command line argument (if applicable - check below or run `perpl <command> -h` for argument explanation)
117+
- check perpl configuration files located at `$configDir` or point to the correct location using the `--config-dir` argument
118+
- consult configuration documentation at https://perplorm.github.io/documentation/10-configuration.html
119+
";
84120
}
85121

86122
/**
@@ -89,7 +125,7 @@ protected function buildGeneratorConfig(?array $properties = null, ?InputInterfa
89125
* @param array<string>|string $directory Path to the input directory
90126
* @param bool $recursive Search for file inside the input directory and all subdirectories
91127
*
92-
* @return array List of schema files
128+
* @return array<\Symfony\Component\Finder\SplFileInfo> List of schema files
93129
*/
94130
protected function getSchemas($directory, bool $recursive = false): array
95131
{
@@ -107,20 +143,34 @@ protected function getSchemas($directory, bool $recursive = false): array
107143

108144
/**
109145
* @param \Propel\Generator\Config\GeneratorConfig $generatorConfig
146+
* @param bool $required
110147
*
111148
* @throws \Symfony\Component\Console\Exception\MissingInputException
149+
* @throws \Propel\Common\Config\Exception\InvalidConfigurationException
112150
*
113-
* @return array
151+
* @return array<\Symfony\Component\Finder\SplFileInfo>
114152
*/
115-
protected function getSchemasFromConfig(GeneratorConfig $generatorConfig): array
153+
protected function getSchemasFromConfig(GeneratorConfig $generatorConfig, bool $required = true): array
116154
{
117155
$schemaDir = $generatorConfig->getConfigPropertyString('paths.schemaDir');
118156
if (!$schemaDir) {
119157
throw new MissingInputException('Path to schema directory is missing. Use the --schema-dir option or the propel.paths.schemaDir configuration property to set it.');
120158
}
121159
$recursive = (bool)$generatorConfig->getConfigProperty('generator.recursive');
160+
$schemas = $this->getSchemas($schemaDir, $recursive);
161+
if ($schemas || !$required) {
162+
return $schemas;
163+
}
164+
$errorMessage = "
165+
No schema files found in directory `$schemaDir`.
166+
167+
Hint:
168+
- Check location
169+
- Update `propel.path.schemaDir` in the configuration file or --schema-dir command parameter
170+
- To search subdirectories, set `propel.generator.recursive` in configuration file or run command with `--recursive`
171+
";
122172

123-
return $this->getSchemas($schemaDir, $recursive);
173+
throw new InvalidConfigurationException($errorMessage);
124174
}
125175

126176
/**
@@ -151,7 +201,7 @@ protected function createDirectory(string $directory): void
151201
try {
152202
$filesystem->mkdir($directory);
153203
} catch (IOException $e) {
154-
throw new RuntimeException(sprintf('Unable to write the "%s" directory', $directory), 0, $e);
204+
throw new RuntimeException(sprintf('Unable to create directory "%s"', $directory), 0, $e);
155205
}
156206
}
157207

src/Propel/Generator/Command/AbstractMigrationCommand.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,6 @@ protected function setUpMigrationManager(GeneratorConfig $generatorConfig): Migr
7373
{
7474
$manager = new MigrationManager();
7575
$manager->setGeneratorConfig($generatorConfig);
76-
$manager->setSchemas($this->getSchemasFromConfig($generatorConfig));
7776

7877
return $manager;
7978
}

src/Propel/Generator/Command/TestPrepareCommand.php

Lines changed: 149 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -124,94 +124,39 @@ protected function buildFixtures(string $fixturesDir, array $connections, InputI
124124
return static::CODE_ERROR;
125125
}
126126

127-
$output->writeln(sprintf('Building fixtures in <info>%-40s</info> ' . ($input->getOption('exclude-database') ? '(exclude-database)' : ''), $fixturesDir));
127+
$vendor = $input->getOption('vendor');
128+
$dsn = $input->getOption('dsn');
129+
$user = $input->getOption('user');
130+
$password = $input->getOption('password');
131+
$verbose = $input->getOption('verbose');
132+
$excludeDataBase = (bool)$input->getOption('exclude-database');
128133

129-
chdir($this->root . '/' . $fixturesDir);
130-
131-
if (is_file('propel.yaml.dist')) {
132-
$content = (string)file_get_contents('propel.yaml.dist');
134+
$output->writeln(sprintf('Building fixtures in <info>%-40s</info> %s', $fixturesDir, $excludeDataBase ? '(exclude-database)' : ''));
133135

134-
$content = str_replace('##DATABASE_VENDOR##', $input->getOption('vendor'), $content);
135-
$content = str_replace('##DATABASE_URL##', $input->getOption('dsn'), $content);
136-
$content = str_replace('##DATABASE_USER##', $input->getOption('user'), $content);
137-
$content = str_replace('##DATABASE_PASSWORD##', $input->getOption('password'), $content);
136+
chdir($this->root . '/' . $fixturesDir);
138137

139-
file_put_contents('propel.yaml', $content);
140-
} else {
138+
if (!$this->updatePropelYamlDistFile($vendor, $dsn, $user, $password)) {
141139
$output->writeln('<comment>No "propel.yaml.dist" file found, skipped.</comment>');
142140
}
143141

144-
if (is_file('propel.yaml')) {
145-
$in = new ArrayInput([
146-
'command' => 'config:convert',
147-
'--output-dir' => './build/conf',
148-
'--output-file' => sprintf('%s-conf.php', $connections[0]), // the first connection is the main one
149-
'--loader-script-dir' => './build/conf',
150-
]);
142+
$mainConnection = $connections[0];
143+
$this->runConfigConvertCommand($output, $mainConnection);
151144

152-
$command = $this->getApplication()->find('config:convert');
153-
$command->run($in, $output);
145+
$hasSchemasInCurrentDir = count($this->getSchemas('.')) > 0;
146+
if (!$hasSchemasInCurrentDir) {
147+
return static::CODE_SUCCESS;
154148
}
155149

156-
if (0 < count($this->getSchemas('.'))) {
157-
$in = new ArrayInput([
158-
'command' => 'model:build',
159-
'--schema-dir' => '.',
160-
'--output-dir' => 'build/classes/',
161-
'--loader-script-dir' => './build/conf',
162-
'--platform' => ucfirst($input->getOption('vendor')) . 'Platform',
163-
'--verbose' => $input->getOption('verbose'),
164-
]);
165-
166-
$command = $this->getApplication()->find('model:build');
167-
$command->run($in, $output);
168-
}
150+
$this->runModelBuildCommand($output, $vendor, $verbose);
169151

170-
if ($input->getOption('exclude-database')) {
152+
if ($excludeDataBase) {
171153
return static::CODE_SUCCESS;
172154
}
173155

174-
if (0 < count($this->getSchemas('.'))) {
175-
$in = new ArrayInput([
176-
'command' => 'sql:build',
177-
'--schema-dir' => '.',
178-
'--output-dir' => 'build/sql/',
179-
'--platform' => ucfirst($input->getOption('vendor')) . 'Platform',
180-
'--verbose' => $input->getOption('verbose'),
181-
]);
182-
183-
$command = $this->getApplication()->find('sql:build');
184-
$command->run($in, $output);
185-
186-
$conParams = [];
187-
foreach ($connections as $con) {
188-
if (substr($input->getOption('dsn'), 0, 6) === 'sqlite') {
189-
$conParams[] = sprintf(
190-
'%s=%s',
191-
$con,
192-
$input->getOption('dsn'),
193-
);
194-
} else {
195-
$conParams[] = sprintf(
196-
'%s=%s;user=%s;password=%s',
197-
$con,
198-
$input->getOption('dsn'),
199-
$input->getOption('user'),
200-
$input->getOption('password'),
201-
);
202-
}
203-
}
204-
205-
$in = new ArrayInput([
206-
'command' => 'sql:insert',
207-
'--sql-dir' => 'build/sql/',
208-
'--connection' => $conParams,
209-
'--verbose' => $input->getOption('verbose'),
210-
]);
156+
$this->runSqlBuildCommand($output, $vendor, $verbose);
211157

212-
$command = $this->getApplication()->find('sql:insert');
213-
$command->run($in, $output);
214-
}
158+
$connectionStrings = $this->buildConnectionString($connections, $dsn, $user, $password);
159+
$this->runSqlInsert($output, $connectionStrings, $verbose);
215160

216161
return static::CODE_SUCCESS;
217162
}
@@ -225,4 +170,134 @@ protected function resetCounters(): void
225170
{
226171
AggregateMultipleColumnsBehavior::resetInsertedAggregationNames();
227172
}
173+
174+
/**
175+
* Updates connection information in propel.yaml.dist in current working directory.
176+
*
177+
* @param string $vendor
178+
* @param string $dsn
179+
* @param string $user
180+
* @param string $password
181+
*
182+
* @return bool
183+
*/
184+
protected function updatePropelYamlDistFile(string $vendor, string $dsn, string $user, string $password): bool
185+
{
186+
if (!is_file('propel.yaml.dist')) {
187+
return false;
188+
}
189+
190+
$content = (string)file_get_contents('propel.yaml.dist');
191+
192+
$content = str_replace('##DATABASE_VENDOR##', $vendor, $content);
193+
$content = str_replace('##DATABASE_URL##', $dsn, $content);
194+
$content = str_replace('##DATABASE_USER##', $user, $content);
195+
$content = str_replace('##DATABASE_PASSWORD##', $password, $content);
196+
197+
file_put_contents('propel.yaml', $content);
198+
199+
return true;
200+
}
201+
202+
/**
203+
* Convert local propel.yaml file via config:convert command.
204+
*
205+
* @param \Symfony\Component\Console\Output\OutputInterface $output
206+
* @param string $connection
207+
*
208+
* @return void
209+
*/
210+
protected function runConfigConvertCommand(OutputInterface $output, string $connection): void
211+
{
212+
if (!is_file('propel.yaml')) {
213+
return;
214+
}
215+
$in = new ArrayInput([
216+
'command' => 'config:convert',
217+
'--output-dir' => './build/conf',
218+
'--output-file' => sprintf('%s-conf.php', $connection),
219+
'--loader-script-dir' => './build/conf',
220+
]);
221+
222+
$command = $this->getApplication()->find('config:convert');
223+
$command->run($in, $output);
224+
}
225+
226+
/**
227+
* @param \Symfony\Component\Console\Output\OutputInterface $output
228+
* @param string $vendor
229+
* @param string $verbose
230+
*
231+
* @return void
232+
*/
233+
protected function runModelBuildCommand(OutputInterface $output, string $vendor, string $verbose): void
234+
{
235+
$in = new ArrayInput([
236+
'command' => 'model:build',
237+
'--schema-dir' => '.',
238+
'--output-dir' => 'build/classes/',
239+
'--loader-script-dir' => './build/conf',
240+
'--platform' => ucfirst($vendor) . 'Platform',
241+
'--verbose' => $verbose,
242+
]);
243+
244+
$command = $this->getApplication()->find('model:build');
245+
$command->run($in, $output);
246+
}
247+
248+
/**
249+
* @param \Symfony\Component\Console\Output\OutputInterface $output
250+
* @param string $vendor
251+
* @param string $verbose
252+
*
253+
* @return void
254+
*/
255+
protected function runSqlBuildCommand(OutputInterface $output, string $vendor, string $verbose): void
256+
{
257+
$in = new ArrayInput([
258+
'command' => 'sql:build',
259+
'--schema-dir' => '.',
260+
'--output-dir' => 'build/sql/',
261+
'--platform' => ucfirst($vendor) . 'Platform',
262+
'--verbose' => $verbose,
263+
]);
264+
265+
$command = $this->getApplication()->find('sql:build');
266+
$command->run($in, $output);
267+
}
268+
269+
/**
270+
* @param array<string> $connections
271+
* @param string $dsn
272+
* @param string $user
273+
* @param string $password
274+
*
275+
* @return array<string>
276+
*/
277+
protected function buildConnectionString(array $connections, string $dsn, string|null $user, string|null $password): array
278+
{
279+
$isSqlite = substr($dsn, 0, 6) === 'sqlite';
280+
281+
return array_map(fn ($con) => $isSqlite ? "$con=$dsn" : "$con=$dsn;user=$user;password=$password", $connections);
282+
}
283+
284+
/**
285+
* @param \Symfony\Component\Console\Output\OutputInterface $output
286+
* @param array<string> $connectionStrings
287+
* @param string $verbose
288+
*
289+
* @return void
290+
*/
291+
protected function runSqlInsert(OutputInterface $output, array $connectionStrings, string $verbose): void
292+
{
293+
$in = new ArrayInput([
294+
'command' => 'sql:insert',
295+
'--sql-dir' => 'build/sql/',
296+
'--connection' => $connectionStrings,
297+
'--verbose' => $verbose,
298+
]);
299+
300+
$command = $this->getApplication()->find('sql:insert');
301+
$command->run($in, $output);
302+
}
228303
}

src/Propel/Generator/Config/GeneratorConfig.php

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -290,11 +290,15 @@ public function getBuildConnection(?string $databaseName = null): array
290290
$databaseName = $this->getConfigPropertyString('generator.defaultConnection', true);
291291
}
292292

293-
if (!array_key_exists($databaseName, $this->getBuildConnections())) {
294-
throw new InvalidArgumentException("Invalid database name: no configured connection named `$databaseName`.");
293+
$connections = $this->getBuildConnections();
294+
if (array_key_exists($databaseName, $connections)) {
295+
return $connections[$databaseName];
295296
}
297+
$availableConnections = array_keys($connections);
298+
$message = "Database connection `$databaseName` is not a registered connection.\n\n"
299+
. 'Update configuration or choose one of [`' . implode('`, `', $availableConnections) . '`]';
296300

297-
return $this->getBuildConnections()[$databaseName];
301+
throw new InvalidArgumentException($message);
298302
}
299303

300304
/**

0 commit comments

Comments
 (0)