Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refine routes list functionality #58

Open
wants to merge 25 commits into
base: 2.10.x
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
7e76f37
Refactor RoutesFilter to use a RouteFilterOptionsInterface object
settermjd Oct 25, 2024
0e75926
Add docblock comments for RouteFilterOptionsInterface methods
settermjd Oct 25, 2024
b42ad5e
Refactor filter options as class member variables
settermjd Oct 25, 2024
bb8a3d1
Refactor RouteFilterOptions to require specific types without defaults
settermjd Nov 14, 2024
d75d4ba
Replace the Exception with a Throwable in RoutesFilter
settermjd Nov 14, 2024
59d84bb
Refactor ListRoutesCommand to take a RouteCollector directly
settermjd Nov 14, 2024
86d1531
Minor code clean up
settermjd Nov 14, 2024
5037475
Refactor how ListRoutesCommandFactory retrieves a ConfigLoaderInterface
settermjd Nov 14, 2024
26bf4e1
Add a default way of retrieving routes from the application
settermjd Nov 14, 2024
0f86b2a
Load the DefaultRoutesConfigLoaderFactory with Mezzio Tooling
settermjd Nov 14, 2024
903ed62
Update src/Routes/Filter/RouteFilterOptionsInterface.php
settermjd Dec 10, 2024
3083c20
Update src/Routes/Filter/RouteFilterOptions.php
settermjd Dec 10, 2024
73e9dc2
Clean up file to match changes in recent commits
settermjd Dec 10, 2024
cd023e6
Update README to match style guide
settermjd Dec 10, 2024
7362681
Remove redundant EmptyRouteFilterOptions
settermjd Dec 10, 2024
9969934
Update RouteFilterOptions to match RouteFilterOptionsInterface
settermjd Dec 10, 2024
3b83456
Remove unrequired RouteFilterOptionsInterface parameter
settermjd Dec 10, 2024
6c60b57
Update ConfigProvider configuration for RoutesFileConfigLoader
settermjd Dec 10, 2024
925c90f
Remove redundant toArray method from RouteFilterOptions
settermjd Dec 10, 2024
dac21eb
Fix up a small bug in RouteFilterOptions
settermjd Dec 10, 2024
fbc82c8
Clean up code to reflect that getMethods only returns an array
settermjd Dec 16, 2024
17ba639
Use Symfony's CommandTester in ListRoutesCommandTest
settermjd Dec 16, 2024
c22c97f
Fix a bug when filtering out null methods
settermjd Dec 19, 2024
94f1ce1
Initialise RoutesFilter with a RouteFilterOptions object
settermjd Dec 19, 2024
d7380b5
Correct code regression from recent rebase
settermjd Dec 19, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,19 @@
| `--has-middleware` | `-w` | Accepts a comma-separated list of one or more middleware classes, and filters out routes that do not require those classes. The classes can be fully-qualified, unqualified, or a regular expression, supported by the preg_* functions. For example, "\Mezzio\Middleware\LazyLoadingMiddleware,LazyLoadingMiddleware,\Mezzio*". |
<!-- markdownlint-enable MD037 -->

##### Configuration

By default, `Mezzio\Tooling\Routes\DefaultRoutesConfigLoaderFactory` registers a `ConfigLoaderInterface` service with the application's DI container, which retrieves the application's routes from two sources:

- `config/routes.php`
- Routes registered by any loaded `ConfigProvider` class

However, this is a default/fallback implementation.
If you don't store any routes in _config/routes.php_ or need a custom implementation, then you need to do two things:

Check failure on line 77 in README.md

View workflow job for this annotation

