Taxonomy.php 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. <?php
  2. /**
  3. * @package Grav\Common
  4. *
  5. * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved.
  6. * @license MIT License; see LICENSE file for details.
  7. */
  8. namespace Grav\Common;
  9. use Grav\Common\Config\Config;
  10. use Grav\Common\Page\Collection;
  11. use Grav\Common\Page\Interfaces\PageInterface;
  12. use function is_string;
  13. /**
  14. * The Taxonomy object is a singleton that holds a reference to a 'taxonomy map'. This map is
  15. * constructed as a multidimensional array.
  16. *
  17. * uses the taxonomy defined in the site.yaml file and is built when the page objects are recursed.
  18. * Basically every time a page is found that has taxonomy references, an entry to the page is stored in
  19. * the taxonomy map. The map has the following format:
  20. *
  21. * [taxonomy_type][taxonomy_value][page_path]
  22. *
  23. * For example:
  24. *
  25. * [category][blog][path/to/item1]
  26. * [tag][grav][path/to/item1]
  27. * [tag][grav][path/to/item2]
  28. * [tag][dog][path/to/item3]
  29. */
  30. class Taxonomy
  31. {
  32. /** @var array */
  33. protected $taxonomy_map;
  34. /** @var Grav */
  35. protected $grav;
  36. /**
  37. * Constructor that resets the map
  38. *
  39. * @param Grav $grav
  40. */
  41. public function __construct(Grav $grav)
  42. {
  43. $this->taxonomy_map = [];
  44. $this->grav = $grav;
  45. }
  46. /**
  47. * Takes an individual page and processes the taxonomies configured in its header. It
  48. * then adds those taxonomies to the map
  49. *
  50. * @param PageInterface $page the page to process
  51. * @param array|null $page_taxonomy
  52. */
  53. public function addTaxonomy(PageInterface $page, $page_taxonomy = null)
  54. {
  55. if (!$page->published()) {
  56. return;
  57. }
  58. if (!$page_taxonomy) {
  59. $page_taxonomy = $page->taxonomy();
  60. }
  61. if (empty($page_taxonomy)) {
  62. return;
  63. }
  64. /** @var Config $config */
  65. $config = $this->grav['config'];
  66. $taxonomies = (array)$config->get('site.taxonomies');
  67. foreach ($taxonomies as $taxonomy) {
  68. // Skip invalid taxonomies.
  69. if (!\is_string($taxonomy)) {
  70. continue;
  71. }
  72. $current = $page_taxonomy[$taxonomy] ?? null;
  73. foreach ((array)$current as $item) {
  74. $this->iterateTaxonomy($page, $taxonomy, '', $item);
  75. }
  76. }
  77. }
  78. /**
  79. * Iterate through taxonomy fields
  80. *
  81. * Reduces [taxonomy_type] to dot-notation where necessary
  82. *
  83. * @param PageInterface $page The Page to process
  84. * @param string $taxonomy Taxonomy type to add
  85. * @param string $key Taxonomy type to concatenate
  86. * @param iterable|string $value Taxonomy value to add or iterate
  87. * @return void
  88. */
  89. public function iterateTaxonomy(PageInterface $page, string $taxonomy, string $key, $value)
  90. {
  91. if (is_iterable($value)) {
  92. foreach ($value as $identifier => $item) {
  93. $identifier = "{$key}.{$identifier}";
  94. $this->iterateTaxonomy($page, $taxonomy, $identifier, $item);
  95. }
  96. } elseif (is_string($value)) {
  97. if (!empty($key)) {
  98. $taxonomy = $taxonomy . $key;
  99. }
  100. $this->taxonomy_map[$taxonomy][(string) $value][$page->path()] = ['slug' => $page->slug()];
  101. }
  102. }
  103. /**
  104. * Returns a new Page object with the sub-pages containing all the values set for a
  105. * particular taxonomy.
  106. *
  107. * @param array $taxonomies taxonomies to search, eg ['tag'=>['animal','cat']]
  108. * @param string $operator can be 'or' or 'and' (defaults to 'and')
  109. * @return Collection Collection object set to contain matches found in the taxonomy map
  110. */
  111. public function findTaxonomy($taxonomies, $operator = 'and')
  112. {
  113. $matches = [];
  114. $results = [];
  115. foreach ((array)$taxonomies as $taxonomy => $items) {
  116. foreach ((array)$items as $item) {
  117. if (isset($this->taxonomy_map[$taxonomy][$item])) {
  118. $matches[] = $this->taxonomy_map[$taxonomy][$item];
  119. } else {
  120. $matches[] = [];
  121. }
  122. }
  123. }
  124. if (strtolower($operator) === 'or') {
  125. foreach ($matches as $match) {
  126. $results = array_merge($results, $match);
  127. }
  128. } else {
  129. $results = $matches ? array_pop($matches) : [];
  130. foreach ($matches as $match) {
  131. $results = array_intersect_key($results, $match);
  132. }
  133. }
  134. return new Collection($results, ['taxonomies' => $taxonomies]);
  135. }
  136. /**
  137. * Gets and Sets the taxonomy map
  138. *
  139. * @param array|null $var the taxonomy map
  140. * @return array the taxonomy map
  141. */
  142. public function taxonomy($var = null)
  143. {
  144. if ($var) {
  145. $this->taxonomy_map = $var;
  146. }
  147. return $this->taxonomy_map;
  148. }
  149. /**
  150. * Gets item keys per taxonomy
  151. *
  152. * @param string $taxonomy taxonomy name
  153. * @return array keys of this taxonomy
  154. */
  155. public function getTaxonomyItemKeys($taxonomy)
  156. {
  157. return isset($this->taxonomy_map[$taxonomy]) ? array_keys($this->taxonomy_map[$taxonomy]) : [];
  158. }
  159. }