Skip to content

Commit 9386e8e

Browse files
committed
feat: Adiciona CLI para a extensão Cycle ORM e implementa comandos de gerenciamento
1 parent e5703f7 commit 9386e8e

File tree

7 files changed

+304
-63
lines changed

7 files changed

+304
-63
lines changed
File renamed without changes.

composer.json

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
"authors": [
88
{
99
"name": "Caio Alberto Fernandes",
10-
"email": "caio@express-php.dev",
1110
"homepage": "https://github.com/CAFernandes"
1211
}
1312
],
@@ -62,6 +61,15 @@
6261
"analyse": "phpstan analyse",
6362
"lint": "php-cs-fixer fix --dry-run --diff",
6463
"fix": "php-cs-fixer fix",
64+
"setup": [
65+
"@composer install",
66+
"@php install.php"
67+
],
68+
"validate": [
69+
"@lint",
70+
"@analyse",
71+
"@test"
72+
],
6573
"ci": [
6674
"@lint",
6775
"@analyse",
@@ -74,12 +82,17 @@
7482
"analyse": "Run PHPStan static analysis",
7583
"lint": "Check code style with PHP CS Fixer",
7684
"fix": "Fix code style issues",
85+
"setup": "Setup development environment",
86+
"validate": "Validate code quality",
7787
"ci": "Run complete CI pipeline"
7888
},
7989
"config": {
8090
"optimize-autoloader": true,
8191
"preferred-install": "dist",
82-
"sort-packages": true
92+
"sort-packages": true,
93+
"allow-plugins": {
94+
"composer/package-versions-deprecated": true
95+
}
8396
},
8497
"minimum-stability": "stable",
8598
"prefer-stable": true

src/Commands/CommandRegistry.php

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<?php
2+
namespace CAFernandes\ExpressPHP\CycleORM\Commands;
3+
4+
class CommandRegistry
5+
{
6+
private array $commands = [];
7+
8+
public function register(string $name, string $commandClass): void
9+
{
10+
if (!class_exists($commandClass)) {
11+
throw new \InvalidArgumentException("Command class {$commandClass} does not exist");
12+
}
13+
14+
if (!is_subclass_of($commandClass, BaseCommand::class)) {
15+
throw new \InvalidArgumentException("Command class {$commandClass} must extend BaseCommand");
16+
}
17+
18+
$this->commands[$name] = $commandClass;
19+
}
20+
21+
public function hasCommand(string $name): bool
22+
{
23+
return isset($this->commands[$name]);
24+
}
25+
26+
public function getRegisteredCommands(): array
27+
{
28+
return array_keys($this->commands);
29+
}
30+
31+
public function run(string $commandName, array $args = []): int
32+
{
33+
if (!$this->hasCommand($commandName)) {
34+
echo "Command '{$commandName}' not found.\n";
35+
return 1;
36+
}
37+
38+
$commandClass = $this->commands[$commandName];
39+
$command = new $commandClass($args);
40+
41+
return $command->handle();
42+
}
43+
}

src/Commands/StatusCommand.php

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<?php
2+
3+
namespace CAFernandes\ExpressPHP\CycleORM\Commands;
4+
5+
class StatusCommand extends BaseCommand
6+
{
7+
public function handle(): int
8+
{
9+
$this->info('Cycle ORM Status Check');
10+
$this->line('========================');
11+
12+
try {
13+
if (function_exists('app')) {
14+
$app = app();
15+
$health = \CAFernandes\ExpressPHP\CycleORM\Health\CycleHealthCheck::check($app);
16+
17+
$this->displayHealthStatus($health);
18+
19+
return $health['cycle_orm'] === 'healthy' ? 0 : 1;
20+
} else {
21+
$this->error('Application container not available');
22+
return 1;
23+
}
24+
25+
} catch (\Exception $e) {
26+
$this->error('Status check failed: ' . $e->getMessage());
27+
return 1;
28+
}
29+
}
30+
31+
private function displayHealthStatus(array $health): void
32+
{
33+
$status = $health['cycle_orm'];
34+
$icon = $status === 'healthy' ? '' : '';
35+
36+
$this->line("{$icon} Overall Status: {$status}");
37+
$this->line("Response Time: {$health['response_time_ms']}ms");
38+
$this->line('');
39+
40+
foreach ($health['checks'] as $checkName => $check) {
41+
$checkIcon = $check['status'] === 'healthy' ? '' : '';
42+
$this->line("{$checkIcon} {$checkName}: {$check['status']}");
43+
44+
if (isset($check['error'])) {
45+
$this->error(" Error: {$check['error']}");
46+
}
47+
}
48+
}
49+
}

src/CycleServiceProvider.php

Lines changed: 100 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
11
<?php
22
namespace CAFernandes\ExpressPHP\CycleORM;
33

