HasTags Trait
The HasTags trait comes from the spatie/laravel-tags package and is automatically included in all WNCMS models via BaseModel.
It allows models to be tagged with one or more tags, where each tag can have a specific type (e.g., post_category, product_tag, link_category).
Overview
WNCMS uses the Spatie Tags package to provide a powerful and flexible tagging system. Every model extending BaseModel automatically inherits tag functionality.
use Wncms\Models\BaseModel;
class Post extends BaseModel
{
// HasTags is already included via BaseModel
}2
3
4
5
6
Core Concepts
Tag Structure
Tags in WNCMS have:
- Name: The tag name (e.g., "Technology", "News")
- Slug: URL-friendly version
- Type: Categories tags by purpose (e.g.,
post_category,post_tag) - Locale: Language-specific tags
Tag Types
Tag types let you organize tags for different purposes on the same model:
protected static array $tagMetas = [
[
'key' => 'post_category',
'short' => 'category',
'route' => 'frontend.posts.tag',
],
[
'key' => 'post_tag',
'short' => 'tag',
'route' => 'frontend.posts.tag',
],
];2
3
4
5
6
7
8
9
10
11
12
Common Methods
Attach Tags
// Single tag
$post->attachTag('Technology');
// Multiple tags
$post->attachTags(['Technology', 'Programming', 'Laravel']);
// With type
$post->attachTag('News', 'post_category');2
3
4
5
6
7
8
Sync Tags (Replace All)
// Replace all tags with new set
$post->syncTags(['PHP', 'Laravel']);
// Sync tags of specific type
$post->syncTagsWithType(['Featured', 'Popular'], 'post_tag');2
3
4
5
Detach Tags
// Remove specific tag
$post->detachTag('Technology');
// Remove multiple tags
$post->detachTags(['PHP', 'Laravel']);
// Remove all tags
$post->detachTags();2
3
4
5
6
7
8
Query Models by Tags
// Get posts with ANY of these tags
$posts = Post::withAnyTags(['Laravel', 'PHP'])->get();
// Get posts with ALL of these tags
$posts = Post::withAllTags(['Laravel', 'PHP'])->get();
// Get posts with specific tag type
$posts = Post::withAnyTags(['News', 'Tech'], 'post_category')->get();2
3
4
5
6
7
8
Retrieve Tags from Model
// Get all tags
$tags = $post->tags;
// Get tags of specific type
$categories = $post->tagsWithType('post_category');
// Get tag names as array
$tagNames = $post->tags->pluck('name')->toArray();2
3
4
5
6
7
8
Tag Meta Configuration
In your model, define $tagMetas to register tag types:
class Product extends BaseModel
{
protected static array $tagMetas = [
[
'key' => 'product_category',
'short' => 'category',
'route' => 'frontend.products.category',
],
[
'key' => 'product_brand',
'short' => 'brand',
'route' => 'frontend.products.brand',
],
];
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
Properties:
key: Unique identifier for the tag typeshort: Abbreviated name for internal useroute: Frontend route name for tag pages
Advanced Usage
Tag Counts
// Load models with tag count
$posts = Post::withCount('tags')->get();
foreach ($posts as $post) {
echo $post->tags_count;
}2
3
4
5
6
Eager Loading
// Avoid N+1 queries
$posts = Post::with('tags')->get();
foreach ($posts as $post) {
foreach ($post->tags as $tag) {
echo $tag->name;
}
}2
3
4
5
6
7
8
Filter by Multiple Tag Types
$posts = Post::query()
->withAnyTags(['Technology'], 'post_category')
->withAnyTags(['Featured'], 'post_tag')
->get();2
3
4
Creating Tags Programmatically
use Wncms\Models\Tag;
// Create a tag
$tag = Tag::findOrCreate('Laravel', 'post_category');
// Create with locale
$tag = Tag::findOrCreate('技術', 'post_category', 'zh_TW');
// Create multiple tags
$tags = Tag::findOrCreateMultiple(['PHP', 'JavaScript'], 'post_tag');2
3
4
5
6
7
8
9
10
Manager Integration
WNCMS managers provide applyTagFilter() for consistent tag filtering:
class PostManager extends ModelManager
{
protected function buildListQuery(array $options): mixed
{
$q = $this->query();
$this->applyTagFilter(
$q,
$options['tags'] ?? [],
$options['tag_type'] ?? 'post_category'
);
return $q;
}
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
Usage:
$posts = wncms()->post()->getList([
'tags' => ['Technology', 'News'],
'tag_type' => 'post_category',
]);2
3
4
Keyword Binding by Model Field
TagManager::getTagsToBind() supports field-level matching for auto-generate flows.
$tagNames = wncms()->tag()->getTagsToBind(
tagType: 'post_category',
contents: [
'title' => $request->title,
'content' => $request->content,
'excerpt' => $request->excerpt,
],
column: 'name',
modelKey: 'post'
);2
3
4
5
6
7
8
9
10
In backend keyword binding (tags.keywords.index), each keyword set can choose field (for example title or content).
When field is *, matching checks all provided content fields.
Best Practices
- Define Tag Metas - Always configure
$tagMetasin your models - Use Type Consistently - Stick to naming conventions like
{model}_category,{model}_tag - Eager Load - Use
with('tags')to prevent N+1 queries - Sync vs Attach - Use
syncTags()for complete replacement,attachTag()for additions - Index Tags - Ensure
nameandtypecolumns are indexed for performance
Related Documentation
- Define Tag Types
- Base Model
- Link Manager - Tag filtering examples