Yii 1.1: slug-behavior

SlugBehavior
12 followers

*This extension is now updated to work with non latin characters and and works with 1.2

This extension is to save pretty url's from titles to be used as unique identifier's

Note it overwrite's beforeSave... use:

return parent::beforeSave();

In your model if it contains beforeSave method..

http://www.yiiframework.com/forum/index.php?/topic/4485-slug-behavior/

Discussion

http://www.yiiframework.com/forum/index.php?/topic/4485-slug-behavior/

Documentation

Requirements

one primary key like id, use utf8 strings

Installation

  • Extract the release file under models/behaviors

Usage

Add in model..

public function behaviors(){
    return array(
        'SlugBehavior' => array(
            'class' => 'application.models.behaviors.SlugBehavior',
            'slug_col' => 'slug',
            'title_col' => 'title',
            'max_slug_chars' => 125,
            'overwrite' => false
        )
    );
}

Table example:

CREATE TABLE IF NOT EXISTS `pages` (
    `id` int(11) NOT NULL AUTO_INCREMENT,
    `title` varchar(125) NOT NULL,
    `slug` varchar(125) NOT NULL,
    `body` text,
    PRIMARY KEY (`id`),
    UNIQUE KEY `slug` (`slug`),
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=0 ;

*Aditional notes becarefull of non latin languages they need to be displayed normally with urldecode(); and take up alot of database characters as they are long like %23# etc :P

Change Log

June 3, 2010

Generate slug only if it needs to be saved.. don't clean non latin characters..

September 22, 2009

  • Update uses primary key to see if the slug has changed so it not keeps changing the slug

September 20, 2009

  • Initial release.

Total 7 comments

#2510 report it
SniperZero at 2011/01/12 06:50am
Awesome but...

Nice extension but I had to make the following changes to make it work.

add:
public $max_slug_chars = 125;
 
change
 
63: $matches = $this->getMatches($this->Owner->slug);
to
63: $matches = $this->getMatches($this->Owner->{$this->slug_col});
 
and change
70: $ar_matches[] = $match->slug;
to
70: $ar_matches[] = $match->{$this->slug_col};

Otherwise it seems to work nicely

#434 report it
mech7 at 2010/06/03 09:55am
Hey :)

Thanks for the comments guys.. I have updated the Behavior with your suggestions... I also replaced the iconv with the same functions from Wordpress as I had some problems with iconv and asian languages.. Now they are still being saved as slug :)

And it will not generate the slug anymore if it does not have to be saved ;)

And it will work on Yii 1.1 / 1.2..

@zitter.. My behavior did check if the slug was unique before ;) and would add the database id if there was a duplicate.. only with asian characters it would be empty.

#436 report it
zitter at 2010/06/02 01:30pm
Cool behavior

Mech7 thanks for sharing this. If you want to improve I suggest you to make slug 'unique'. IMO in most cases slug has sense if it is unique; so I think you could add a mechanism to make each slug unique.

#1152 report it
jerry2801 at 2009/11/21 11:43am
Good!

on svn 1.1 version, must be beforeSave($event) ?

#1248 report it
scythah at 2009/10/14 02:06pm
Code got messed up

The final code block there (main config) has code missing. Markdown was dropping anything inside ' that was also inside <>. It should be:

return array(
    ...
    'components'=>array(
        ...
        'urlManager'=>array(
            'urlFormat'=>'path',
            'showScriptName'=>false,
            'rules'=>array(
                ...
                '&lt;slug:[a-zA-Z0-9_ -]+>'=>'pages/show/slug/&lt;slug>',
                ...
            ),
        ),
        ...
    ),
);
#1249 report it
scythah at 2009/10/14 02:01pm
Updated slugBehaviour

I've modified this behaviour a bit inline with jonah's suggestions and my own thoughts.

Most of my changes are to the function beforesave(). I also changed getSlug() to return the AR by ID.

SlugBehavior.php

<?php
/**
 * SlugBehavior
 *
 * Saves pretty url's from titles to be used as unique identifier's
 * 
 * @author Chris de Kok <chris.de.kok@gmail.com>
 * @copyright Copyright (c) 2009 Chris de Kok. (http://mech7.net)
 *
 */
