Yii Framework Forum: Help With Urls - Yii Framework Forum

Jump to content

Page 1 of 1
  • You cannot start a new topic
  • You cannot reply to this topic

Help With Urls Rate Topic: -----

#1 User is offline   BStep 

  • Junior Member
  • Pip
  • Yii
  • Group: Members
  • Posts: 43
  • Joined: 07-January 11

Posted 13 February 2013 - 01:58 PM

I think this should be a simpler problem than I'm making it, but I've been playing with it for a few hours and can't figure it out. Any help would be appreciated.

I developed a site that has a super-simple content management system that allows an administrator to create/edit pages that are accessed at "url.com/index.php/event/event-id" where event-id is a text string. The page IDs are stored in a database. The Event controller handles the requests using the View action.

I'm trying to use this site as a framework for a new site, but the new site requires that the URLs not have "event" on the front of them. So I want what used to be "/index.php/event/event-id" to just be "/index.php/event-id". I've tried playing with URL rules in the URL manager in config/main.php and I've tried adding a View action to the Site Controller that forwards requests to the Event controller, both with no luck. In particular, when I try to use the View action in the Site controller, the "event-id" gets turned into a string containing the word "error", which then causes a CHttpException error saying the request is invalid - because "error" isn't an ID it can find in the database.

Like I said, I think there's probably a simple URL manager solution or something, but I can't get my head around it. Any ideas? Thanks.
0

#2 User is offline   BStep 

  • Junior Member
  • Pip
  • Yii
  • Group: Members
  • Posts: 43
  • Joined: 07-January 11

Posted 13 February 2013 - 02:30 PM

Here's what I tried as far as URL rules in main.php:

'<id:\w+>'=>'site/<id>'
and
'<id:\w+>'=>'event/<id>'
and
'<\w+>'=>'event/view'


None of these rules seem to have any effect at all. I read through the CUrlManager doc page and don't really understand why, and I don't understand why removing all the existing rules in main.php seems to have no effect either - I guess there must be default rules.
0

#3 User is offline   BStep 

  • Junior Member
  • Pip
  • Yii
  • Group: Members
  • Posts: 43
  • Joined: 07-January 11

Posted 13 February 2013 - 03:40 PM

One of the urls is "about-us" - so "url.com/index.php/about-us"

If some of my examples are confusing due to changing which controller is handling the request, it's because I have my Site controller setup to handle the requests also. Event/view and Site/view can both handle it.

I tried the following rule, which works:

'about-us' => array('site/about-us', 'caseSensitive'=>false)


However, I can't get it to work dynamically...I'm sure I'm just putting it in wrong:

'<\w+>' => array('site/about-us', 'caseSensitive'=>false)
or
'<pg:\w+>' => array('site/<pg>', 'caseSensitive'=>false)
or
'<\w+>' => array('site/view', 'caseSensitive'=>false)

0

#4 User is offline   BStep 

  • Junior Member
  • Pip
  • Yii
  • Group: Members
  • Posts: 43
  • Joined: 07-January 11

Posted 13 February 2013 - 05:36 PM

Well, I ghetto-rigged it so it works, but I'd still rather write it correctly if anyone can help. Instead of writing a correct URL rule, I did this in my controller and looked up the model with this:

$event = Yii::app()->request->pathInfo;


That value contains the ID I need to look up in my "Event" table.
0

#5 User is online   Da:Sourcerer 

  • Elite Member
  • PipPipPipPipPip
  • Yii
  • Group: Members
  • Posts: 1,222
  • Joined: 30-March 11
  • Location:Berlin, Germany

Posted 13 February 2013 - 10:09 PM

I think it would help if you could tell us more precisely how your event id's are constructed. In turn, we might be able to tell you why \w+ doesn't match them.
programmer /ˈprəʊgramə/, noun: a device that converts ►coffee into ►code
0

#6 User is offline   yugene 

  • Advanced Member
  • PipPipPip
  • Yii
  • Group: Members
  • Posts: 504
  • Joined: 08-August 09

Posted 14 February 2013 - 03:19 AM

View PostBStep, on 13 February 2013 - 03:40 PM, said:

