Passed
Push — master ( ab5b91...849dc0 )
by Fran
03:17
created

GeneratorService::checkIfIsModel()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 11
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 3

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
eloc 8
c 1
b 0
f 0
nc 2
nop 3
dl 0
loc 11
ccs 9
cts 9
cp 1
crap 3
rs 10
1
<?php
2
3
namespace PSFS\services;
4
5
use Propel\Common\Config\ConfigurationManager;
6
use Propel\Generator\Model\Database;
7
use Propel\Generator\Model\Diff\DatabaseComparator;
8
use Propel\Generator\Model\IdMethod;
9
use Propel\Generator\Model\Schema;
10
use PSFS\base\config\Config;
11
use PSFS\base\exception\ApiException;
12
use PSFS\base\exception\GeneratorException;
13
use PSFS\base\Logger;
14
use PSFS\base\types\helpers\GeneratorHelper;
15
use PSFS\base\types\SimpleService;
16
use PSFS\base\types\traits\Generator\PropelHelperTrait;
17
use PSFS\base\types\traits\Generator\StructureTrait;
18
use Symfony\Component\Console\Output\Output;
19
20
/**
21
 * Class GeneratorService
22
 * @package PSFS\services
23
 */
24
class GeneratorService extends SimpleService
25
{
26
    use PropelHelperTrait;
27
    use StructureTrait;
28
29
    /**
30
     * @Injectable
31
     * @var \PSFS\base\config\Config Servicio de configuración
32
     */
33
    protected $config;
34
    /**
35
     * @Injectable
36
     * @var \PSFS\base\Security Servicio de autenticación
37
     */
38
    protected $security;
39
40
    /**
41
     * Servicio que genera la estructura de un módulo o lo actualiza en caso de ser necesario
42
     * @param string $module
43
     * @param boolean $force
44
     * @param string $type
45
     * @param string $apiClass
46
     * @param bool $skipMigration
47
     * @throws GeneratorException
48
     * @throws \ReflectionException
49
     */
50 1
    public function createStructureModule(string $module, bool $force = false, string $type = "", string $apiClass = "", bool $skipMigration = false): void
51
    {
52 1
        $modPath = CORE_DIR . DIRECTORY_SEPARATOR;
53 1
        $module = ucfirst($module);
54 1
        $this->createModulePath($module, $modPath);
55 1
        $this->createModulePathTree($module, $modPath);
0 ignored issues
show
Bug introduced by
$modPath of type string is incompatible with the type boolean expected by parameter $modPath of PSFS\services\GeneratorS...:createModulePathTree(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

55
        $this->createModulePathTree($module, /** @scrutinizer ignore-type */ $modPath);
Loading history...
56 1
        $this->createModuleBaseFiles($module, $modPath, $force, $type);
57 1
        $this->createModuleModels($module, $modPath);
58 1
        $this->generateBaseApiTemplate($module, $modPath, $force, $apiClass);
59 1
        if(!$skipMigration) {
60
            $this->createModuleMigrations($module, $modPath);
61
        }
62
        //Redireccionamos al home definido
63 1
        Logger::log("Módulo generado correctamente");
64
    }
65
66
    /**
67
     * Servicio que genera las plantillas básicas de ficheros del módulo
68
     * @param string $module
69
     * @param string $modPath
70
     * @param boolean $force
71
     * @param string $controllerType
72
     * @throws GeneratorException
73
     */
74 1
    private function createModuleBaseFiles($module, $modPath, $force = false, $controllerType = '')
75
    {
76 1
        $modulePath = $modPath . $module;
77 1
        $this->generateControllerTemplate($module, $modulePath, $force, $controllerType);
78 1
        $this->generateServiceTemplate($module, $modulePath, $force);
79 1
        $this->generateSchemaTemplate($module, $modulePath, $force);
80 1
        $this->generateConfigurationTemplates($module, $modulePath, $force);
81 1
        $this->generateIndexTemplate($module, $modulePath, $force);
82 1
        $this->generatePublicTemplates($modulePath, $force);
83
    }
84
85
    /**
86
     * @param string $module
87
     * @param string $modulePath
88
     * @param bool $force
89
     * @return void
90
     * @throws GeneratorException
91
     */
92 1
    public function generateConfigurationTemplates(string $module, string $modulePath, bool $force = false): void
93
    {
94 1
        $this->genereateAutoloaderTemplate($module, $modulePath, $force);
95 1
        $this->generatePropertiesTemplate($module, $modulePath, $force);
96 1
        $this->generateConfigTemplate($modulePath, $force);
97 1
        $this->createModuleModels($module, CORE_DIR . DIRECTORY_SEPARATOR);
98
    }
99
100
    /**
101
     * @param string $module
102
     * @param string $modPath
103
     * @param boolean $force
104
     * @param string $controllerType
105
     * @return boolean
106
     */
107 1
    private function generateControllerTemplate($module, $modPath, $force = false, $controllerType = "")
108
    {
109
        //Generamos el controlador base
110 1
        Logger::log("Generamos el controlador BASE");
111 1
        $class = preg_replace('/(\\\|\/)/', '', $module);
112 1
        $controllerBody = $this->tpl->dump("generator/controller.template.twig", array(
113 1
            "module" => $module,
114 1
            "namespace" => preg_replace('/(\\\|\/)/', '\\', $module),
115 1
            "url" => preg_replace('/(\\\|\/)/', '/', $module),
116 1
            "class" => $class,
117 1
            "controllerType" => $class . "Base",
118 1
            "is_base" => false
119 1
        ));
120 1
        $controller = $this->writeTemplateToFile($controllerBody, $modPath . DIRECTORY_SEPARATOR . "Controller" .
121 1
            DIRECTORY_SEPARATOR . "{$class}Controller.php", $force);
122
123 1
        $controllerBody = $this->tpl->dump("generator/controller.template.twig", array(
124 1
            "module" => $module,
125 1
            "namespace" => preg_replace('/(\\\|\/)/', '\\', $module),
126 1
            "url" => preg_replace('/(\\\|\/)/', '/', $module),
127 1
            "class" => $class . "Base",
128 1
            "service" => $class,
129 1
            "controllerType" => $controllerType,
130 1
            "is_base" => true,
131 1
            "domain" => $class,
132 1
        ));
133 1
        $controllerBase = $this->writeTemplateToFile($controllerBody, $modPath . DIRECTORY_SEPARATOR . "Controller" .
134 1
            DIRECTORY_SEPARATOR . "base" . DIRECTORY_SEPARATOR . "{$class}BaseController.php", true);
135
136 1
        $filename = $modPath . DIRECTORY_SEPARATOR . "Test" . DIRECTORY_SEPARATOR . "{$class}Test.php";
137 1
        $test = true;
138 1
        if (!file_exists($filename)) {
139 1
            $testTemplate = $this->tpl->dump("generator/testCase.template.twig", array(
140 1
                "module" => $module,
141 1
                "namespace" => preg_replace('/(\\\|\/)/', '\\', $module),
142 1
                "class" => $class,
143 1
            ));
144 1
            $test = $this->writeTemplateToFile($testTemplate, $filename, true);
145
        }
146 1
        return ($controller && $controllerBase && $test);
147
    }
148
149
    /**
150
     * Servicio que ejecuta Propel y genera el modelo de datos
151
     * @param string $module
152
     * @param string $path
153
     * @throws \PSFS\base\exception\GeneratorException
154
     */
155 1
    private function createModuleModels($module, $path)
156
    {
157 1
        $modulePath = str_replace(CORE_DIR . DIRECTORY_SEPARATOR, '', $path . $module);
158 1
        $configGenerator = $this->getConfigGenerator($modulePath);
159
160 1
        $this->buildModels($configGenerator);
161 1
        $this->buildSql($configGenerator);
162
163 1
        $configTemplate = $this->tpl->dump("generator/config.propel.template.twig", array(
164 1
            "module" => $module,
165 1
        ));
166 1
        $this->writeTemplateToFile($configTemplate, CORE_DIR . DIRECTORY_SEPARATOR . $modulePath . DIRECTORY_SEPARATOR . "Config" .
167 1
            DIRECTORY_SEPARATOR . "config.php", true);
168 1
        Logger::log("Generado config genérico para propel");
169
    }
170
171
    /**
172
     * @throws GeneratorException
173
     */
174
    private function createModuleMigrations($module, $path)
175
    {
176
        $migrationService = MigrationService::getInstance();
177
        list($manager, $generatorConfig) = $migrationService->getConnectionManager($module, $path);
178
179
        if ($manager->hasPendingMigrations()) {
180
            throw new ApiException(t(sprintf('Módulo %s generado correctamente. Hay una migración pendiente de aplicar, ejecute comando `psfs:migrate` o elimine el fichero generado en el módulo', $module)), 400);
181
        }
182
183
        $totalNbTables = 0;
184
        $reversedSchema = new Schema();
185
        $debugLogger = Config::getParam('log.level') === 'DEBUG';
186
187
        foreach ($manager->getDatabases() as $appDatabase) {
188
            $name = $appDatabase->getName();
189
            $params = $connections[$name] ?? [];
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $connections seems to never exist and therefore isset should always be false.
Loading history...
190
            if (!$params) {
191
                Logger::log(sprintf('<info>No connection configured for database "%s"</info>', $name));
192
            }
193
194
            if ($debugLogger) {
195
                Logger::log(sprintf('Connecting to database "%s" using DSN "%s"', $name, $params['dsn']));
196
            }
197
198
            $conn = $manager->getAdapterConnection($name);
199
            $platform = $generatorConfig->getConfiguredPlatform($conn, $name);
200
201
            $appDatabase->setPlatform($platform);
202
203
            if ($platform && !$platform->supportsMigrations()) {
204
                Logger::log(sprintf('Skipping database "%s" since vendor "%s" does not support migrations', $name, $platform->getDatabaseType()));
205
                continue;
206
            }
207
208
            $additionalTables = [];
209
            foreach ($appDatabase->getTables() as $table) {
210
                if ($table->getSchema() && $table->getSchema() != $appDatabase->getSchema()) {
211
                    $additionalTables[] = $table;
212
                }
213
            }
214
215
            $database = new Database($name);
216
            $database->setPlatform($platform);
217
            $database->setSchema($appDatabase->getSchema());
218
            $database->setDefaultIdMethod(IdMethod::NATIVE);
219
220
            $parser = $generatorConfig->getConfiguredSchemaParser($conn, $name);
221
            $nbTables = $parser->parse($database, $additionalTables);
222
223
            $reversedSchema->addDatabase($database);
224
            $totalNbTables += $nbTables;
225
226
            if ($debugLogger) {
227
                Logger::log(sprintf('%d tables found in database "%s"', $nbTables, $name), Output::VERBOSITY_VERBOSE);
228
            }
229
        }
230
231
        if ($totalNbTables) {
232
            Logger::log(sprintf('%d tables found in all databases.', $totalNbTables));
233
        } else {
234
            Logger::log('No table found in all databases');
235
        }
236
237
        // comparing models
238
        Logger::log('Comparing models...');
239
        $migrationsUp = [];
240
        $migrationsDown = [];
241
        $configManager = new ConfigurationManager($generatorConfig->getSection('paths')['phpConfDir']);
242
        $excludedTables = (array)$configManager->getSection('exclude_tables');
243
244
        foreach ($reversedSchema->getDatabases() as $database) {
245
            $name = $database->getName();
246
247
            if ($debugLogger) {
248
                Logger::log(sprintf('Comparing database "%s"', $name));
249
            }
250
251
            $appDataDatabase = $manager->getDatabase($name);
252
            if (!$appDataDatabase) {
253
                Logger::log(sprintf('<error>Database "%s" does not exist in schema.xml. Skipped.</error>', $name));
254
                continue;
255
            }
256
257
            $databaseDiff = DatabaseComparator::computeDiff($database, $appDataDatabase, true, false, false, $excludedTables);
258
259
            if (!$databaseDiff) {
260
                if ($debugLogger) {
261
                    Logger::log(sprintf('Same XML and database structures for datasource "%s" - no diff to generate', $name));
262
                }
263
                continue;
264
            }
265
266
            Logger::log(sprintf('Structure of database was modified in datasource "%s": %s', $name, $databaseDiff->getDescription()));
267
268
            foreach ($databaseDiff->getPossibleRenamedTables() as $fromTableName => $toTableName) {
269
                Logger::log(sprintf(
270
                    '<info>Possible table renaming detected: "%s" to "%s". It will be deleted and recreated. Use --table-renaming to only rename it.</info>',
271
                    $fromTableName,
272
                    $toTableName,
273
                ));
274
            }
275
276
            $conn = $manager->getAdapterConnection($name);
277
            /** @var \Propel\Generator\Platform\DefaultPlatform $platform */
278
            $platform = $generatorConfig->getConfiguredPlatform($conn, $name);
279
            $migrationsUp[$name] = $platform->getModifyDatabaseDDL($databaseDiff);
280
            $migrationsDown[$name] = $platform->getModifyDatabaseDDL($databaseDiff->getReverseDiff());
281
        }
282
        if (!$migrationsUp) {
283
            Logger::log('Same XML and database structures for all datasource - no diff to generate');
284
            return true;
285
        }
286
287
        $timestamp = time();
288
        $migrationFileName = $manager->getMigrationFileName($timestamp);
289
        $migrationClassBody = $manager->getMigrationClassBody($migrationsUp, $migrationsDown, $timestamp);
290
291
        $file = $generatorConfig->getSection('paths')['migrationDir'] . DIRECTORY_SEPARATOR . $migrationFileName;
292
        file_put_contents($file, $migrationClassBody);
293
294
        Logger::log(sprintf('"%s" file successfully created.', $file));
295
        return true;
296
    }
297
298
    /**
299
     * @param string $module
300
     * @param string $modPath
301
     * @param boolean $force
302
     * @param string $apiClass
303
     * @return boolean
304
     * @throws \ReflectionException
305
     */
306 1
    private function generateBaseApiTemplate($module, $modPath, $force = false, $apiClass = "")
307
    {
308 1
        $created = true;
309 1
        $modelPath = $modPath . $module . DIRECTORY_SEPARATOR . 'Models';
310 1
        $apiPath = $modPath . $module . DIRECTORY_SEPARATOR . 'Api';
311 1
        if (file_exists($modelPath)) {
312 1
            $dir = dir($modelPath);
313 1
            $this->generateApiFiles($module, $force, $apiClass, $dir, $apiPath);
314
        }
315 1
        return $created;
316
    }
317
318
    /**
319
     * @param string $modPath
320
     * @param boolean $force
321
     * @return boolean
322
     */
323 1
    private function generateConfigTemplate($modPath, $force = false)
324
    {
325
        //Generamos el fichero de configuración
326 1
        Logger::log("Generamos fichero vacío de configuración");
327 1
        return $this->writeTemplateToFile("<?php\n\t",
328 1
            $modPath . DIRECTORY_SEPARATOR . "Config" . DIRECTORY_SEPARATOR . "config.php",
329 1
            $force);
330
    }
331
332
    /**
333
     * @param string $module
334
     * @param string $modPath
335
     * @param boolean $force
336
     * @return boolean
337
     */
338 1
    private function generateSchemaTemplate($module, $modPath, $force = false)
339
    {
340
        //Generamos el autoloader del módulo
341 1
        Logger::log("Generamos el schema");
342 1
        $schema = $this->tpl->dump("generator/schema.propel.twig", array(
343 1
            "module" => $module,
344 1
            "namespace" => preg_replace('/(\\\|\/)/', '', $module),
345 1
            "prefix" => preg_replace('/(\\\|\/)/', '', $module),
346 1
            "db" => $this->config->get("db_name"),
347 1
        ));
348 1
        return $this->writeTemplateToFile($schema,
349 1
            $modPath . DIRECTORY_SEPARATOR . "Config" . DIRECTORY_SEPARATOR . "schema.xml",
350 1
            $force);
351
    }
352
353
    /**
354
     * @param string $module
355
     * @param string $modPath
356
     * @param boolean $force
357
     * @return boolean
358
     */
359 1
    private function generatePropertiesTemplate($module, $modPath, $force = false)
360
    {
361 1
        Logger::log("Generamos la configuración de Propel");
362 1
        $buildProperties = $this->tpl->dump("generator/build.properties.twig", array(
363 1
            "module" => $module,
364 1
            "namespace" => preg_replace('/(\\\|\/)/', '', $module),
365 1
        ));
366 1
        return $this->writeTemplateToFile($buildProperties,
367 1
            $modPath . DIRECTORY_SEPARATOR . "Config" . DIRECTORY_SEPARATOR . "propel.php",
368 1
            $force);
369
    }
370
371
    /**
372
     * @param string $module
373
     * @param string $modPath
374
     * @param boolean $force
375
     * @return boolean
376
     */
377 1
    private function generateIndexTemplate($module, $modPath, $force = false)
378
    {
379
        //Generamos la plantilla de index
380 1
        Logger::log("Generamos una plantilla base por defecto");
381 1
        $index = $this->tpl->dump("generator/index.template.twig", array(
382 1
            "module" => $module,
383 1
        ));
384 1
        return $this->writeTemplateToFile($index,
385 1
            $modPath . DIRECTORY_SEPARATOR . "Templates" . DIRECTORY_SEPARATOR . "index.html.twig",
386 1
            $force);
387
    }
388
389
    /**
390
     * Create ApiBase
391
     * @param string $module
392
     * @param string $modPath
393
     * @param string $api
394
     * @param string $apiClass
395
     * @param string $package
396
     *
397
     * @return bool
398
     */
399 1
    private function createApiBaseFile($module, $modPath, $api, $apiClass = '', $package = null)
400
    {
401 1
        $class = preg_replace('/(\\\|\/)/', '', $module);
402 1
        $customClass = GeneratorHelper::extractClassFromNamespace($apiClass);
403 1
        $controller = $this->tpl->dump("generator/api.base.template.twig", array(
404 1
            "module" => $module,
405 1
            "api" => $api,
406 1
            "namespace" => preg_replace('/(\\\|\/)/', '\\', $module),
407 1
            "url" => preg_replace('/(\\\|\/)/', '/', $module),
408 1
            "class" => $class,
409 1
            'customClass' => $customClass,
410 1
            'customNamespace' => $apiClass,
411 1
            'package' => $package,
412 1
        ));
413
414 1
        return $this->writeTemplateToFile($controller,
415 1
            $modPath . DIRECTORY_SEPARATOR . 'base' . DIRECTORY_SEPARATOR . "{$api}BaseApi.php", true);
416
    }
417
418
    /**
419
     * Create Api
420
     * @param string $module
421
     * @param string $modPath
422
     * @param bool $force
423
     * @param string $api
424
     * @param string $package
425
     *
426
     * @return bool
427
     */
428 1
    private function createApi($module, $modPath, $force, $api, $package = null)
429
    {
430 1
        $class = preg_replace('/(\\\|\/)/', '', $module);
431 1
        $controller = $this->tpl->dump("generator/api.template.twig", array(
432 1
            "module" => $module,
433 1
            "api" => $api,
434 1
            "namespace" => preg_replace('/(\\\|\/)/', '\\', $module),
435 1
            "url" => preg_replace('/(\\\|\/)/', '/', $module),
436 1
            "class" => $class,
437 1
            "package" => $package,
438 1
        ));
439
440 1
        return $this->writeTemplateToFile($controller, $modPath . DIRECTORY_SEPARATOR . "{$api}.php", $force);
441
    }
442
443
    /**
444
     * @param $module
445
     * @param $force
446
     * @param $apiClass
447
     * @param \Directory|null $dir
448
     * @param string $apiPath
449
     * @param string $package
450
     * @throws \ReflectionException
451
     */
452 1
    private function generateApiFiles($module, $force, $apiClass, \Directory $dir, string $apiPath, $package = null)
453
    {
454 1
        $base = $dir->path;
455 1
        while ($file = $dir->read()) {
456 1
            if (!in_array(strtolower($file), ['.', '..', 'base', 'map'])) {
457 1
                if (is_dir($base . DIRECTORY_SEPARATOR . $file)) {
458 1
                    $this->generateApiFiles($module, $force, $apiClass, dir($base . DIRECTORY_SEPARATOR . $file), $apiPath . DIRECTORY_SEPARATOR . $file, $file);
459 1
                } else if (!preg_match('/Query\.php$/i', $file)
460 1
                    && !preg_match('/I18n\.php$/i', $file)
461 1
                    && preg_match('/\.php$/i', $file)
462
                ) {
463 1
                    $filename = str_replace(".php", "", $file);
464 1
                    $this->log->addLog("Generamos Api BASES para {$filename}");
465 1
                    if ($this->checkIfIsModel($module, $filename, $package)) {
466 1
                        $this->createApiBaseFile($module, $apiPath, $filename, $apiClass, $package);
467 1
                        $this->createApi($module, $apiPath, $force, $filename, $package);
468
                    }
469
                }
470
            }
471
        }
472
    }
473
474
    /**
475
     * @param string $module
476
     * @param string $package
477
     * @param string $filename
478
     * @return bool
479
     * @throws \ReflectionException
480
     */
481 1
    private function checkIfIsModel($module, $filename, $package = null)
482
    {
483 1
        $parts = [$module, 'Models'];
484 1
        if (strlen($package ?: '')) {
485 1
            $parts[] = $package;
486
        }
487 1
        $parts[] = $filename;
488 1
        $namespace = '\\' . implode('\\', $parts);
489 1
        $reflectorClass = new \ReflectionClass($namespace);
490 1
        $isModel = $reflectorClass->isInstantiable();
491 1
        return $isModel;
492
    }
493
}
494