Help With Urls
#1
Posted 13 February 2013 - 01:58 PM
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.
#2
Posted 13 February 2013 - 02:30 PM
'<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.
#3
Posted 13 February 2013 - 03:40 PM
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)
#4
Posted 13 February 2013 - 05:36 PM
$event = Yii::app()->request->pathInfo;
That value contains the ID I need to look up in my "Event" table.
#5
Posted 13 February 2013 - 10:09 PM
#6
Posted 14 February 2013 - 03:19 AM
BStep, on 13 February 2013 - 03:40 PM, said:
'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', ....
#7
Posted 14 February 2013 - 11:23 AM
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
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.
#8
Posted 14 February 2013 - 03:06 PM
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.
#9
Posted 14 February 2013 - 03:35 PM
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.
#10
Posted 14 February 2013 - 03:44 PM
<?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'; } }
#11
Posted 14 February 2013 - 04:26 PM
It's working.
[code]
Great

#12
Posted 15 February 2013 - 05:07 AM
#13
Posted 15 February 2013 - 11:51 AM
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.
#14
Posted 15 February 2013 - 12:44 PM
#15
Posted 17 February 2013 - 03:36 AM
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