PageRenderTime 41ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/models/behaviors/sluggable.php

http://github.com/CakeDC/utils
PHP | 175 lines | 93 code | 18 blank | 64 comment | 24 complexity | f8cd084353bf999037370c90b9d4129d MD5 | raw file
Possible License(s): MIT
  1. <?php
  2. /**
  3. * Copyright 2007-2010, Cake Development Corporation (http://cakedc.com)
  4. *
  5. * Licensed under The MIT License
  6. * Redistributions of files must retain the above copyright notice.
  7. *
  8. * @copyright Copyright 2007-2010, Cake Development Corporation (http://cakedc.com)
  9. * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
  10. */
  11. App::import('Core', 'Multibyte');
  12. /**
  13. * Utils Plugin
  14. *
  15. * Utils Sluggable Behavior
  16. *
  17. * @package utils
  18. * @subpackage utils.models.behaviors
  19. */
  20. class SluggableBehavior extends ModelBehavior {
  21. /**
  22. * Settings to configure the behavior
  23. *
  24. * @var array
  25. */
  26. public $settings = array();
  27. /**
  28. * Default settings
  29. *
  30. * label - The field used to generate the slug from
  31. * slug - The field to store the slug in
  32. * scope - conditions for the find query to check if the slug already exists
  33. * separator - the character used to separate the words in the slug
  34. * length - the maximum length of the slug
  35. * unique - check if the slug is unique
  36. * update - update the slug or not
  37. * trigger - defines a property in the model that has to be true to generate the slug
  38. *
  39. * Note that trigger will temporary bypass update and act like update is set to true.
  40. *
  41. * @var array
  42. */
  43. protected $_defaults = array(
  44. 'label' => 'title',
  45. 'slug' => 'slug',
  46. 'scope' => array(),
  47. 'separator' => '_',
  48. 'length' => 255,
  49. 'unique' => true,
  50. 'update' => false,
  51. 'trigger' => false);
  52. /**
  53. * Initiate behaviour
  54. *
  55. * @param object $Model
  56. * @param array $settings
  57. */
  58. public function setup(Model $Model, $settings = array()) {
  59. $this->settings[$Model->alias] = array_merge($this->_defaults, $settings);
  60. }
  61. /**
  62. * beforeSave callback
  63. *
  64. * @param object $Model
  65. */
  66. public function beforeSave(Model $Model) {
  67. $settings = $this->settings[$Model->alias];
  68. if (is_string($this->settings[$Model->alias]['trigger'])) {
  69. if ($Model->{$this->settings[$Model->alias]['trigger']} != true) {
  70. return;
  71. }
  72. }
  73. if (empty($Model->data[$Model->alias])) {
  74. return;
  75. } else if (empty($Model->data[$Model->alias][$this->settings[$Model->alias]['label']])) {
  76. return;
  77. } else if (!$this->settings[$Model->alias]['update'] && !empty($Model->id) && !is_string($this->settings[$Model->alias]['trigger'])) {
  78. return;
  79. }
  80. $slug = $Model->data[$Model->alias][$settings['label']];
  81. if (method_Exists($Model, 'beforeSlugGeneration')) {
  82. $slug = $Model->beforeSlugGeneration($slug, $settings['separator']);
  83. }
  84. $settings = $this->settings[$Model->alias];
  85. if (method_exists($Model, 'multibyteSlug')) {
  86. $slug = $Model->multibyteSlug($slug, $settings['separator']);
  87. } else {
  88. $slug = $this->multibyteSlug($Model, $slug);
  89. }
  90. if ($settings['unique'] === true || is_array($settings['unique'])) {
  91. $slug = $this->makeUniqueSlug($Model, $slug);
  92. }
  93. if (!empty($Model->whitelist) && !in_array($settings['slug'], $Model->whitelist)) {
  94. $Model->whitelist[] = $settings['slug'];
  95. }
  96. $Model->data[$Model->alias][$settings['slug']] = $slug;
  97. }
  98. /**
  99. * Searche if the slug already exists and if yes increments it
  100. *
  101. * @param object $Model
  102. * @param string the raw slug
  103. * @return string The incremented unique slug
  104. *
  105. */
  106. public function makeUniqueSlug(Model $Model, $slug = '') {
  107. $settings = $this->settings[$Model->alias];
  108. $conditions = array();
  109. if ($settings['unique'] === true) {
  110. $conditions[$Model->alias . '.' . $settings['slug'] . ' LIKE'] = $slug . '%';
  111. } else if (is_array($settings['unique'])) {
  112. foreach ($settings['unique'] as $field) {
  113. $conditions[$Model->alias . '.' . $field] = $Model->data[$Model->alias][$field];
  114. }
  115. $conditions[$Model->alias . '.' . $settings['slug'] . ' LIKE'] = $slug . '%';
  116. }
  117. if (!empty($Model->id)) {
  118. $conditions[$Model->alias . '.' . $Model->primaryKey . ' !='] = $Model->id;
  119. }
  120. $conditions = array_merge($conditions, $settings['scope']);
  121. $duplicates = $Model->find('all', array(
  122. 'recursive' => -1,
  123. 'conditions' => $conditions,
  124. 'fields' => array($settings['slug'])));
  125. if (!empty($duplicates)) {
  126. $duplicates = Set::extract($duplicates, '{n}.' . $Model->alias . '.' . $settings['slug']);
  127. $startSlug = $slug;
  128. $index = 1;
  129. while ($index > 0) {
  130. if (!in_array($startSlug . $settings['separator'] . $index, $duplicates)) {
  131. $slug = $startSlug . $settings['separator'] . $index;
  132. $index = -1;
  133. }
  134. $index++;
  135. }
  136. }
  137. return $slug;
  138. }
  139. /**
  140. * Generates a slug from a (multibyte) string
  141. *
  142. * @param object $Model
  143. * @param string $string
  144. * @return string
  145. */
  146. public function multibyteSlug(Model $Model, $string = null) {
  147. $str = mb_strtolower($string);
  148. $str = preg_replace('/\xE3\x80\x80/', ' ', $str);
  149. $str = preg_replace('[\'s ]', 's ', $str);
  150. $str = str_replace($this->settings[$Model->alias]['separator'], ' ', $str);
  151. $str = preg_replace( '#[:\#\*"()~$^{}`@+=;,<>!&%\.\]\/\'\\\\|\[]#', "\x20", $str );
  152. $str = str_replace('?', '', $str);
  153. $str = trim($str);
  154. $str = preg_replace('#\x20+#', $this->settings[$Model->alias]['separator'], $str);
  155. return $str;
  156. }
  157. }