4-
use Express\Support\ServiceProvider;
5-
use Express\Core\Application;
6-
use Cycle\ORM\ORM;
7-
use Cycle\ORM\Factory;
4+
use Cycle\Schema\Generator;
85
use Cycle\ORM\EntityManager;
6+
use Cycle\ORM\Factory;
7+
use Cycle\ORM\ORM;
98
use Cycle\Database\DatabaseManager;
109
use Cycle\Database\Config\DatabaseConfig;
1110
use Cycle\Schema\Compiler;
1211
use Cycle\Annotated\Locator\TokenizerEntityLocator;
13-
use Cycle\Schema\Generator;
1412
use Spiral\Tokenizer\ClassesInterface;
13+
use Spiral\Tokenizer\Classes;
14+
use Spiral\Tokenizer\ClassLocator;
15+
use Spiral\Tokenizer\Reflection\ReflectionFile;
1516

1617
class CycleServiceProvider extends ServiceProvider
1718
{
@@ -150,4 +151,98 @@ private function logError(string $message): void
150151
error_log($message);
151152
}
152153
}
154+
155+
private function validateDatabaseConfig(array $config): void
156+
{
157+
$required = ['default', 'databases', 'connections'];
158+
159+
foreach ($required as $key) {
160+
if (!isset($config[$key])) {
161+
throw new \InvalidArgumentException("Missing required database config key: {$key}");
162+
}
163+
}
164+
165+
$default = $config['default'];
166+
if (!isset($config['connections'][$default])) {
167+
throw new \InvalidArgumentException("Default connection '{$default}' not configured");
168+
}
169+
}
170+
171+
private function validateEntityConfig(array $config): void
172+
{
173+
if (!isset($config['directories']) || empty($config['directories'])) {
174+
throw new \InvalidArgumentException('At least one entity directory must be configured');
175+
}
176+
177+
foreach ($config['directories'] as $dir) {
178+
if (!is_dir($dir)) {
179+
// Criar diretório se não existir
180+
if (!mkdir($dir, 0755, true) && !is_dir($dir)) {
181+
throw new \InvalidArgumentException("Entity directory cannot be created: {$dir}");
182+
}
183+
}
184+
}
185+
}
186+
187+
private function addSchemaGenerators(Compiler $compiler): void
188+
{
189+
$compiler->addGenerator(new Generator\ResetTables());
190+
$compiler->addGenerator(new Generator\GenerateRelations());
191+
$compiler->addGenerator\GenerateModifiers());
192+
$compiler->addGenerator(new Generator\ValidateEntities());
193+
$compiler->addGenerator(new Generator\RenderTables());
194+
$compiler->addGenerator(new Generator\RenderRelations());
195+
$compiler->addGenerator(new Generator\RenderModifiers());
196+
}
197+
198+
private function enableDevelopmentFeatures(): void
199+
{
200+
if (config('cycle.development.log_queries', false)) {
201+
// Implementar query logging
202+
$this->app->singleton('cycle.query_logger', function() {
203+
return new QueryLogger();
204+
});
205+
}
206+
207+
if (config('cycle.development.profile_queries', false)) {
208+
// Implementar query profiling
209+
$this->app->singleton('cycle.profiler', function() {
210+
return new PerformanceProfiler();
211+
});
212+
}
213+
}
214+
215+
private function registerORM(): void
216+
{
217+
$this->app->singleton('cycle.orm', function (Application $app) {
218+
$factory = new Factory(
219+
$app->make('cycle.database'),
220+
null, // Use default selector factory
221+
new \Cycle\ORM\Collection\ArrayCollectionFactory()
222+
);
223+
224+
return new ORM(
225+
$factory,
226+
$app->make('cycle.schema')
227+
);
228+
});
229+
230+
$this->app->alias('cycle.orm', 'orm');
231+
}
232+
233+
private function registerEntityManager(): void
234+
{
235+
$this->app->singleton('cycle.em', function (Application $app) {
236+
return new EntityManager($app->make('cycle.orm'));
237+
});
238+
239+
$this->app->alias('cycle.em', 'em');
240+
}
241+
242+
private function registerRepositoryFactory(): void
243+
{
244+
$this->app->singleton('cycle.repository', function (Application $app) {
245+
return new RepositoryFactory($app->make('cycle.orm'));
246+
});
247+
}
153248
}

src/Middleware/CycleMiddleware.php

