Creating Path Urls

Hello

I’ve got url rules as follows which seem to work for inbound requests




'urlManager' => [

        	'enablePrettyUrl' => true,

        	'showScriptName' => false,

	        //'urlFormat' => 'path',

        		'rules' => [

        				'<orgname:\w+>/<orgid:\d+>/<controller:\w+>/<id:\d+>' => '<controller>/view/<id>',

        				'<orgname:\w+>/<orgid:\d+>/<controller:\w+>/<action:\w+>/<id:\d+>' => '<controller>/<action>',

        				'<controller:\w+>/<id:\d+>' => '<controller>/view',

        				'<controller:\w+>/<action:\w+>/<id:\d+>' => '<controller>/<action>',

        				'<controller:\w+>/<action:\w+>' => '<controller>/<action>',

        				'<action:\w+>' => 'site/<action>',

        		],

        ],




When I try and create a URL using an orgname and orgid, I would have expected these to be part of the url path and not parameters





echo Yii::$app->urlManager->createUrl(["test/action",'orgname'=>'organization','orgid'=>1]);




I would expect this to produce mydomain.com/organization/1/test/action

but instead it producs mydomain.com/test/action?orgname=organization&orgid=1

As you can see from my config, I’ve tried using ‘UrlFormat’=>‘path’ which, I believe, worked in Yii1.x But I can’t find the equivalent in Yii2

I’ve answered this for myself but it’s not a very elegant solution as it doesn’t strictly run off the url rules. However, it works for me, for now!

The requirement;

  • having clicked to view an organization, I only want to display items for that organization only, so any links must include the organization_id.

I would like to have URLs like mysite.com/<org_name>/<org_id>/item/<item_id>

The orgname is included for SEO reasons

I decided to store the Org_id and org_name in $app->params rather than SESSION. That’s just my choice.

  1. Extend Controller and put code in here to store org_id if present




<?php


namespace frontend\components;


use Yii;

use yii\web\Controller;

use common\models\Organization;


/**

 * ActivityController implements the CRUD actions for Activity model.

 */

class ZController extends Controller

{

    

    public function init() {

        

        parent::init();


        if (Yii::$app->requestedRoute=='organization/view' || Yii::$app->getRequest()->getQueryParam('orgid')) {

            $id = (Yii::$app->requestedRoute=='organization/view') ? Yii::$app->getRequest()->getQueryParam('id') : Yii::$app->getRequest()->getQueryParam('orgid');

            $org = Organization::find($id)->asArray()->one();

            // make the name URL friendly

            $org = str_replace([' ','\'','"','-'],"_",$org);

            // store the data in params

            Yii::$app->params['organization_id'] = $id;

            Yii::$app->params['organization_name'] = $org['name'];

        }

        else

            Yii::$app->params['organization_id']=0;

        

    }

}




  1. Extend the URLManager




<?php 


namespace app\components;


use yii\web\UrlManager;

use Yii;


class XUrlManager extends UrlManager

{

    public function createXUrl($params = [])

    {

        $params =(array) $params;

        // make sure we have all the data

        if (isset(Yii::$app->params['organization_id']) && isset(Yii::$app->params['organization_name']))

        {

            // grab the requested route

            $route=$params[0];

            // add on the bits we want org_name/org_id

            $route=Yii::$app->params['organization_name']."/".Yii::$app->params['organization_id']."/".$route;

            // put the new route back into array

            $params[0]=$route;

            // just in case, get rid of org_id from parameters in url eg: controller/action?orgid=x

            if (array_key_exists('orgid',$params))

            {

                unset($params['orgid']);

            }

        }

        // call the regular urlManager

        $urlManager = Yii::$app->urlManager;

        return $urlManager->createUrl($params);

    }

}




  1. Call new URL manager




 $url=XUrlManager::createXUrl('controller/action');




If you’ve got a better way then please let me know …

btw: I left the URL rules as in my original post …

I’m not an expert but I think your rules are not correct for what you have created.


echo Yii::$app->urlManager->createUrl(["test/action",'orgname'=>'organization','orgid'=>1]);

This will look for a rule whose right-hand-side matches test/action. In your case, that would potentially match the following rules:


'<orgname:\w+>/<orgid:\d+>/<controller:\w+>/<action:\w+>/<id:\d+>' => '<controller>/<action>',

'<controller:\w+>/<action:\w+>/<id:\d+>' => '<controller>/<action>',

'<controller:\w+>/<action:\w+>' => '<controller>/<action>',

It won’t match the first or second because you have not provided the id parameter used on the left-hand-side of those rules. It will instead match the third rule producing what you have seen. To get what you want, the rule would have to be:


'<orgname:\w+>/<orgid:\d+>/<controller:\w+>/<action:\w+>' => '<controller>/<action>'

or possibly replace the id regex + symbol with a * to mean zero or more instead of 1 or more:


'<orgname:\w+>/<orgid:\d+>/<controller:\w+>/<action:\w+>/<id:\d*>' => '<controller>/<action>'