GitHub Actions / ci / QA Checks (README Linting [8.1, locked], ubuntu-latest, laminas/laminas-continuous-integration-ac...

Emphasis style [Expected: asterisk; Actual: underscore]

Check failure on line 77 in README.md

View workflow job for this annotation

GitHub Actions / ci / QA Checks (README Linting [8.1, locked], ubuntu-latest, laminas/laminas-continuous-integration-ac...

Emphasis style [Expected: asterisk; Actual: underscore]

1. Write a custom loader implementation that implements `Mezzio\Tooling\Routes\ConfigLoaderInterface`
2. Register it with the application's DI container as an alias for `Mezzio\Tooling\Routes\ConfigLoaderInterface`

##### Usage Example

Here is an example of what you can expect from running the command.
Expand Down
30 changes: 15 additions & 15 deletions composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions src/ConfigProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,11 @@
use Mezzio\Tooling\Module\DeregisterCommandFactory;
use Mezzio\Tooling\Module\RegisterCommand;
use Mezzio\Tooling\Module\RegisterCommandFactory;
use Mezzio\Tooling\Routes\ConfigLoaderInterface;
use Mezzio\Tooling\Routes\DefaultRoutesConfigLoaderFactory;
use Mezzio\Tooling\Routes\ListRoutesCommand;
use Mezzio\Tooling\Routes\ListRoutesCommandFactory;
use Mezzio\Tooling\Routes\RoutesFileConfigLoader;

final class ConfigProvider
{
Expand Down Expand Up @@ -60,11 +63,14 @@
}

/**
* @return array{factories: array<class-string, class-string>}

Check failure on line 66 in src/ConfigProvider.php

View workflow job for this annotation

GitHub Actions / ci / QA Checks (Psalm [8.1, locked], ubuntu-latest, laminas/laminas-continuous-integration-action@v1, ...

InvalidReturnType

src/ConfigProvider.php:66:16: InvalidReturnType: The declared return type 'array{factories: array<class-string, class-string>}' for Mezzio\Tooling\ConfigProvider::getDependencies is incorrect, got 'array{aliases: array{'Mezzio\\Tooling\\Routes\\ConfigLoaderInterface'::class: Mezzio\Tooling\Routes\RoutesFileConfigLoader::class}, factories: array{'Mezzio\\Tooling\\CreateHandler\\CreateActionCommand'::class: Mezzio\Tooling\CreateHandler\CreateActionCommandFactory::class, 'Mezzio\\Tooling\\CreateHandler\\CreateHandlerCommand'::class: Mezzio\Tooling\CreateHandler\CreateHandlerCommandFactory::class, 'Mezzio\\Tooling\\CreateMiddleware\\CreateMiddlewareCommand'::class: Mezzio\Tooling\CreateMiddleware\CreateMiddlewareCommandFactory::class, 'Mezzio\\Tooling\\Factory\\Create'::class: Mezzio\Tooling\Factory\CreateFactory::class, 'Mezzio\\Tooling\\Factory\\CreateFactoryCommand'::class: Mezzio\Tooling\Factory\CreateFactoryCommandFactory::class, 'Mezzio\\Tooling\\MigrateInteropMiddleware\\MigrateInteropMiddlewareCommand'::class: Mezzio\Tooling\MigrateInteropMiddleware\MigrateInteropMiddlewareCommandFactory::class, 'Mezzio\\Tooling\\MigrateMiddlewareToRequestHandler\\MigrateMiddlewareToRequestHandlerCommand'::class: Mezzio\Tooling\MigrateMiddlewareToRequestHandler\MigrateMiddlewareToRequestHandlerCommandFactory::class, 'Mezzio\\Tooling\\Module\\CreateCommand'::class: Mezzio\Tooling\Module\CreateCommandFactory::class, 'Mezzio\\Tooling\\Module\\DeregisterCommand'::class: Mezzio\Tooling\Module\DeregisterCommandFactory::class, 'Mezzio\\Tooling\\Module\\RegisterCommand'::class: Mezzio\Tooling\Module\RegisterCommandFactory::class, 'Mezzio\\Tooling\\Routes\\ListRoutesCommand'::class: Mezzio\Tooling\Routes\ListRoutesCommandFactory::class, 'Mezzio\\Tooling\\Routes\\RoutesFileConfigLoader'::class: Mezzio\Tooling\Routes\DefaultRoutesConfigLoaderFactory::class}}' which is different due to additional array shape fields (aliases) (see https://psalm.dev/011)
*/
public function getDependencies(): array
{
return [

Check failure on line 70 in src/ConfigProvider.php

View workflow job for this annotation

GitHub Actions / ci / QA Checks (Psalm [8.1, locked], ubuntu-latest, laminas/laminas-continuous-integration-action@v1, ...

InvalidReturnStatement

src/ConfigProvider.php:70:16: InvalidReturnStatement: The inferred type 'array{aliases: array{'Mezzio\\Tooling\\Routes\\ConfigLoaderInterface'::class: Mezzio\Tooling\Routes\RoutesFileConfigLoader::class}, factories: array{'Mezzio\\Tooling\\CreateHandler\\CreateActionCommand'::class: Mezzio\Tooling\CreateHandler\CreateActionCommandFactory::class, 'Mezzio\\Tooling\\CreateHandler\\CreateHandlerCommand'::class: Mezzio\Tooling\CreateHandler\CreateHandlerCommandFactory::class, 'Mezzio\\Tooling\\CreateMiddleware\\CreateMiddlewareCommand'::class: Mezzio\Tooling\CreateMiddleware\CreateMiddlewareCommandFactory::class, 'Mezzio\\Tooling\\Factory\\Create'::class: Mezzio\Tooling\Factory\CreateFactory::class, 'Mezzio\\Tooling\\Factory\\CreateFactoryCommand'::class: Mezzio\Tooling\Factory\CreateFactoryCommandFactory::class, 'Mezzio\\Tooling\\MigrateInteropMiddleware\\MigrateInteropMiddlewareCommand'::class: Mezzio\Tooling\MigrateInteropMiddleware\MigrateInteropMiddlewareCommandFactory::class, 'Mezzio\\Tooling\\MigrateMiddlewareToRequestHandler\\MigrateMiddlewareToRequestHandlerCommand'::class: Mezzio\Tooling\MigrateMiddlewareToRequestHandler\MigrateMiddlewareToRequestHandlerCommandFactory::class, 'Mezzio\\Tooling\\Module\\CreateCommand'::class: Mezzio\Tooling\Module\CreateCommandFactory::class, 'Mezzio\\Tooling\\Module\\DeregisterCommand'::class: Mezzio\Tooling\Module\DeregisterCommandFactory::class, 'Mezzio\\Tooling\\Module\\RegisterCommand'::class: Mezzio\Tooling\Module\RegisterCommandFactory::class, 'Mezzio\\Tooling\\Routes\\ListRoutesCommand'::class: Mezzio\Tooling\Routes\ListRoutesCommandFactory::class, 'Mezzio\\Tooling\\Routes\\RoutesFileConfigLoader'::class: Mezzio\Tooling\Routes\DefaultRoutesConfigLoaderFactory::class}}' does not match the declared return type 'array{factories: array<class-string, class-string>}' for Mezzio\Tooling\ConfigProvider::getDependencies due to additional array shape fields (aliases) (see https://psalm.dev/128)
'aliases' => [
ConfigLoaderInterface::class => RoutesFileConfigLoader::class,
],
'factories' => [
Create::class => CreateFactory::class,
CreateActionCommand::class => CreateActionCommandFactory::class,
Expand All @@ -77,6 +83,7 @@
MigrateInteropMiddlewareCommand::class => MigrateInteropMiddlewareCommandFactory::class,
MigrateMiddlewareToRequestHandlerCommand::class => MigrateMiddlewareToRequestHandlerCommandFactory::class,
RegisterCommand::class => RegisterCommandFactory::class,
RoutesFileConfigLoader::class => DefaultRoutesConfigLoaderFactory::class,
],
];
}
Expand Down
36 changes: 36 additions & 0 deletions src/Routes/DefaultRoutesConfigLoaderFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php

declare(strict_types=1);

namespace Mezzio\Tooling\Routes;

use Mezzio\Application;
use Mezzio\MiddlewareFactory;
use Psr\Container\ContainerInterface;

/**
* This class provides a default factory implementation for retrieving an app's routes

* The assumption with this "default" config loader factory is that you're
* loading routes from config/routes.php in your Mezzio application. So, if
* that's not the case in your application, create a custom loader
* implementation and override the alias in the DiC.
*/
final class DefaultRoutesConfigLoaderFactory
{
public function __invoke(ContainerInterface $container): ConfigLoaderInterface
{
/** @var Application $application */
$application = $container->get(Application::class);

/** @var MiddlewareFactory $factory */
$factory = $container->get(MiddlewareFactory::class);

return new RoutesFileConfigLoader(
'config/routes.php',
$application,
$factory,
$container
);
}
}
74 changes: 27 additions & 47 deletions src/Routes/Filter/RouteFilterOptions.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,85 +4,65 @@

namespace Mezzio\Tooling\Routes\Filter;

use function get_object_vars;
use function array_filter;
use function array_map;
use function in_array;
use function is_array;
use function is_string;
use function strtoupper;

final class RouteFilterOptions
final class RouteFilterOptions implements RouteFilterOptionsInterface
{
private string $middleware;
private string $name;
private string $path;
private array $allowedFilterOptions = ['methods', 'middleware', 'name', 'path'];
private array $methods;

/** @var array<array-key,string> */
private array $methods = [];

/**
* @param string|array<array-key,string> $methods
*/
/** @param list<string> $methods */
public function __construct(
string $middleware = '',
string $name = '',
string $path = '',
$methods = []
private string|null $middleware,
private string|null $name,
private string|null $path,
array $methods
) {
if (is_string($methods)) {
$this->methods = [$methods];
}
if (is_array($methods)) {
$this->methods = $methods;
}
$this->middleware = $middleware;
$this->name = $name;
$this->path = $path;
$methods = array_filter($methods, 'strlen');
$this->methods = array_map(static fn(string $value): string => strtoupper($value), $methods);
}

private function getFilterOptionsMinusMethods(): array
{
return array_filter($this->allowedFilterOptions, fn($value) => $value !== "methods");
}

public function has(string $filterOption): bool
{
if (in_array($filterOption, ['middleware', 'name', 'path'])) {
return $this->$filterOption !== null;
if (! in_array($filterOption, $this->allowedFilterOptions)) {
return false;
}

if ($filterOption === 'methods') {
return [] !== $this->methods;
if (in_array($filterOption, $this->getFilterOptionsMinusMethods())) {
return $this->$filterOption !== null;
}

return false;
return $this->methods !== [];
}

public function getMiddleware(): string
public function getMiddleware(): string|null
{
return $this->middleware;
}

public function getName(): string
public function getName(): string|null
{
return $this->name;
}

public function getPath(): string
public function getPath(): string|null
{
return $this->path;
}

/**
* @return array<array-key,string>
* @return array<array-key, string>

Check failure on line 62 in src/Routes/Filter/RouteFilterOptions.php

View workflow job for this annotation

GitHub Actions / ci / QA Checks (Psalm [8.1, locked], ubuntu-latest, laminas/laminas-continuous-integration-action@v1, ...

LessSpecificImplementedReturnType

src/Routes/Filter/RouteFilterOptions.php:62:16: LessSpecificImplementedReturnType: The inherited return type 'list<string>' for Mezzio\Tooling\Routes\Filter\RouteFilterOptionsInterface::getMethods is more specific than the implemented return type for Mezzio\Tooling\Routes\Filter\RouteFilterOptions::getmethods 'array<array-key, string>' (see https://psalm.dev/166)

Check failure on line 62 in src/Routes/Filter/RouteFilterOptions.php

View workflow job for this annotation

GitHub Actions / ci / QA Checks (Psalm [8.1, locked], ubuntu-latest, laminas/laminas-continuous-integration-action@v1, ...

MixedReturnTypeCoercion

src/Routes/Filter/RouteFilterOptions.php:62:16: MixedReturnTypeCoercion: The declared return type 'array<array-key, string>' for Mezzio\Tooling\Routes\Filter\RouteFilterOptions::getMethods is more specific than the inferred return type 'array<array-key, mixed>' (see https://psalm.dev/197)
*/
public function getMethods(): array
{
return $this->methods;

Check failure on line 66 in src/Routes/Filter/RouteFilterOptions.php

View workflow job for this annotation

GitHub Actions / ci / QA Checks (Psalm [8.1, locked], ubuntu-latest, laminas/laminas-continuous-integration-action@v1, ...

MixedReturnTypeCoercion

src/Routes/Filter/RouteFilterOptions.php:66:16: MixedReturnTypeCoercion: The type 'array<array-key, mixed>' is more general than the declared return type 'array<array-key, string>' for Mezzio\Tooling\Routes\Filter\RouteFilterOptions::getMethods (see https://psalm.dev/197)
}

public function toArray(): array
{
$values = [];
foreach (get_object_vars($this) as $key => $value) {
if (! empty($value)) {
$values[$key] = $value;
}
}

return $values;
}
}
35 changes: 35 additions & 0 deletions src/Routes/Filter/RouteFilterOptionsInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php

declare(strict_types=1);
settermjd marked this conversation as resolved.
Show resolved Hide resolved

namespace Mezzio\Tooling\Routes\Filter;

interface RouteFilterOptionsInterface
{
/**
* Determines if the specified filter ($filterOption) has been set
*/
public function has(string $filterOption): bool;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The has method seems unnecessary when consumers can test for $options->getMiddleware() === [] or $options->getName() === null


/**
* Retrieves a route middleware filter the available routing data by
*/
public function getMiddleware(): string|null;

/**
* Retrieves a route name filter the available routing data by
*/
public function getName(): string|null;

/**
* Retrieves a route path to filter the available routing data by
*/
public function getPath(): string|null;

/**
* Returns any route methods to filter the available routing data by
*
* @return list<string>
*/
public function getMethods(): array;
}
Loading
Loading