From 8a323795379a918c0931e518c4e0f5dd5f29d4db Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Fri, 28 Feb 2014 16:14:22 +0100 Subject: [PATCH 1/8] fixed test break --- tests/unit/extensions/redis/ActiveRecordTest.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/unit/extensions/redis/ActiveRecordTest.php b/tests/unit/extensions/redis/ActiveRecordTest.php index 5a2102f..a66ef87 100644 --- a/tests/unit/extensions/redis/ActiveRecordTest.php +++ b/tests/unit/extensions/redis/ActiveRecordTest.php @@ -33,13 +33,13 @@ class ActiveRecordTest extends RedisTestCase ActiveRecord::$db = $this->getConnection(); $customer = new Customer(); - $customer->setAttributes(['email' => 'user1@example.com', 'name' => 'user1', 'address' => 'address1', 'status' => 1], false); + $customer->setAttributes(['email' => 'user1@example.com', 'name' => 'user1', 'address' => 'address1', 'status' => 1, 'profile_id' => 1], false); $customer->save(false); $customer = new Customer(); - $customer->setAttributes(['email' => 'user2@example.com', 'name' => 'user2', 'address' => 'address2', 'status' => 1], false); + $customer->setAttributes(['email' => 'user2@example.com', 'name' => 'user2', 'address' => 'address2', 'status' => 1, 'profile_id' => null], false); $customer->save(false); $customer = new Customer(); - $customer->setAttributes(['email' => 'user3@example.com', 'name' => 'user3', 'address' => 'address3', 'status' => 2], false); + $customer->setAttributes(['email' => 'user3@example.com', 'name' => 'user3', 'address' => 'address3', 'status' => 2, 'profile_id' => 2], false); $customer->save(false); // INSERT INTO tbl_category (name) VALUES ('Books'); From 5feba5946efefd1fb344236a2c13208345d80add Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Fri, 28 Feb 2014 16:29:50 +0100 Subject: [PATCH 2/8] Added relational link tags to LinkPager widget for prev, next, first and last page. http://www.w3.org/TR/html401/struct/links.html#h-12.1.2 --- framework/CHANGELOG.md | 1 + framework/widgets/LinkPager.php | 14 ++++++++++++++ 2 files changed, 15 insertions(+) diff --git a/framework/CHANGELOG.md b/framework/CHANGELOG.md index dad25bb..85ae5e1 100644 --- a/framework/CHANGELOG.md +++ b/framework/CHANGELOG.md @@ -148,6 +148,7 @@ Yii Framework 2 Change Log - Enh: Added support for building SQLs with sub-queries (qiangxue) - Enh: Added `Pagination::getLinks()` (qiangxue) - Enh: Added support for reading page size from query parameters by `Pagination` (qiangxue) +- Enh: LinkPager will now register relational link tags in the html header for prev, next, first and last page (cebe) - Chg #1186: Changed `Sort` to use comma to separate multiple sort fields and use negative sign to indicate descending sort (qiangxue) - Chg #1519: `yii\web\User::loginRequired()` now returns the `Response` object instead of exiting the application (qiangxue) - Chg #1586: `QueryBuilder::buildLikeCondition()` will now escape special characters and use percentage characters by default (qiangxue) diff --git a/framework/widgets/LinkPager.php b/framework/widgets/LinkPager.php index 06d688a..93ca282 100644 --- a/framework/widgets/LinkPager.php +++ b/framework/widgets/LinkPager.php @@ -107,10 +107,24 @@ class LinkPager extends Widget */ public function run() { + $this->registerLinkTags(); echo $this->renderPageButtons(); } /** + * Registers relational link tags in the html header for prev, next, first and last page. + * These links are generated using [[yii\data\Pagination::getLinks()]]. + * @see http://www.w3.org/TR/html401/struct/links.html#h-12.1.2 + */ + protected function registerLinkTags() + { + $view = $this->getView(); + foreach($this->pagination->getLinks() as $rel => $href) { + $view->registerLinkTag(['rel' => $rel, 'href' => $href], $rel); + } + } + + /** * Renders the page buttons. * @return string the rendering result */ From 4d5ba13cba81a7ff8ccfd2542be2c9f6fa138cba Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Fri, 28 Feb 2014 16:37:34 +0100 Subject: [PATCH 3/8] make registering of link tags optional in LinkPager --- framework/CHANGELOG.md | 2 +- framework/widgets/LinkPager.php | 11 ++++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/framework/CHANGELOG.md b/framework/CHANGELOG.md index 85ae5e1..5823558 100644 --- a/framework/CHANGELOG.md +++ b/framework/CHANGELOG.md @@ -148,7 +148,7 @@ Yii Framework 2 Change Log - Enh: Added support for building SQLs with sub-queries (qiangxue) - Enh: Added `Pagination::getLinks()` (qiangxue) - Enh: Added support for reading page size from query parameters by `Pagination` (qiangxue) -- Enh: LinkPager will now register relational link tags in the html header for prev, next, first and last page (cebe) +- Enh: LinkPager can now register relational link tags in the html header for prev, next, first and last page (cebe) - Chg #1186: Changed `Sort` to use comma to separate multiple sort fields and use negative sign to indicate descending sort (qiangxue) - Chg #1519: `yii\web\User::loginRequired()` now returns the `Response` object instead of exiting the application (qiangxue) - Chg #1586: `QueryBuilder::buildLikeCondition()` will now escape special characters and use percentage characters by default (qiangxue) diff --git a/framework/widgets/LinkPager.php b/framework/widgets/LinkPager.php index 93ca282..a2c905c 100644 --- a/framework/widgets/LinkPager.php +++ b/framework/widgets/LinkPager.php @@ -89,6 +89,13 @@ class LinkPager extends Widget * If this property is null, the "last" page button will not be displayed. */ public $lastPageLabel; + /** + * @var bool whether to register link tags in the HTML header for prev, next, first and last page. + * Defaults to `false` to avoid conflicts when multiple pagers are used on one page. + * @see http://www.w3.org/TR/html401/struct/links.html#h-12.1.2 + * @see registerLinkTags() + */ + public $registerLinkTags = false; /** @@ -107,7 +114,9 @@ class LinkPager extends Widget */ public function run() { - $this->registerLinkTags(); + if ($this->registerLinkTags) { + $this->registerLinkTags(); + } echo $this->renderPageButtons(); } From 8a2903de03b58a26c6d9fae47a4c8373c25df9fe Mon Sep 17 00:00:00 2001 From: Alexander Makarov Date: Fri, 28 Feb 2014 21:13:30 +0400 Subject: [PATCH 4/8] Added verb filter to advanced app logout --- apps/advanced/backend/controllers/SiteController.php | 7 +++++++ apps/advanced/frontend/controllers/SiteController.php | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/apps/advanced/backend/controllers/SiteController.php b/apps/advanced/backend/controllers/SiteController.php index baa8dbe..60d3c1b 100644 --- a/apps/advanced/backend/controllers/SiteController.php +++ b/apps/advanced/backend/controllers/SiteController.php @@ -5,6 +5,7 @@ use Yii; use yii\web\AccessControl; use yii\web\Controller; use common\models\LoginForm; +use yii\web\VerbFilter; /** * Site controller @@ -31,6 +32,12 @@ class SiteController extends Controller ], ], ], + 'verbs' => [ + 'class' => VerbFilter::className(), + 'actions' => [ + 'logout' => ['post'], + ], + ], ]; } diff --git a/apps/advanced/frontend/controllers/SiteController.php b/apps/advanced/frontend/controllers/SiteController.php index 6816daf..a45c837 100644 --- a/apps/advanced/frontend/controllers/SiteController.php +++ b/apps/advanced/frontend/controllers/SiteController.php @@ -10,6 +10,7 @@ use yii\base\InvalidParamException; use yii\web\BadRequestHttpException; use yii\web\Controller; use Yii; +use yii\web\VerbFilter; /** * Site controller @@ -38,6 +39,12 @@ class SiteController extends Controller ], ], ], + 'verbs' => [ + 'class' => VerbFilter::className(), + 'actions' => [ + 'logout' => ['post'], + ], + ], ]; } From ebe972f36d1e31b36e8e580224260e45524723e8 Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Fri, 28 Feb 2014 17:52:09 +0100 Subject: [PATCH 5/8] fixed links in elasticsearch README --- extensions/apidoc/models/PropertyDoc.php | 2 +- .../apidoc/templates/html/views/eventDetails.php | 2 +- .../apidoc/templates/html/views/methodDetails.php | 4 +-- .../templates/html/views/propertyDetails.php | 2 +- extensions/elasticsearch/README.md | 30 ++++++++++++---------- 5 files changed, 22 insertions(+), 18 deletions(-) diff --git a/extensions/apidoc/models/PropertyDoc.php b/extensions/apidoc/models/PropertyDoc.php index 85c482c..fc7d99d 100644 --- a/extensions/apidoc/models/PropertyDoc.php +++ b/extensions/apidoc/models/PropertyDoc.php @@ -69,7 +69,7 @@ class PropertyDoc extends BaseDoc $this->types = $tag->getTypes(); $this->description = ucfirst($tag->getDescription()); if (($pos = strpos($this->description, '.')) !== false) { - $this->shortDescription = substr($this->description, 0, $pos); + $this->shortDescription = substr($this->description, 0, $pos + 1); } else { $this->shortDescription = $this->description; } diff --git a/extensions/apidoc/templates/html/views/eventDetails.php b/extensions/apidoc/templates/html/views/eventDetails.php index dd4293e..15a6751 100644 --- a/extensions/apidoc/templates/html/views/eventDetails.php +++ b/extensions/apidoc/templates/html/views/eventDetails.php @@ -32,7 +32,7 @@ ArrayHelper::multisort($events, 'name'); trigger->signature; ?> */ ?> -

