[Zend Framework] Slug filter

While studying for my Zend Framework certification, I tried to build a small application to test my knowledge. One of the tasks in that project was to try and build a slug filter which converts a particular string to slug, for example, 'My Test String' to 'my-test-string'. It also checks for slug duplication if you are storing it into database table but for that you need to provide model object and field name to the filter.

Following is the code for slug filter, you can put the class file anywhere you want but I put it in application/models as SlugFilter.php because that directory was already added in include path.

PHP:
  1. /**
  2. * Slug filter
  3. *
  4. * Filter used to generate slug
  5. *
  6. * @copyright  2010 SANIsoft Technologies Pvt. Ltd.
  7. * @link       http://www.sanisoft.com/
  8. */
  9.  
  10. /**
  11. * Slug filter
  12. *
  13. * Filter used to generate slug
  14. *
  15. * @copyright  2010 SANIsoft Technologies Pvt. Ltd.
  16. * @version    Release: 1.0
  17. * @link       http://www.sanisoft.com/
  18. */
  19. class SlugFilter implements Zend_Filter_Interface
  20. {
  21.     var $field;
  22.  
  23.     var $model;
  24.  
  25.     /**
  26.      * Class constructor
  27.      *
  28.      * @param array $parameters Parameters used to define field name and model object
  29.      */
  30.     public function __construct(array $parameters)
  31.     {
  32.         // If field name and model object are provided then store them in class variables
  33.         if (isset($parameters['field']) && isset($parameters['model'])) {
  34.             $this->field = $parameters['field'];
  35.             $this->model = $parameters['model'];
  36.         }
  37.     }
  38.  
  39.     /**
  40.      * Method used to generate slug
  41.      *
  42.      * @param string $value String to generate its slug
  43.      *
  44.      * @return string Generated slug
  45.      */
  46.     public function filter($value, $conditions = null)
  47.     {
  48.         // Lowercase the string
  49.         $value = strtolower($value);
  50.  
  51.         // Generate slug by removing unwanted (other than alphanumeric and dash [-]) characters from the string
  52.         $value = preg_replace('/[^a-z0-9-]/i', '-', $value);
  53.         $value = preg_replace('/-[-]*/', '-', $value);
  54.         $value = preg_replace('/-$/', '', $value);
  55.         $value = preg_replace('/^-/', '', $value);
  56.  
  57.         // If field name and model object are provided then check for slug duplication
  58.         if ($this->field && $this->model) {
  59.             // Initialize variable used to store matching slugs for currently generated slug
  60.             $slugs = array();
  61.  
  62.             // Need to append ' AND ' to existing conditions
  63.             if ($conditions) {
  64.                 $conditions .= ' AND ';
  65.             }
  66.  
  67.             // Build conditions for slug duplication
  68.             $conditions .= $this->field . ' LIKE "' . $value . '%"';
  69.  
  70.             // Get matching slugs for currently generated slug
  71.             $select = $this->model->select();
  72.             $select->from($this->model, array($this->field))
  73.                    ->where($conditions);
  74.  
  75.             // Store matching slugs for currently generated slug
  76.             foreach ($this->model->fetchAll($select) as $record) {
  77.                 $slugs[] = $record->{$this->field};
  78.             }
  79.  
  80.             // If matching slugs for currently generated slug then try to append -1, -2, ... to currently generated slug to avoid duplication
  81.             if (0 <count($slugs) && in_array($value, $slugs)) {
  82.                 // Lets start duplication checking with index as 1
  83.                 $index = 1;
  84.  
  85.                 // Check for slug duplication
  86.                 while (true) {
  87.                     // If currently generated slug is not duplicate then use it
  88.                     if (!in_array($value . '-' . $index, $slugs)) {
  89.                         $value .= '-' . $index;
  90.                         break;
  91.                     }
  92.  
  93.                     // Increment counter by 1
  94.                     $index++;
  95.                 }
  96.             }
  97.         }
  98.  
  99.         // Return generated slug
  100.         return $value;
  101.     }
  102. }

Well, how to use this filter? Let me show you below. I used it for categories, so the category add code was something like

PHP:
  1. // Categories model's object
  2. $categories = new Categories();
  3.  
  4. // Slug filter's object
  5. $slug = new SlugFilter(array('field' => 'slug', 'model' => $categories));
  6.  
  7. // Create empty row object
  8. $row = $categories->createRow();
  9.  
  10. // Set category's name
  11. $row->name = $form->getValue('name');
  12.  
  13. // Generate slug for category using its name
  14. $row->slug = $slug->filter($form->getValue('name'));
  15.  
  16. // Add category
  17. $row->save();

assuming categories database table had 'name' and 'slug' fields.

Usually slug once generated does not change, especially if it's being used in URLs. But if you still want to re-generate it while editing category then following code should do that

PHP:
  1. // Categories model's object
  2. $categories = new Categories();
  3.  
  4. // Slug filter's object
  5. $slug = new SlugFilter(array('field' => 'slug', 'model' => $categories));
  6.  
  7. // Category's id
  8. $id = (int)$form->getValue('id');
  9.  
  10. // Get existing row object
  11. $row = $categories->fetchRow('id = ' . $id);
  12.  
  13. // Set category's name
  14. $row->name = $form->getValue('name');
  15.  
  16. // Re-generate slug for category using its name
  17. $row->slug = $slug->filter($form->getValue('name'), 'id != ' . $id);
  18.  
  19. // Edit category
  20. $row->save();

The difference between add and edit code? we provided condition for re-generating slug to avoid slug duplication.

That's it for now!!!

As usual, your suggestions and comments are most welcome :)

About Amit Badkas

Amit Badkas is Zend certified PHP5 and Zend Framework engineer, and has been working in SANIsoft for past 9 years, his present designation is 'Technical Manager'
No comments yet.

Leave a Reply