I tried the following rule, which works:

'about-us' => array('site/about-us', 'caseSensitive'=>false)


However, I can't get it to work dynamically...I'm sure I'm just putting it in wrong:

'<\w+>' => array('site/about-us', 'caseSensitive'=>false)
or
'<pg:\w+>' => array('site/<pg>', 'caseSensitive'=>false)
or
'<\w+>' => array('site/view', 'caseSensitive'=>false)


First of all, \w means letter or number or underscore, but not a dash, so you'd rather need to use
[-\w]+

If to take it all together, that's what you need:
public function actionEvent($slug){
	findPageBySlugIsHere($slug);
}
'<slug:[-\w]+>'=>'event/view'


The second approach I prefer to use in such a case - is to build url rule dynamically, based on actual slugs (event-ids) stored in db.
This way only URLs with existen slugs (event-ids) will be handled by the url rule.
I'll just provide piece of my code with my names:

class UrlManager extends CUrlManager
{

    /**
 	* Initializes the application component.
 	*/
    public function init()
    {
        $this->setRules();
        parent::init();
    }

    /**
 	*  Sets application url rules
 	*/
    protected function setRules()
    {
        // add text pages support
        $pages = CHtml::listData(Text::model()->findAll(array(
            'select'=>'slug', 
            'condition'=>'`type`=:type', 
            'params'=>array(':type'=>Text::T_PAGE)
            )), 'slug', 'slug');
        if($pages){
            $this->rules = array_merge(
                    array('<slug:('.implode('|', $pages).')>' => 'site/page'),
                    $this->rules
            );
        }
    }
}

In your config file don't forget to change urlmanager component class:
[size=2]
[/size]

        'urlManager' => array(
            'class'=>'UrlManager',
....

0

#7 User is offline   BStep 

  • Junior Member
  • Pip
  • Yii
  • Group: Members
  • Posts: 43
  • Joined: 07-January 11

Posted 14 February 2013 - 11:23 AM

Thanks, yugene! I could have sworn I read somewhere in the docs that said \w covered everything except vertical bar (|) or something like that.

The following rule works for me now:
'<id:[-\w]+>'=>'site/<id>'


I tried to implement some code like yours for a custom UrlManager:

<?php

class QUrlManager extends CUrlManager
{
	/**
	* Initializes the application component.
	*/
	public function init()
	{
		$this->setRules();
		parent::init();
	}

	/**
	*  Sets application url rules
	*/
	protected function setRules()
	{
		$criteria = new CDbCriteria();
		$criteria->select = 'id';

		$slugs = Event::findAll($criteria);

		foreach($slugs as $slug)
		{
			$this->rules[] = array($slug,'site/'.$slug);
		}
	}
}


When I attempt to access a page after implementing that code and setting main.php to use that class for the UrlManager, I get the following error:

Quote

QUrlManager does not have a method named "getCommandBuilder".


I can't find anything helpful on Google for that. Having the URL rule correct is "good enough" but if you have any insight on that error, I'd appreciate it.
0

#8 User is offline   yugene 

  • Advanced Member
  • PipPipPip
  • Yii
  • Group: Members
  • Posts: 504
  • Joined: 08-August 09

Posted 14 February 2013 - 03:06 PM

Shouldn't you have
Event::model()->findAll($criteria)
?
And what looks a bit strange to me is how your final url rules look (even working one): do you have actions that named according to event-ids from db?
Third, I think you're setting dynamic url rules a bit wrong way. Should be something like:
$this->rules['<id:('.implode('|', $slugs).')>'] = 'controller/action';

Replace controller/action with your actual names and please notice with such approach $slugs must be an array. Just study the example I sent you more carefully.
0

#9 User is offline   BStep 

  • Junior Member
  • Pip
  • Yii
  • Group: Members
  • Posts: 43
  • Joined: 07-January 11

Posted 14 February 2013 - 03:35 PM

Thanks again, yugene. Yes, the id in the database is actually the slug. I didn't think to use the name "slug" when I wrote the site in the first place - it would have been a better name.

Edit: I understand your question better now. No, I don't have a separate action for each id/slug in the database. I'm honestly not sure why it even works with that rule, now that you mention it.

I see how your rule works now. I thought the CHtml piece was a little bit of a long way around to solve the problem, so I skipped your rule line. What I'm doing is writing a separate rule for each URL, but you're probably handling it more efficiently by adding every ID to one rule.

I'll try these suggestions and post again.
0

#10 User is offline   BStep 

  • Junior Member
  • Pip
  • Yii
  • Group: Members
  • Posts: 43
  • Joined: 07-January 11

Posted 14 February 2013 - 03:44 PM

Ok, here's my custom UrlManager code now. It's working. Thanks a lot.

<?php

class QUrlManager extends CUrlManager
{
	/**
	* Initializes the application component.
	*/
	public function init()
	{
		$this->setRules();
		parent::init();
	}

	/**
	*  Sets application url rules
	*/
	protected function setRules()
	{
		$criteria = new CDbCriteria();
		$criteria->select = 'id';

		$events = Event::model()->findAll($criteria);
		foreach($events as $event)
			$slugs[] = $event->id;

		$this->rules['<id:(' . implode('|', $slugs) . ')>'] = 'event/view';
	}
}

0

#11 User is offline   yugene 

  • Advanced Member
  • PipPipPip
  • Yii
  • Group: Members
  • Posts: 504
  • Joined: 08-August 09

Posted 14 February 2013 - 04:26 PM

[quote name='BStep' timestamp='1360874681' post='193184']
It's working.
[code]
Great :)
0

