WebOrbiton
v1.0.0.1

StocketBase

129 lines · 3.8 KB
  1. <?php
  2. ​
  3. declare(strict_types=1);
  4. ​
  5. require_once __DIR__ . '/database.php';
  6. require_once __DIR__ . '/slugger.php';
  7. ​
  8. final class ProductTags
  9. {
  10. public const MAX_TAGS = 10;
  11. public const NAME_MAX = 60;
  12. ​
  13. private static ?bool $enabled = null;
  14. ​
  15. public static function isEnabled(): bool
  16. {
  17. if (self::$enabled !== null) {
  18. return self::$enabled;
  19. }
  20. ​
  21. try {
  22. $statement = Database::site()->prepare('SELECT setting_value FROM site_settings WHERE setting_key = :key LIMIT 1');
  23. $statement->execute(['key' => 'tags_enabled']);
  24. self::$enabled = $statement->fetchColumn() === '1';
  25. } catch (PDOException $e) {
  26. self::$enabled = false;
  27. }
  28. ​
  29. return self::$enabled;
  30. }
  31. ​
  32. public static function parse(string $input): array
  33. {
  34. $names = [];
  35. $seen = [];
  36. ​
  37. foreach (preg_split('/[,;\n]+/', $input) ?: [] as $raw) {
  38. $name = strip_tags($raw);
  39. $name = (string) preg_replace('/[\x00-\x1F\x7F]+/u', ' ', $name);
  40. $name = trim((string) preg_replace('/\s+/u', ' ', $name));
  41. $name = mb_substr($name, 0, self::NAME_MAX);
  42. ​
  43. $key = mb_strtolower($name);
  44. if ($name === '' || isset($seen[$key])) {
  45. continue;
  46. }
  47. ​
  48. $seen[$key] = true;
  49. $names[] = $name;
  50. ​
  51. if (count($names) >= self::MAX_TAGS) {
  52. break;
  53. }
  54. }
  55. ​
  56. return $names;
  57. }
  58. ​
  59. public static function slugFor(string $name): string
  60. {
  61. $slug = substr(Slugger::make($name), 0, 80);
  62. ​
  63. return $slug !== '' ? $slug : 'tag-' . substr(sha1(mb_strtolower($name)), 0, 8);
  64. }
  65. ​
  66. public static function sync(int $productId, array $names): void
  67. {
  68. $db = Database::site();
  69. ​
  70. try {
  71. $db->beginTransaction();
  72. ​
  73. $upsert = $db->prepare('INSERT INTO tags (name, slug) VALUES (:name, :slug) ON DUPLICATE KEY UPDATE id = LAST_INSERT_ID(id)');
  74. $tagIds = [];
  75. foreach ($names as $name) {
  76. $upsert->execute(['name' => $name, 'slug' => self::slugFor($name)]);
  77. $tagIds[] = (int) $db->lastInsertId();
  78. }
  79. ​
  80. $db->prepare('DELETE FROM product_tags WHERE product_id = :id')->execute(['id' => $productId]);
  81. ​
  82. $link = $db->prepare('INSERT IGNORE INTO product_tags (product_id, tag_id) VALUES (:product, :tag)');
  83. foreach (array_unique($tagIds) as $tagId) {
  84. $link->execute(['product' => $productId, 'tag' => $tagId]);
  85. }
  86. ​
  87. $db->exec('DELETE FROM tags WHERE id NOT IN (SELECT tag_id FROM product_tags)');
  88. ​
  89. $db->commit();
  90. } catch (PDOException $e) {
  91. if ($db->inTransaction()) {
  92. $db->rollBack();
  93. }
  94. error_log('StocketBase: could not save tags: ' . $e->getMessage());
  95. }
  96. }
  97. ​
  98. public static function forProduct(int $productId): array
  99. {
  100. try {
  101. $statement = Database::site()->prepare(
  102. 'SELECT t.name, t.slug FROM tags t JOIN product_tags l ON l.tag_id = t.id WHERE l.product_id = :id ORDER BY t.name ASC'
  103. );
  104. $statement->execute(['id' => $productId]);
  105. ​
  106. return $statement->fetchAll();
  107. } catch (PDOException $e) {
  108. return [];
  109. }
  110. }
  111. ​
  112. public static function csvFor(int $productId): string
  113. {
  114. return implode(', ', array_column(self::forProduct($productId), 'name'));
  115. }
  116. ​
  117. public static function findBySlug(string $slug): ?array
  118. {
  119. if (preg_match('/^[a-z0-9-]{1,80}$/', $slug) !== 1) {
  120. return null;
  121. }
  122. ​
  123. $statement = Database::site()->prepare('SELECT id, name, slug FROM tags WHERE slug = :slug LIMIT 1');
  124. $statement->execute(['slug' => $slug]);
  125. ​
  126. return $statement->fetch() ?: null;
  127. }
  128. }
  129. ​