UrlManager Configuration for Unlimited Depth of category/subcateogries

Hi,

I am facing problem in writing rule for the URLManager to get Unlimited depth of Categories and Subcategories

I have created the Url as category/sub-category/sub-category but when I use it without using UrlManger, it works fine but when I use it with UrlManager it gives 404 request not found.

Any help?


                'urlManager'=>array(

                'urlFormat'=>'path',

                            'showScriptName'=>false,

                'rules'=>array(

                                    '/'=>'site/index',

                                      array(

                                            'class' => 'application.components.CatUrl',

                                        ),

                                       '//'=>'site/page',

                                       '/.html'=>'site/page',

                                   '/site/categories/'=>'site/categories',

                                      '/'=>'/view',

                    '//'=>'/',

                    '/'=>'/',

     

                ),

            ),

CatUrl code


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

        {

            if ($route==='site/page')

            {

                if (isset($params['parent'], $params['current']))

                    return $params['parent'] . '/' . $params['current'];

                else if (isset($params['current']))

                    return $params['current'];

            }

            return false;

        }

     

        public function parseUrl($manager,$request,$pathInfo,$rawPathInfo)

        {

            if (preg_match('%^(\w+)(/(\w+))?$%', $pathInfo, $matches))

            {

     

            }

            return false;

        }



That’s pretty simple actually. I use same thing for page/subpages in an unlimited number of subpages.

The approach is like this, in my page table, i have a "nice_url"(varchar 255) columns and a "url_path"(TEXT) column.

When a new record is created, i generate a nice_url from the page title. IE (the model):




public function beforeSave()

{

	$this->nice_url=Yii::app()->seo->niceUrl($this->title);

	$this->url_path=$this->nice_url;

	if(!empty($this->parent_id))

	{

		$parent=self::model()->find(array(

			'select'=>'url_path',

			'condition'=>'page_id=:id',

			'params'=>array(':id'=>$this->parent_id)

		));

		

		if(!empty($parent)&&!empty($parent->url_path))

			$this->url_path=$parent->url_path.'/'.$this->nice_url;

	}

	return parent::beforeSave();

}



As you see, when the record is saved, if it is a child record of another record, we modify the url_path accordingly, something like: parent-nice-slug/child-nice-slug, if it’s not a child record we set same url_path as the nice_url.

Next, the url class that handles the request:




class PageUrlRule extends CBaseUrlRule

{

    public $connectionID = 'db';

 

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

    {

        if($route==='page/index')

        {

            if (isset($params['nice_url']))

                return $params['nice_url'].$manager->urlSuffix;

        }

        return false;  // this rule does not apply

    }

 

    public function parseUrl($manager,$request,$pathInfo,$rawPathInfo)

    {

        if(preg_match('%^[a-z0-9\-\/]+$%', $pathInfo, $matches))

        {

            $url=$matches[0];

            if(strpos($url,'/')!==false)

            {

                $urlPieces=explode('/',$url);

                $url=end($urlPieces);

            }

            $command=Yii::app()->{$this->connectionID}->createCommand();

            $result=$command->select('url_path')->from('{{page}}');

            $result->where('nice_url=:url AND `status`=:st', array(':url'=>$url,':st'=>Page::STATUS_ACTIVE));

            $result = $result->queryRow();

            if(!empty($result)&&$result['url_path']===$matches[0])

            {

                $_GET['nice_url']=$result['url_path'];

                return 'page/index';

            }

        }

        return false;  // this rule does not apply

    }

}



Next, creating url’s is pretty simple:




echo Yii::app()->createUrl('page/index',array('nice_url'=>$model->url_path));

// you might need to decode it, in case the / is converted

echo urldecode(Yii::app()->createUrl('page/index',array('nice_url'=>$model->url_path)));



Now when a request like: http://www.mysite.com/parent/child/sub-child is made, the PageUrlRule class is triggered and will match the "parent/child/sub-child" string, which is exploded and the last piece(sub-child) is used to retrieve the page content

here is the controller action




public function actionIndex($nice_url)

{

	$urlParts=array();

	

	//for seo purposes, we can have a url-1/url-2/.../url-n url, 

	//in which case, we only need the last part or the url

	if(strpos($nice_url,'/')!==false)

	{

		$urlParts=explode('/',$nice_url);

		$nice_url=end($urlParts);

	}

	

	//fetch the page

	$model=Page::model()->find(array(

		'condition'=>'nice_url=:url AND `status`=:st',

		'params'=>array(':url'=>$nice_url, ':st'=>Page::STATUS_ACTIVE)

	));

	

	//empty page ? well, bye...

	if(empty($model))

		throw new CHttpException(404,Yii::t('app','The requested page does not exist.')); 

	

	// do things with $model here

}



Enjoy :)