diff --git a/src/Pluf/DB.php b/src/Pluf/DB.php index e99391f..8a44eef 100644 --- a/src/Pluf/DB.php +++ b/src/Pluf/DB.php @@ -116,6 +116,8 @@ function Pluf_DB_defaultTypecast() array('Pluf_DB_IdentityFromDb', 'Pluf_DB_PasswordToDb'), 'Pluf_DB_Field_Sequence' => array('Pluf_DB_IntegerFromDb', 'Pluf_DB_IntegerToDb'), + 'Pluf_DB_Field_Slug' => + array('Pluf_DB_IdentityFromDb', 'Pluf_DB_SlugToDb'), 'Pluf_DB_Field_Text' => array('Pluf_DB_IdentityFromDb', 'Pluf_DB_IdentityToDb'), 'Pluf_DB_Field_Varchar' => @@ -214,3 +216,6 @@ function Pluf_DB_PasswordToDb($val, $db) { return $db->esc('sha1:'.$salt.':'.sha1($salt.$val)); } +function Pluf_DB_SlugToDB($val, $db) { + return $db->esc(Pluf_DB_Field_Slug::slugify($val)); +} diff --git a/src/Pluf/DB/Field/Slug.php b/src/Pluf/DB/Field/Slug.php new file mode 100644 index 0000000..cc9bbbf --- /dev/null +++ b/src/Pluf/DB/Field/Slug.php @@ -0,0 +1,71 @@ +slug-separator. Default to -. + * + * @param $value string Value to convert + * @return string The slugify version. + */ + public static function slugify($value) + { + $separator = Pluf::f('slug-separator', '-'); + $value = Pluf_Text_UTF8::romanize(Pluf_Text_UTF8::deaccent($value)); + $value = preg_replace('#[^'.$separator.'\w]#u', + $separator, + mb_strtolower($value, Pluf::f('encoding', 'UTF-8'))); + + // remove redundant + $value = preg_replace('#'.$separator.'{2,}#u', + $separator, + trim($value, $separator)); + + return $value; + } + +} diff --git a/src/Pluf/Form/Field/Slug.php b/src/Pluf/Form/Field/Slug.php new file mode 100644 index 0000000..d8d5861 --- /dev/null +++ b/src/Pluf/Form/Field/Slug.php @@ -0,0 +1,111 @@ +help_text, $this->empty_values)) { + $this->help_text = __('The “slug” is the URL-friendly'. + ' version of the name, consisting of '. + 'letters, numbers, underscores or hyphens.'); + } + $this->_error_messages = array( + 'min_size' => __('Ensure this value has at most %1$d characters (it has %2$d).'), + 'max_size' => __('Ensure this value has at least %1$d characters (it has %2$d).') + ); + + parent::__construct($params); + } + + /** + * Removes any character not allowed and valid the size of the field. + * + * @see Pluf_Form_Field::clean() + * @throws Pluf_Form_Invalid If the lenght of the field has not a valid size. + */ + public function clean($value) + { + parent::clean($value); + if ($value) { + $value = Pluf_DB_Field_Slug::slugify($value); + $len = mb_strlen($value, Pluf::f('encoding', 'UTF-8')); + if ($this->max_size < $len) { + throw new Pluf_Form_Invalid(sprintf($this->_error_messages['max_size'], + $this->max_size, + $len)); + } + if ($this->min_size > $len) { + throw new Pluf_Form_Invalid(sprintf($this->_error_messages['min_size'], + $this->min_size, + $len)); + } + } + else + $value = ''; + + return $value; + } + + /** + * @see Pluf_Form_Field::widgetAttrs() + */ + public function widgetAttrs($widget) + { + $attrs = array(); + if (!isset($widget->attrs['maxlength'])) { + $attrs['maxlength'] = $this->max_size; + } else { + $this->max_size = $widget->attrs['maxlength']; + } + + return $attrs; + } +} diff --git a/src/Pluf/Tests/Model/SlugField.php b/src/Pluf/Tests/Model/SlugField.php new file mode 100644 index 0000000..1d93cdc --- /dev/null +++ b/src/Pluf/Tests/Model/SlugField.php @@ -0,0 +1,124 @@ +_a['verbose'] = 'slug'; + $this->_a['table'] = 'slug'; + $this->_a['model'] = __CLASS__; + $this->_a['cols'] = array( + 'id' => array( + 'type' => 'Pluf_DB_Field_Sequence', + 'blank' => true, + ), + 'slug_default_length' => array( + 'type' => 'Pluf_DB_Field_Slug', + ), + 'slug_custom_legnth' => array( + 'type' => 'Pluf_DB_Field_Slug', + 'widget_attrs' => array( + 'maxlength' => 5, + ), + ), + ); + } +} + +class Pluf_Tests_Model_SlugField extends UnitTestCase +{ + + function __construct() + { + parent::__construct('Test the slug field.'); + } + + function testSlugifyLowercase() + { + $slug = Pluf_DB_Field_Slug::slugify('Pluf'); + $this->assertEqual('pluf', $slug); + } + + function testSlugifyReplaceWhiteSpaces() + { + // replaces a white space by the separator + $slug = Pluf_DB_Field_Slug::slugify('ceondo pluf'); + $this->assertEqual('ceondo-pluf', $slug); + + // replaces several white spaces by a single the separator + $slug = Pluf_DB_Field_Slug::slugify('ceondo pluf'); + $this->assertEqual('ceondo-pluf', $slug); + + // removes separator at the bound of a string + $slug = Pluf_DB_Field_Slug::slugify(' ceondo pluf'); + $this->assertEqual('ceondo-pluf', $slug); + $slug = Pluf_DB_Field_Slug::slugify('ceondo pluf '); + $this->assertEqual('ceondo-pluf', $slug); + } + + function testSlugifyNonASCII() + { + // replaces non-ASCII characters by the separator + $slug = Pluf_DB_Field_Slug::slugify('ceondo,pluf'); + $this->assertEqual('ceondo-pluf', $slug); + $slug = Pluf_DB_Field_Slug::slugify('ceondo€pluf'); + $this->assertEqual('ceondo-pluf', $slug); + + // replaces accents by their equivalent non-accented + $slug = Pluf_DB_Field_Slug::slugify('éiùàñ'); + $this->assertEqual('eiuan', $slug); + } + + function testSlugifyWithCustomSeparator() + { + $backup = $GLOBALS['_PX_config']; + $GLOBALS['_PX_config']['slug-separator'] = '_'; + + $slug = Pluf_DB_Field_Slug::slugify('ceondo pluf'); + $this->assertEqual('ceondo_pluf', $slug); + $slug = Pluf_DB_Field_Slug::slugify('ceondo pluf'); + $this->assertEqual('ceondo_pluf', $slug); + + $GLOBALS['_PX_config'] = $backup; + } + + function testCreate() + { + $db = Pluf::db(); + $schema = new Pluf_DB_Schema($db); + $m = new Pluf_Tests_Model_SlugField_Model(); + $schema->model = $m; + $schema->createTables(); + + $m->slug_default_length = 'Pluf, supported by Céondo Ltd.'; + $m->create(); + $this->assertEqual(1, $m->id); + + $m = new Pluf_Tests_Model_SlugField_Model(1); + $this->assertEqual('pluf-supported-by-ceondo-ltd', $m->slug_default_length); + $schema->dropTables(); + } +} \ No newline at end of file