Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use beforeAction in Yii or slug troubles

Tags:

php

yii

Yii-jedis!

I'm working on some old Yii-project and must to add to them some features. Yii is quite logical framework but it has some things I couldn't understand. Perhaps I haven't understand Yii-way yet. So I'll describe my problem step-by-step. For impatients - briefly question at the end.

Intro: I want to add human-readable URLs to my project. Now URLs looks like: www.site.com/article/359
And I want them to look like this: www.site.com/article/how-to-make-pretty-urls
Very important: old articles must be available on old format URLs, and new - on new URLs.

Step 1: First, I've updated rewrite rules in config/main.php:

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

And I've added new texturl column to article table. So we will store here human-readable-part-of-url for new articles. Then I've updated one article with texturl for tests.

Step 2: Application show articles in actionView of ArticleController so I've added there this code for preproccessing ID parameter:

if (is_numeric($id)) {
    // User try to get /article/359
    $model = $this->loadModel($id); // Article::model()->findByPk($id);
    if ($model->text_url !== null) {
        // If article with ID=359 have text url -> redirect to /article/text-url
        $this->redirect(array('view', 'id' => $model->text_url), true, 301);
    }
} else {
    // User try to get /article/text-url
    $model = Article::model()->findByAttributes(array('text_url' => $id));
    $id = ($model !== null) ? $model->id : null ;
}

And then begin legacy code:

$model = $this->loadModel($id); // Load article by numeric ID
// etc

It works perfectly! But...

Step 3: But we have many actions with ID parameter! What we have to do? Update all actions with that code? I think it's ugly. I've found CController::beforeAction method. Looks good! So I declare beforeAction and place ID preproccessing there:

protected function beforeAction($action) {
    $actionToRun = $action->getId(); 
    $id = Yii::app()->getRequest()->getQuery('id');
    if (is_numeric($id)) {
        $model = $this->loadModel($id);
        if ($model->text_url !== null) {
            $this->redirect(array('view', 'id' => $model->text_url), true, 301);
        } 
    } else {
        $model = Article::model()->findByAttributes(array('text_url' => $id));
        $id = ($model !== null) ? $model->id : null ;
    }
    return parent::beforeAction($action->runWithParams(array('id' => $id)));
}

Yes, it works with both URL-formats, but it executes actionView TWICE and shows page two times! What can I do with this? I've totally confused. Have I choose a right way to solve my problem?

Briefly: Can I proceess ID (GET-parameter) before execute of any actions and then run requested action (once!) with modified only ID parameter?

like image 222
Alex Belyaev Avatar asked Aug 08 '12 05:08

Alex Belyaev


2 Answers

Last line should be:

return parent::beforeAction($action);

Also to ask you i didnt get your step:3.

As you said you have many controller and you don't need to write code in each file, so you are using beforeAction: But you have only text_url related to article for all controllers??

$model = Article::model()->findByAttributes(array('text_url' => $id));

===== updated answer ======

I have changed this function, check now.

If $id is not nummeric then we will find it's id using model and set $_GET['id'], so in further controller it will use that numberic id.

protected function beforeAction($action) {          
    $id = Yii::app()->getRequest()->getQuery('id');
    if(!is_numeric($id)) // $id = how-to-make-pretty-urls
    {
        $model = Article::model()->findByAttributes(array('text_url' => $id));
        $_GET['id'] = $model->id ; 
    }
    return parent::beforeAction($action);
}
like image 53
VibhaJ Avatar answered Oct 13 '22 11:10

VibhaJ


Sorry, I haven't read it all carefully but have you considered using this extension?

like image 38
Boaz Rymland Avatar answered Oct 13 '22 11:10

Boaz Rymland