diff --git a/docs/guide/url.md b/docs/guide/url.md index 2347ae17a5..eb76c6a7d5 100644 --- a/docs/guide/url.md +++ b/docs/guide/url.md @@ -51,20 +51,30 @@ examples may also output: * `http://www.example.com/blog/post/index/` * `http://www.example.com/index.php?r=blog/post/index` -Inside a web application controller, you can use the controller's `createUrl` shortcut method. Unlike the global -`createUrl` method, the controller version is context sensitive: +In order to simplify URL creation there is [[yii\helpers\Url]] helper that is able to do the following: ```php -echo $this->createUrl(''); // currently active route -echo $this->createUrl(['view', 'id' => 'contact']); // same controller, different action -echo $this->createUrl('post/index'); // same module, different controller and action -echo $this->createUrl('/site/index'); // absolute route no matter what controller is making this call -echo $this->createurl('hi-tech'); // url for the case sensitive action `actionHiTech` of the current controller -echo $this->createurl(['/date-time/fast-forward', 'id' => 105]); // url for action the case sensitive controller, `DateTimeController::actionFastForward` +use yii\helpers\Url; + +echo Url::to(''); // currently active URL + +echo Url::toRoute(['view', 'id' => 'contact']); // same controller, different action +echo Url::toRoute('post/index'); // same module, different controller and action +echo Url::toRoute('/site/index'); // absolute route no matter what controller is making this call +echo Url::toRoute('hi-tech'); // url for the case sensitive action `actionHiTech` of the current controller +echo Url::toRoute(['/date-time/fast-forward', 'id' => 105]); // url for action the case sensitive controller, `DateTimeController::actionFastForward` + +echo Url::to('@web'); // get URL from alias + +echo Url::canonical(); // get canonical URL for the curent page +echo Url::home(); // get home URL + +Url::remember(); // save URL to be used later +Url::previous(); // get previously saved URL ``` > **Tip**: In order to generate URL with a hashtag, for example `/index.php?r=site/page&id=100#title`, you need to - specify the parameter named `#` using `$this->createUrl(['post/read', 'id' => 100, '#' => 'title'])`. + specify the parameter named `#` using `Url::to(['post/read', 'id' => 100, '#' => 'title'])`. Customizing URLs ---------------- @@ -114,21 +124,25 @@ Let's use some examples to explain how URL rules work. We assume that our rule s ] ``` -- Calling `$this->createUrl('post/list')` generates `/index.php/posts`. The first rule is applied. -- Calling `$this->createUrl(['post/read', 'id' => 100])` generates `/index.php/post/100`. The second rule is applied. -- Calling `$this->createUrl(['post/read', 'year' => 2008, 'title' => 'a sample post'])` generates +- Calling `Url::toRoute('post/list')` generates `/index.php/posts`. The first rule is applied. +- Calling `Url::toRoute(['post/read', 'id' => 100])` generates `/index.php/post/100`. The second rule is applied. +- Calling `Url::toRoute(['post/read', 'year' => 2008, 'title' => 'a sample post'])` generates `/index.php/post/2008/a%20sample%20post`. The third rule is applied. -- Calling `$this->createUrl('post/read')` generates `/index.php/post/read`. None of the rules is applied, convention is used +- Calling `Url::toRoute('post/read')` generates `/index.php/post/read`. None of the rules is applied, convention is used instead. In summary, when using `createUrl` to generate a URL, the route and the `GET` parameters passed to the method are used to decide which URL rule to be applied. If every parameter associated with a rule can be found in the `GET` parameters passed to `createUrl`, and if the route of the rule also matches the route parameter, the rule will be used to generate the URL. -If the `GET` parameters passed to `createUrl` are more than those required by a rule, the additional parameters will appear in the query string. For example, if we call `$this->createUrl('post/read', ['id' => 100, 'year' => 2008])`, we will obtain `/index.php/post/100?year=2008`. +If the `GET` parameters passed to `Url::toRoute` are more than those required by a rule, the additional parameters will +appear in the query string. For example, if we call `Url::toRoute(['post/read', 'id' => 100, 'year' => 2008])`, we will +obtain `/index.php/post/100?year=2008`. -As we mentioned earlier, the other purpose of URL rules is to parse the requesting URLs. Naturally, this is an inverse process of URL creation. For example, when a user requests for `/index.php/post/100`, the second rule in the above example will apply, which resolves in the route `post/read` and the `GET` parameter `['id' => 100]` (accessible via -`Yii::$app->request->getQueryParam('id')`). +As we mentioned earlier, the other purpose of URL rules is to parse the requesting URLs. Naturally, this is an inverse +process of URL creation. For example, when a user requests for `/index.php/post/100`, the second rule in the above example +will apply, which resolves in the route `post/read` and the `GET` parameter `['id' => 100]` (accessible via +`Yii::$app->request->get('id')`). ### Parameterizing Routes diff --git a/framework/CHANGELOG.md b/framework/CHANGELOG.md index aba1b4ec2b..f42f5e891e 100644 --- a/framework/CHANGELOG.md +++ b/framework/CHANGELOG.md @@ -191,7 +191,11 @@ Yii Framework 2 Change Log - Chg #2544: Changed `DetailView`'s `name:format:label` to `attribute:format:label` to match `GridView` (samdark) - Chg #2603: `yii\base\ErrorException` now extends `\ErrorException` (samdark) - Chg #2629: `Module::controllerPath` is now read only, and all controller classes must be namespaced under `Module::controllerNamespace`. (qiangxue) -- Chg #2630: `yii\heplers\Html::url` removed (samdark) +- Chg #2630: API changes for URLs generation (samdark, qiangxue, cebe) + - Added `yii\helpers\Url`. + - Removed `yii\heplers\Html::url`, use `yii\helpers\Url::to` instead. + - Removed `yii\web\Controller::createUrl` and `yii\web\Controller::createAbsoluteUrl`, use `yii\helpers::toRoute` instead. + - Removed `yii\web\Controller::getCanonicalUrl`, use `yii\helpers::canonical` instead. - Chg: Renamed `yii\jui\Widget::clientEventsMap` to `clientEventMap` (qiangxue) - Chg: Renamed `ActiveRecord::getPopulatedRelations()` to `getRelatedRecords()` (qiangxue) - Chg: Renamed `attributeName` and `className` to `targetAttribute` and `targetClass` for `UniqueValidator` and `ExistValidator` (qiangxue) @@ -231,7 +235,6 @@ Yii Framework 2 Change Log - New #1956: Implemented test fixture framework (qiangxue) - New #2149: Added `yii\base\DynamicModel` to support ad-hoc data validation (qiangxue) - New #2360: Added `AttributeBehavior` and `BlameableBehavior`, and renamed `AutoTimestamp` to `TimestampBehavior` (lucianobaraglia, qiangxue) -- New #2630: Added `yii\helpers\Url` (samdark) - New: Yii framework now comes with core messages in multiple languages - New: Added yii\codeception\DbTestCase (qiangxue) diff --git a/framework/helpers/BaseUrl.php b/framework/helpers/BaseUrl.php index 24224e8b1e..f72b5e9705 100644 --- a/framework/helpers/BaseUrl.php +++ b/framework/helpers/BaseUrl.php @@ -26,6 +26,14 @@ class BaseUrl * @param array|string $route route as a string or route and parameters in form of * ['route', 'param1' => 'value1', 'param2' => 'value2']. * + * If there is a controller running relative routes are recognized: + * + * - If the route is an empty string, the current [[route]] will be used; + * - If the route contains no slashes at all, it is considered to be an action ID + * of the current controller and will be prepended with [[uniqueId]]; + * - If the route has no leading slash, it is considered to be a route relative + * to the current module and will be prepended with the module's uniqueId. + * * In case there is no controller, [[\yii\web\UrlManager::createUrl()]] will be used. * * @param string $schema URI schema to use. If specified absolute URL with the schema specified is returned. @@ -39,10 +47,36 @@ class BaseUrl throw new InvalidParamException('$route should contain at least one element.'); } if (Yii::$app->controller instanceof \yii\web\Controller) { - return $schema === null ? Yii::$app->controller->createUrl($route) : Yii::$app->controller->createAbsoluteUrl($route, $schema); - } else { - return $schema === null ? Yii::$app->getUrlManager()->createUrl($route) : Yii::$app->getUrlManager()->createAbsoluteUrl($route, $schema); + $route[0] = static::getNormalizedRoute($route[0]); } + return $schema === null ? Yii::$app->getUrlManager()->createUrl($route) : Yii::$app->getUrlManager()->createAbsoluteUrl($route, $schema); + } + + /** + * Normalizes route making it suitable for UrlManager. Absolute routes are staying as is + * while relative routes are converted to absolute routes. + * + * A relative route is a route without a leading slash, such as "view", "post/view". + * + * - If the route is an empty string, the current [[route]] will be used; + * - If the route contains no slashes at all, it is considered to be an action ID + * of the current controller and will be prepended with [[uniqueId]]; + * - If the route has no leading slash, it is considered to be a route relative + * to the current module and will be prepended with the module's uniqueId. + * + * @param string $route the route. This can be either an absolute route or a relative route. + * @return string normalized route suitable for UrlManager + */ + private static function getNormalizedRoute($route) + { + if (strpos($route, '/') === false) { + // empty or an action ID + $route = $route === '' ? Yii::$app->controller->getRoute() : Yii::$app->controller->getUniqueId() . '/' . $route; + } elseif ($route[0] !== '/') { + // relative to module + $route = ltrim(Yii::$app->controller->module->getUniqueId() . '/' . $route, '/'); + } + return $route; } /** @@ -108,7 +142,7 @@ class BaseUrl * @param string $url URL to remember. Default is current URL. * @param string $name Name to use to remember URL. Defaults to `yii\web\User::returnUrlParam`. */ - public function remember($url = '', $name = null) + public static function remember($url = '', $name = null) { if ($url === '') { $url = Yii::$app->getRequest()->getUrl(); @@ -127,7 +161,7 @@ class BaseUrl * @param string $name Name used to remember URL. Defaults to `yii\web\User::returnUrlParam`. * @return string URL */ - public function previous($name = null) + public static function previous($name = null) { if ($name === null) { return Yii::$app->getUser()->getReturnUrl(); @@ -137,13 +171,22 @@ class BaseUrl } /** - * Returns canonical URL for the current page + * Returns the canonical URL of the currently requested page. + * The canonical URL is constructed using current controller's [[yii\web\Controller::route]] and + * [[yii\web\Controller::actionParams]]. You may use the following code in the layout view to add a link tag + * about canonical URL: * - * @return string canonical URL + * ```php + * $this->registerLinkTag(['rel' => 'canonical', 'href' => Url::canonical()]); + * ``` + * + * @return string the canonical URL of the currently requested page */ - public function canonical() + public static function canonical() { - return Yii::$app->controller->getCanonicalUrl(); + $params = Yii::$app->controller->actionParams; + $params[0] = Yii::$app->controller->getRoute(); + return Yii::$app->getUrlManager()->createAbsoluteUrl($params); } /** @@ -152,7 +195,7 @@ class BaseUrl * @param string $schema URI schema to use. If specified absolute URL with the schema specified is returned. * @return string home URL */ - public function home($schema = null) + public static function home($schema = null) { if ($schema === null) { return Yii::$app->getHomeUrl(); diff --git a/framework/web/Controller.php b/framework/web/Controller.php index b747343c50..f74dffe6ac 100644 --- a/framework/web/Controller.php +++ b/framework/web/Controller.php @@ -28,7 +28,7 @@ class Controller extends \yii\base\Controller */ public $enableCsrfValidation = true; /** - * @var array the parameters bound to the current action. This is mainly used by [[getCanonicalUrl()]]. + * @var array the parameters bound to the current action. */ public $actionParams = []; @@ -118,101 +118,6 @@ class Controller extends \yii\base\Controller } } - /** - * Normalizes route making it suitable for UrlManager. Absolute routes are staying as is - * while relative routes are converted to absolute routes. - * - * A relative route is a route without a leading slash, such as "view", "post/view". - * - * - If the route is an empty string, the current [[route]] will be used; - * - If the route contains no slashes at all, it is considered to be an action ID - * of the current controller and will be prepended with [[uniqueId]]; - * - If the route has no leading slash, it is considered to be a route relative - * to the current module and will be prepended with the module's uniqueId. - * - * @param string $route the route. This can be either an absolute route or a relative route. - * @return string normalized route suitable for UrlManager - */ - protected function getNormalizedRoute($route) - { - if (strpos($route, '/') === false) { - // empty or an action ID - $route = $route === '' ? $this->getRoute() : $this->getUniqueId() . '/' . $route; - } elseif ($route[0] !== '/') { - // relative to module - $route = ltrim($this->module->getUniqueId() . '/' . $route, '/'); - } - return $route; - } - - /** - * Creates a relative URL using the given route and parameters. - * - * This method enhances [[UrlManager::createUrl()]] by supporting relative routes. - * A relative route is a route without a leading slash, such as "view", "post/view". - * - * - If the route is an empty string, the current [[route]] will be used; - * - If the route contains no slashes at all, it is considered to be an action ID - * of the current controller and will be prepended with [[uniqueId]]; - * - If the route has no leading slash, it is considered to be a route relative - * to the current module and will be prepended with the module's uniqueId. - * - * After this route conversion, the method calls [[UrlManager::createUrl()]] to create a URL. - * - * @param string|array $params route as a string or route and parameters in form of ['route', 'param1' => 'value1', 'param2' => 'value2'] - * @return string the created relative URL - */ - public function createUrl($params) - { - $params = (array)$params; - $params[0] = $this->getNormalizedRoute($params[0]); - return Yii::$app->getUrlManager()->createUrl($params); - } - - /** - * Creates an absolute URL using the given route and parameters. - * - * This method enhances [[UrlManager::createAbsoluteUrl()]] by supporting relative routes. - * A relative route is a route without a leading slash, such as "view", "post/view". - * - * - If the route is an empty string, the current [[route]] will be used; - * - If the route contains no slashes at all, it is considered to be an action ID - * of the current controller and will be prepended with [[uniqueId]]; - * - If the route has no leading slash, it is considered to be a route relative - * to the current module and will be prepended with the module's uniqueId. - * - * After this route conversion, the method calls [[UrlManager::createUrl()]] to create a URL. - * - * @param string|array $params route as a string or route and parameters in form of ['route', 'param1' => 'value1', 'param2' => 'value2'] - * @param string $schema the schema to use for the url. e.g. 'http' or 'https'. If not specified - * the schema of the current request will be used. - * @return string the created absolute URL - */ - public function createAbsoluteUrl($params, $schema = null) - { - $params = (array)$params; - $params[0] = $this->getNormalizedRoute($params[0]); - return Yii::$app->getUrlManager()->createAbsoluteUrl($params, $schema); - } - - /** - * Returns the canonical URL of the currently requested page. - * The canonical URL is constructed using [[route]] and [[actionParams]]. You may use the following code - * in the layout view to add a link tag about canonical URL: - * - * ~~~ - * $this->registerLinkTag(['rel' => 'canonical', 'href' => Yii::$app->controller->canonicalUrl]); - * ~~~ - * - * @return string the canonical URL of the currently requested page - */ - public function getCanonicalUrl() - { - $params = $this->actionParams; - $params[0] = $this->getRoute(); - return Yii::$app->getUrlManager()->createAbsoluteUrl($params); - } - /** * Redirects the browser to the specified URL. * This method is a shortcut to [[Response::redirect()]].