Uri Space Replacement With Underscores Like Wikipedia

I am creating a website for books like hostname.com/book/title. So for obvious reasons it is likely that someone will add spaces in the title which I want to avoid and replace them with underscores when found.

Without yii my approach after the Rewrite rule was:


 if(isset($_GET['title'])){

	if(count(explode(' ',$_GET['title'])) > 1){

		header("Location: /".$_GET['target']."/".str_replace(" ","_",$_GET['title']));

	}

Where would I include it best? I suppose as early as possible without changes in the framework itself. And how would it be done in yii?

there more than one way to tackle this problem you could probably add an extra field in your database and create a url and then use that field as a url wordpress, joomla, drupal do it or alternatively you could create a helper to generate urls for you

I guess i wasn’t clear anough with my question:

Even though some user types in "hostname.com/book/book title" I want that my application redirects the request to "hostname.com/book/book_title" which would be the correct URI. Just as in Wikipedia when you try to access an URI with spaces like en.wikipedia.org/wiki/Albert Einstein and it gets redirected to en.wikipedia.org/wiki/Albert_Einstein

if thats the case you can redirect what you have already done. but I would suggest you to use yii’s builtin redirect method

If I understand you right it would be something like:




if(isset($_GET['title'])){

   if(count(explode(' ',$_GET['title'])) > 1){

      $this->redirect(Yii::app()->user->'controller/'.str_replace(" ","_",$_GET['title']));

   }

}



Where would be a good place to include this lines - I guess the earlier the better in script to save time. What do you think?

Is there any reason why I would not want to put some redirection in my yii/index.php right in the beginning? That should be the fastest.

Yii’s builtin redirect method doesn’t help me here, since I also want to URI to change - just like Wikipedia.

something like:


if(count(explode(' ',$_SERVER[REQUEST_URI])) > 1){

      header("Location: /"str_replace(" ","_",$_SERVER[REQUEST_URI]));

   }

What you need is this: http://www.yiiframew.../slug-behavior/

It will by default clean out all funky characters and replace spaces with a dash.

I am using it, and it works great.

What you need to change is these lines (line 130 onwards) in the behavior:







        $title = strtolower($title);

        $title = preg_replace('/&.+?;/', '', $title); // kill entities

        $title = str_replace('.', '-', $title);

        $title = preg_replace('/[^%a-z0-9 _-]/', '', $title);

        $title = preg_replace('/\s+/', '-', $title);

        $title = preg_replace('|-+|', '-', $title);

        $title = trim($title, '-');



You could change it to this:





        $title = strtolower($title);

        $title = preg_replace('/&.+?;/', '', $title); // kill entities

        $title = str_replace('.', '_', $title);

        $title = preg_replace('/[^%a-z0-9 _-]/', '', $title);

        $title = preg_replace('/\s+/', '_', $title);

        $title = preg_replace('|-+|', '_', $title);

        $title = trim($title, '_');



Not tested. :)

Then you need to adjust your controller(s) to accept the slug instead of the id:





    public function getPost() {

        if(isset($_GET['slug'])){

            return $this->loadModel($_GET['slug']);

        }

    }







    public function loadModel($slug) {

        if ($this->_model === null) {

            if (Yii::app()->user->isGuest)

                $condition = 'status="' . Post::STATUS_PUBLISHED . '" OR status="' . Post::STATUS_ARCHIVED . '"';

            else

                $condition='';

            $this->_model = Post::model()->findByAttributes(array('slug' => $slug), $condition);

            if ($this->_model === null)

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

        }

        return $this->_model;

    }







    public function actionView($slug) {

        $post = $this->loadModel($slug);


	$this->pageDescription = $post->description;

	$this->pageKeywords = $post->tags->toString();


        $comment = $this->newComment($post);


        $this->render('view', array(

            'model' => $post,

            'comment' => $comment,

        ));

    }



And the URL rules:





                ''=>'post/index',

                '<slug:[a-z0-9-]+>'=>'post/view',

                'page/<slug:[a-z0-9-]+>'=>'page/view',



Thank you for your reply but as far as I can see, all that would not change the URI displayed in the browser.

My aim though would be that people have always displayed the URI with underlines in order not to be able to send a link to a friend they copy&paste from the browser with spaces in it.

Therefore I thought first about the header("Location: thing and wanted to put it as early as possible so save time when loading.

So I put the lines


if(count(explode('%20',$_SERVER['REQUEST_URI'])) > 1){

      header("Location: ".str_replace("%20","_",$_SERVER['REQUEST_URI']));

   }

into the yii/index.php right in the beginning and so far it works as I want it to. Since Apache’s mod_rewrite changes spaces by default to ‘%20’ I used it instead.

Even better would be probably to tell Apache to exchange the spaces to underlines in the first place.

I explained how you would do that. You simply need to sanitize the title of the post/whatever before saving it.

This is much better than relying on rewrite rules and/or header tricks, and is also good for performance reasons.

Sorry - I didn’t see the obvious. It is not about the titles in the database. I am only talking about the case when a user types in the book title in his browser URI line. Therefore I used the example with Wikipedia. When you try there to change the URI in your browser, you get to another article if it exists. And when you put spaces in the URI they will be removed automatically and exchanged for underlines. That was all I wanted to do.

Sorry again for not expressing myself precisely enough.

ps. what you explained before is useful anyways - thanks for that!

Oh, my bad. :)

Yes, you definitely need some rewrite rules then. You can’t do this from Yii.

I know zero about that.