Add tags support for domains with filtering and bulk actions

Introduces a 'tags' field to the domains table and UI, allowing users to organize domains with custom tags. Adds tag input and display to create, edit, bulk-add, and view pages, as well as tag-based filtering and bulk tag management (add/remove) in the domain list. Updates backend validation, controller logic, and migrations to support tags, including a new migration and index for efficient tag searches.
This commit is contained in:
Hosteroid
2025-10-12 12:46:16 +03:00
parent 823248f025
commit df2942b356
13 changed files with 868 additions and 16 deletions

View File

@@ -203,5 +203,52 @@ class InputValidator
}
return null;
}
/**
* Validate and sanitize tags
*
* @param string $tagsString Comma-separated tags
* @param int $maxTags Maximum number of tags allowed (default 10)
* @param int $maxLength Maximum length per tag (default 50)
* @return array Array with 'valid' (bool), 'tags' (string), and 'error' (string|null)
*/
public static function validateTags(string $tagsString, int $maxTags = 10, int $maxLength = 50): array
{
if (empty($tagsString)) {
return ['valid' => true, 'tags' => '', 'error' => null];
}
// Split tags and clean them
$tags = array_filter(array_map('trim', explode(',', $tagsString)));
// Check tag count
if (count($tags) > $maxTags) {
return ['valid' => false, 'tags' => '', 'error' => "Maximum $maxTags tags allowed"];
}
// Validate each tag
$validatedTags = [];
foreach ($tags as $tag) {
$tag = strtolower($tag);
// Check length
if (strlen($tag) > $maxLength) {
return ['valid' => false, 'tags' => '', 'error' => "Tag '$tag' is too long (maximum $maxLength characters)"];
}
// Check format (alphanumeric and hyphens only)
if (!preg_match('/^[a-z0-9-]+$/', $tag)) {
return ['valid' => false, 'tags' => '', 'error' => "Tag '$tag' contains invalid characters (use only letters, numbers, and hyphens)"];
}
// Avoid duplicates
if (!in_array($tag, $validatedTags)) {
$validatedTags[] = $tag;
}
}
return ['valid' => true, 'tags' => implode(',', $validatedTags), 'error' => null];
}
}