description, $type); ?>

+ description, $type); ?> render('seeAlso', ['object' => $event]); ?> diff --git a/extensions/apidoc/templates/html/views/methodDetails.php b/extensions/apidoc/templates/html/views/methodDetails.php index a3f9ff9..79cb959 100644 --- a/extensions/apidoc/templates/html/views/methodDetails.php +++ b/extensions/apidoc/templates/html/views/methodDetails.php @@ -62,8 +62,8 @@ ArrayHelper::multisort($methods, 'name'); renderPartial('sourceCode',array('object'=>$method)); ?> -

shortDescription, $type, true) ?>

-

description, $type) ?>

+

shortDescription, $type, true) ?>

+ description, $type) ?> render('seeAlso', ['object' => $method]); ?> diff --git a/extensions/apidoc/templates/html/views/propertyDetails.php b/extensions/apidoc/templates/html/views/propertyDetails.php index 4b45351..d02cf62 100644 --- a/extensions/apidoc/templates/html/views/propertyDetails.php +++ b/extensions/apidoc/templates/html/views/propertyDetails.php @@ -35,7 +35,7 @@ ArrayHelper::multisort($properties, 'name');
context->renderPropertySignature($property); ?>
-

description, $type) ?>

+ description, $type) ?> render('seeAlso', ['object' => $property]); ?> diff --git a/extensions/elasticsearch/README.md b/extensions/elasticsearch/README.md index 63df0eb..e2037cb 100644 --- a/extensions/elasticsearch/README.md +++ b/extensions/elasticsearch/README.md @@ -50,9 +50,6 @@ TBD > **NOTE:** elasticsearch limits the number of records returned by any query to 10 records by default. > If you expect to get more records you should specify limit explicitly in relation definition. - * This is also important for relations that use [[via()]] so that if via records are limited to 10 - * the relations records can also not be more than 10. - * Using the ActiveRecord @@ -60,14 +57,15 @@ Using the ActiveRecord For general information on how to use yii's ActiveRecord please refer to the [guide](https://github.com/yiisoft/yii2/blob/master/docs/guide/active-record.md). -For defining an elasticsearch ActiveRecord class your record class needs to extend from `yii\elasticsearch\ActiveRecord` and -implement at least the `attributes()` method to define the attributes of the record. +For defining an elasticsearch ActiveRecord class your record class needs to extend from [[yii\elasticsearch\ActiveRecord]] and +implement at least the [[yii\elasticsearch\ActiveRecord::attributes()|attributes()]] method to define the attributes of the record. The handling of primary keys is different in elasticsearch as the primary key (the `_id` field in elasticsearch terms) is not part of the attributes by default. However it is possible to define a [path mapping](http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/mapping-id-field.html) for the `_id` field to be part of the attributes. See [elasticsearch docs](http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/mapping-id-field.html) on how to define it. -The `_id` field of a document/record can be accessed using [[ActiveRecord::getPrimaryKey()]] and [[ActiveRecord::setPrimaryKey()]]. -When path mapping is defined, the attribute name can be defined using the [[primaryKey()]] method. +The `_id` field of a document/record can be accessed using [[yii\elasticsearch\ActiveRecord::getPrimaryKey()|getPrimaryKey()]] and +[[yii\elasticsearch\ActiveRecord::setPrimaryKey()|setPrimaryKey()]]. +When path mapping is defined, the attribute name can be defined using the [[yii\elasticsearch\ActiveRecord::primaryKey()|primaryKey()]] method. The following is an example model called `Customer`: @@ -101,7 +99,8 @@ class Customer extends \yii\elasticsearch\ActiveRecord } ``` -You may override [[index()]] and [[type()]] to define the index and type this record represents. +You may override [[yii\elasticsearch\ActiveRecord::index()|index()]] and [[yii\elasticsearch\ActiveRecord::type()|type()]] +to define the index and type this record represents. The general usage of elasticsearch ActiveRecord is very similar to the database ActiveRecord as described in the [guide](https://github.com/yiisoft/yii2/blob/master/docs/guide/active-record.md). @@ -109,13 +108,18 @@ It supports the same interface and features except the following limitations and - As elasticsearch does not support SQL, the query API does not support `join()`, `groupBy()`, `having()` and `union()`. Sorting, limit, offset and conditional where are all supported. -- `from()` does not select the tables, but the [index](http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/glossary.html#glossary-index) +- [[yii\elasticsearch\ActiveQuery::from()|from()]] does not select the tables, but the + [index](http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/glossary.html#glossary-index) and [type](http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/glossary.html#glossary-type) to query against. -- `select()` has been replaced with `fields()` which basically does the same but `fields` is more elasticsearch terminology. +- `select()` has been replaced with [[yii\elasticsearch\ActiveQuery::fields()|fields()]] which basically does the same but + `fields` is more elasticsearch terminology. It defines the fields to retrieve from a document. -- `via`-relations can not be defined via a table as there are no tables in elasticsearch. You can only define relations via other records. -- As elasticsearch is not only a data storage but also a search engine there is of course support added for search your records. - There are `query()`, `filter()` and `addFacets()` methods that allows to compose an elasticsearch query. +- [[yii\elasticsearch\ActiveQuery::via()|via]]-relations can not be defined via a table as there are no tables in elasticsearch. You can only define relations via other records. +- As elasticsearch is not only a data storage but also a search engine there is of course support added for searching your records. + There are + [[yii\elasticsearch\ActiveQuery::query()|query()]], + [[yii\elasticsearch\ActiveQuery::filter()|filter()]] and + [[yii\elasticsearch\ActiveQuery::addFacets()|addFacets()]] methods that allows to compose an elasticsearch query. See the usage example below on how they work and check out the [Query DSL](http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/query-dsl.html) on how to compose `query` and `filter` parts. - It is also possible to define relations from elasticsearch ActiveRecords to normal ActiveRecord classes and vice versa. From 8c7eb97175f90589049dfd20fbbad321863fb419 Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Fri, 28 Feb 2014 18:36:27 +0100 Subject: [PATCH 6/8] apidoc style and typo --- extensions/apidoc/templates/bootstrap/assets/css/style.css | 4 ++++ extensions/elasticsearch/README.md | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/extensions/apidoc/templates/bootstrap/assets/css/style.css b/extensions/apidoc/templates/bootstrap/assets/css/style.css index 57e3de9..f68ab16 100644 --- a/extensions/apidoc/templates/bootstrap/assets/css/style.css +++ b/extensions/apidoc/templates/bootstrap/assets/css/style.css @@ -50,3 +50,7 @@ body { background: #E6ECFF; border: 1px #BFCFFF solid; } + +blockquote { + font-size: 14px; +} \ No newline at end of file diff --git a/extensions/elasticsearch/README.md b/extensions/elasticsearch/README.md index e2037cb..ba6c168 100644 --- a/extensions/elasticsearch/README.md +++ b/extensions/elasticsearch/README.md @@ -119,7 +119,7 @@ It supports the same interface and features except the following limitations and There are [[yii\elasticsearch\ActiveQuery::query()|query()]], [[yii\elasticsearch\ActiveQuery::filter()|filter()]] and - [[yii\elasticsearch\ActiveQuery::addFacets()|addFacets()]] methods that allows to compose an elasticsearch query. + [[yii\elasticsearch\ActiveQuery::addFacet()|addFacet()]] methods that allows to compose an elasticsearch query. See the usage example below on how they work and check out the [Query DSL](http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/query-dsl.html) on how to compose `query` and `filter` parts. - It is also possible to define relations from elasticsearch ActiveRecords to normal ActiveRecord classes and vice versa. From e99d5dba7b140acbff886a1ec1f84ddb8ade637a Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Fri, 28 Feb 2014 18:51:28 +0100 Subject: [PATCH 7/8] fixed apidoc table style --- extensions/apidoc/templates/bootstrap/assets/css/style.css | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/extensions/apidoc/templates/bootstrap/assets/css/style.css b/extensions/apidoc/templates/bootstrap/assets/css/style.css index f68ab16..6240146 100644 --- a/extensions/apidoc/templates/bootstrap/assets/css/style.css +++ b/extensions/apidoc/templates/bootstrap/assets/css/style.css @@ -53,4 +53,8 @@ body { blockquote { font-size: 14px; +} + +td p { + margin: 0; } \ No newline at end of file From 24e086deaf6336706624e77cc0f63a7062280821 Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Fri, 28 Feb 2014 14:40:25 -0500 Subject: [PATCH 8/8] Added `yii\web\UrlRuleInterface` and `yii\web\CompositeUrlRule` --- framework/CHANGELOG.md | 1 + framework/web/CompositeUrlRule.php | 73 ++++++++++++++++++++++++++++++++ framework/web/UrlManager.php | 13 ++++-- framework/web/UrlRule.php | 13 ++++-- framework/web/UrlRuleInterface.php | 34 +++++++++++++++ tests/unit/framework/web/UrlRuleTest.php | 6 +++ 6 files changed, 132 insertions(+), 8 deletions(-) create mode 100644 framework/web/CompositeUrlRule.php create mode 100644 framework/web/UrlRuleInterface.php diff --git a/framework/CHANGELOG.md b/framework/CHANGELOG.md index 5823558..f7c2ff2 100644 --- a/framework/CHANGELOG.md +++ b/framework/CHANGELOG.md @@ -149,6 +149,7 @@ Yii Framework 2 Change Log - Enh: Added `Pagination::getLinks()` (qiangxue) - Enh: Added support for reading page size from query parameters by `Pagination` (qiangxue) - Enh: LinkPager can now register relational link tags in the html header for prev, next, first and last page (cebe) +- Enh: Added `yii\web\UrlRuleInterface` and `yii\web\CompositeUrlRule` (qiangxue) - Chg #1186: Changed `Sort` to use comma to separate multiple sort fields and use negative sign to indicate descending sort (qiangxue) - Chg #1519: `yii\web\User::loginRequired()` now returns the `Response` object instead of exiting the application (qiangxue) - Chg #1586: `QueryBuilder::buildLikeCondition()` will now escape special characters and use percentage characters by default (qiangxue) diff --git a/framework/web/CompositeUrlRule.php b/framework/web/CompositeUrlRule.php new file mode 100644 index 0000000..2382ec5 --- /dev/null +++ b/framework/web/CompositeUrlRule.php @@ -0,0 +1,73 @@ + + * @since 2.0 + */ +abstract class CompositeUrlRule extends Object implements UrlRuleInterface +{ + /** + * @var UrlRuleInterface[] the URL rules contained in this composite rule. + * This property is set in [[init()]] by the return value of [[createRules()]]. + */ + protected $rules = []; + + + /** + * Creates the URL rules that should be contained within this composite rule. + * @return UrlRuleInterface[] the URL rules + */ + abstract protected function createRules(); + + /** + * @inheritdoc + */ + public function init() + { + parent::init(); + $this->rules = $this->createRules(); + } + + /** + * @inheritdoc + */ + public function parseRequest($manager, $request) + { + foreach ($this->rules as $rule) { + /** @var \yii\web\UrlRule $rule */ + if (($result = $rule->parseRequest($manager, $request)) !== false) { + Yii::trace("Request parsed with URL rule: {$rule->name}", __METHOD__); + return $result; + } + } + return false; + } + + /** + * @inheritdoc + */ + public function createUrl($manager, $route, $params) + { + foreach ($this->rules as $rule) { + /** @var \yii\web\UrlRule $rule */ + if (($url = $rule->createUrl($manager, $route, $params)) !== false) { + return $url; + } + } + return false; + } +} diff --git a/framework/web/UrlManager.php b/framework/web/UrlManager.php index 80f2cb6..85709c9 100644 --- a/framework/web/UrlManager.php +++ b/framework/web/UrlManager.php @@ -9,6 +9,7 @@ namespace yii\web; use Yii; use yii\base\Component; +use yii\base\InvalidConfigException; use yii\caching\Cache; /** @@ -156,17 +157,22 @@ class UrlManager extends Component } $rules = []; + $verbs = 'GET|HEAD|POST|PUT|PATCH|DELETE|OPTIONS'; foreach ($this->rules as $key => $rule) { if (!is_array($rule)) { $rule = ['route' => $rule]; - if (preg_match('/^((?:(GET|HEAD|POST|PUT|PATCH|DELETE),)*(GET|HEAD|POST|PUT|PATCH|DELETE))\s+(.*)$/', $key, $matches)) { + if (preg_match("/^((?:($verbs),)*($verbs))\\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)); + $rule = Yii::createObject(array_merge($this->ruleConfig, $rule)); + if (!$rule instanceof UrlRuleInterface) { + throw new InvalidConfigException('URL rule class must implement UrlRuleInterface.'); + } + $rules[] = $rule; } $this->rules = $rules; @@ -188,7 +194,6 @@ class UrlManager extends Component /** @var UrlRule $rule */ foreach ($this->rules as $rule) { if (($result = $rule->parseRequest($this, $request)) !== false) { - Yii::trace("Request parsed with URL rule: {$rule->name}", __METHOD__); return $result; } } @@ -245,7 +250,7 @@ class UrlManager extends Component /** @var UrlRule $rule */ foreach ($this->rules as $rule) { if (($url = $rule->createUrl($this, $route, $params)) !== false) { - if ($rule->host !== null) { + if (strpos($url, '://') !== false) { if ($baseUrl !== '' && ($pos = strpos($url, '/', 8)) !== false) { return substr($url, 0, $pos) . $baseUrl . substr($url, $pos); } else { diff --git a/framework/web/UrlRule.php b/framework/web/UrlRule.php index 5a8c3f9..b069cb3 100644 --- a/framework/web/UrlRule.php +++ b/framework/web/UrlRule.php @@ -7,6 +7,7 @@ namespace yii\web; +use Yii; use yii\base\Object; use yii\base\InvalidConfigException; @@ -26,7 +27,7 @@ use yii\base\InvalidConfigException; * @author Qiang Xue * @since 2.0 */ -class UrlRule extends Object +class UrlRule extends Object implements UrlRuleInterface { /** * Set [[mode]] with this value to mark that this rule is for URL parsing only @@ -47,7 +48,7 @@ class UrlRule extends Object */ public $pattern; /** - * @var string the pattern used to parse and create the host info part of a URL. + * @var string the pattern used to parse and create the host info part of a URL (e.g. `http://example.com`). * @see pattern */ public $host; @@ -127,7 +128,8 @@ class UrlRule extends Object $this->pattern = trim($this->pattern, '/'); if ($this->host !== null) { - $this->pattern = rtrim($this->host, '/') . rtrim('/' . $this->pattern, '/') . '/'; + $this->host = rtrim($this->host, '/'); + $this->pattern = rtrim($this->host . '/' . $this->pattern, '/'); } elseif ($this->pattern === '') { $this->_template = ''; $this->pattern = '#^$#u'; @@ -157,7 +159,7 @@ class UrlRule extends Object foreach ($matches as $match) { $name = $match[1][0]; $pattern = isset($match[2][0]) ? $match[2][0] : '[^\/]+'; - if (isset($this->defaults[$name])) { + if (array_key_exists($name, $this->defaults)) { $length = strlen($match[0][0]); $offset = $match[0][1]; if ($offset > 1 && $this->pattern[$offset - 1] === '/' && $this->pattern[$offset + $length] === '/') { @@ -243,6 +245,9 @@ class UrlRule extends Object } else { $route = $this->route; } + + Yii::trace("Request parsed with URL rule: {$this->name}", __METHOD__); + return [$route, $params]; } diff --git a/framework/web/UrlRuleInterface.php b/framework/web/UrlRuleInterface.php new file mode 100644 index 0000000..e6a5385 --- /dev/null +++ b/framework/web/UrlRuleInterface.php @@ -0,0 +1,34 @@ + + * @since 2.0 + */ +interface UrlRuleInterface +{ + /** + * Parses the given request and returns the corresponding route and parameters. + * @param UrlManager $manager the URL manager + * @param Request $request the request component + * @return array|boolean the parsing result. The route and the parameters are returned as an array. + * If false, it means this rule cannot be used to parse this path info. + */ + public function parseRequest($manager, $request); + /** + * Creates a URL according to the given route and parameters. + * @param UrlManager $manager the URL manager + * @param string $route the route. It should not have slashes at the beginning or the end. + * @param array $params the parameters + * @return string|boolean the created URL, or false if this rule cannot be used for creating this URL. + */ + public function createUrl($manager, $route, $params); +} diff --git a/tests/unit/framework/web/UrlRuleTest.php b/tests/unit/framework/web/UrlRuleTest.php index 39fa9bd..2909e35 100644 --- a/tests/unit/framework/web/UrlRuleTest.php +++ b/tests/unit/framework/web/UrlRuleTest.php @@ -12,6 +12,12 @@ use yiiunit\TestCase; */ class UrlRuleTest extends TestCase { + protected function setUp() + { + parent::setUp(); + $this->mockApplication(); + } + public function testCreateUrl() { $manager = new UrlManager(['cache' => null]);