BreadcrumbsHelper.php 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  1. <?php
  2. /**
  3. * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
  4. * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
  5. *
  6. * Licensed under The MIT License
  7. * For full copyright and license information, please see the LICENSE.txt
  8. * Redistributions of files must retain the above copyright notice.
  9. *
  10. * @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
  11. * @link http://cakephp.org CakePHP(tm) Project
  12. * @since 3.3.6
  13. * @license http://www.opensource.org/licenses/mit-license.php MIT License
  14. */
  15. namespace Cake\View\Helper;
  16. use Cake\View\Helper;
  17. use Cake\View\StringTemplateTrait;
  18. use LogicException;
  19. /**
  20. * BreadcrumbsHelper to register and display a breadcrumb trail for your views
  21. *
  22. * @property \Cake\View\Helper\UrlHelper $Url
  23. */
  24. class BreadcrumbsHelper extends Helper
  25. {
  26. use StringTemplateTrait;
  27. /**
  28. * Other helpers used by BreadcrumbsHelper
  29. *
  30. * @var array
  31. */
  32. public $helpers = ['Url'];
  33. /**
  34. * Default config for the helper.
  35. *
  36. * @var array
  37. */
  38. protected $_defaultConfig = [
  39. 'templates' => [
  40. 'wrapper' => '<ul{{attrs}}>{{content}}</ul>',
  41. 'item' => '<li{{attrs}}><a href="{{link}}"{{innerAttrs}}>{{title}}</a></li>{{separator}}',
  42. 'itemWithoutLink' => '<li{{attrs}}><span{{innerAttrs}}>{{title}}</span></li>{{separator}}',
  43. 'separator' => '<li{{attrs}}><span{{innerAttrs}}>{{custom}}{{separator}}</span></li>'
  44. ]
  45. ];
  46. /**
  47. * The crumbs list.
  48. *
  49. * @var array
  50. */
  51. protected $crumbs = [];
  52. /**
  53. * Add a crumb to the trail.
  54. *
  55. * @param string $title Title of the crumb
  56. * @param string|array|null $link Link of the crumb. Either a string, an array of route params to pass to
  57. * Url::build() or null / empty if the crumb does not have a link
  58. * @param array $options Array of options
  59. * @return $this
  60. */
  61. public function add($title, $link = null, array $options = [])
  62. {
  63. $this->crumbs[] = ['title' => $title, 'link' => $link, 'options' => $options];
  64. return $this;
  65. }
  66. /**
  67. * Prepend a crumb to the start of the queue.
  68. *
  69. * @param string $title Title of the crumb
  70. * @param string|array|null $link Link of the crumb. Either a string, an array of route params to pass to
  71. * Url::build() or null / empty if the crumb does not have a link
  72. * @param array $options Array of options
  73. * @return $this
  74. */
  75. public function prepend($title, $link = null, array $options = [])
  76. {
  77. array_unshift($this->crumbs, ['title' => $title, 'link' => $link, 'options' => $options]);
  78. return $this;
  79. }
  80. /**
  81. * Insert a crumb at a specific index.
  82. *
  83. * If the index already exists, the new crumb will be inserted,
  84. * and the existing element will be shifted one index greater.
  85. *
  86. * @param int $index The index to insert at.
  87. * @param string $title Title of the crumb
  88. * @param string|array|null $link Link of the crumb. Either a string, an array of route params to pass to
  89. * Url::build() or null / empty if the crumb does not have a link
  90. * @param array $options Array of options
  91. * @return $this
  92. */
  93. public function insertAt($index, $title, $link = null, array $options = [])
  94. {
  95. array_splice($this->crumbs, $index, 0, [['title' => $title, 'link' => $link, 'options' => $options]]);
  96. return $this;
  97. }
  98. /**
  99. * Insert a crumb before the first matching crumb with the specified title.
  100. *
  101. * Finds the index of the first middleware that matches the provided class,
  102. * and inserts the supplied callable before it.
  103. *
  104. * @param string $matchingTitle The title of the crumb you want to insert this one before
  105. * @param string $title Title of the crumb
  106. * @param string|array|null $link Link of the crumb. Either a string, an array of route params to pass to
  107. * Url::build() or null / empty if the crumb does not have a link
  108. * @param array $options Array of options
  109. * @return $this
  110. */
  111. public function insertBefore($matchingTitle, $title, $link = null, array $options = [])
  112. {
  113. $key = $this->findCrumb($matchingTitle);
  114. if ($key !== null) {
  115. return $this->insertAt($key, $title, $link, $options);
  116. }
  117. throw new LogicException(sprintf("No crumb matching '%s' could be found.", $matchingTitle));
  118. }
  119. /**
  120. * Insert a crumb after the first matching crumb with the specified title.
  121. *
  122. * Finds the index of the first middleware that matches the provided class,
  123. * and inserts the supplied callable before it.
  124. *
  125. * @param string $matchingTitle The title of the crumb you want to insert this one after
  126. * @param string $title Title of the crumb
  127. * @param string|array|null $link Link of the crumb. Either a string, an array of route params to pass to
  128. * Url::build() or null / empty if the crumb does not have a link
  129. * @param array $options Array of options
  130. * @return $this
  131. */
  132. public function insertAfter($matchingTitle, $title, $link = null, array $options = [])
  133. {
  134. $key = $this->findCrumb($matchingTitle);
  135. if ($key !== null) {
  136. return $this->insertAt($key + 1, $title, $link, $options);
  137. }
  138. throw new LogicException(sprintf("No crumb matching '%s' could be found.", $matchingTitle));
  139. }
  140. /**
  141. * Returns the crumbs list
  142. *
  143. * @return array
  144. */
  145. public function getCrumbs()
  146. {
  147. return $this->crumbs;
  148. }
  149. /**
  150. * Renders the breadcrumbs trail
  151. *
  152. * @param array $attributes Array of attributes applied to the wrapper element
  153. * @param array $separator Array of attributes for the `separator` template
  154. * @return string The breadcrumbs trail
  155. */
  156. public function render(array $attributes = [], array $separator = [])
  157. {
  158. $crumbs = $this->crumbs;
  159. $crumbsCount = count($crumbs);
  160. $templater = $this->templater();
  161. $separatorParams = [];
  162. if ($separator) {
  163. if (isset($separator['innerAttrs'])) {
  164. $separatorParams['innerAttrs'] = $templater->formatAttributes($separator['innerAttrs']);
  165. unset($separator['innerAttrs']);
  166. }
  167. if (isset($separator['separator'])) {
  168. $separatorParams['separator'] = $separator['separator'];
  169. unset($separator['separator']);
  170. }
  171. if (isset($separator['templateVars'])) {
  172. $separatorParams['templateVars'] = $separator['templateVars'];
  173. unset($separator['templateVars']);
  174. }
  175. $separatorParams['attrs'] = $templater->formatAttributes($separator);
  176. }
  177. $crumbTrail = '';
  178. foreach ($crumbs as $key => $crumb) {
  179. $link = $this->prepareLink($crumb['link']);
  180. $title = $crumb['title'];
  181. $options = $crumb['options'];
  182. $optionsLink = [];
  183. if (isset($options['innerAttrs'])) {
  184. $optionsLink = $options['innerAttrs'];
  185. unset($options['innerAttrs']);
  186. }
  187. $template = 'item';
  188. $templateParams = [
  189. 'attrs' => $templater->formatAttributes($options, ['templateVars']),
  190. 'innerAttrs' => $templater->formatAttributes($optionsLink),
  191. 'title' => $title,
  192. 'link' => $link,
  193. 'separator' => '',
  194. 'templateVars' => isset($options['templateVars']) ? $options['templateVars'] : []
  195. ];
  196. if (!($link)) {
  197. $template = 'itemWithoutLink';
  198. }
  199. if ($separatorParams && $key !== ($crumbsCount - 1)) {
  200. $templateParams['separator'] = $this->formatTemplate('separator', $separatorParams);
  201. }
  202. $crumbTrail .= $this->formatTemplate($template, $templateParams);
  203. }
  204. $crumbTrail = $this->formatTemplate('wrapper', [
  205. 'content' => $crumbTrail,
  206. 'attrs' => $templater->formatAttributes($attributes, ['templateVars']),
  207. 'templateVars' => isset($attributes['templateVars']) ? $attributes['templateVars'] : []
  208. ]);
  209. return $crumbTrail;
  210. }
  211. /**
  212. * Search a crumb in the current stack which title matches the one provided as argument.
  213. * If found, the index of the matching crumb will be returned.
  214. *
  215. * @param string $title Title to find
  216. * @return int|null Index of the crumb found, or null if it can not be found
  217. */
  218. protected function findCrumb($title)
  219. {
  220. foreach ($this->crumbs as $key => $crumb) {
  221. if ($crumb['title'] === $title) {
  222. return $key;
  223. }
  224. }
  225. return null;
  226. }
  227. /**
  228. * Prepare the URL for a specific `link` param of a crumb
  229. *
  230. * @param array|string|null $link If array, an array of Router url params
  231. * If string, will be used as is
  232. * If empty, will consider that there is no link
  233. *
  234. * @return null|string The URL of a crumb
  235. */
  236. protected function prepareLink($link = null)
  237. {
  238. if (!$link) {
  239. return null;
  240. }
  241. return $this->Url->build($link);
  242. }
  243. }