#12 User is online   Da:Sourcerer 

  • Elite Member
  • PipPipPipPipPip
  • Yii
  • Group: Members
  • Posts: 1,222
  • Joined: 30-March 11
  • Location:Berlin, Germany

Posted 15 February 2013 - 05:07 AM

Wow. That looks expensive and unsafe. Don't you want to pass the event ids through preg_quote() at least?
programmer /ˈprəʊgramə/, noun: a device that converts ►coffee into ►code
0

#13 User is offline   BStep 

  • Junior Member
  • Pip
  • Yii
  • Group: Members
  • Posts: 43
  • Joined: 07-January 11

Posted 15 February 2013 - 11:51 AM

Da:Sourcerer,

Do you have a suggestion relating to the "expensive" comment? It seems to load quickly and it'll be a low-traffic site, so it shouldn't be a problem, but I'd like to learn if you have a thought on it.

I'm the only one who has the ability to insert the event ids into the database, so I don't think I need to do any filtering on them unless that changes.
0

#14 User is offline   yugene 

  • Advanced Member
  • PipPipPip
  • Yii
  • Group: Members
  • Posts: 504
  • Joined: 08-August 09

Posted 15 February 2013 - 12:44 PM

Yeah, I must be had to mention in the first post I use dynamic approach to create url-rules for site static pages (about-us and similar) and slugs are created programmatically, which is similar to BStep's situation. I'm also interested what did you mean by saying 'expensive' - do you mean rule will be too slow when parsing with many items in it? Could you be more specific?
0

#15 User is online   Da:Sourcerer 

  • Elite Member
  • PipPipPipPipPip
  • Yii
  • Group: Members
  • Posts: 1,222
  • Joined: 30-March 11
  • Location:Berlin, Germany

Posted 17 February 2013 - 03:36 AM

Well, I'm deeply concerned since this code will be invoked on every page request. This rule will accumulate more costs as additional event ids will be added. Given that complex regular expressions have the potential to be very cpu intensive, I'd feel uneasy with this on my system. And there's still that preg_quote() thing.

So, while this solution might work for you, I'm under the impression a lot of technical debts were gathered here. So, some advice from my side:
  • Extend CBaseUrlRule instead of CUrlManager to set your own custom URL rule in place
  • Use DAO instead of ActiveRecords to fetch the event ids
  • Set some caching mechanism in place so the regex doesn't need to be regenerated on each and every request

programmer /ˈprəʊgramə/, noun: a device that converts ►coffee into ►code
0

Share this topic:


Page 1 of 1
  • You cannot start a new topic
  • You cannot reply to this topic

1 User(s) are reading this topic
0 members, 1 guests, 0 anonymous users