Skip to content

Commit

Permalink
Merge pull request #24 from codeburnerframework/dev
Browse files Browse the repository at this point in the history
merge support to named routes
  • Loading branch information
Alex Rohleder committed Feb 25, 2016
2 parents 022ed45 + e97db1f commit 2b0e803
Show file tree
Hide file tree
Showing 10 changed files with 241 additions and 7 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ language: php
php:
- 5.5
- 5.6
- hhvm
- 7.0

before_script:
- composer self-update
Expand Down
43 changes: 42 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
# Codeburner Router

[![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg)](LICENSE)
[![Latest Stable Version](https://poser.pugx.org/codeburner/router/v/stable)](https://packagist.org/packages/codeburner/router)
[![Build Status](https://travis-ci.org/codeburnerframework/router.svg?branch=master)](https://travis-ci.org/codeburnerframework/router)
[![Code Coverage](https://scrutinizer-ci.com/g/codeburnerframework/routing/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/codeburnerframework/routing/?branch=master)
[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/codeburnerframework/routing/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/codeburnerframework/routing/?branch=master)
[![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg)](LICENSE)

[![SensioLabsInsight](https://insight.sensiolabs.com/projects/d96c4a67-982b-4e16-a24d-7b490bf11bc7/big.png)](https://insight.sensiolabs.com/projects/d96c4a67-982b-4e16-a24d-7b490bf11bc7)

Expand Down Expand Up @@ -46,6 +47,7 @@ $ composer require codeburner/router --save
- [PSR7](#psr7)
- [Default Arguments](#default-arguments)
- [Container Integration](#container-integration)
- [Names](#names)
- [Metadata](#metadata)
- [Collector](#collector)
- [Groups](#groups)
Expand All @@ -57,6 +59,7 @@ $ composer require codeburner/router --save
- [Nesting Limit](#nesting-limit)
- [Shallow Resources](#shallow-resources)
- [Adding More Actions](#adding-more-actions)
- [Resources Route Names](#resources-route-names)
- [Translated Patterns](#translated-patterns)
- [Controllers](#controllers)
- [Annotated Definition](#annotated-definition)
Expand Down Expand Up @@ -247,6 +250,25 @@ $route->call(function ($class) use ($container) {
```


### Names

All the routes allow you to apply names to then, this names can be used to find a route in the `Collector` or to generate links with `Path`'s method `to(string name, array args = [])`. E.g.

```php
// ...
// The Path class will create several links for us, just give they new object a instance of the collector.
$path = new Codeburner\Router\Path($collector);
// ...
// Setting the name of route to blog.article
$collector->get("/blog/{article:slug+}", "blog::show")->setName("blog.article");
// ...
// this will print an anchor tag with "/blog/my-first-article" in href.
echo "<a href='", $path->to("blog.article", ["article" => "my-first-article"]), "'>My First Article</a>";
```

> **NOTE:** For best practice use the dot for delimiting namespaces in your route names, so you can group and find they names easily. The [resource](#resources-route-names) collector adopt this concept.

### Metadata

Sometimes you want to delegate more information to a route, for post match filters or action execution strategies. For persist data that will not be passed to action but used in somewhere before the execution use the `setMetadata(string key, mixed value)` method.
Expand Down Expand Up @@ -400,6 +422,25 @@ $collector->resource("PhotosResource")->member(
);
```


#### Resources Route Names

All the routes in resource receive a [name](#names) that will be composed by the resource name or prefix, a dot and the action name. e.g.

```php
class PhotosResource {
public function index() {

}
}

$collector->resource("PhotosResource")->only("index");
$collector->resource("PhotosResource", ["as" => "picture"])->only("index");

echo $path->to("photos.index"), "<br>", $path->to("picture.index");
```


#### Translated Patterns

If you prefer to translate the patterns generated by the resource, just define an `translate` option that receives an array with one or the two keys, `new` and `edit`.
Expand Down
35 changes: 33 additions & 2 deletions src/Collector.php
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ class Collector
* dimension is indexed by an http method and hold an array indexed with the patterns
* and holding the route. ex. [METHOD => [PATTERN => ROUTE]]
*
* @var array
* @var Route[][]
*/

protected $statics = [];
Expand All @@ -63,11 +63,17 @@ class Collector
* The dynamic routes have parameters and are stored in a hashtable that every cell have
* an array with route patterns as indexes and routes as values. ex. [INDEX => [PATTERN => ROUTE]]
*
* @var array
* @var Route[][]
*/

protected $dynamics = [];

/**
* @var Route[]
*/

protected $named = [];

/**
* The pattern parser instance.
*
Expand Down Expand Up @@ -200,6 +206,18 @@ public function forget($method, $pattern)
} else unset($this->dynamics[$this->getDynamicIndex($method, $pattern)][$pattern]);
}

/**
* @param string $name
* @return Route|false
*/

public function findNamedRoute($name)
{
if (!isset($this->named[$name])) {
return false;
} else return $this->named[$name];
}

/**
* @param string $method
* @param string $pattern
Expand Down Expand Up @@ -260,6 +278,19 @@ protected function getValidMethod($method)
return $method;
}

/**
* @param string $name
* @param Route $route
*
* @return self
*/

public function setRouteName($name, Route $route)
{
$this->named[$name] = $route;
return $this;
}

/**
* @return string[]
*/
Expand Down
17 changes: 16 additions & 1 deletion src/Collectors/ResourceCollectorTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,14 @@
trait ResourceCollectorTrait
{

/**
* @param string $method
* @param string $pattern
* @param string $action
*
* @return \Codeburner\Router\Group
*/

abstract public function set($method, $pattern, $action);

/**
Expand Down Expand Up @@ -65,7 +73,14 @@ public function resource($controller, array $options = array())
$resource = new RouteResource;

foreach ($actions as $action => $map) {
$resource->set($this->set($map[0], $this->getResourcePath($action, $map[1], $name, $options), [$controller, $action]));
$resource->set(
$this->set(
$map[0],
$this->getResourcePath($action, $map[1], $name, $options),
[$controller, $action]
)
->setName("$name.$action")
);
}

return $resource;
Expand Down
17 changes: 17 additions & 0 deletions src/Group.php
Original file line number Diff line number Diff line change
Expand Up @@ -244,4 +244,21 @@ public function setConstraint($name, $regex)
return $this;
}

/**
* Set a name to a Route.
*
* @param string $name
* @return self
*/

public function setName($name)
{
if (count($this->routes) > 1) {
throw new \LogicException("You cannot set the same name to several routes.");
}

$this->routes[0]->setName($name);
return $this;
}

}
3 changes: 1 addition & 2 deletions src/Parser.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,7 @@
use Codeburner\Router\Exceptions\BadRouteException;

/**
* Representation of a group of several routes with same
* controller and respecting the resourceful actions.
* All the parsing route paths logic are maintained by this class.
*
* @author Alex Rohleder <contato@alexrohleder.com.br>
*/
Expand Down
68 changes: 68 additions & 0 deletions src/Path.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<?php

/**
* Codeburner Framework.
*
* @author Alex Rohleder <contato@alexrohleder.com.br>
* @copyright 2016 Alex Rohleder
* @license http://opensource.org/licenses/MIT
*/

namespace Codeburner\Router;

/**
* Create paths based on registered routes.
*
* @author Alex Rohleder <contato@alexrohleder.com.br>
*/

class Path
{

/**
* @var Collector
*/

protected $collector;

/**
* Link constructor.
*
* @param Collector $collector
*/

public function __construct(Collector $collector)
{
$this->collector = $collector;
}

/**
* Generate a path to a route named by $name.
*
* @param string $name
* @param array $args
*
* @throws \BadMethodCallException
* @return string
*/

public function to($name, array $args = [])
{
$route = $this->collector->findNamedRoute($name);
$parser = $this->collector->getParser();
$pattern = $route->getPattern();

preg_match_all("~" . $parser::DYNAMIC_REGEX . "~x", $pattern, $matches, PREG_SET_ORDER);

foreach ((array) $matches as $key => $match) {
if (!isset($args[$match[1]])) {
throw new \BadMethodCallException("Missing argument '{$match[1]}' on creation of link for '{$name}' route.");
}

$pattern = str_replace($match[0], $args[$match[1]], $pattern);
}

return $pattern;
}

}
24 changes: 24 additions & 0 deletions src/Route.php
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,14 @@ class Route

protected $matcher;

/**
* Route name, or alias.
*
* @var string $name
*/

protected $name;

/**
* The function used to create controllers from name.
*
Expand Down Expand Up @@ -415,6 +423,15 @@ public function getMatcher()
return $this->matcher;
}

/**
* @return string
*/

public function getName()
{
return $this->name;
}

/**
* Verify if a Route have already been blocked.
*
Expand Down Expand Up @@ -591,6 +608,13 @@ public function setMatcher(Matcher $matcher)
return $this;
}

public function setName($name)
{
$this->name = $name;
$this->collector->setRouteName($name, $this);
return $this;
}

/**
* Set a constraint to a token in the route pattern.
*
Expand Down
25 changes: 25 additions & 0 deletions tests/BaseCollectorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

use Codeburner\Router\Collector;
use Codeburner\Router\Matcher;
use Codeburner\Router\Path;

class BaseCollectorTest extends PHPUnit_Framework_TestCase
{
Expand Down Expand Up @@ -365,4 +366,28 @@ public function test_EnsureAssignments()
$this->assertEquals($this->matcher, $route->getMatcher());
}

public function test_NamedStaticRoutes()
{
$this->collector->get("/", "")->setName("test");
$link = new Path($this->collector);
$this->assertTrue("/" === $link->to("test"));
}

public function test_NamedDynamicRoutes()
{
$this->collector->get("/myresource/{myresource_id:int+}/resource/{id:int+}", "")->setName("test");
$link = new Path($this->collector);
$this->assertTrue("/myresource/2/resource/3" === $link->to("test", ["myresource_id" => 2, "id" => 3]));
}

public function test_GroupNamedRouteException()
{
$this->setExpectedException("LogicException");

$this->collector->group([
$this->collector->get("/", ""),
$this->collector->get("/1", "")
])->setName("test");
}

}
14 changes: 14 additions & 0 deletions tests/ResourceCollectorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

use Codeburner\Router\Collector;
use Codeburner\Router\Matcher;
use Codeburner\Router\Path;

class ResourceCollectorTest extends PHPUnit_Framework_TestCase
{
Expand Down Expand Up @@ -129,6 +130,19 @@ public function test_Shallow()
$this->assertInstanceOf('Codeburner\Router\Route', $this->matcher->match('post', '/fst/1/snd/2/edit'));
}

public function test_RouteNames()
{
$this->collector->resource('Resource');
$this->collector->resource('Resource', ['as' => 'fst']);
$link = new Path($this->collector);

foreach (['resource', 'fst'] as $prefix) {
foreach ($this->actions as $action => $map) {
$this->assertEquals(str_replace("{name}", $prefix, $map[1]), $link->to("$prefix.$action", ["id" => "123"]));
}
}
}

private function doTestActions($actions)
{
foreach ($this->actions as $action => $inf) {
Expand Down

0 comments on commit 2b0e803

Please sign in to comment.