Routing with "/" in parameter

Hi all,

i would like to build some urls like this:


http://www.example.com/products/category/subcateg/subsubcateg/.../

now, my route is like this:




'products/<txtCategs:(.*)>'=>array('products/index'),



this works well in parsing the request and building the variables, but when i try to construct the URL




Yii::app()->getUrlManager()->createUrl('products/index', array('txtCategs'=>'category/subcateg/subsubcateg'))



the "/" is escaped.

i know this is the expected result, but how can i make it so it will not escape the "/" ?

Not quite sure what you mean but I think you need to setup additional routes

so the resulting routes should look something like this

‘products/<txtCategs:(.)>/<txtSubCat:(.)>/<txtSubSubCat:(.*)>’=>array(‘products/index’),

‘products/<txtCategs:(.)>/<txtSubCat:(.)>’=>array(‘products/index’),

‘products/<txtCategs:(.*)>’=>array(‘products/index’),

I think you understand how the rest works.

That’s not an option, the tree of categories is unlimited.

you could use something like


'products/*' => 'products/index'

The following URL will give you


http://domain.com/products/console/games/pc/action



e.g.


print_r($_GET);


Array

(

    [console] => games

    [pc] => action

)

I think it might be possible to extract something like this using a regex pattern or you could use some array functions to restructure the GET request.




Array

(

    [0] => console

    [1] => games

    [2] => pc

    [3] => action

)

Don’t know if this can be done better, but maybe somebody will have time to think about it more. Maybe it can also be more optimized.

Here is my solution.

I’ve extended the CUrlManager and CUrlRule.




class BUrlManager extends CUrlManager {

	

	/**

	 * Creates a URL rule instance.

	 * The default implementation returns a CUrlRule object.

	 * @param string the pattern part of the rule

	 * @param mixed the route part of the rule. This could be a string or an array

	 * @return BUrlRule the URL rule instance

	 * @see CUrlManager::createUrlRule

	 */

	protected function createUrlRule($route,$pattern)

	{

		return new BUrlRule($route,$pattern);

	}

}




class BUrlRule extends CUrlRule {

	

	public $noEscape;

	

	

	public function __construct($route,$pattern) {

		if(is_array($route))

		{

			if(isset($route['noEscape']))

				$this->noEscape=$route['noEscape'];

		}

		parent::__construct($route, $pattern);

	}

	

	/**

	 * Creates a URL based on this rule.

	 * @param CUrlManager the manager

	 * @param string the route

	 * @param array list of parameters

	 * @param string the token separating name-value pairs in the URL.

	 * @return string the constructed URL

	 */

	public function createUrl($manager,$route,$params,$ampersand)

	{

		

		if (is_array($this->noEscape)) {

			

			$replaceSequance = uniqid(__CLASS__,true);

			foreach ($this->noEscape as $p=>$ne){

				if (isset($params[$p]) && $ne) {

					$params[$p] = strtr($params[$p], array($ne=>trim(base64_encode($replaceSequance.$ne), '=')));

				} else {

					unset($this->noEscape[$p]);

				}

			}

			$url = parent::createUrl($manager, $route, $params, $ampersand);

			

			foreach ($this->noEscape as $p=>$ne){

				$url = strtr($url, array(trim(base64_encode($replaceSequance.$ne), '=')=>$ne));

			}

			

		} else {

			$url = parent::createUrl($manager, $route, $params, $ampersand);

		}

		

		return $url;

	}

	

}



Now i can write the rule as:




'products/<txtCategs:(.*)>'=>array('products/index', 'noEscape'=>array('txtCategs'=>'/')),



The "noEscape" index in the rule array must always be an array. The key is the parameter that needs the special treatment and the value is the text that needs to be escaped.

Of course this can be modified to accept multiple values to be escaped. I will modify it upon request

I’m not really sure if this is expected. Usually only the query part should get escaped right? Means if you have an uri like ‘/?foo=bar/baz’, then the ‘/’ should get escaped. But in an actual path (/foo/bar/baz?foo=bar), the ‘/’ should not get escaped.

Not sure what your URL manager does but this (http://domain.com/products/console/games/pc/action)


'products/<products:.+>' => 'products/index'

will return




Array

(

    [products] => console/games/pc/action

)



Sniper the problem is with createUrl.

@Sniper:

The problem is not creating the parameter from the URL, the problem is creating the URL passing the parameter.

@Y!!

You are right, but using urlencode() in the createUrl() method escapes the "/".

got it :) after calling createUrl, you could urldecode it I guess?

Yes, I will take a look at this later. I’m not much into the url handling internals.

For now you can use your class or urldecode like Sniper suggested.