Lines changed: 57 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -21,69 +21,70 @@ public function __construct(Application $app)
2121
* CORREÇÃO: Signature correta para middleware do Express-PHP
2222
*/
2323
public function handle(Request $req, Response $res, callable $next): void
24-
{
25-
try {
26-
// CORREÇÃO: Verificar se serviços estão disponíveis
27-
if (!$this->app->has('cycle.orm')) {
28-
throw new \RuntimeException('Cycle ORM not properly registered');
29-
}
30-
31-
// Injetar serviços com verificação de disponibilidade
32-
$req->orm = $this->app->make('cycle.orm');
33-
$req->em = $this->app->make('cycle.em');
34-
$req->db = $this->app->make('cycle.database');
24+
{
25+
try {
26+
// Verificar se serviços estão disponíveis
27+
if (!$this->app->has('cycle.orm')) {
28+
throw new \RuntimeException('Cycle ORM not properly registered');
29+
}
3530

36-
// CORREÇÃO: Helper mais robusto para repositories
37-
$req->repository = function (string $entityClass) use ($req) {
38-
if (!class_exists($entityClass)) {
39-
throw new \InvalidArgumentException("Entity class {$entityClass} does not exist");
40-
}
41-
return $req->orm->getRepository($entityClass);
42-
};
31+
// Injetar serviços principais
32+
$req->orm = $this->app->make('cycle.orm');
33+
$req->em = $this->app->make('cycle.em');
34+
$req->db = $this->app->make('cycle.database');
4335

44-
// CORREÇÃO: Helper para criação de entidades com validação
45-
$req->entity = function (string $entityClass, array $data = []) {
46-
if (!class_exists($entityClass)) {
47-
throw new \InvalidArgumentException("Entity class {$entityClass} does not exist");
48-
}
36+
// CORREÇÃO: Helper repository mais robusto
37+
$req->repository = function (string $entityClass) use ($req) {
38+
if (!class_exists($entityClass)) {
39+
throw new \InvalidArgumentException("Entity class {$entityClass} does not exist");
40+
}
41+
return $req->orm->getRepository($entityClass);
42+
};
4943

50-
// CORREÇÃO: Usar reflection para criar entidade corretamente
51-
$reflection = new \ReflectionClass($entityClass);
52-
if ($reflection->getConstructor()) {
53-
$constructor = $reflection->getConstructor();
54-
$params = [];
44+
// CORREÇÃO: Helper entity com validação aprimorada
45+
$req->entity = function (string $entityClass, array $data = []) {
46+
if (!class_exists($entityClass)) {
47+
throw new \InvalidArgumentException("Entity class {$entityClass} does not exist");
48+
}
5549

56-
foreach ($constructor->getParameters() as $param) {
57-
$paramName = $param->getName();
58-
if (isset($data[$paramName])) {
59-
$params[] = $data[$paramName];
60-
} elseif (!$param->isOptional()) {
61-
throw new \InvalidArgumentException("Missing required parameter: {$paramName}");
62-
}
63-
}
50+
$entity = new $entityClass();
6451

65-
return $reflection->newInstanceArgs($params);
52+
// Aplicar dados se fornecidos
53+
foreach ($data as $property => $value) {
54+
if (property_exists($entity, $property)) {
55+
$entity->$property = $value;
6656
}
57+
}
6758

68-
return new $entityClass();
69-
};
70-
71-
// CORREÇÃO: Dispatch event para hooks
72-
$this->app->fireAction('cycle.middleware.before', ['request' => $req]);
73-
74-
$next();
75-
76-
// CORREÇÃO: Dispatch event pós-processamento
77-
$this->app->fireAction('cycle.middleware.after', ['request' => $req, 'response' => $res]);
78-
79-
} catch (\Exception $e) {
80-
// CORREÇÃO: Log de erro e propagação
81-
$this->app->logger()->error('Cycle middleware error', [
82-
'error' => $e->getMessage(),
83-
'trace' => $e->getTraceAsString()
84-
]);
85-
86-
throw $e;
59+
return $entity;
60+
};
61+
62+
// CORREÇÃO: Helper find com validação
63+
$req->find = function (string $entityClass, $id) use ($req) {
64+
return $req->repository($entityClass)->findByPK($id);
65+
};
66+
67+
// CORREÇÃO: Helper paginate
68+
$req->paginate = function ($query, int $page = 1, int $perPage = 15) {
69+
return \CAFernandes\ExpressPHP\CycleORM\Helpers\CycleHelpers::paginate($query, $page, $perPage);
70+
};
71+
72+
// CORREÇÃO: Helper validateEntity
73+
$req->validateEntity = function ($entity) {
74+
$middleware = new \CAFernandes\ExpressPHP\CycleORM\Middleware\EntityValidationMiddleware($this->app);
75+
$reflection = new \ReflectionMethod($middleware, 'validateEntity');
76+
$reflection->setAccessible(true);
77+
return $reflection->invoke($middleware, $entity);
78+
};
79+
80+
$next();
81+
82+
} catch (\Exception $e) {
83+
// Log erro se logger disponível
84+
if (method_exists($this->app, 'logger')) {
85+
$this->app->logger()->error('Cycle middleware error: ' . $e->getMessage());
8786
}
87+
throw $e;
8888
}
8989
}
90+
}

0 commit comments

Comments
 (0)