PageRenderTime 52ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/models/behaviors/sluggable.php

https://github.com/LubosRemplik/CakePHP-Slug-Plugin
PHP | 241 lines | 219 code | 7 blank | 15 comment | 26 complexity | 21ffd05e780525ec128d238ff5fb4136 MD5 | raw file
  1. <?php
  2. class SluggableBehavior extends ModelBehavior {
  3. /*
  4. * Diacritic characters (Czech)
  5. */
  6. var $_diacritic = array (
  7. 'á', 'ä', 'č', 'ď', 'é', 'ě', 'í', 'ĺ', 'ľ', 'ň', 'ó', 'ô', 'ő', 'ö',
  8. 'ŕ', 'š', 'ť', 'ú', 'ů', 'ű', 'ü', 'ý', 'ř', 'ž', 'Á', 'Ä', 'Č', 'Ď',
  9. 'É', 'Ě', 'Í', 'Ĺ', 'Ľ', 'Ň', 'Ó', 'Ô', 'Ő', 'Ö', 'Ŕ', 'Š', 'Ť', 'Ú',
  10. 'Ů', 'Ű', 'Ü', 'Ý', 'Ř', 'Ž'
  11. );
  12. var $_diacRemoved = array (
  13. 'a', 'a', 'c', 'd', 'e', 'e', 'i', 'l', 'l', 'n', 'o', 'o', 'o', 'o',
  14. 'r', 's', 't', 'u', 'u', 'u', 'u', 'y', 'r', 'z', 'A', 'A', 'C', 'D',
  15. 'E', 'E', 'I', 'L', 'L', 'N', 'O', 'O', 'O', 'O', 'R', 'S', 'T', 'U',
  16. 'U', 'U', 'U', 'Y', 'R', 'Z'
  17. );
  18. function setup(&$model, $settings = array()) {
  19. if (!isset($this->settings[$model->alias])) {
  20. $this->settings[$model->alias] = array(
  21. 'field' => null,
  22. 'saveField' => 'slug',
  23. 'override' => false,
  24. 'separator' => '-',
  25. 'length' => 100,
  26. 'translation' => 'utf-8'
  27. );
  28. }
  29. $this->settings[$model->alias] = array_merge($this->settings[$model->alias], (array) $settings);
  30. }
  31. public function beforeValidate(&$model) {
  32. $this->createSlug($model);
  33. return true;
  34. }
  35. public function createSlug(&$model) {
  36. extract($this->settings[$model->alias]);
  37. if(is_null($field)) {
  38. if($model->hasField('name')) {
  39. $field = 'name';
  40. } elseif($model->hasField('title')) {
  41. $field = 'title';
  42. }
  43. }
  44. $slug = '';
  45. if(!empty($model->data[$model->alias][$field]) && empty($model->data[$model->alias][$saveField])) {
  46. $base = $slug = $this->__slug($model, str_replace($this->_diacritic, $this->_diacRemoved, $model->data[$model->alias][$field]));
  47. $conditions = array();
  48. if(!empty($this->settings[$model->alias]['unique'])) {
  49. $uniqueFields = array();
  50. if(!is_array($this->settings[$model->alias]['unique'])) {
  51. $uniqueFields = array($this->settings[$model->alias]['unique']);
  52. } else {
  53. $uniqueFields = $this->settings[$model->alias]['unique'];
  54. }
  55. foreach($uniqueFields as $uniqueField) {
  56. if(isset($model->data[$model->alias][$uniqueField])) {
  57. $conditions = am($conditions, array(
  58. $model->alias . '.' . $uniqueField . ' =' => $model->data[$model->alias][$uniqueField]
  59. ));
  60. }
  61. }
  62. }
  63. if($model->id) {
  64. $conditions = am($conditions, array(
  65. "{$model->alias}.{$model->primaryKey} !=" => $model->id
  66. ));
  67. }
  68. $i = 1;
  69. while($model->hasAny(am($conditions, array("{$model->alias}.{$saveField}" => $slug))) == true) {
  70. $slug = $base . $separator . $i;
  71. $i++;
  72. }
  73. $model->data[$model->alias][$saveField] = $slug;
  74. }
  75. return $slug;
  76. }
  77. /**
  78. * Generate a slug for the given string using specified settings.
  79. *
  80. * @param string $string String from where to generate slug
  81. * @param array $settings Settings to use (looks for 'separator' and 'length')
  82. * @return string Slug for given string
  83. * @access private
  84. */
  85. function __slug(&$model, $string) {
  86. $settings = $this->settings[$model->alias];
  87. $defaultTranslations = array(
  88. 'iso-8859-1' => array(
  89. chr(128).chr(131).chr(138).chr(142).chr(154).chr(158)
  90. .chr(159).chr(162).chr(165).chr(181).chr(192).chr(193).chr(194)
  91. .chr(195).chr(196).chr(197).chr(199).chr(200).chr(201).chr(202)
  92. .chr(203).chr(204).chr(205).chr(206).chr(207).chr(209).chr(210)
  93. .chr(211).chr(212).chr(213).chr(214).chr(216).chr(217).chr(218)
  94. .chr(219).chr(220).chr(221).chr(224).chr(225).chr(226).chr(227)
  95. .chr(228).chr(229).chr(231).chr(232).chr(233).chr(234).chr(235)
  96. .chr(236).chr(237).chr(238).chr(239).chr(241).chr(242).chr(243)
  97. .chr(244).chr(245).chr(246).chr(248).chr(249).chr(250).chr(251)
  98. .chr(252).chr(253).chr(255),
  99. 'EfSZsz' . 'YcYuAAA' . 'AAACEEE' . 'EIIIINO' . 'OOOOOUU' . 'UUYaaaa' . 'aaceeee' . 'iiiinoo' . 'oooouuu' . 'uyy',
  100. array(chr(140), chr(156), chr(198), chr(208), chr(222), chr(223), chr(230), chr(240), chr(254)),
  101. array('OE', 'oe', 'AE', 'DH', 'TH', 'ss', 'ae', 'dh', 'th')
  102. ),
  103. 'utf-8' => array(
  104. array(
  105. // Decompositions for Latin-1 Supplement
  106. chr(195).chr(128) => 'A', chr(195).chr(129) => 'A',
  107. chr(195).chr(130) => 'A', chr(195).chr(131) => 'A',
  108. chr(195).chr(132) => 'A', chr(195).chr(133) => 'A',
  109. chr(195).chr(135) => 'C', chr(195).chr(136) => 'E',
  110. chr(195).chr(137) => 'E', chr(195).chr(138) => 'E',
  111. chr(195).chr(139) => 'E', chr(195).chr(140) => 'I',
  112. chr(195).chr(141) => 'I', chr(195).chr(142) => 'I',
  113. chr(195).chr(143) => 'I', chr(195).chr(145) => 'N',
  114. chr(195).chr(146) => 'O', chr(195).chr(147) => 'O',
  115. chr(195).chr(148) => 'O', chr(195).chr(149) => 'O',
  116. chr(195).chr(150) => 'O', chr(195).chr(153) => 'U',
  117. chr(195).chr(154) => 'U', chr(195).chr(155) => 'U',
  118. chr(195).chr(156) => 'U', chr(195).chr(157) => 'Y',
  119. chr(195).chr(159) => 's', chr(195).chr(160) => 'a',
  120. chr(195).chr(161) => 'a', chr(195).chr(162) => 'a',
  121. chr(195).chr(163) => 'a', chr(195).chr(164) => 'a',
  122. chr(195).chr(165) => 'a', chr(195).chr(167) => 'c',
  123. chr(195).chr(168) => 'e', chr(195).chr(169) => 'e',
  124. chr(195).chr(170) => 'e', chr(195).chr(171) => 'e',
  125. chr(195).chr(172) => 'i', chr(195).chr(173) => 'i',
  126. chr(195).chr(174) => 'i', chr(195).chr(175) => 'i',
  127. chr(195).chr(177) => 'n', chr(195).chr(178) => 'o',
  128. chr(195).chr(179) => 'o', chr(195).chr(180) => 'o',
  129. chr(195).chr(181) => 'o', chr(195).chr(182) => 'o',
  130. chr(195).chr(182) => 'o', chr(195).chr(185) => 'u',
  131. chr(195).chr(186) => 'u', chr(195).chr(187) => 'u',
  132. chr(195).chr(188) => 'u', chr(195).chr(189) => 'y',
  133. chr(195).chr(191) => 'y',
  134. // Decompositions for Latin Extended-A
  135. chr(196).chr(128) => 'A', chr(196).chr(129) => 'a',
  136. chr(196).chr(130) => 'A', chr(196).chr(131) => 'a',
  137. chr(196).chr(132) => 'A', chr(196).chr(133) => 'a',
  138. chr(196).chr(134) => 'C', chr(196).chr(135) => 'c',
  139. chr(196).chr(136) => 'C', chr(196).chr(137) => 'c',
  140. chr(196).chr(138) => 'C', chr(196).chr(139) => 'c',
  141. chr(196).chr(140) => 'C', chr(196).chr(141) => 'c',
  142. chr(196).chr(142) => 'D', chr(196).chr(143) => 'd',
  143. chr(196).chr(144) => 'D', chr(196).chr(145) => 'd',
  144. chr(196).chr(146) => 'E', chr(196).chr(147) => 'e',
  145. chr(196).chr(148) => 'E', chr(196).chr(149) => 'e',
  146. chr(196).chr(150) => 'E', chr(196).chr(151) => 'e',
  147. chr(196).chr(152) => 'E', chr(196).chr(153) => 'e',
  148. chr(196).chr(154) => 'E', chr(196).chr(155) => 'e',
  149. chr(196).chr(156) => 'G', chr(196).chr(157) => 'g',
  150. chr(196).chr(158) => 'G', chr(196).chr(159) => 'g',
  151. chr(196).chr(160) => 'G', chr(196).chr(161) => 'g',
  152. chr(196).chr(162) => 'G', chr(196).chr(163) => 'g',
  153. chr(196).chr(164) => 'H', chr(196).chr(165) => 'h',
  154. chr(196).chr(166) => 'H', chr(196).chr(167) => 'h',
  155. chr(196).chr(168) => 'I', chr(196).chr(169) => 'i',
  156. chr(196).chr(170) => 'I', chr(196).chr(171) => 'i',
  157. chr(196).chr(172) => 'I', chr(196).chr(173) => 'i',
  158. chr(196).chr(174) => 'I', chr(196).chr(175) => 'i',
  159. chr(196).chr(176) => 'I', chr(196).chr(177) => 'i',
  160. chr(196).chr(178) => 'IJ',chr(196).chr(179) => 'ij',
  161. chr(196).chr(180) => 'J', chr(196).chr(181) => 'j',
  162. chr(196).chr(182) => 'K', chr(196).chr(183) => 'k',
  163. chr(196).chr(184) => 'k', chr(196).chr(185) => 'L',
  164. chr(196).chr(186) => 'l', chr(196).chr(187) => 'L',
  165. chr(196).chr(188) => 'l', chr(196).chr(189) => 'L',
  166. chr(196).chr(190) => 'l', chr(196).chr(191) => 'L',
  167. chr(197).chr(128) => 'l', chr(197).chr(129) => 'L',
  168. chr(197).chr(130) => 'l', chr(197).chr(131) => 'N',
  169. chr(197).chr(132) => 'n', chr(197).chr(133) => 'N',
  170. chr(197).chr(134) => 'n', chr(197).chr(135) => 'N',
  171. chr(197).chr(136) => 'n', chr(197).chr(137) => 'N',
  172. chr(197).chr(138) => 'n', chr(197).chr(139) => 'N',
  173. chr(197).chr(140) => 'O', chr(197).chr(141) => 'o',
  174. chr(197).chr(142) => 'O', chr(197).chr(143) => 'o',
  175. chr(197).chr(144) => 'O', chr(197).chr(145) => 'o',
  176. chr(197).chr(146) => 'OE',chr(197).chr(147) => 'oe',
  177. chr(197).chr(148) => 'R',chr(197).chr(149) => 'r',
  178. chr(197).chr(150) => 'R',chr(197).chr(151) => 'r',
  179. chr(197).chr(152) => 'R',chr(197).chr(153) => 'r',
  180. chr(197).chr(154) => 'S',chr(197).chr(155) => 's',
  181. chr(197).chr(156) => 'S',chr(197).chr(157) => 's',
  182. chr(197).chr(158) => 'S',chr(197).chr(159) => 's',
  183. chr(197).chr(160) => 'S', chr(197).chr(161) => 's',
  184. chr(197).chr(162) => 'T', chr(197).chr(163) => 't',
  185. chr(197).chr(164) => 'T', chr(197).chr(165) => 't',
  186. chr(197).chr(166) => 'T', chr(197).chr(167) => 't',
  187. chr(197).chr(168) => 'U', chr(197).chr(169) => 'u',
  188. chr(197).chr(170) => 'U', chr(197).chr(171) => 'u',
  189. chr(197).chr(172) => 'U', chr(197).chr(173) => 'u',
  190. chr(197).chr(174) => 'U', chr(197).chr(175) => 'u',
  191. chr(197).chr(176) => 'U', chr(197).chr(177) => 'u',
  192. chr(197).chr(178) => 'U', chr(197).chr(179) => 'u',
  193. chr(197).chr(180) => 'W', chr(197).chr(181) => 'w',
  194. chr(197).chr(182) => 'Y', chr(197).chr(183) => 'y',
  195. chr(197).chr(184) => 'Y', chr(197).chr(185) => 'Z',
  196. chr(197).chr(186) => 'z', chr(197).chr(187) => 'Z',
  197. chr(197).chr(188) => 'z', chr(197).chr(189) => 'Z',
  198. chr(197).chr(190) => 'z', chr(197).chr(191) => 's',
  199. // Euro Sign
  200. chr(226).chr(130).chr(172) => 'E'
  201. )
  202. )
  203. );
  204. if(is_string($settings['translation'] && array_key_exists($settings['translation'], $defaultTranslations))) {
  205. $translation = $defaultTranslations[$settings['translation']];
  206. }
  207. // Run user-defined translation tables
  208. if (!empty($translation) && is_array($translation)) {
  209. if (count($translation) >= 2 && count($translation) % 2 == 0) {
  210. for($i = 0, $limiti = count($translation); $i < $limiti; $i += 2) {
  211. $from = $translation[$i];
  212. $to = $translation[$i + 1];
  213. if (is_string($from) && is_string($to)) {
  214. $string = strtr($string, $from, $to);
  215. } else {
  216. $string = r($from, $to, $string);
  217. }
  218. }
  219. } else if (count($translation) == 1) {
  220. $string = strtr($string, $translation[0]);
  221. }
  222. $string = low($string);
  223. }
  224. $string = low($string);
  225. $string = preg_replace('/[^a-z0-9_]/i', $settings['separator'], $string);
  226. $string = preg_replace('/' . preg_quote($settings['separator']) . '[' . preg_quote($settings['separator']) . ']*/', $settings['separator'], $string);
  227. if (strlen($string) > $settings['length']) {
  228. $string = substr($string, 0, $settings['length']);
  229. }
  230. $string = preg_replace('/' . preg_quote($settings['separator']) . '$/', '', $string);
  231. $string = preg_replace('/^' . preg_quote($settings['separator']) . '/', '', $string);
  232. return $string;
  233. }
  234. }