{"id":17001651,"url":"https://github.com/juliangut/slim-routing","last_synced_at":"2025-03-22T16:30:38.315Z","repository":{"id":48122552,"uuid":"91021757","full_name":"juliangut/slim-routing","owner":"juliangut","description":"Slim framework powered-up routing","archived":false,"fork":false,"pushed_at":"2024-04-03T21:18:11.000Z","size":513,"stargazers_count":21,"open_issues_count":4,"forks_count":5,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-03-18T13:32:04.554Z","etag":null,"topics":["annotations-processor","builder","routing","slim-framework"],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/juliangut.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":"juliangut"}},"created_at":"2017-05-11T20:43:19.000Z","updated_at":"2025-01-15T12:04:21.000Z","dependencies_parsed_at":"2024-10-28T14:03:33.974Z","dependency_job_id":"c9bc871d-081b-4511-87b4-5b0f90a933a4","html_url":"https://github.com/juliangut/slim-routing","commit_stats":{"total_commits":141,"total_committers":3,"mean_commits":47.0,"dds":"0.014184397163120588","last_synced_commit":"8578c6893c75b97ee6d95253dad7c88d200c8f0b"},"previous_names":[],"tags_count":20,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/juliangut%2Fslim-routing","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/juliangut%2Fslim-routing/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/juliangut%2Fslim-routing/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/juliangut%2Fslim-routing/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/juliangut","download_url":"https://codeload.github.com/juliangut/slim-routing/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244986240,"owners_count":20542980,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["annotations-processor","builder","routing","slim-framework"],"created_at":"2024-10-14T04:25:40.013Z","updated_at":"2025-03-22T16:30:38.031Z","avatar_url":"https://github.com/juliangut.png","language":"PHP","funding_links":["https://github.com/sponsors/juliangut"],"categories":[],"sub_categories":[],"readme":"[![PHP version](https://img.shields.io/badge/PHP-%3E%3D8.0-8892BF.svg?style=flat-square)](http://php.net)\n[![Latest Version](https://img.shields.io/packagist/v/juliangut/slim-routing.svg?style=flat-square)](https://packagist.org/packages/juliangut/slim-routing)\n[![License](https://img.shields.io/github/license/juliangut/slim-routing.svg?style=flat-square)](https://github.com/juliangut/slim-routing/blob/master/LICENSE)\n\n[![Total Downloads](https://img.shields.io/packagist/dt/juliangut/slim-routing.svg?style=flat-square)](https://packagist.org/packages/juliangut/slim-routing/stats)\n[![Monthly Downloads](https://img.shields.io/packagist/dm/juliangut/slim-routing.svg?style=flat-square)](https://packagist.org/packages/juliangut/slim-routing/stats)\n\n# slim-routing\n\nA replacement for Slim's router that adds Attribute and configuration based routing as well as expands the possibilities of your route callbacks by handling different response types\n\nThanks to this library, instead of configuring routes by hand one by one and including them into Slim's routing you can create mapping files that define and structure your routes and let them be included automatically instead\n\nAdditionally, if you're familiar with Symfony's definition of routes through Attributes you'll feel at home with slim-routing because route mappings can be defined the same way as well\n\n* On class Attributes (i.e. controller classes)\n* In routing definition files, currently supported in PHP, JSON, XML and YAML\n\n\u003e Route gathering and compilation can be quite a heavy process depending on how many classes/files and routes are defined, specially in the case of attributes. For this reason it's strongly advised to always use route collector's cache and Slim's [route expression caching](https://www.slimframework.com/docs/v4/objects/routing.html#route-expressions-caching) on production applications and invalidate cache on deployment\n\nThanks to slim-routing route callbacks can now return `\\Jgut\\Slim\\Routing\\Response\\ResponseType` objects that will be ultimately transformed into the mandatory `Psr\\Message\\ResponseInterface` in a way that lets you fully decouple view from the route\n\n## Installation\n\n### Composer\n\n```\ncomposer require juliangut/slim-routing\n```\n\nsymfony/yaml to parse yaml routing files\n\n```\ncomposer require symfony/yaml\n```\n\nspatie/array-to-xml to return XML responses\n\n```\ncomposer require spatie/array-to-xml\n```\n\nslim/twig-view to return Twig rendered responses\n\n```\ncomposer require slim/twig-view\n```\n\n## Usage\n\nRequire composer autoload file\n\n```php\nrequire './vendor/autoload.php';\n```\n\n```php\nuse Jgut\\Slim\\Routing\\AppFactory;\nuse Jgut\\Slim\\Routing\\Configuration;\nuse Jgut\\Slim\\Routing\\Response\\PayloadResponse;\nuse Jgut\\Slim\\Routing\\Response\\RedirectResponse;\nuse Jgut\\Slim\\Routing\\Response\\ResponseType;\nuse Jgut\\Slim\\Routing\\Response\\Handler\\JsonResponseHandler;\nuse Jgut\\Slim\\Routing\\Response\\Handler\\RedirectResponseHandler;\nuse Jgut\\Slim\\Routing\\Strategy\\RequestHandler;\nuse Psr\\Http\\Message\\ServerRequestInterface;\n\n$configuration = new Configuration([\n    'sources' =\u003e ['/path/to/routing/files'],\n]);\nAppFactory::setRouteCollectorConfiguration($configuration);\n\n// Instantiate the app\n$app = AppFactory::create();\n\n$routeCollector = $app-\u003egetRouteCollector();\n$responseFactory = $app-\u003egetResponseFactory();\n\n// Register custom invocation strategy to handle ResponseType objects\n$invocationStrategy = new RequestHandler(\n    [\n        RedirectResponse::class =\u003e new RedirectResponseHandler($responseFactory, $routeCollector),\n        // Handlers can be pulled from the container\n        PayloadResponse::class =\u003e JsonResponseHandler::class,\n    ],\n    $responseFactory,\n    $app-\u003egetContainer()\n);\n$routeCollector-\u003esetDefaultInvocationStrategy($invocationStrategy);\n\n$cache = new PSR16Cache();\n$routeCollector-\u003esetCache($cache);\n\n// Recommended if you want to add more routes manually\n$routeCollector-\u003eregisterRoutes();\n\n// Additional routes if needed\n$app-\u003eget('/', function(ServerRequestInterface $request): ResponseType {\n    return new PayloadResponse(['param' =\u003e 'value'], $request);\n});\n\n$app-\u003erun();\n```\n\n### Configuration\n\n* `sources` must be an array containing arrays of configurations to create MappingDriver objects:\n    * `type` one of \\Jgut\\Slim\\Routing\\Mapping\\Driver\\DriverFactory constants: `DRIVER_ATTRIBUTE`, `DRIVER_PHP`, `DRIVER_JSON`, `DRIVER_XML`, `DRIVER_YAML` or `DRIVER_ANNOTATION` **if no driver, defaults to DRIVER_ATTRIBUTE**\n    * `path` a string path or array of paths to where mapping files are located (files or directories) **REQUIRED if no driver**\n    * `driver` an already created \\Jgut\\Slim\\Routing\\Mapping\\Driver\\DriverInterface object **REQUIRED if no type AND path**\n* `trailingSlash` boolean, indicates whether to append a trailing slash on route pattern (true) or remove it completely (false), by default. False by default\n* `placeholderAliases` array of additional placeholder aliases. There are some default aliases already available:\n  * numeric =\u003e `\\d+`\n  * alpha =\u003e `[a-zA-Z]+`\n  * alnum =\u003e `[a-zA-Z0-9]+`\n  * slug -\u003e `[a-zA-Z0-9-]+`\n  * uuid -\u003e `[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}`\n  * mongoid -\u003e `[0-9a-f]{24}`\n  * any =\u003e `[^}]+`\n* `namingStrategy`, instance of \\Jgut\\Slim\\Routing\\Naming\\Strategy (\\Jgut\\Slim\\Routing\\Naming\\SnakeCase by default)\n\nIn the case of Attributes mapping all classes in source paths will be traversed in search of routing definitions\n\n## Response handling\n\nEver wondered why you should encode output or call template renderer in every single route? or even why respond with a ResponseInterface object in the end?\n\n```php\n$app-\u003eget('/hello/{name}', function (ServerRequestInterface $request, ResponseInterface $response, array $args): ResponseInterface {\n    return $this-\u003eview-\u003erender(\n        $response,\n        'greet.html',\n        [\n            'name' =\u003e $args['name']\n        ]\n    );\n})-\u003esetName('greet');\n```\n\nRoute callbacks normally respond with a `Psr\\Message\\ResponseInterface` object, but thanks to slim-routing they can now respond with a string, null or even better with a more intent expressive ResponseType object that will be handled afterward\n\n```php\n$app-\u003eget(\n    '/hello/{name}', \n    fn (array $args): string =\u003e 'Hello ' . $args['name'],\n)-\u003esetName('greet');\n```\n\nOf course normal ResponseInterface responses from route callback will be treated as usual\n\n### ResponseType aware invocation strategies\n\nFor the new response handling to work you need to register a new invocation strategy provided by this library, there are four strategies provided out of the box that plainly mimic the ones provided by Slim\n\n* `Jgut\\Slim\\Routing\\Strategy\\RequestHandler`\n* `Jgut\\Slim\\Routing\\Strategy\\RequestResponse`\n* `Jgut\\Slim\\Routing\\Strategy\\RequestResponseArgs`\n* `Jgut\\Slim\\Routing\\Strategy\\RequestResponseNamedArgs`\n\n### Response type\n\nResponse types are Value Objects with the needed data to later produce a ResponseInterface object. This leaves the presentation logic out of routes allowing for cleaner routes and easy presentation logic reuse\n\n```php\nuse Jgut\\Slim\\Routing\\Response\\ResponseType;\nuse Jgut\\Slim\\Routing\\Response\\ViewResponse;\nuse Psr\\Http\\Message\\ServerRequestInterface;\nuse Psr\\Http\\Message\\ResponseInterface;\n\n$app-\u003eget(\n    '/hello/{name}', \n    fn (ServerRequestInterface $request, ResponseInterface $response, array $args): ResponseType \n        =\u003e new ViewResponse('greet.html', ['name' =\u003e $args['name']], $request, $response),\n)-\u003esetName('greet');\n```\n\nIf a route returns an instance of `\\Jgut\\Slim\\Routing\\Response\\ResponseType` it will be passed to the corresponding handler according to configuration\n\nThere are three response types already provided:\n\n* `RedirectResponse` Slim's route aware redirection, can redirect to a Slim route or an external location\n* `PayloadResponse` stores simple payload data to be later transformed for example into JSON or XML\n* `ViewResponse` keeps agnostic template parameters, so they can be rendered in a handler\n\nYou can easily create your own.\n\n### Response type handler\n\nMapped on invocation strategy, a response handler will be responsible for returning a `Psr\\Message\\ResponseInterface` from the received `\\Jgut\\Slim\\Routing\\Response\\ResponseType`\n\nTypically, they will agglutinate presentation logic: how to represent the data contained in the response type, such as transform it into JSON, XML, etc. or render it with a template engine such as Twig\n\nRegister response type handlers on invocation strategy creation or\n\n```php\nuse Jgut\\Slim\\Routing\\Response\\PayloadResponse;\nuse Jgut\\Slim\\Routing\\Response\\Handler\\JsonResponseHandler;\n\n$invocationStrategy-\u003esetResponseHandler(PayloadResponse::class, JsonResponseHandler::class);\n```\n\nProvided response types handlers:\n\n* `RedirectResponseHandler` receives a RedirectResponse type and returns the corresponding PSR7 redirect response\n* `JsonResponseHandler` receives a PayloadResponse type and returns a PSR7 JSON response\n* `XmlResponseHandler` receives a PayloadResponse type and returns a PSR7 XML response (requires [spatie/array-to-xml](https://github.com/spatie/array-to-xml))\n* `TwigViewResponseHandler` receives a generic ViewResponse type and returns a template rendered PSR7 response thanks to [slim/twig-view](https://github.com/slimphp/Twig-View)\n\nYou can create your own response type handlers to compose specifically formatted response (JSON:API, ...) or use another template engines (Plates, Blade, ...), or craft any other response\n\n## Parameter transformation\n\nRoute parameters can be transformed before arriving to route callable. The most common use of this feature would be to transform IDs into actual object/entity used in the callable\n\nTo achieve this you need to provide a `\\Jgut\\Slim\\Routing\\Transformer\\ParameterTransformer` instance\n\nFor example, you would want to transform parameters into Doctrine entities\n\n```php\nuse Jgut\\Slim\\Routing\\Transformer\\ParameterTransformer;\nuse Slim\\Exception\\HttpNotFoundException;\n\nfinal class UserEntityTransformer implements ParameterTransformer\n{\n    public function __construct(\n        private EntityManager $entityManager,\n    ) {}\n\n    protected function supports(string $parameter, string $type) : bool\n    {\n        return $parameter === 'user' \u0026\u0026 $type === UserEntity::class;\n    }\n\n    protected function transform(string $parameter, string $type, mixed $value): mixed\n    {\n        $user = $this-\u003eentityManager-\u003egetRepository($type)-\u003efind($value);\n        if ($user === null) {\n            throw new HttpNotFoundException('User not found');\n        }\n\n        return $user;\n    }\n}\n```\n\n_Mind that a single transformer can transform one or several parameters_\n\n## Console commands\n\n```php\nuse Symfony\\Component\\Console\\Application;\nuse Jgut\\Slim\\Routing\\Console\\ListCommand;\nuse Jgut\\Slim\\Routing\\Console\\MatchCommand;\n\n/** @var \\Slim\\App $app */\n$routeResolver = $app-\u003egetRouteResolver();\n\n$cli = new Application('Slim CLI');\n$cli-\u003eadd(new ListCommand($routeResolver));\n$cli-\u003eadd(new MatchCommand($routeResolver));\n\n$app-\u003erun();\n```\n\n### List routing\n\nList defined routes supporting searching and sorting \n\n```bash\nphp -f cli.php slim:routing:list --help\n```\n\n### Match routing\n\nMatch routes with Slim's route resolver, so results will be exactly the same as through HTTP requests\n\n```bash\nphp -f cli.php slim:routing:match --help\n```\n\n## Route mapping\n\nRoutes can be defined in two basic ways: by writing them down in definition files of various formats or directly in classes with Attributes\n\n### Attributes\n\n#### Group (Class level)\n\nDefines a group in which routes may reside. It is not mandatory but useful when you want to do route grouping or apply middleware to several routes at the same time\n\n```php\nuse Jgut\\Slim\\Routing\\Mapping\\Attribute\\Group;\n\n#[Group(\n    prefix: 'routePrefix',\n    parent: Area::class,\n    pattern: 'section/{section}',\n    placeholders: ['section': 'alpha'],\n    arguments: ['scope' =\u003e 'public'],\n )]\nclass Section {}\n```\n\n* `prefix`, optional, prefix to be prepended to route names\n* `parent`, optional, references a parent group name\n* `pattern`, optional, path pattern, prepended to route patterns. Do not use placeholders in the pattern\n* `placeholders`, optional, array of regex/alias for pattern placeholders,\n* `arguments`, optional, array of arguments to attach to final route \n\n#### Route (Method level)\n\nDefines the final routes added to Slim\n\n```php\nuse Jgut\\Slim\\Routing\\Mapping\\Attribute\\Route;\n\nclass Section\n{\n    #[Route(\n        name: 'routeName',\n        methods: ['GET', 'POST'],\n        pattern: 'user/{user}',\n        placeholders: ['user': 'alnum'],\n        arguments: ['scope': 'admin.read']\n        xmlHttpRequest: true,\n        priority: -10,\n    )]\n    public function userAction() {}\n}\n```\n\n* `name`, optional, route name so it can be referenced in Slim\n* `methods`, optional, one or a list of accepted HTTP route methods. ºANY\" is a special method that transforms to `[GET, POST, PUT, PATCH, DELETE]`, if ANY is used no other method is allowed in the list (defaults to GET)\n* `pattern`, optional, path pattern (defaults to '/'). Do not use placeholders in the pattern\n* `placeholders`, optional, array of regex/alias for pattern placeholders\n* `arguments`, optional, array of arguments to attach to the route\n* `xmlHttpRequest`, request should be AJAX, false by default\n* `priority`, optional, integer for ordering route registration. The order is global among all loaded routes. Negative routes get loaded first (defaults to 0)\n\n#### Middleware (Class and Method level)\n\nDefines middleware to apply. Multiple Attributes can be added\n\n```php\nuse Jgut\\Slim\\Routing\\Mapping\\Attribute\\Group;\nuse Jgut\\Slim\\Routing\\Mapping\\Attribute\\Middleware;\nuse Jgut\\Slim\\Routing\\Mapping\\Attribute\\Route;\nuse Psr\\Http\\Message\\ResponseInterface;\n\n#[Group()]\n#[Middleware(GroupMiddleware::class)]\n#[Middleware(AdditionalMiddleware::class)]\nclass Section\n{\n    #[Route(methods: ['GET'], pattern: 'user')]\n    #[Middleware(RouteMiddleware::class)]\n    public function userAction(): ResponseInterface {}\n}\n```\n\n* `middleware`, MiddlewareInterface class that will be extracted from the container\n\n#### Transformer (Class and Method level)\n\nDefines route transformers. Multiple Attributes can be added\n\n```php\nuse Jgut\\Slim\\Routing\\Mapping\\Attribute\\Group;\nuse Jgut\\Slim\\Routing\\Mapping\\Attribute\\Route;\nuse Jgut\\Slim\\Routing\\Mapping\\Attribute\\Transformer;\nuse Psr\\Http\\Message\\ResponseInterface;\n\n#[Group()]\n#[Transformer(transformer: SectionEntityTransfomer::class)]\nclass Section\n{\n    #[Route(methods: ['GET'], pattern: 'user/{user}')]\n    #[Transformer(\n        transformer: UserEntityTransfomer::class),\n        parameters: ['user': User::class],\n    )]\n    public function userAction($request, $response, $user): ResponseInterface {}\n}\n```\n\n* `transformer`, ParameterTransformer class that will be extracted from the container\n* `parameters`, optional, array of definitions of parameters\n\n### Definition files\n\n##### PHP\n\n```php\nreturn [\n  [\n    // Group\n    'prefix' =\u003e 'prefix',\n    'pattern' =\u003e 'group-pattern',\n    'placeholders' =\u003e [\n        'group-placeholder' =\u003e 'type',\n    ],\n    'arguments' =\u003e [\n        'group-argument' =\u003e 'value',\n    ],\n    'parameters' =\u003e [\n        'group-parameters' =\u003e 'type',\n    ],\n    'transformers' =\u003e ['group-transformer'],\n    'middlewares' =\u003e ['group-middleware'],\n    'routes' =\u003e [\n      [\n        // Route\n        'name' =\u003e 'routeName',\n        'xmlHttpRequest' =\u003e true,\n        'methods' =\u003e ['GET', 'POST'],\n        'priority' =\u003e 0,\n        'pattern' =\u003e 'route-pattern',\n        'placeholders' =\u003e [\n            'route-placeholder' =\u003e 'type',\n        ],\n        'parameters' =\u003e [\n            'route-parameters' =\u003e 'type',\n        ],\n        'transformers' =\u003e ['route-transformer'],\n        'arguments' =\u003e [\n            'route-argument' =\u003e 'value',\n        ],\n        'middlewares' =\u003e ['route-middleware'],\n        'invokable' =\u003e 'callable',\n      ],\n      [\n        // Subgroup\n        'pattern' =\u003e 'subgroup-pattern',\n        'placeholders' =\u003e [\n            'subgroup-placeholder' =\u003e 'type',\n        ],\n        'arguments' =\u003e [\n            'subgroup-argument' =\u003e 'value',\n        ],\n        'middlewares' =\u003e ['subgroup-middleware'],\n        'routes' =\u003e [\n          // Routes/groups ...\n        ],\n      ],\n      // Routes/groups ...\n    ],\n  ],\n  // Routes/groups ...\n];\n```\n\n##### XML\n\n```xml\n\u003c?xml version=\"1.0\" encoding=\"utf-8\"?\u003e\n\u003croot\u003e\n    \u003cgroup1 prefix=\"prefix\" pattern=\"group-pattern\"\u003e\n        \u003cplaceholders\u003e\n            \u003cgroup-placeholder1\u003etype\u003c/group-placeholder1\u003e\n        \u003c/placeholders\u003e\n        \u003carguments\u003e\n            \u003cgroup-argument1\u003evalue\u003c/group-argument1\u003e\n        \u003c/arguments\u003e\n        \u003cparameters\u003e\n          \u003cgroup-parameter1\u003etype\u003c/group-parameter1\u003e\n        \u003c/parameters\u003e\n        \u003ctransformers\u003e\n          \u003ctransformer1\u003egroup-transformer\u003c/transformer1\u003e\n        \u003c/transformers\u003e\n        \u003cmiddlewares\u003e\n            \u003cmiddleware1\u003egroup-middleware\u003c/middleware1\u003e\n        \u003c/middlewares\u003e\n        \u003croutes\u003e\n            \u003croute1 name=\"routeName\" priority=\"0\" pattern=\"route-pattern\"\u003e\n                \u003cxmlHttpRequest\u003etrue\u003c/xmlHttpRequest\u003e\n                \u003cmethods\u003e\n                    \u003cmethod1\u003eGET\u003c/method1\u003e\n                    \u003cmethod2\u003ePOST\u003c/method2\u003e\n                \u003c/methods\u003e\n                \u003cplaceholders\u003e\n                    \u003croute-placeholder1\u003etype\u003c/route-placeholder1\u003e\n                \u003c/placeholders\u003e\n                \u003cparameters\u003e\n                    \u003croute-parameter1\u003etype\u003c/route-parameter1\u003e\n                \u003c/parameters\u003e\n                \u003ctransformers\u003e\n                     \u003ctransformer1\u003eroute-ransformer\u003c/transformer1\u003e\n                \u003c/transformers\u003e\n                \u003carguments\u003e\n                    \u003croute-argument1\u003evalue\u003c/route-argument1\u003e\n                \u003c/arguments\u003e\n                \u003cmiddlewares\u003e\n                    \u003cmiddleware1\u003eroute-middleware\u003c/middleware1\u003e\n                \u003c/middlewares\u003e\n                \u003cinvokable\u003ecallable\u003c/invokable\u003e\n            \u003c/route1\u003e\n            \u003csubgroup1 prefix=\"prefix\" pattern=\"subgroup-pattern\"\u003e\n                \u003cplaceholders\u003e\n                    \u003csubgroup-placeholder1\u003etype\u003c/subgroup-placeholder1\u003e\n                \u003c/placeholders\u003e\n                \u003cargument\u003e\n                    \u003csubgroup-argument1\u003evalue\u003c/subgroup-argument1\u003e\n                \u003c/argument\u003e\n                \u003cmiddlewares\u003e\n                    \u003cmiddleware1\u003esubgroup-middleware\u003c/middleware1\u003e\n                \u003c/middlewares\u003e\n                \u003croutes\u003e\n                    \u003c!-- Routes/groups... --\u003e\n                \u003c/routes\u003e\n            \u003c/subgroup1\u003e\n            \u003c!-- Routes/groups... --\u003e\n        \u003c/routes\u003e\n    \u003c/group1\u003e\n    \u003c!-- Routes/groups... --\u003e\n\u003c/root\u003e\n```\n\n##### JSON\n\n_Mind comments are not valid standard JSON_\n\n```json\n[\n  {\n    // Group\n    \"prefix\": \"prefix\",\n    \"pattern\": \"group-pattern\",\n    \"placeholders\": [{\n      \"group-placeholder\": \"type\"\n    }],\n    \"arguments\": [{\n      \"group-argument\": \"value\"\n    }],\n    \"parameters\": [{\n      \"group-parameter\": \"type\"\n    }],\n    \"transformers\": [\"group-transformer\"],\n    \"middlewares\": [\"group-middleware\"],\n    \"routes\": [\n      {\n        // Route\n        \"name\": \"routeName\",\n        \"xmlHttpRequest\": true,\n        \"methods\": [\"GET\", \"POST\"],\n        \"priority\": 0,\n        \"pattern\": \"route-pattern\",\n        \"placeholders\": [{\n          \"route-placeholder\": \"type\"\n        }],\n        \"parameters\": [{\n          \"route-parameter\": \"type\"\n        }],\n        \"transformers\": [\"route-transformer\"],\n        \"arguments\": [{\n          \"route-argument\": \"value\"\n        }],\n        \"middlewares\": [\"route-middleware\"],\n        \"invokable\": \"callable\"\n      },\n      {\n        // Subgroup\n        \"pattern\": \"subgroup-pattern\",\n        \"placeholders\": [{\n          \"subgroup-placeholder\": \"type\"\n        }],\n        \"arguments\": [{\n          \"subgroup-argument\": \"value\"\n        }],\n        \"middlewares\": [\"subgroup-middleware\"],\n        \"routes\": [\n          // Routes/groups ...\n        ]\n      }\n      // Routes/groups ...\n    ]\n  }\n  // Routes/groups ...\n]\n```\n\n##### YAML\n\n```yaml\n# Group\n- prefix: prefix\n  pattern: group-pattern\n  placeholders: \n    - group-placeholder: type\n  arguments: \n    - group-argument: value\n  parameters:\n    - group-parameter: type\n  transformers: [group-ransformer]\n  middlewares: [group-middleware]\n  routes:\n    # Route\n    - name: routeName\n      xmlHttpRequest: true\n      methods: [GET, POST]\n      priority: 0\n      pattern: route-pattern\n      placeholders:\n        - route-placeholder: type\n      parameters:\n        - route-parameter: type\n      transformers: [route-ransformer]\n      arguments:\n        - route-argument: value\n      middlewares: [route-middleware]\n      invokable: callable\n    # Subgroup\n    - pattern: subgroup-pattern\n      placeholders: \n        - subgroup-placeholder: type\n      arguments: \n        - subgroup-argument: value\n      middlewares: [subgroup-middleware]\n      routes:\n        # Routes/groups ...\n    # Routes/groups ...\n# Routes/groups ...\n```\n\n#### Group\n\nDefines a group in which routes may reside\n\n* `routes`, array of routes and/or subgroups (this key identifies a group)\n* `prefix`, optional, prefix to be prepended to route names\n* `pattern`, optional, path pattern, prepended to route patterns. Do not use placeholders in the pattern\n* `placeholders`, optional, array of regex/alias for pattern placeholders,\n* `parameters`, optional, array of definitions of parameters, to be used in transformer\n* `transformers`, optional, array of ParameterTransformer class that will be extracted from the container\n* `arguments`, optional, array of arguments to attach to final route\n* `middlewares`, optional, array of MiddlewareInterface class to be added to all group routes\n\n#### Route\n\nDefines a route added to Slim\n\n* `invokable`, callable to be invoked on route match. Can be a container entry, class name or an array of [class, method]\n* `name`, optional, route name so it can be referenced in Slim\n* `pattern`, optional, path pattern (defaults to '/'). Do not use placeholders in the pattern\n* `placeholders`, optional, array of regex/alias for pattern placeholders\n* `xmlHttpRequest`, request should be AJAX, false by default\n* `methods`, optional, one or a list of accepted HTTP route methods. \"ANY\" is a special method that transforms to `[GET, POST, PUT, PATCH, DELETE]`, if ANY is used no other method is allowed (defaults to GET)\n* `parameters`, optional, array of definitions of parameters, to be used in transformer\n* `transformers`, optional, array of ParameterTransformer class that will be extracted from the container\n* `arguments`, optional, array of arguments to attach to the route\n* `middlewares`, optional, array of MiddlewareInterface class to be added to the route\n* `priority`, optional, integer for ordering route registration. The order is global among all loaded routes. Negative routes get loaded first (defaults to 0)\n\n### Annotations\n\n__Annotations are deprecated and will be removed eventually. Use Attribute mapping when possible__.\n\nYou need to require Doctrine's annotation package\n\n```\ncomposer require doctrine/annotations\n```\n\n#### Group (Class level)\n\nDefines a group in which routes may reside. It is not mandatory but useful when you want to do route grouping or apply middleware to several routes at the same time\n\n```php\nuse Jgut\\Slim\\Routing\\Mapping\\Annotation as JSR;\n\n/**\n * @JSR\\Group(\n *     prefix=\"routePrefix\",\n *     parent=Area::class,\n *     pattern=\"section/{section}\",\n *     placeholders={\"section\": \"[a-z]+\"},\n *     arguments={\"scope\": \"public\"}\n *     parameters={\"section\": \"\\Namespace\\To\\Section\"},\n *     transformers={\"\\Namespace\\To\\GroupTransformer\"}\n *     middleware={\"\\Namespace\\To\\GroupMiddleware\"}\n * )\n */\nclass Section {}\n```\n\n* `prefix`, optional, prefix to be prepended to route names\n* `parent`, optional, references a parent group name\n* `pattern`, optional, path pattern, prepended to route patterns\n* `placeholders`, optional, array of regex/alias for pattern placeholders,\n* `parameters`, optional, array of definitions of parameters, to be used in route transformer\n* `transformers`, optional, array of ParameterTransformer class that will be extracted from the container\n* `arguments`, optional, array of arguments to attach to final route\n* `middlewares`, optional, array of MiddlewareInterface class to be added to all group routes\n\n#### Route (Method level)\n\nDefines the final routes added to Slim\n\n```php\nuse Jgut\\Slim\\Routing\\Mapping\\Annotation as JSR;\n\nclass Section\n{\n    /**\n     * @JSR\\Route(\n     *     name=\"routeName\",\n     *     xmlHttpRequest=true,\n     *     methods={\"GET\", \"POST\"},\n     *     pattern=\"user/{user}\",\n     *     placeholders={\"user\": \"[a-z0-9]+\"},\n     *     arguments={\"scope\": \"admin.read\"}\n     *     parameters={\"user\": \"\\Namespace\\To\\User\"},\n     *     transformers={\"\\Namespace\\To\\RouteTransformer\"},\n     *     middleware={\"\\Namespace\\To\\RouteMiddleware\"},\n     *     priority=-10\n     * )\n     */\n    public function userAction() {}\n}\n```\n\n* `name`, optional, route name so it can be referenced in Slim\n* `pattern`, optional, path pattern (defaults to '/')\n* `placeholders`, optional, array of regex/alias for pattern placeholders\n* `xmlHttpRequest`, request should be AJAX, false by default\n* `methods`, optional, list of accepted HTTP route methods. \"ANY\" is a special method that transforms to `[GET, POST, PUT, PATCH, DELETE]`, if ANY is used no other method is allowed in the list (defaults to GET)\n* `parameters`, optional, array of definitions of parameters, to be used in transformer\n* `transformers`, optional, array of ParameterTransformer class that will be extracted from the container\n* `arguments`, optional, array of arguments to attach to the route\n* `middlewares`, optional, array of MiddlewareInterface class to be added to the route\n* `priority`, optional, integer for ordering route registration. The order is global among all loaded routes. Negative routes get loaded first (defaults to 0)\n\n## Route composition\n\nUsing grouping with juliangut/slim-routing is a little different to how default Slim's router works\n\nGroups are never really added to the router (in the sense you can add them in Slim with `$app-\u003egroup(...)`), instead, routes are a composition of definitions that makes the final route\n\n### Name\n\nFinal route name is composed of the concatenation of group prefixes followed by route name according to configured route _naming strategy_\n\n### Pattern\n\nResulting route pattern is composed of the concatenation of group patterns if any and finally route pattern\n\n### Placeholders\n\nResulting placeholders list is composed of all group placeholders if any and route placeholders\n\nIt is important to pay attention not to duplicate placeholder names in the resulting pattern as this can't be handled by FastRoute. Check group tree patterns for placeholder names\n\n### Arguments\n\nResulting route arguments is composed of all group arguments if any and route arguments\n\n### Middlewares\n\nResulting middlewares added to a route will be the result of combining group middleware and route middleware and are applied to the route in the following order, so that final middleware execution order will be the same as expected in any Slim app:\n\n* Firstly route middleware will be set to the route **in the order they are defined**\n* Then group middleware (if any) are to be set into the route **in the same order they are defined**\n* If group has a parent then parent's middleware are set **in the order they are defined**, and this goes up until no parent group is left\n\n### Transformers and parameters\n\nResulting transformers and parameters list are a combination of all group transformers and parameters if any and route transformers and parameters\n\nAs with placeholders, it is important to pay attention not to duplicate parameter names as child groups and routes will replace previous parameters\n\n## Migration from 2.x\n\n* Minimum PHP version is now 8.0\n* Minimum Slim version is now 4.7\n* PHP8 Attributes have been introduced for route mapping\n* ParameterTransformer methods and signatures have changed\n* AbstractTransformer has been removed, simply implement ParameterTransformer\n* Some internal methods have changed their signatures or have been made final\n\n##### Annotations\n\n__Annotations have been deprecated and its use is highly discouraged in favor of Attributes__\n\n* @Router Annotation optional and triggers an E_USER_DEPRECATED\n* @Group and @Route Annotations \"middleware\" parameter have changed to \"middlewares\" and accepts a list or a single middleware\n* @Route Annotation transformers now accepts a list or a single transformer\n* @Group Annotation now supports transformers as well\n\n## Contributing\n\nFound a bug or have a feature request? [Please open a new issue](https://github.com/juliangut/slim-routing/issues). Have a look at existing issues before.\n\nSee file [CONTRIBUTING.md](https://github.com/juliangut/slim-routing/blob/master/CONTRIBUTING.md)\n\n## License\n\nSee file [LICENSE](https://github.com/juliangut/slim-routing/blob/master/LICENSE) included with the source code for a copy of the license terms.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjuliangut%2Fslim-routing","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjuliangut%2Fslim-routing","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjuliangut%2Fslim-routing/lists"}