Commit 55a4c0b1 by Qiang Xue

Merge pull request #461 from cebe/rest-urlmanager

[READY] RESTful routing syntax for UrlManager
parents 3680a478 e94cc6bb
...@@ -43,6 +43,31 @@ class UrlManager extends Component ...@@ -43,6 +43,31 @@ class UrlManager extends Component
* array, one can use the key to represent the pattern and the value the corresponding route. * array, one can use the key to represent the pattern and the value the corresponding route.
* For example, `'post/<id:\d+>' => 'post/view'`. * For example, `'post/<id:\d+>' => 'post/view'`.
* *
* For RESTful routing the mentioned shortcut format also allows you to specify the
* [[UrlRule::verb|HTTP verb]] that the rule should apply for.
* You can do that by prepending it to the pattern, separated by space.
* For example, `'PUT post/<id:\d+>' => 'post/update'`.
* You may specify multiple verbs by separating them with comma
* like this: `'POST,PUT post/index' => 'post/create'`.
* The supported verbs in the shortcut format are: GET, HEAD, POST, PUT and DELETE.
* Note that [[UrlRule::mode|mode]] will be set to PARSING_ONLY when specifying verb in this way
* so you normally would not specify a verb for normal GET request.
*
* Here is an example configuration for RESTful CRUD controller:
*
* ~~~php
* array(
* 'dashboard' => 'site/index',
*
* 'POST <controller:\w+>s' => '<controller>/create',
* '<controller:\w+>s' => '<controller>/index',
*
* 'PUT <controller:\w+>/<id:\d+>' => '<controller>/update',
* 'DELETE <controller:\w+>/<id:\d+>' => '<controller>/delete',
* '<controller:\w+>/<id:\d+>' => '<controller>/view',
* );
* ~~~
*
* Note that if you modify this property after the UrlManager object is created, make sure * Note that if you modify this property after the UrlManager object is created, make sure
* you populate the array with rule objects instead of rule configurations. * you populate the array with rule objects instead of rule configurations.
*/ */
...@@ -115,9 +140,14 @@ class UrlManager extends Component ...@@ -115,9 +140,14 @@ class UrlManager extends Component
foreach ($this->rules as $key => $rule) { foreach ($this->rules as $key => $rule) {
if (!is_array($rule)) { if (!is_array($rule)) {
$rule = array( $rule = array(
'pattern' => $key,
'route' => $rule, 'route' => $rule,
); );
if (preg_match('/^((?:(GET|HEAD|POST|PUT|DELETE),)*(GET|HEAD|POST|PUT|DELETE))\s+(.*)$/', $key, $matches)) {
$rule['verb'] = explode(',', $matches[1]);
$rule['mode'] = UrlRule::PARSING_ONLY;
$key = $matches[4];
}
$rule['pattern'] = $key;
} }
$rules[] = Yii::createObject(array_merge($this->ruleConfig, $rule)); $rules[] = Yii::createObject(array_merge($this->ruleConfig, $rule));
} }
......
...@@ -38,14 +38,13 @@ abstract class TestCase extends \yii\test\TestCase ...@@ -38,14 +38,13 @@ abstract class TestCase extends \yii\test\TestCase
* The application will be destroyed on tearDown() automatically. * The application will be destroyed on tearDown() automatically.
* @param array $config The application configuration, if needed * @param array $config The application configuration, if needed
*/ */
protected function mockApplication($config=array()) protected function mockApplication($config = array(), $appClass = '\yii\console\Application')
{ {
static $defaultConfig = array( static $defaultConfig = array(
'id' => 'testapp', 'id' => 'testapp',
'basePath' => __DIR__, 'basePath' => __DIR__,
); );
$appClass = $this->getParam( 'appClass', '\yii\web\Application' );
new $appClass(array_merge($defaultConfig,$config)); new $appClass(array_merge($defaultConfig,$config));
} }
......
<?php <?php
return array( return array(
//'appClass' => '\yii\web\Application',
'appClass' => '\yii\console\Application',
'databases' => array( 'databases' => array(
'mysql' => array( 'mysql' => array(
'dsn' => 'mysql:host=127.0.0.1;dbname=yiitest', 'dsn' => 'mysql:host=127.0.0.1;dbname=yiitest',
......
...@@ -248,4 +248,58 @@ class UrlManagerTest extends TestCase ...@@ -248,4 +248,58 @@ class UrlManagerTest extends TestCase
$result = $manager->parseRequest($request); $result = $manager->parseRequest($request);
$this->assertFalse($result); $this->assertFalse($result);
} }
public function testParseRESTRequest()
{
$manager = new UrlManager(array(
'cache' => null,
));
$request = new Request;
// pretty URL rules
$manager = new UrlManager(array(
'enablePrettyUrl' => true,
'cache' => null,
'rules' => array(
'PUT,POST post/<id>/<title>' => 'post/create',
'DELETE post/<id>' => 'post/delete',
'post/<id>/<title>' => 'post/view',
'POST/GET' => 'post/get',
),
));
// matching pathinfo GET request
$_SERVER['REQUEST_METHOD'] = 'GET';
$request->pathInfo = 'post/123/this+is+sample';
$result = $manager->parseRequest($request);
$this->assertEquals(array('post/view', array('id' => '123', 'title' => 'this+is+sample')), $result);
// matching pathinfo PUT/POST request
$_SERVER['REQUEST_METHOD'] = 'PUT';
$request->pathInfo = 'post/123/this+is+sample';
$result = $manager->parseRequest($request);
$this->assertEquals(array('post/create', array('id' => '123', 'title' => 'this+is+sample')), $result);
$_SERVER['REQUEST_METHOD'] = 'POST';
$request->pathInfo = 'post/123/this+is+sample';
$result = $manager->parseRequest($request);
$this->assertEquals(array('post/create', array('id' => '123', 'title' => 'this+is+sample')), $result);
// no wrong matching
$_SERVER['REQUEST_METHOD'] = 'POST';
$request->pathInfo = 'POST/GET';
$result = $manager->parseRequest($request);
$this->assertEquals(array('post/get', array()), $result);
// createUrl should ignore REST rules
$this->mockApplication(array(
'components' => array(
'request' => array(
'hostInfo' => 'http://localhost/',
'baseUrl' => '/app'
)
)
), \yii\web\Application::className());
$this->assertEquals('/app/post/delete?id=123', $manager->createUrl('post/delete', array('id' => 123)));
$this->destroyApplication();
unset($_SERVER['REQUEST_METHOD']);
}
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment