$value) $this->$attribute = $value; } public function __toString() { return $this->tag; } public static function fromId($id_tag, $return_format = 'object') { $db = Registry::get('db'); $row = $db->queryAssoc(' SELECT * FROM tags WHERE id_tag = {int:id_tag}', [ 'id_tag' => $id_tag, ]); // Tag not found? if (empty($row)) throw new NotFoundException(); return $return_format === 'object' ? new Tag($row) : $row; } public static function fromSlug($slug, $return_format = 'object') { $db = Registry::get('db'); $row = $db->queryAssoc(' SELECT * FROM tags WHERE slug = {string:slug}', [ 'slug' => $slug, ]); // Tag not found? if (empty($row)) throw new NotFoundException(); return $return_format === 'object' ? new Tag($row) : $row; } public static function getAll($limit = 0, $return_format = 'array') { $rows = Registry::get('db')->queryAssocs(' SELECT * FROM tags ORDER BY ' . ($limit > 0 ? 'count LIMIT {int:limit}' : 'tag'), [ 'limit' => $limit, ]); // No tags found? if (empty($rows)) return []; // Limited? Make sure the lot is sorted alphabetically. if (!empty($limit)) { usort($rows, function($a, $b) { return strcmp($a['tag'], $b['tag']); }); } if ($return_format === 'object') { $return = []; foreach ($rows as $row) $return[$row['id_tag']] = new Tag($row); return $return; } else return $rows; } public static function getAllByOwner($id_user_owner) { $db = Registry::get('db'); $res = $db->query(' SELECT * FROM tags WHERE id_user_owner = {int:id_user_owner} ORDER BY tag', [ 'id_user_owner' => $id_user_owner, ]); $objects = []; while ($row = $db->fetch_assoc($res)) $objects[$row['id_tag']] = new Tag($row); return $objects; } public static function getAlbums($id_parent = 0, $offset = 0, $limit = 24, $return_format = 'array') { $rows = Registry::get('db')->queryAssocs(' SELECT * FROM tags WHERE id_parent = {int:id_parent} AND kind = {string:kind} ORDER BY tag ASC LIMIT {int:offset}, {int:limit}', [ 'id_parent' => $id_parent, 'kind' => 'Album', 'offset' => $offset, 'limit' => $limit, ]); if ($return_format === 'object') { $return = []; foreach ($rows as $row) $return[$row['id_tag']] = new Tag($row); return $return; } else return $rows; } public function getContributorList() { return Registry::get('db')->queryPairs(' SELECT u.id_user, u.first_name, u.surname, u.slug, COUNT(*) AS num_assets FROM assets_tags AS at LEFT JOIN assets AS a ON at.id_asset = a.id_asset LEFT JOIN users AS u ON a.id_user_uploaded = u.id_user WHERE at.id_tag = {int:id_tag} GROUP BY a.id_user_uploaded ORDER BY u.first_name, u.surname', [ 'id_tag' => $this->id_tag, ]); } public static function getPeople($id_parent = 0, $offset = 0, $limit = 24, $return_format = 'array') { $rows = Registry::get('db')->queryAssocs(' SELECT * FROM tags WHERE id_parent = {int:id_parent} AND kind = {string:kind} ORDER BY tag ASC LIMIT {int:offset}, {int:limit}', [ 'id_parent' => $id_parent, 'kind' => 'Person', 'offset' => $offset, 'limit' => $limit, ]); if ($return_format === 'object') { $return = []; foreach ($rows as $row) $return[$row['id_tag']] = new Tag($row); return $return; } else return $rows; } public static function byAssetId($id_asset, $return_format = 'object') { $rows = Registry::get('db')->queryAssocs(' SELECT * FROM tags WHERE id_tag IN( SELECT id_tag FROM assets_tags WHERE id_asset = {int:id_asset} ) ORDER BY count DESC', [ 'id_asset' => $id_asset, ]); // No tags found? if (empty($rows)) return []; if ($return_format === 'object') { $return = []; foreach ($rows as $row) $return[$row['id_tag']] = new Tag($row); return $return; } else return $rows; } public static function byPostId($id_post, $return_format = 'object') { $rows = Registry::get('db')->queryAssocs(' SELECT * FROM tags WHERE id_tag IN( SELECT id_tag FROM posts_tags WHERE id_post = {int:id_post} ) ORDER BY count DESC', [ 'id_post' => $id_post, ]); // No tags found? if (empty($rows)) return []; if ($return_format === 'object') { $return = []; foreach ($rows as $row) $return[$row['id_tag']] = new Tag($row); return $return; } else return $rows; } public static function recount(array $id_tags = []) { return Registry::get('db')->query(' UPDATE tags AS t SET count = ( SELECT COUNT(*) FROM `assets_tags` AS at WHERE at.id_tag = t.id_tag )' . (!empty($id_tags) ? ' WHERE t.id_tag IN({array_int:id_tags})' : ''), ['id_tags' => $id_tags]); } public static function createNew(array $data, $return_format = 'object') { $db = Registry::get('db'); if (!isset($data['id_parent'])) $data['id_parent'] = 0; if (!isset($data['description'])) $data['description'] = ''; if (!isset($data['count'])) $data['count'] = 0; $res = $db->query(' INSERT IGNORE INTO tags (id_parent, tag, slug, kind, description, count) VALUES ({int:id_parent}, {string:tag}, {string:slug}, {string:kind}, {string:description}, {int:count}) ON DUPLICATE KEY UPDATE count = count + 1', $data); if (!$res) trigger_error('Could not create the requested tag.', E_USER_ERROR); $data['id_tag'] = $db->insert_id(); return $return_format === 'object' ? new Tag($data) : $data; } public function getUrl() { return BASEURL . '/' . $this->slug . '/'; } public function save() { return Registry::get('db')->query(' UPDATE tags SET id_parent = {int:id_parent}, id_asset_thumb = {int:id_asset_thumb},' . (isset($this->id_user_owner) ? ' id_user_owner = {int:id_user_owner},' : '') . ' tag = {string:tag}, slug = {string:slug}, description = {string:description}, count = {int:count} WHERE id_tag = {int:id_tag}', get_object_vars($this)); } public function delete() { $db = Registry::get('db'); $res = $db->query(' DELETE FROM assets_tags WHERE id_tag = {int:id_tag}', [ 'id_tag' => $this->id_tag, ]); if (!$res) return false; return $db->query(' DELETE FROM tags WHERE id_tag = {int:id_tag}', [ 'id_tag' => $this->id_tag, ]); } public function resetIdAsset() { $db = Registry::get('db'); $new_id = $db->queryValue(' SELECT MAX(id_asset) as new_id FROM assets_tags WHERE id_tag = {int:id_tag}', [ 'id_tag' => $this->id_tag, ]); return $db->query(' UPDATE tags SET id_asset_thumb = {int:new_id} WHERE id_tag = {int:id_tag}', [ 'new_id' => $new_id ?? 0, 'id_tag' => $this->id_tag, ]); } public static function match($tokens) { if (!is_array($tokens)) $tokens = explode(' ', $tokens); return Registry::get('db')->queryPair(' SELECT id_tag, tag FROM tags WHERE LOWER(tag) LIKE {string:tokens} ORDER BY tag ASC', ['tokens' => '%' . strtolower(implode('%', $tokens)) . '%']); } public static function matchPeople($tokens) { if (!is_array($tokens)) $tokens = explode(' ', $tokens); return Registry::get('db')->queryPairs(' SELECT id_tag, tag, slug FROM tags WHERE LOWER(tag) LIKE {string:tokens} AND kind = {string:person} ORDER BY tag ASC', [ 'tokens' => '%' . strtolower(implode('%', $tokens)) . '%', 'person' => 'Person', ]); } public static function exactMatch($tag) { if (!is_string($tag)) throw new InvalidArgumentException('Expecting a string!'); return Registry::get('db')->queryPair(' SELECT id_tag, tag FROM tags WHERE tag = {string:tag}', ['tag' => $tag]); } public static function matchSlug($slug) { if (!is_string($slug)) throw new InvalidArgumentException('Expecting a string!'); return Registry::get('db')->queryValue(' SELECT id_tag FROM tags WHERE slug = {string:slug}', ['slug' => $slug]); } public static function matchAll(array $tags) { return Registry::get('db')->queryPair(' SELECT tag, id_tag FROM tags WHERE tag IN ({array_string:tags})', ['tags' => $tags]); } public static function getCount($only_used = true, $kind = '', $isAlbum = false) { $where = []; if ($only_used) $where[] = 'count > 0'; if (empty($kind)) $kind = 'Album'; $where[] = 'kind {raw:operator} {string:kind}'; $where = implode(' AND ', $where); return Registry::get('db')->queryValue(' SELECT COUNT(*) FROM tags WHERE ' . $where, [ 'kind' => $kind, 'operator' => $isAlbum ? '=' : '!=', ]); } public static function getOffset($offset, $limit, $order, $direction, $isAlbum = false) { assert(in_array($order, ['id_tag', 'tag', 'slug', 'count'])); $db = Registry::get('db'); $res = $db->query(' SELECT t.*, u.id_user, u.first_name, u.surname FROM tags AS t LEFT JOIN users AS u ON t.id_user_owner = u.id_user WHERE kind {raw:operator} {string:album} ORDER BY id_parent, {raw:order} LIMIT {int:offset}, {int:limit}', [ 'order' => $order . ($direction === 'up' ? ' ASC' : ' DESC'), 'offset' => $offset, 'limit' => $limit, 'album' => 'Album', 'operator' => $isAlbum ? '=' : '!=', ]); $albums_by_parent = []; while ($row = $db->fetch_assoc($res)) { if (!isset($albums_by_parent[$row['id_parent']])) $albums_by_parent[$row['id_parent']] = []; $albums_by_parent[$row['id_parent']][] = $row + ['children' => []]; } $albums = self::getChildrenRecursively(0, 0, $albums_by_parent); $rows = self::flattenChildrenRecursively($albums); return $rows; } private static function getChildrenRecursively($id_parent, $level, &$albums_by_parent) { $children = []; if (!isset($albums_by_parent[$id_parent])) return $children; foreach ($albums_by_parent[$id_parent] as $child) { if (isset($albums_by_parent[$child['id_tag']])) $child['children'] = self::getChildrenRecursively($child['id_tag'], $level + 1, $albums_by_parent); $child['tag'] = ($level ? str_repeat('—', $level * 2) . ' ' : '') . $child['tag']; $children[] = $child; } return $children; } private static function flattenChildrenRecursively($albums) { if (empty($albums)) return []; $rows = []; foreach ($albums as $album) { static $headers_to_keep = ['id_tag', 'tag', 'slug', 'count', 'id_user', 'first_name', 'surname']; $rows[] = array_intersect_key($album, array_flip($headers_to_keep)); if (!empty($album['children'])) { $children = self::flattenChildrenRecursively($album['children']); foreach ($children as $child) $rows[] = array_intersect_key($child, array_flip($headers_to_keep)); } } return $rows; } }