Taxonomies let you classify and organize content. Common examples include categories, tags, and authors, but you can create any grouping system your site needs. Unlike traditional CMS platforms that require database tables, Ava stores taxonomy terms in simple YAML files.
Quick Start
- Define a taxonomy in
app/config/taxonomies.php - Assign terms to content in frontmatter
- Display terms in your templates using helper methods
---
title: Getting Started with PHP
category: tutorials
tag:
- php
- beginner
---
Your content here...
That's it. When you reference a term, Ava creates it automatically (unless you've disabled allow_unknown_terms).
Concepts
Taxonomies vs Terms
- A taxonomy is a classification system (e.g., "category", "tag", "author")
- A term is a specific value within that taxonomy (e.g., "tutorials", "php", "jane-doe")
How Terms Are Created
Terms can be created in three ways:
- Auto-creation — Reference a term in content frontmatter and it's created automatically
- Registry files — Pre-define terms with metadata in YAML files
- Admin UI — Create and edit terms through the dashboard
Configuration
Taxonomies are defined in app/config/taxonomies.php:
return [
'category' => [
'label' => 'Categories',
'hierarchical' => true,
'public' => true,
'rewrite' => ['base' => '/category'],
],
'tag' => [
'label' => 'Tags',
'hierarchical' => false,
'public' => true,
'rewrite' => ['base' => '/tag'],
],
];
For complete configuration options (behaviour, ui, rewrite settings), see Configuration: Taxonomies.
content_types.php under the taxonomies array. See Configuration: Content Types.
Term Storage
Registry Files
Terms can be pre-defined with metadata in YAML files at content/_taxonomies/{taxonomy}.yml:
# content/_taxonomies/category.yml
- slug: tutorials
name: Tutorials
description: Step-by-step guides and how-tos
icon: book
color: '#3b82f6'
- slug: news
name: News
description: Latest updates and announcements
icon: newspaper
Standard Fields
| Field | Required | Description |
|---|---|---|
slug |
Yes | URL-safe identifier (lowercase, hyphens) |
name |
Yes | Display name for the term |
description |
No | Description shown on archive pages |
Custom Fields
Add any additional fields you need—icon, color, image, featured, etc. These are available in templates:
$terms = $ava->terms('category');
$tutorials = $terms['tutorials'];
echo $tutorials['icon']; // book
echo $tutorials['color']; // #3b82f6
The admin term editor supports adding custom fields as key-value pairs, and suggests field names used by other terms in the same taxonomy.
How Registry Merging Works
- Terms used in published content get their
countanditemsfrom indexing, plus any extra fields from the registry - Terms that exist only in the registry appear with
count: 0 - Auto-generated term names (from slugs like
php-tutorials) can be overridden by registry entries
Assigning Terms to Content
In content frontmatter, assign terms using the taxonomy name as the key:
Single Term
---
title: My Post
category: tutorials
---
Multiple Terms
---
title: My Post
category:
- tutorials
- php
tag:
- beginner
- installation
---
Alternative: Grouped Format
If you prefer to group all taxonomies under a single key:
---
title: My Post
tax:
category: tutorials
tag: [beginner, installation]
---
Both formats work identically—use whichever keeps your frontmatter tidy.
Indexing Behaviour
- Only taxonomies defined in
taxonomies.phpare indexed and routed - Only published content is included in taxonomy indexes (not drafts or unlisted)
- Terms do not need to be pre-created — referencing a term creates it automatically
- Term slugs should be lowercase alphanumeric with hyphens
Hierarchical Taxonomies
When hierarchical: true, terms can have parent/child relationships:
# content/_taxonomies/category.yml
- slug: programming
name: Programming
- slug: php
name: PHP
parent: programming
- slug: javascript
name: JavaScript
parent: programming
Hierarchy Rollup
With hierarchy_rollup: true (the default), the parent term archive includes content from all child terms. For example, visiting /category/programming would show content tagged with "php" and "javascript" as well.
Set hierarchy_rollup: false if you want parent archives to only show directly-tagged content.
Displaying Hierarchical Terms
<?php $terms = $ava->terms('category'); ?>
<ul>
<?php foreach ($terms as $slug => $term): ?>
<li class="<?= isset($term['parent']) ? 'child' : 'parent' ?>">
<?= str_repeat('— ', substr_count($term['parent'] ?? '', '/')) ?>
<a href="<?= $ava->termUrl('category', $slug) ?>">
<?= $ava->e($term['name']) ?>
</a>
</li>
<?php endforeach; ?>
</ul>
Routing
When public: true, Ava automatically creates routes for each taxonomy:
| URL | Description | Template |
|---|---|---|
/category |
Taxonomy index (all terms) | taxonomy-index.php |
/category/tutorials |
Term archive (content with this term) | taxonomy.php |
The URL prefix comes from rewrite.base in your taxonomy config.
Template Resolution
For term archive pages:
taxonomy-{taxonomy}-{term}.php(e.g.,taxonomy-category-tutorials.php)taxonomy-{taxonomy}.php(e.g.,taxonomy-category.php)taxonomy.phparchive.phpindex.php
For taxonomy index pages:
taxonomy-index-{taxonomy}.php(e.g.,taxonomy-index-category.php)taxonomy-index.phparchive.phpindex.php
For detailed template examples, see Theming: Taxonomy Templates.
Template Helpers
Getting Terms for a Taxonomy
// All terms in a taxonomy (site-wide)
$categories = $ava->terms('category');
foreach ($categories as $slug => $term) {
echo $term['name']; // Display name
echo $term['description']; // Description (if set)
echo $term['icon']; // Custom field (if set)
echo $term['count']; // Number of content items
}
Getting Terms for Content
// Terms assigned to a specific content item
<?php foreach ($content->terms('category') as $term): ?>
<a href="<?= $ava->termUrl('category', $term) ?>">
<?= $ava->termName('category', $term) ?>
</a>
<?php endforeach; ?>
Generating Term URLs
$ava->termUrl('category', 'tutorials') // /category/tutorials
$ava->termUrl('tag', 'php') // /tag/php
Getting Term Names
$ava->termName('category', 'tutorials') // "Tutorials"
Returns the display name from the registry, or auto-generates one from the slug.
Querying Content by Taxonomy
$tutorials = $ava->query()
->type('post')
->published()
->whereTax('category', 'tutorials')
->orderBy('date', 'desc')
->get();
See Theming: Querying Content for all query methods.
The $tax Variable
The $tax array is available in taxonomy templates with context about the current page:
In Term Archive Templates (taxonomy.php)
$tax = [
'name' => 'category', // Taxonomy slug
'term' => [ // Current term data
'slug' => 'tutorials',
'name' => 'Tutorials',
'description' => 'Step-by-step guides',
'icon' => 'book', // Custom fields from registry
'color' => '#3b82f6',
'count' => 12, // Number of items
],
];
In Taxonomy Index Templates (taxonomy-index.php)
$tax = [
'name' => 'category', // Taxonomy slug
'terms' => [ // All terms (keyed by slug)
'tutorials' => ['name' => 'Tutorials', 'count' => 12, ...],
'news' => ['name' => 'News', 'count' => 5, ...],
],
];
Example Templates
Term Archive (taxonomy.php)
<?= $ava->partial('header', ['title' => $tax['term']['name']]) ?>
<header class="archive-header">
<?php if (!empty($tax['term']['icon'])): ?>
<span class="term-icon"><?= $ava->e($tax['term']['icon']) ?></span>
<?php endif; ?>
<h1><?= $ava->e($tax['term']['name']) ?></h1>
<?php if (!empty($tax['term']['description'])): ?>
<p class="term-description"><?= $ava->e($tax['term']['description']) ?></p>
<?php endif; ?>
<p class="term-count"><?= $tax['term']['count'] ?> items</p>
</header>
<div class="posts-grid">
<?php foreach ($query->get() as $item): ?>
<article>
<h2>
<a href="<?= $ava->url($item->type(), $item->slug()) ?>">
<?= $ava->e($item->title()) ?>
</a>
</h2>
<?php if ($item->excerpt()): ?>
<p><?= $ava->e($item->excerpt()) ?></p>
<?php endif; ?>
</article>
<?php endforeach; ?>
</div>
<?= $ava->pagination($query, $request->path()) ?>
<?= $ava->partial('footer') ?>
Taxonomy Index (taxonomy-index.php)
<?= $ava->partial('header', ['title' => ucfirst($tax['name'])]) ?>
<h1>All <?= ucfirst($tax['name']) ?>s</h1>
<ul class="term-list">
<?php foreach ($tax['terms'] as $slug => $term): ?>
<li>
<a href="<?= $ava->termUrl($tax['name'], $slug) ?>">
<?= $ava->e($term['name']) ?>
</a>
<span class="count">(<?= $term['count'] ?>)</span>
<?php if (!empty($term['description'])): ?>
<p><?= $ava->e($term['description']) ?></p>
<?php endif; ?>
</li>
<?php endforeach; ?>
</ul>
<?= $ava->partial('footer') ?>
Admin Management
Terms can be managed via the admin dashboard:
- Navigate to the taxonomy in the sidebar
- View all terms with their content counts
- Create new terms with name, slug, description, and custom fields
- Edit existing terms
- Delete unused terms (terms with content show a warning first)
The admin term editor supports:
- Name, slug, and description fields
- Custom fields as key-value pairs
- Field name suggestions based on existing terms
See Admin Dashboard: Taxonomy Management for more details.
The Taxonomy Field Type
When defining fields for content types, use the taxonomy field type to create a term selector in the admin editor:
'category' => [
'type' => 'taxonomy',
'taxonomy' => 'category',
'label' => 'Categories',
'required' => true,
'multiple' => true,
],
| Option | Default | Description |
|---|---|---|
taxonomy |
Required | Taxonomy name from taxonomies.php |
multiple |
true |
Allow multiple term selection |
allowNew |
true |
Allow creating new terms inline |
See Fields: taxonomy for full documentation.