n_oct/plugins/rainlab/builder/classes/BlueprintGenerator.php

324 lines
8.5 KiB
PHP

<?php namespace RainLab\Builder\Classes;
use Lang;
use File;
use Twig;
use Schema;
use RainLab\Builder\Classes\TailorBlueprintLibrary;
use ApplicationException;
use ValidationException;
use Throwable;
/**
* BlueprintGenerator is a helper class for generating controller class files and associated files.
*
* @package rainlab\builder
* @author Alexey Bobkov, Samuel Georges
*/
class BlueprintGenerator
{
use \RainLab\Builder\Classes\BlueprintGenerator\HasMigrations;
use \RainLab\Builder\Classes\BlueprintGenerator\HasVersionFile;
use \RainLab\Builder\Classes\BlueprintGenerator\HasControllers;
use \RainLab\Builder\Classes\BlueprintGenerator\HasPermissions;
use \RainLab\Builder\Classes\BlueprintGenerator\HasNavigation;
use \RainLab\Builder\Classes\BlueprintGenerator\HasExpandoModels;
use \RainLab\Builder\Classes\BlueprintGenerator\HasModels;
/**
* @var object sourceModel is the imports model
*/
protected $sourceModel;
/**
* @var array sourceBlueprints are blueprint objects that can be saved to disk
*/
protected $sourceBlueprints = [];
/**
* @var array templateVars are used when rendering templates
*/
protected $templateVars;
/**
* @var array filesGenerated by this process
*/
protected $filesGenerated;
/**
* @var array filesValidated by this process
*/
protected $filesValidated;
/**
* @var array blueprintFiles to decommission
*/
protected $blueprintFiles = [];
/**
* @var array migrationScripts that have been generated
*/
protected $migrationScripts = [];
/**
* __construct
*/
public function __construct($source)
{
$this->sourceModel = $source;
}
/**
* generate
*/
public function inspect($blueprint)
{
$this->setBlueprintContext($blueprint);
$result = [
'controllerFile' => null,
'modelFiles' => [],
'migrationFiles' => [],
'errorMessage' => null
];
try {
if ($model = $this->makeControllerModel()) {
$result['controllerFile'] = $model->getControllerFilePath();
}
if ($model = $this->makeModelModel()) {
$result['modelFiles'][] = $model->getModelFilePath();
}
foreach ($this->makeExpandoModels(true) as $model) {
$result['modelFiles'][] = $model->getModelFilePath();
}
$result['migrationFiles'] = array_keys($this->inspectMigrations());
}
catch (Throwable $ex) {
$result['errorMessage'] = $ex->getMessage();
}
return $result;
}
/**
* generate
*/
public function generate()
{
$this->templateVars = [];
$this->filesGenerated = [];
$this->filesValidated = [];
$this->blueprintFiles = [];
$this->migrationScripts = [];
$this->sourceBlueprints = [];
$this->loadSourceBlueprints();
$this->validateNavigation();
// Validate
foreach ($this->sourceBlueprints as $blueprint) {
$this->setBlueprintContext($blueprint);
$this->validateModel();
$this->validateExpandoModels();
$this->validateController();
$this->validatePermission();
}
// Generate
try {
foreach ($this->sourceBlueprints as $blueprint) {
$this->setBlueprintContext($blueprint);
$this->generateMigrations();
$this->generateModel();
$this->generateExpandoModels();
$this->generateController();
$this->generatePermission();
$this->blueprintFiles[] = $blueprint->getFilePath();
}
}
catch (Throwable $ex) {
$this->rollback();
throw $ex;
}
$this->generateNavigation();
$this->generateVersionUpdate();
if ($this->sourceModel->deleteBlueprintData) {
$this->deleteGeneratedBlueprintData();
}
if ($this->sourceModel->disableBlueprints) {
$this->disableGeneratedBlueprints();
}
}
/**
* loadSourceBlueprints
*/
protected function loadSourceBlueprints()
{
$blueprintLib = TailorBlueprintLibrary::instance();
foreach ($this->sourceModel->blueprints as $uuid => $config) {
$blueprint = $blueprintLib->getBlueprintObject($uuid);
if ($blueprint) {
$this->sourceBlueprints[$uuid] = $blueprint;
}
}
}
/**
* setBlueprintContext
*/
protected function setBlueprintContext($blueprint)
{
$config = $this->sourceModel->blueprints[$blueprint->uuid] ?? [];
$this->sourceModel->setBlueprintContext($blueprint, $config);
$this->setTemplateVars();
}
/**
* disableGeneratedBlueprints
*/
protected function disableGeneratedBlueprints()
{
foreach ($this->blueprintFiles as $filePath) {
File::move(
$filePath,
str_replace('.yaml', '.yaml.bak', $filePath)
);
}
}
/**
* deleteGeneratedBlueprintData
*/
protected function deleteGeneratedBlueprintData()
{
foreach ($this->sourceBlueprints as $blueprint) {
$contentTable = $blueprint->getContentTableName();
Schema::dropIfExists($contentTable);
$joinTable = $blueprint->getJoinTableName();
Schema::dropIfExists($joinTable);
$repeaterTable = $blueprint->getRepeaterTableName();
Schema::dropIfExists($repeaterTable);
}
}
/**
* setTemplateVars
*/
protected function setTemplateVars()
{
$pluginCodeObj = $this->sourceModel->getPluginCodeObj();
$this->templateVars = $this->getConfig();
$this->templateVars['pluginNamespace'] = $pluginCodeObj->toPluginNamespace();
$this->templateVars['pluginCode'] = $pluginCodeObj->toCode();
}
/**
* getTemplatePath
*/
protected function getTemplatePath($template)
{
return __DIR__.'/blueprintgenerator/templates/'.$template;
}
/**
* parseTemplate
*/
protected function parseTemplate($templatePath, $vars = [])
{
$template = File::get($templatePath);
$vars = array_merge($this->templateVars, $vars);
$code = Twig::parse($template, $vars);
return $code;
}
/**
* writeFile
*/
protected function writeFile($path, $data)
{
$fileDirectory = dirname($path);
if (!File::isDirectory($fileDirectory)) {
if (!File::makeDirectory($fileDirectory, 0777, true, true)) {
throw new ApplicationException(Lang::get('rainlab.builder::lang.common.error_make_dir', [
'name' => $fileDirectory
]));
}
}
if (@File::put($path, $data) === false) {
throw new ApplicationException(Lang::get('rainlab.builder::lang.controller.error_save_file', [
'file' => basename($path)
]));
}
@File::chmod($path);
$this->filesGenerated[] = $path;
}
/**
* rollback
*/
protected function rollback()
{
foreach ($this->filesGenerated as $path) {
@unlink($path);
}
}
/**
* makeTabs
*/
protected function makeTabs($str)
{
return str_replace('\t', ' ', $str);
}
/**
* getConfig
*/
protected function getConfig($key = null, $default = null)
{
return $this->sourceModel->getBlueprintConfig($key, $default);
}
/**
* validateUniqueFiles
*/
protected function validateUniqueFiles(array $files)
{
if (!is_array($this->filesValidated)) {
$this->filesValidated = [];
}
foreach ($files as $path) {
if (File::isFile($path) || in_array($path, $this->filesValidated)) {
throw new ValidationException([
'modelClass' => __("File [:file] already exists when trying to import [:blueprint]", [
'file' => basename(dirname($path)) . '/' . basename($path),
'blueprint' => $this->sourceModel->getBlueprintObject()->handle ?? 'unknown'
])
]);
}
}
$this->filesValidated = array_merge($this->filesValidated, $files);
}
}