Skip to content

Instantly share code, notes, and snippets.

@meotimdihia
Created April 5, 2011 01:21
Show Gist options
  • Save meotimdihia/902833 to your computer and use it in GitHub Desktop.
Save meotimdihia/902833 to your computer and use it in GitHub Desktop.
<?php
/**
* Copyright 2007-2010, Cake Development Corporation (http://cakedc.com)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2007-2010, Cake Development Corporation (http://cakedc.com)
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
/**
* Utils Plugin
*
* Utils Tiny Sluggable Behavior
*
* @package utils
* @subpackage utils.models.behaviors
*/
class TinySluggableBehavior extends ModelBehavior {
/**
* Settings to configure the behavior
*
* @var array
*/
public $settings = array();
/**
* Default settings
*
* @var array
*/
protected $_defaults = array(
'tinySlug' => 'tiny_slug',
'codeset' => '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ',
'order' => 'created'
);
/**
* Initiate behavior - The Model must have a field for the tiny_slug along with a "created" field
*
* @param object $Model
* @param array $settings Settings for the behavior. Keys:
* - tinySlug: name of the tiny slug field in the table [default: tiny_slug]
* - codeset: valid characters for tiny slug [default: 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ]
*/
public function setup(&$Model, $settings = array()) {
$this->settings[$Model->alias] = array_merge($this->_defaults, $settings);
$Model->tinySlug = $this->settings[$Model->alias]['tinySlug'];
$this->settings[$Model->alias]['base'] = strlen($this->settings[$Model->alias]['codeset']);
}
/**
* beforeSave callback
*
* @param object $Model
*/
public function beforeSave(&$Model) {
if (empty($Model->data[$Model->alias])) {
return;
}
if (empty($Model->data[$Model->alias][$Model->tinySlug])) {
$Model->data[$Model->alias][$Model->tinySlug] = $this->__getNextSlug($Model);
}
return true;
}
/**
* Calculates the next available slug and returns it
*
* @return string next avalible tiny slug
*/
private function __getNextSlug(&$Model) {
$new = '';
$prev = $Model->find('first', array(
'contain' => array(),
'fields' => array("{$Model->alias}.{$Model->tinySlug}", "{$Model->alias}." . $this->settings[$Model->alias]['order']),
'order' => "{$Model->alias}.{$this->settings[$Model->alias]['order']} DESC"));
if (empty($prev)) {
$new = $this->settings[$Model->alias]['codeset'][0];
} else {
$new = $this->__toShort($Model, (string) $this->__toDecimal($Model, $prev[$Model->alias][$Model->tinySlug]) + 1);
$attempts = 0;
$maxAttempts = 5; // Overriden after the first attempt
$new = $prev[$Model->alias][$Model->tinySlug];
// Check if this slug does not already exists
do {
if ($attempts == 1) {
$maxAttempts = $Model->find('count', array(
'conditions' => array(
$Model->alias . '.' . $this->settings[$Model->alias]['order'] => $prev[$Model->alias][$this->settings[$Model->alias]['order']])));
}
$new = $this->__toShort($Model, $this->__toDecimal($Model, $new) + 1);
$existing = $Model->find('count', array(
'conditions' => array(
$Model->alias . '.' . $Model->tinySlug => $new)));
$attempts++;
} while (!empty($existing) && $attempts < $maxAttempts);
}
return $new;
}
/**
* Calculates the
*
* @param int $decimal the decimal to convert
* @return string
*/
private function __toShort(&$Model, $decimal) {
$codeSet = $this->settings[$Model->alias]['codeset'];
$base = $this->settings[$Model->alias]['base'];
$short = '';
while ($decimal > 0) {
$short = substr($codeSet, ($decimal % $base), 1) . $short;
$decimal = floor($decimal / $base);
}
return $short;
}
/**
* Converts a tiny slug into an integer
*
* @param string $short
* @return integer
*/
private function __toDecimal(&$Model, $short) {
$codeSet = $this->settings[$Model->alias]['codeset'];
$base = $this->settings[$Model->alias]['base'];
$decimal = 0;
for ($i = strlen($short); $i; $i--) {
$decimal += strpos($codeSet, substr($short, (-1 * ( $i - strlen($short) )), 1)) * pow($base, $i - 1);
}
return $decimal;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment