SproutCMS

This is the code documentation for the SproutCMS project

source of /sprout/Helpers/Tags.php

  1. <?php
  2. /*
  3.  * Copyright (C) 2017 Karmabunny Pty Ltd.
  4.  *
  5.  * This file is a part of SproutCMS.
  6.  *
  7.  * SproutCMS is free software: you can redistribute it and/or modify it under the terms
  8.  * of the GNU General Public License as published by the Free Software Foundation, either
  9.  * version 2 of the License, or (at your option) any later version.
  10.  *
  11.  * For more information, visit <http://getsproutcms.com>.
  12.  */
  13.  
  14. namespace Sprout\Helpers;
  15.  
  16. use karmabunny\pdb\Exceptions\QueryException;
  17.  
  18.  
  19. /**
  20. * No description yet.
  21. **/
  22. class Tags
  23. {
  24.  
  25. /**
  26.   * Returns all of the tags for a given record
  27.   *
  28.   * @param string $table
  29.   * @param int $record_id
  30.   * @return array Tags
  31.   * @throws QueryException
  32.   */
  33. public static function byRecord($table, $record_id)
  34. {
  35. $q = "SELECT name
  36. FROM ~tags
  37. WHERE record_table = ? AND record_id = ?
  38. GROUP BY name
  39. ORDER BY name";
  40. return Pdb::q($q, [$table, $record_id], 'col');
  41. }
  42.  
  43.  
  44. /**
  45.   * Returns all of the tags for a given table
  46.   *
  47.   * @param string $table
  48.   * @return array Tags
  49.   * @throws QueryException
  50.   */
  51. public static function byTable($table)
  52. {
  53. $q = "SELECT DISTINCT name
  54. FROM ~tags
  55. WHERE record_table = ?
  56. ORDER BY name";
  57. return Pdb::q($q, [$table], 'col');
  58. }
  59.  
  60.  
  61. /**
  62.   * Returns all of the records for a given table/tag
  63.   *
  64.   * @param string $table Table name
  65.   * @param string $tag Tag name
  66.   * @return array Record IDs
  67.   * @throws QueryException
  68.   */
  69. public static function findRecords($table, $tag)
  70. {
  71. $q = "SELECT DISTINCT record_id
  72. FROM ~tags
  73. WHERE record_table = ? AND name = ?
  74. ORDER BY name";
  75. return Pdb::query($q, [$table, $tag], 'col');
  76. }
  77.  
  78.  
  79. /**
  80.   * Return the tags for a table, along with the number of records for each tag
  81.   *
  82.   * @example
  83.   * $tags = Tags::recordCounts('blog_posts', 50);
  84.   * foreach ($tags as $tag => $count) {
  85.   * echo Enc::html($tag), ' (', $count, ')';
  86.   * }
  87.   *
  88.   * @param string $table Table to get counts for, e.g 'blog_posts'
  89.   * @param int $limit Maximum number of tags to return. Use 999999 for "unlimited".
  90.   * @return array Keys are tag names, values are counts
  91.   */
  92. public static function recordCounts($table, $limit)
  93. {
  94. Pdb::validateIdentifier($table);
  95. $limit = (int) $limit;
  96. $q = "SELECT tag.name, COUNT(item.id) AS num
  97. FROM ~tags AS tag
  98. INNER JOIN ~{$table} AS item
  99. ON tag.record_table = ? AND tag.record_id = item.id
  100. GROUP BY tag.name
  101. ORDER BY COUNT(item.id) DESC, tag.name
  102. LIMIT {$limit}";
  103. return Pdb::query($q, [$table], 'map');
  104. }
  105.  
  106.  
  107. /**
  108.   * Return the tags for a table, along with the number of records for each tag
  109.   * Results are filtered by subsite, including records with subsite_id of NULL which is all sites
  110.   *
  111.   * @example
  112.   * $tags = Tags::recordCountsPerSubsite('blog_posts', 50, SubsiteSelector::$subsite_id);
  113.   * foreach ($tags as $tag => $count) {
  114.   * echo Enc::html($tag), ' (', $count, ')';
  115.   * }
  116.   *
  117.   * @param string $table Table to get counts for, e.g 'blog_posts'
  118.   * @param int $limit Maximum number of tags to return. Use 999999 for "unlimited".
  119.   * @param int $subsite_id Subsite to return record counts for; NULL for current subsite
  120.   * @return array Keys are tag names, values are counts
  121.   */
  122. public static function recordCountsPerSubsite($table, $limit, $subsite_id = null)
  123. {
  124. Pdb::validateIdentifier($table);
  125. $limit = (int) $limit;
  126. if ($subsite_id === null) {
  127. $subsite_id = SubsiteSelector::$subsite_id;
  128. }
  129.  
  130. $q = "SELECT tag.name, COUNT(item.id) AS num
  131. FROM ~tags AS tag
  132. INNER JOIN ~{$table} AS item
  133. ON tag.record_table = ? AND tag.record_id = item.id
  134. WHERE item.subsite_id = ? OR item.subsite_id IS NULL
  135. GROUP BY tag.name
  136. ORDER BY COUNT(item.id) DESC, tag.name
  137. LIMIT {$limit}";
  138. return Pdb::query($q, [$table, $subsite_id], 'map');
  139. }
  140.  
  141.  
  142. /**
  143.   * Returns all tags which begin with a given string
  144.   *
  145.   * @param string $prefix Search prefix
  146.   * @return array Tags
  147.   * @throws QueryException
  148.   */
  149. public static function beginsWith($prefix)
  150. {
  151. $q = "SELECT DISTINCT name
  152. FROM ~tags
  153. WHERE name LIKE CONCAT(?, '%');
  154. ORDER BY name";
  155. return Pdb::query($q, [Pdb::likeEscape($prefix)], 'col');
  156. }
  157.  
  158.  
  159. /**
  160.   * Returns common tags. Typically tags are returned from the provided table, but not necessarily.
  161.   *
  162.   * @param string $table
  163.   * @param string $prefix
  164.   * @param int $number
  165.   * @return array Tags
  166.   * @throws QueryException
  167.   */
  168. public static function suggestTags($table = null, $prefix = '', $number = 10)
  169. {
  170. $number = (int) $number;
  171.  
  172. $tags = [];
  173. $where = '1';
  174. $values = [];
  175.  
  176. if ($prefix) {
  177. $values['prefix'] = Pdb::likeEscape($prefix);
  178. $where = "name LIKE CONCAT(:prefix, '%')";
  179. }
  180.  
  181. // Load tags from this table
  182. if ($table) {
  183. $values['table'] = $table;
  184. $q = "SELECT name
  185. FROM ~tags
  186. WHERE record_table = :table AND {$where}
  187. GROUP BY name
  188. ORDER BY COUNT(record_id) DESC, name
  189. LIMIT {$number}";
  190. $tags = Pdb::q($q, $values, 'col');
  191.  
  192. $where .= " AND record_table != :table";
  193. }
  194.  
  195. // If not enough found, load from all other tables
  196. if (count($tags) < $number) {
  197. $q = "SELECT name
  198. FROM ~tags
  199. WHERE {$where}
  200. GROUP BY name
  201. ORDER BY COUNT(record_id) DESC, name
  202. LIMIT {$number}";
  203. $tags = array_merge($tags, Pdb::q($q, $values, 'col'));
  204. }
  205.  
  206. $tags = array_unique($tags);
  207. $tags = array_slice($tags, 0, $number);
  208.  
  209. return $tags;
  210. }
  211.  
  212.  
  213. /**
  214.   * Updates tags for a given record
  215.   *
  216.   * @param string $table The table name of the record which is being updated
  217.   * @param int $record_id The record to update
  218.   * @param array $new_tags Array of tag names of the new tags
  219.   * @param bool $remove Should removals be processed? Set to FALSE to only add tags. Default true.
  220.   * @return bool True on success, false on failure
  221.   * @throws QueryException
  222.   */
  223. public static function update($table, $record_id, array $new_tags, $remove = true)
  224. {
  225. if ($table == '') return false;
  226. if ($record_id == 0) return false;
  227.  
  228. Pdb::transact();
  229.  
  230. try {
  231. // Determine which tags need adding and removing
  232. $current_tags = Tags::byRecord($table, $record_id);
  233. $add_tags = array_diff($new_tags, $current_tags);
  234. $del_tags = array_diff($current_tags, $new_tags);
  235.  
  236. // Add tags that should be added
  237. foreach ($add_tags as $tag) {
  238. Pdb::insert('tags', ['record_table' => $table, 'record_id' => $record_id, 'name' => $tag]);
  239. }
  240.  
  241. // Remove tags that should be added
  242. if ($remove and count($del_tags) > 0) {
  243. Pdb::delete('tags', ['record_table' => $table, 'record_id' => $record_id, ['name', 'IN', $del_tags]]);
  244. }
  245.  
  246. Pdb::commit();
  247. return true;
  248.  
  249. } catch (QueryException $ex) {
  250. Pdb::rollback();
  251. return false;
  252. }
  253. }
  254.  
  255.  
  256. /**
  257.   * Converts a comma-separated string into a list of tags
  258.   *
  259.   * @param string $string
  260.   * @return array Tags
  261.   */
  262. public static function splitupTags($string)
  263. {
  264. $string = strtolower($string);
  265. $string = preg_replace('/[^-a-z0-9 ,]/', '', $string);
  266. $string = trim($string, ' ,');
  267.  
  268. if ($string == '') return array();
  269.  
  270. $tags = preg_split('/ *, */', $string);
  271. $tags = array_filter($tags);
  272. $tags = array_unique($tags);
  273.  
  274. return array_merge($tags);
  275. }
  276.  
  277.  
  278. /**
  279.   * Get HTML for a tag list (i.e. for tag display on the front-end)
  280.   *
  281.   * @param array $tags
  282.   * @return string HTML
  283.   */
  284. public static function getList(array $tags = null)
  285. {
  286. if (empty($tags)) return null;
  287.  
  288. sort($tags);
  289.  
  290. $view = new View('sprout/tag_list');
  291. $view->tags = $tags;
  292. echo $view->render();
  293. }
  294.  
  295. }
  296.  
  297.  
  298.