class SlugBehavior extends CActiveRecordBehavior {

/**
* The column name for the unqiue url
*/
public $slug_col = 'slug';

/**
* The column name for the title
*/
public $title_col = 'title';

/**
 * Primary key column name needs to be an id
 * @var string
 */
private $pk_col;

/**
 * Character to replace spaces
 * @var string
 */
public $replace_space = '-';

/**
 * Overwrite slug when updating
 */
public $overwrite = true;

/**
 * Before saving to database
 */
public function beforeSave() {
    $this->pk_col = $this->Owner->getMetaData()->tableSchema->primaryKey;

    $new_slug = $this->makeSlug($this->Owner->{$this->title_col});
    $old_slug = $this->getSlug($this->Owner->{$this->pk_col})->{$this->slug_col};

    if($new_slug != $old_slug)
    {
    /*
     * Either this is a new page, or the page title has been updated
     */
    if (!$old_slug || $this->overwrite === true)
    {
        $this->Owner->{$this->slug_col} = $new_slug;
    }
    }
    return true;
}

/**
 * Lookup current slug for a page
 * @param string $id
 */
public function getSlug($id){
    $slug=$this->Owner->findByPk($id);

    return $slug;
}

/**
 * Convert string to slug 
 * @param string $title
 * @return mixed $slug
 */
public function makeSlug($title){
    $clean = iconv('UTF-8', 'ASCII//TRANSLIT', $title);
    $clean = preg_replace("/[^a-zA-Z0-9\/_| -]/", '', $clean);
    $clean = strtolower(trim($clean));
    $clean = preg_replace("/[\/_| -]+/", $this->replace_space, $clean);

    return $clean;
}
}

I added into my protected/config/params.php file:

return array(
...

// Overwrite page slugs when updating
'overwriteSlugs'=>false,
);

Which allows me to change whether slugs get updated for the entire site via a single location.

Then in my Model files (where I want the slugs, leaving all of the other options at their default):

public function behaviors(){
    return array(
        'SlugBehavior' => array(
        'class' => 'application.models.behaviors.SlugBehavior',
        'overwrite' => Yii::app()->params['overwriteSlugs'],
        ),
    );
}

In my controllers I can load pages using:

/**
 * Returns the data model based on the primary key given in the GET variable.
 * If the data model is not found, an HTTP exception will be raised.
 * @param integer the primary key value. Defaults to null, meaning using the 'id' GET variable
 */
protected function loadPage($id=null,$slug=null)
{
    if($this->_page===null)
    {
        if($slug!==null || isset($_GET['slug']))
        {
            $criteria=new CDbCriteria;
            $criteria->condition='slug=:slug';
            $criteria->params=array(':slug'=>Pages::model()->makeSlug($_GET['slug']));
            $this->_page=Pages::model()->find($criteria);
        }
        if($id!==null || isset($_GET['id']))
            $this->_page=Pages::model()->findbyPk($id!==null ? $id : $_GET['id']);
        if($this->_page===null)
            throw new CHttpException(404,'The requested page does not exist.');
    }
    return $this->_page;
}

Which gets the slugs passed to it from the urlManager (in config/main.php):

return array(
    ...
'components'=>array(
            ...
    'urlManager'=>array(
        'urlFormat'=>'path',
        'showScriptName'=>false,
        'rules'=>array(
            'tag/<tag>'=>'post/list',
            'posts'=>'post/list',
            'post/<id:\d+>'=>'post/show',
            'post/update/<id:\d+>'=>'post/update',
            '<slug:[a-zA-Z0-9_ -]+>'=>'pages/show/slug/<slug>',
        ),
    ),
    ...
),
);

Hope this helps someone in getting slugs working how they (or at least I) expect.

#1304 report it
jonah at 2009/09/22 05:27pm
Cool!

good job!

Some notes:

1) pk_col attribute could be protected 2) This behavior generates the slug even if it's not going to be saved 3) there could be a condition in getMatches() so that it does not return the AR in question. This will allow you to get rid of a whole foreach loop. 4) perhaps prefix slug with the PK value, so that you don't have to worry about making sure there are no duplicate slugs.

Leave a comment

Please to leave your comment.

Create extension
  • Yii Version: 1.1
  • License: New BSD License
  • Developed by: mech7
  • Category: Others
  • Votes: +13
  • Downloaded: 1,501 times
  • Created on: Sep 20, 2009
  • Last updated: Jun 4, 2010