Commit 1f292ef5 by Qiang Xue

Merge commit '24e086de' into feature-restapi

parents 6c88183b 24e086de
...@@ -5,6 +5,7 @@ use Yii; ...@@ -5,6 +5,7 @@ use Yii;
use yii\web\AccessControl; use yii\web\AccessControl;
use yii\web\Controller; use yii\web\Controller;
use common\models\LoginForm; use common\models\LoginForm;
use yii\web\VerbFilter;
/** /**
* Site controller * Site controller
...@@ -31,6 +32,12 @@ class SiteController extends Controller ...@@ -31,6 +32,12 @@ class SiteController extends Controller
], ],
], ],
], ],
'verbs' => [
'class' => VerbFilter::className(),
'actions' => [
'logout' => ['post'],
],
],
]; ];
} }
......
...@@ -10,6 +10,7 @@ use yii\base\InvalidParamException; ...@@ -10,6 +10,7 @@ use yii\base\InvalidParamException;
use yii\web\BadRequestHttpException; use yii\web\BadRequestHttpException;
use yii\web\Controller; use yii\web\Controller;
use Yii; use Yii;
use yii\web\VerbFilter;
/** /**
* Site controller * Site controller
...@@ -38,6 +39,12 @@ class SiteController extends Controller ...@@ -38,6 +39,12 @@ class SiteController extends Controller
], ],
], ],
], ],
'verbs' => [
'class' => VerbFilter::className(),
'actions' => [
'logout' => ['post'],
],
],
]; ];
} }
......
...@@ -69,7 +69,7 @@ class PropertyDoc extends BaseDoc ...@@ -69,7 +69,7 @@ class PropertyDoc extends BaseDoc
$this->types = $tag->getTypes(); $this->types = $tag->getTypes();
$this->description = ucfirst($tag->getDescription()); $this->description = ucfirst($tag->getDescription());
if (($pos = strpos($this->description, '.')) !== false) { if (($pos = strpos($this->description, '.')) !== false) {
$this->shortDescription = substr($this->description, 0, $pos); $this->shortDescription = substr($this->description, 0, $pos + 1);
} else { } else {
$this->shortDescription = $this->description; $this->shortDescription = $this->description;
} }
......
...@@ -50,3 +50,11 @@ body { ...@@ -50,3 +50,11 @@ body {
background: #E6ECFF; background: #E6ECFF;
border: 1px #BFCFFF solid; border: 1px #BFCFFF solid;
} }
blockquote {
font-size: 14px;
}
td p {
margin: 0;
}
\ No newline at end of file
...@@ -32,7 +32,7 @@ ArrayHelper::multisort($events, 'name'); ...@@ -32,7 +32,7 @@ ArrayHelper::multisort($events, 'name');
<?php echo $event->trigger->signature; ?> <?php echo $event->trigger->signature; ?>
</div>*/ ?> </div>*/ ?>
<p><?= ApiMarkdown::process($event->description, $type); ?></p> <?= ApiMarkdown::process($event->description, $type); ?>
<?= $this->render('seeAlso', ['object' => $event]); ?> <?= $this->render('seeAlso', ['object' => $event]); ?>
......
...@@ -62,8 +62,8 @@ ArrayHelper::multisort($methods, 'name'); ...@@ -62,8 +62,8 @@ ArrayHelper::multisort($methods, 'name');
<!-- --><?php //$this->renderPartial('sourceCode',array('object'=>$method)); ?> <!-- --><?php //$this->renderPartial('sourceCode',array('object'=>$method)); ?>
<p><?= ApiMarkdown::process($method->shortDescription, $type, true) ?></strong></p> <p><strong><?= ApiMarkdown::process($method->shortDescription, $type, true) ?></strong></p>
<p><?= ApiMarkdown::process($method->description, $type) ?></p> <?= ApiMarkdown::process($method->description, $type) ?>
<?= $this->render('seeAlso', ['object' => $method]); ?> <?= $this->render('seeAlso', ['object' => $method]); ?>
......
...@@ -35,7 +35,7 @@ ArrayHelper::multisort($properties, 'name'); ...@@ -35,7 +35,7 @@ ArrayHelper::multisort($properties, 'name');
<div class="signature"><?php echo $this->context->renderPropertySignature($property); ?></div> <div class="signature"><?php echo $this->context->renderPropertySignature($property); ?></div>
<p><?= ApiMarkdown::process($property->description, $type) ?></p> <?= ApiMarkdown::process($property->description, $type) ?>
<?= $this->render('seeAlso', ['object' => $property]); ?> <?= $this->render('seeAlso', ['object' => $property]); ?>
......
...@@ -50,9 +50,6 @@ TBD ...@@ -50,9 +50,6 @@ TBD
> **NOTE:** elasticsearch limits the number of records returned by any query to 10 records by default. > **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. > 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 Using the ActiveRecord
...@@ -60,14 +57,15 @@ 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 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 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. 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) 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) 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. 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. 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()]]. The `_id` field of a document/record can be accessed using [[yii\elasticsearch\ActiveRecord::getPrimaryKey()|getPrimaryKey()]] and
When path mapping is defined, the attribute name can be defined using the [[primaryKey()]] method. [[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`: The following is an example model called `Customer`:
...@@ -101,7 +99,8 @@ class Customer extends \yii\elasticsearch\ActiveRecord ...@@ -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 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). [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 ...@@ -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()`. - 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. 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. 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. 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. - [[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 search your 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 `query()`, `filter()` and `addFacets()` methods that allows to compose an elasticsearch query. There are
[[yii\elasticsearch\ActiveQuery::query()|query()]],
[[yii\elasticsearch\ActiveQuery::filter()|filter()]] and
[[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) 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. 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. - It is also possible to define relations from elasticsearch ActiveRecords to normal ActiveRecord classes and vice versa.
......
...@@ -148,6 +148,8 @@ Yii Framework 2 Change Log ...@@ -148,6 +148,8 @@ Yii Framework 2 Change Log
- Enh: Added support for building SQLs with sub-queries (qiangxue) - Enh: Added support for building SQLs with sub-queries (qiangxue)
- Enh: Added `Pagination::getLinks()` (qiangxue) - Enh: Added `Pagination::getLinks()` (qiangxue)
- Enh: Added support for reading page size from query parameters by `Pagination` (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 #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 #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) - Chg #1586: `QueryBuilder::buildLikeCondition()` will now escape special characters and use percentage characters by default (qiangxue)
......
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\web;
use Yii;
use yii\base\Object;
/**
* CompositeUrlRule represents a collection of related URL rules.
*
* These URL rules are typically created for a common purpose (e.g. RESTful API for a resource).
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @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;
}
}
...@@ -9,6 +9,7 @@ namespace yii\web; ...@@ -9,6 +9,7 @@ namespace yii\web;
use Yii; use Yii;
use yii\base\Component; use yii\base\Component;
use yii\base\InvalidConfigException;
use yii\caching\Cache; use yii\caching\Cache;
/** /**
...@@ -156,17 +157,22 @@ class UrlManager extends Component ...@@ -156,17 +157,22 @@ class UrlManager extends Component
} }
$rules = []; $rules = [];
$verbs = 'GET|HEAD|POST|PUT|PATCH|DELETE|OPTIONS';
foreach ($this->rules as $key => $rule) { foreach ($this->rules as $key => $rule) {
if (!is_array($rule)) { if (!is_array($rule)) {
$rule = ['route' => $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['verb'] = explode(',', $matches[1]);
$rule['mode'] = UrlRule::PARSING_ONLY; $rule['mode'] = UrlRule::PARSING_ONLY;
$key = $matches[4]; $key = $matches[4];
} }
$rule['pattern'] = $key; $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; $this->rules = $rules;
...@@ -188,7 +194,6 @@ class UrlManager extends Component ...@@ -188,7 +194,6 @@ class UrlManager extends Component
/** @var UrlRule $rule */ /** @var UrlRule $rule */
foreach ($this->rules as $rule) { foreach ($this->rules as $rule) {
if (($result = $rule->parseRequest($this, $request)) !== false) { if (($result = $rule->parseRequest($this, $request)) !== false) {
Yii::trace("Request parsed with URL rule: {$rule->name}", __METHOD__);
return $result; return $result;
} }
} }
...@@ -245,7 +250,7 @@ class UrlManager extends Component ...@@ -245,7 +250,7 @@ class UrlManager extends Component
/** @var UrlRule $rule */ /** @var UrlRule $rule */
foreach ($this->rules as $rule) { foreach ($this->rules as $rule) {
if (($url = $rule->createUrl($this, $route, $params)) !== false) { if (($url = $rule->createUrl($this, $route, $params)) !== false) {
if ($rule->host !== null) { if (strpos($url, '://') !== false) {
if ($baseUrl !== '' && ($pos = strpos($url, '/', 8)) !== false) { if ($baseUrl !== '' && ($pos = strpos($url, '/', 8)) !== false) {
return substr($url, 0, $pos) . $baseUrl . substr($url, $pos); return substr($url, 0, $pos) . $baseUrl . substr($url, $pos);
} else { } else {
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
namespace yii\web; namespace yii\web;
use Yii;
use yii\base\Object; use yii\base\Object;
use yii\base\InvalidConfigException; use yii\base\InvalidConfigException;
...@@ -26,7 +27,7 @@ use yii\base\InvalidConfigException; ...@@ -26,7 +27,7 @@ use yii\base\InvalidConfigException;
* @author Qiang Xue <qiang.xue@gmail.com> * @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0 * @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 * Set [[mode]] with this value to mark that this rule is for URL parsing only
...@@ -47,7 +48,7 @@ class UrlRule extends Object ...@@ -47,7 +48,7 @@ class UrlRule extends Object
*/ */
public $pattern; 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 * @see pattern
*/ */
public $host; public $host;
...@@ -127,7 +128,8 @@ class UrlRule extends Object ...@@ -127,7 +128,8 @@ class UrlRule extends Object
$this->pattern = trim($this->pattern, '/'); $this->pattern = trim($this->pattern, '/');
if ($this->host !== null) { 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 === '') { } elseif ($this->pattern === '') {
$this->_template = ''; $this->_template = '';
$this->pattern = '#^$#u'; $this->pattern = '#^$#u';
...@@ -157,7 +159,7 @@ class UrlRule extends Object ...@@ -157,7 +159,7 @@ class UrlRule extends Object
foreach ($matches as $match) { foreach ($matches as $match) {
$name = $match[1][0]; $name = $match[1][0];
$pattern = isset($match[2][0]) ? $match[2][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]); $length = strlen($match[0][0]);
$offset = $match[0][1]; $offset = $match[0][1];
if ($offset > 1 && $this->pattern[$offset - 1] === '/' && $this->pattern[$offset + $length] === '/') { if ($offset > 1 && $this->pattern[$offset - 1] === '/' && $this->pattern[$offset + $length] === '/') {
...@@ -243,6 +245,9 @@ class UrlRule extends Object ...@@ -243,6 +245,9 @@ class UrlRule extends Object
} else { } else {
$route = $this->route; $route = $this->route;
} }
Yii::trace("Request parsed with URL rule: {$this->name}", __METHOD__);
return [$route, $params]; return [$route, $params];
} }
......
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\web;
/**
* UrlRuleInterface is the interface that should be implemented URL rule classes.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @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);
}
...@@ -89,6 +89,13 @@ class LinkPager extends Widget ...@@ -89,6 +89,13 @@ class LinkPager extends Widget
* If this property is null, the "last" page button will not be displayed. * If this property is null, the "last" page button will not be displayed.
*/ */
public $lastPageLabel; 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,10 +114,26 @@ class LinkPager extends Widget ...@@ -107,10 +114,26 @@ class LinkPager extends Widget
*/ */
public function run() public function run()
{ {
if ($this->registerLinkTags) {
$this->registerLinkTags();
}
echo $this->renderPageButtons(); 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. * Renders the page buttons.
* @return string the rendering result * @return string the rendering result
*/ */
......
...@@ -33,13 +33,13 @@ class ActiveRecordTest extends RedisTestCase ...@@ -33,13 +33,13 @@ class ActiveRecordTest extends RedisTestCase
ActiveRecord::$db = $this->getConnection(); ActiveRecord::$db = $this->getConnection();
$customer = new Customer(); $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->save(false);
$customer = new Customer(); $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->save(false);
$customer = new Customer(); $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); $customer->save(false);
// INSERT INTO tbl_category (name) VALUES ('Books'); // INSERT INTO tbl_category (name) VALUES ('Books');
......
...@@ -12,6 +12,12 @@ use yiiunit\TestCase; ...@@ -12,6 +12,12 @@ use yiiunit\TestCase;
*/ */
class UrlRuleTest extends TestCase class UrlRuleTest extends TestCase
{ {
protected function setUp()
{
parent::setUp();
$this->mockApplication();
}
public function testCreateUrl() public function testCreateUrl()
{ {
$manager = new UrlManager(['cache' => null]); $manager = new UrlManager(['cache' => null]);
......
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