Skip to content

Commit

Permalink
feat: 支持插件生成模型文件到插件目录下
Browse files Browse the repository at this point in the history
  • Loading branch information
frank committed Nov 28, 2024
1 parent 666fd46 commit e6e761c
Showing 1 changed file with 182 additions and 0 deletions.
182 changes: 182 additions & 0 deletions app/Command/PluginGenModelCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
<?php

declare(strict_types=1);

namespace App\Command;

use Hyperf\Command\Annotation\Command;
use Hyperf\Database\Commands\Ast\ModelRewriteConnectionVisitor;
use Hyperf\Database\Commands\Ast\ModelUpdateVisitor;
use Hyperf\Database\Commands\ModelCommand;
use Hyperf\Database\Commands\ModelData;
use Hyperf\Database\Commands\ModelOption;
use Hyperf\Database\PgSQL\Schema\PostgresBuilder;
use Hyperf\Database\Schema\Builder;
use Hyperf\Database\Schema\MySqlBuilder;
use Hyperf\Stringable\Str;
use PhpParser\NodeTraverser;
use PhpParser\NodeVisitor\CloningVisitor;
use Psr\Container\ContainerInterface;
use RuntimeException;
use Symfony\Component\Console\Input\InputOption;
use function Hyperf\Support\make;

/**
* @noinspection PhpUnused
*/
#[Command]
class PluginGenModelCommand extends ModelCommand
{
public function __construct(protected ContainerInterface $container)
{
parent::__construct($container);
$this->setName('plugin:gen:model');
$this->setDescription('为插件生成模型文件');
$this->setHelp('plugin:gen:model 表名(不带前缀) 插件名称(取mine.json中的name的值)');
$this->addOption('plugin', '', InputOption::VALUE_REQUIRED, '插件名称');
}

protected string $pluginModelNamespace = '';

protected string $pluginModelPath = '';

/** @noinspection DuplicatedCode */
protected function createModel(string $table, ModelOption $option): void
{
if (! $this->input->getOption('plugin')) {
$this->output?->error('插件名称不能为空');
exit();
}

/** @var Builder|MySqlBuilder|PostgresBuilder $builder */
$builder = $this->getSchemaBuilder($option->getPool());
$table = Str::replaceFirst($option->getPrefix(), '', $table);
$pureTable = Str::after($table, '.');
$databaseName = Str::contains($table, '.') ? Str::before($table, '.') : null;
$columns = $this->formatColumns($builder->getColumnTypeListing($pureTable, $databaseName));

Check failure on line 56 in app/Command/PluginGenModelCommand.php

View workflow job for this annotation

GitHub Actions / build Code coverage report (ubuntu-latest, 8.1, v5.1.3)

Call to an undefined method Hyperf\Database\Schema\Builder::getColumnTypeListing().

if (empty($columns)) {
$this->output?->error(
sprintf('Query columns empty, maybe is table `%s` does not exist.You can check it in database.', $table)
);
}

$class = $option->getTableMapping()[$table] ?? Str::studly(Str::singular($pureTable));
$class = $this->getPluginNamespace($this->input->getOption('plugin')) . $class;
$path = $this->getPathWithExtension($class);
$class = $this->getPluginModelClass($class);

if (! file_exists($path)) {
$this->mkdir($path);
file_put_contents($path, $this->buildClass($table, $class, $option));
}

$columns = $this->getColumns($class, $columns, $option->isForceCasts());
$traverser = new NodeTraverser();
$traverser->addVisitor(make(ModelUpdateVisitor::class, [
'class' => $class,
'columns' => $columns,
'option' => $option,
]));
$traverser->addVisitor(make(ModelRewriteConnectionVisitor::class, [$class, $option->getPool()]));
$data = make(ModelData::class, ['class' => $class, 'columns' => $columns]);

foreach ($option->getVisitors() as $visitorClass) {
$traverser->addVisitor(make($visitorClass, [$option, $data]));
}

$traverser->addVisitor(new CloningVisitor());
$originStmts = $this->astParser->parse(file_get_contents($path));
$originTokens = $this->lexer->getTokens();
$newStmts = $traverser->traverse($originStmts);
$code = $this->printer->printFormatPreserving($newStmts, $originStmts, $originTokens);
file_put_contents($path, $code);
$this->output->writeln(sprintf('<info>Model %s was created.</info>', $class));

if ($option->isWithIde()) {
$this->generateIDE($code, $option, $data);
}
}

/**
* 根据插件名称生成规范路径
* @param string $pluginName
* @return string
*/
private function getPathByPluginName(string $pluginName): string
{
$jsonPath = BASE_PATH . '/plugin/' . $pluginName . '/mine.json';

if (! is_file($jsonPath)) {
$this->output?->error('插件缺少配置文件');
exit();
}

$jsonArray = json_decode(file_get_contents($jsonPath), true);
$psrArray = $jsonArray['composer']['psr-4'];

foreach ($psrArray as $value => $key) {
if ($key === 'src') {
$this->pluginModelNamespace = $value . 'Model';
}
}

$path = 'plugin/' . $pluginName . '/src/Model';
$this->pluginModelPath = $path;

return $path;
}

/**
* 优化类名的生成
* @param $class
* @return string
*/
private function getPluginModelClass($class): string
{
$classArr = explode('\\', $class);
$classStr = '';

foreach ($classArr as $k => $value) {
if (strtolower($value) !== 'src') {
$classStr .= ($k > 0 ? '\\' : '') . Str::studly($value);
}
}

return $classStr;
}

/**
* 获取模型文件路径
* @param string $path
* @param string $extension
* @return string
* @noinspection PhpSameParameterValueInspection*/
private function getPathWithExtension(string $path, string $extension = '.php'): string
{
if (Str::endsWith($path, '\\')) {
$extension = '';
}

return BASE_PATH . '/' . str_replace('\\', '/', substr($path, 0)) . $extension;
}

private function getPluginNamespace(string $pluginName): string
{
$path = $this->getPathByPluginName($pluginName);

if ($this->pluginModelNamespace === '') {
throw new RuntimeException("Invalid plugin config");
}

$ext = pathinfo($path, PATHINFO_EXTENSION);

if ($ext !== '') {
$path = substr($path, 0, -(strlen($ext) + 1));
} else {
$path = trim($path, '/') . '/';
}

return str_replace('/', '\\', $path);
}
}

0 comments on commit e6e761c

Please sign in to comment.