SproutCMS

This is the code documentation for the SproutCMS project

source of /sprout/Helpers/Slug.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\RowMissingException;
  17. use Sprout\Exceptions\ValidationException;
  18.  
  19.  
  20. /**
  21.  * Generate and validate slugs - i.e. unique, SEO-friendly URL segments.
  22.  * See e.g. https://en.wikipedia.org/wiki/Semantic_URL#Slug
  23.  */
  24. class Slug
  25. {
  26.  
  27. /**
  28.   * Create a slug, and ensure it's unique
  29.   *
  30.   * @param string $name The name of the item
  31.   * @param string $table The table the item is on
  32.   * @return string The generated slug, should always be unique
  33.   **/
  34. public static function create($table, $name)
  35. {
  36. $base = Enc::urlname($name, '-');
  37.  
  38. $index = 0;
  39. do {
  40. $trial = $base . ($index > 0 ? $index : '');
  41. try {
  42. self::get($table, $trial);
  43. } catch (RowMissingException $exp) {
  44. // No existing record found with that slug, so it's available
  45. return $trial;
  46. }
  47. } while ($index++ < 100);
  48.  
  49. return $base . '-' . Sprout::randStr(20);
  50. }
  51.  
  52.  
  53. /**
  54.   * Return all columns for a single row of a table (similar to {@see Pdb::get})
  55.   * The row is specified using its slug.
  56.   *
  57.   * @param string $table The table name, not prefixed
  58.   * @param string $slug The slug of the record to fetch
  59.   * @param string $conditions Extra WHERE clause if required, in the format prescribed by {@see Pdb::buildClause}
  60.   * @return array The record data
  61.   * @throws RowMissingException If the record wasn't found
  62.   * @throws QueryException if the query failed
  63.   */
  64. public static function get($table, $slug, array $conditions = [])
  65. {
  66. Pdb::validateIdentifier($table);
  67.  
  68. $params = [$slug];
  69. $q = "SELECT * FROM ~{$table} WHERE slug = ?";
  70. if (!empty($conditions)) {
  71. $q .= " AND " . Pdb::buildClause($conditions, $params);
  72. }
  73. return Pdb::q($q, $params, 'row');
  74. }
  75.  
  76.  
  77. /**
  78.   * Verify that a slug contains only valid characters
  79.   *
  80.   * @example
  81.   * $valid->check('slug', 'Slug::valid');
  82.   *
  83.   * @param string $value The slug
  84.   * @throws ValidationException
  85.   */
  86. public static function valid($value)
  87. {
  88. if (preg_match('/^[a-z0-9\-]+$/', $value) !== 1) {
  89. throw new ValidationException('contains invalid characters. Slugs may only contain a to z (lower-case), 0 to 9 and hyphens (-)');
  90. }
  91. }
  92.  
  93. /**
  94.   * Verify that a slug is unique for a given table
  95.   *
  96.   * @example
  97.   * // Exclude the current record when checking uniqueness
  98.   * $valid->check('slug', 'Slug::unique', 'pages', [['id', '!=', $page_id]]);
  99.   *
  100.   * @param string $value The slug
  101.   * @param string $table The table to check, which must contain columns 'id', 'name', and 'slug'
  102.   * @param array $conditions Extra conditions to check, in the format prescribed by {@see Pdb::buildClause}
  103.   * @return void
  104.   * @throws ValidationException If the slug isn't unique in the table under the specified conditions
  105.   * @throws InvalidArgumentException If the $table argument is not a valid table name
  106.   */
  107. public static function unique($value, $table, array $conditions = [])
  108. {
  109. Pdb::validateIdentifier($table);
  110.  
  111. $conditions['slug'] = $value;
  112.  
  113. $params = [];
  114. $where = Pdb::buildClause($conditions, $params);
  115. try {
  116. $q = "SELECT id, name FROM ~{$table} WHERE {$where}";
  117. $row = Pdb::q($q, $params, 'row');
  118.  
  119. $err = 'The slug "' . $value . '" is already in use';
  120. if (!empty($row['name'])) {
  121. $err .= ' by the record "' . $row['name'] . '"';
  122. }
  123.  
  124. throw new ValidationException($err);
  125. } catch (RowMissingException $exp) {
  126. }
  127. }
  128. }
  129.