diff --git a/.gitattributes b/.gitattributes index e5b5b48..c274cc9 100644 --- a/.gitattributes +++ b/.gitattributes @@ -13,4 +13,5 @@ CONTRIBUTING.md export-ignore pint.json export-ignore phpstan.neon.dist export-ignore phpunit.xml.dist export-ignore +structarmed.php export-ignore testbench.yaml export-ignore diff --git a/composer.json b/composer.json index 15b2902..8d20463 100644 --- a/composer.json +++ b/composer.json @@ -43,6 +43,7 @@ "illuminate/support": "^12.0 || ^13.0" }, "require-dev": { + "boundwize/structarmed": "^0.19.3", "calebdw/phpstan-laravel": "^1.0", "laravel/pint": "^1.16.2", "orchestra/testbench": "^10.0 || ^11.0", @@ -74,6 +75,7 @@ "test:lint": "pint --test", "test:lint-fix": "pint", "test:static": "phpstan analyze --ansi -v", + "test:architecture": "structarmed analyze", "test:unit": "@php -d zend.assertions=1 vendor/bin/pest --no-coverage", "test:coverage": [ "@putenv XDEBUG_MODE=coverage", @@ -82,6 +84,7 @@ "test": [ "@test:lint", "@test:static", + "@test:architecture", "@test:coverage" ], "lint": [ diff --git a/src/ServiceProvider.php b/src/ServiceProvider.php index a08305b..1ca6dec 100644 --- a/src/ServiceProvider.php +++ b/src/ServiceProvider.php @@ -60,12 +60,12 @@ public function boot(): void Event::subscribe(SyncSqlEntities::class); } - $this->app->afterResolving(Schedule::class, function (Schedule $schedule) { + $this->app->afterResolving(Schedule::class, static function (Schedule $schedule) { $manager = resolve(SqlEntityManager::class); $manager->entities - ->filter(fn ($entity) => $entity instanceof MaterializedView) - ->each(function ($entity) use ($schedule) { + ->filter(static fn ($entity) => $entity instanceof MaterializedView) + ->each(static function ($entity) use ($schedule) { $frequency = $entity->schedule(new Frequency()); if ($frequency === null) { @@ -92,10 +92,10 @@ protected function getEntities(Application $app): Collection ->in($app->basePath()) ->path('database/entities'), )) - ->map(fn ($file) => (string) $file->getRealPath()) - ->pipe(fn ($files) => collect($composer->classFromFile($files->all()))) - ->filter(fn ($class) => is_subclass_of($class, SqlEntity::class)) - ->map(fn ($class) => $app->make($class)) + ->map(static fn ($file) => (string) $file->getRealPath()) + ->pipe(static fn ($files) => collect($composer->classFromFile($files->all()))) + ->filter(static fn ($class) => is_subclass_of($class, SqlEntity::class)) + ->map(static fn ($class) => $app->make($class)) ->values(); } } diff --git a/src/SqlEntityManager.php b/src/SqlEntityManager.php index 56e3ee2..72ad3d4 100644 --- a/src/SqlEntityManager.php +++ b/src/SqlEntityManager.php @@ -57,15 +57,15 @@ public function __construct( protected DatabaseManager $db, protected TopologicalSorter $sorter, ) { - $this->entities = $entities->keyBy(fn ($e) => $e::class); + $this->entities = $entities->keyBy(static fn ($e) => $e::class); $sorted = $this->sorter->sort( $this->entities, fn ($e) => collect($e->dependencies())->map($this->get(...)), - fn ($e) => $e::class, + static fn ($e) => $e::class, ); - $this->entities = collect($sorted)->keyBy(fn ($e) => $e::class); + $this->entities = collect($sorted)->keyBy(static fn ($e) => $e::class); } /** @@ -203,8 +203,8 @@ public function dropAll( ->reverse() ->when($connections, $this->filterByConnections(...)) ->when($types, $this->filterByTypes(...)) - ->when(! $force && $types === null, fn ($entities) => $entities->reject( - fn ($entity) => $entity instanceof RequiresExplicitDrop, + ->when(! $force && $types === null, static fn ($entities) => $entities->reject( + static fn ($entity) => $entity instanceof RequiresExplicitDrop, )) ->each($this->drop(...)); } @@ -246,7 +246,7 @@ public function refreshMaterializedData( $this->entities ->when($connections, $this->filterByConnections(...)) ->when($entities, $this->filterByTypes(...)) - ->filter(fn ($entity) => $entity instanceof MaterializedView) + ->filter(static fn ($entity) => $entity instanceof MaterializedView) ->each(function ($entity) use ($concurrent) { $connection = $this->connection($entity->connectionName()); $grammar = $this->grammar($connection); @@ -277,10 +277,10 @@ public function withoutEntities( $groups = $this->entities ->when($connections, $this->filterByConnections(...)) ->when($types, $this->filterByTypes(...)) - ->when(! $force && $types === null, fn ($entities) => $entities->reject( - fn ($entity) => $entity instanceof RequiresExplicitDrop, + ->when(! $force && $types === null, static fn ($entities) => $entities->reject( + static fn ($entity) => $entity instanceof RequiresExplicitDrop, )) - ->groupBy(fn ($e) => $e->connectionName() ?? $defaultConnection); + ->groupBy(static fn ($e) => $e->connectionName() ?? $defaultConnection); foreach ($groups as $connectionName => $entities) { $connection = $this->connection($connectionName); @@ -325,7 +325,7 @@ protected function filterByTypes( Collection $entities, array|string $types, ): Collection { - return $entities->filter(function ($entity) use ($types) { + return $entities->filter(static function ($entity) use ($types) { foreach (Arr::wrap($types) as $type) { if (is_a($entity, $type, allow_string: false)) { return true; @@ -349,7 +349,7 @@ protected function filterByConnections( ): Collection { $default = $this->db->getDefaultConnection(); - return $entities->filter(function ($entity) use ($connections, $default) { + return $entities->filter(static function ($entity) use ($connections, $default) { $name = $entity->connectionName() ?? $default; return in_array($name, Arr::wrap($connections), strict: true); diff --git a/src/Support/TopologicalSorter.php b/src/Support/TopologicalSorter.php index 220959b..3f5a5bc 100644 --- a/src/Support/TopologicalSorter.php +++ b/src/Support/TopologicalSorter.php @@ -23,7 +23,7 @@ public function sort(iterable $nodes, callable $edges, ?callable $getKey = null) { $sorted = []; $visited = []; - $getKey ??= fn ($node) => $node; + $getKey ??= static fn ($node) => $node; foreach ($nodes as $node) { $this->visit($node, $edges, $sorted, $visited, $getKey); diff --git a/structarmed.php b/structarmed.php new file mode 100644 index 0000000..03b4ce5 --- /dev/null +++ b/structarmed.php @@ -0,0 +1,42 @@ +layer('Source', 'src') + ->withPresets(Preset::PSR4(), Preset::CODEQUALITY()) + ->layer('Contracts', 'src/Contracts') + ->layer('Support', 'src/Support') + ->layer('Attributes', 'src/Attributes') + ->layer('Concerns', 'src/Concerns') + ->layer('Entity', [ + 'src/Function_.php', + 'src/MaterializedView.php', + 'src/Procedure.php', + 'src/Trigger.php', + 'src/View.php', + ]) + ->layer('Grammars', 'src/Grammars') + ->layer('Manager', 'src/SqlEntityManager.php') + ->layer('Console', 'src/Console') + ->layer('Facades', 'src/Facades') + ->layer('Listeners', 'src/Listeners') + ->layer('PHPStan', 'src/PHPStan') + ->layer('ServiceProvider', 'src/ServiceProvider.php') + ->ruleset([ + 'Contracts' => [], + 'Support' => [], + 'Attributes' => ['Contracts'], + 'Concerns' => ['+Attributes'], + 'Entity' => ['+Concerns', 'Support'], + 'Grammars' => ['+Entity'], + 'Manager' => ['+Grammars'], + 'Console' => ['+Manager'], + 'Facades' => ['+Manager'], + 'Listeners' => ['+Manager'], + 'PHPStan' => ['+Entity'], + 'ServiceProvider' => ['+Console', '+Listeners'], + ]);