From redaxo-structure
Traverses REDAXO category trees via rex_category, builds navigation menus, breadcrumbs, sitemaps. Use for REDAXO CMS category queries, parents/children, current path.
How this skill is triggered — by the user, by Claude, or both
Slash command
/redaxo-structure:structure-categoriesThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
`rex_category` represents a node in the structure tree. Each category has a "start article" (the page shown when the category itself is requested) plus zero or more child articles and child categories.
rex_category represents a node in the structure tree. Each category has a "start article" (the page shown when the category itself is requested) plus zero or more child articles and child categories.
Like articles, a category exists per language. The tree topology is shared across languages, but name and online status differ.
// By ID (current clang)
$cat = rex_category::get($id);
// By ID + specific clang
$cat = rex_category::get($id, $clangId);
// Current category (parent of the current article)
$cat = rex_category::getCurrent();
// All top-level (root) categories
$rootCats = rex_category::getRootCategories(true); // true = only online
$cat->getId();
$cat->getName();
$cat->getParentId(); // 0 for root categories
$cat->getParent(); // rex_category or null
$cat->getPath(); // pipe-separated ID path: "|2|7|"
$cat->getPathAsArray(); // [2, 7]
$cat->getLevel(); // depth (0 = root)
$cat->getStartArticleId(); // article shown when category is requested
$cat->getStartArticle(); // rex_article instance
$cat->getUrl(); // URL of the start article
$cat->getValue('cat_meta_X'); // category meta_info field
$cat->isOnline();
$cat->isPermitted(); // current user can see it (backend perms)
$cat->getChildren(true); // direct subcategories (true = only online)
$cat->getArticles(true); // direct articles in this category
For children of a category by ID without instantiating it first:
rex_category::getChildrenById($parentId, true); // root if $parentId === 0
Recursive simple example (top-2-level menu):
function renderNavigation(array $categories, int $maxLevel = 2): string
{
$html = '<ul class="nav">';
foreach ($categories as $cat) {
if (!$cat->isOnline()) {
continue;
}
$isCurrent = in_array($cat->getId(), rex_category::getCurrent()?->getPathAsArray() ?? [], true)
|| $cat->getId() === rex_category::getCurrent()?->getId();
$cls = $isCurrent ? ' class="active"' : '';
$html .= '<li' . $cls . '>';
$html .= '<a href="' . $cat->getUrl() . '">' . rex_escape($cat->getName()) . '</a>';
if ($cat->getLevel() < $maxLevel - 1 && ($children = $cat->getChildren(true))) {
$html .= renderNavigation($children, $maxLevel);
}
$html .= '</li>';
}
return $html . '</ul>';
}
echo renderNavigation(rex_category::getRootCategories(true));
$current = rex_category::getCurrent();
if ($current) {
$crumbs = [];
foreach ($current->getPathAsArray() as $id) {
$crumbs[] = rex_category::get($id);
}
$crumbs[] = $current; // include the current category itself
echo '<nav class="breadcrumb"><ol>';
foreach ($crumbs as $crumb) {
echo '<li><a href="' . $crumb->getUrl() . '">' . rex_escape($crumb->getName()) . '</a></li>';
}
// current article (if not the start article of the category)
$article = rex_article::getCurrent();
if ($article && !$article->isStartArticle()) {
echo '<li class="active">' . rex_escape($article->getName()) . '</li>';
}
echo '</ol></nav>';
}
Use rex_category_service, not direct SQL:
$id = rex_category_service::addCategory(
$parentId, // 0 for root
rex_clang::getCurrentId(),
[
'name' => 'New section',
'priority' => 1,
'status' => 1,
]
);
rex_category_service::editCategory($id, rex_clang::getCurrentId(), [
'name' => 'Renamed section',
]);
rex_category_service::deleteCategory($id);
Setting/changing the start article of a category:
rex_category_service::categoryIsStartArticle($categoryId, $articleId);
In backend tools, always check before showing/manipulating:
$cat = rex_category::get($id);
if (!$cat || !rex_article_perm::has(rex::getUser(), $cat->getId())) {
return;
}
For frontend navigation, isOnline() is enough – the structure addon already filters by online status.
isOnline() on each level – offline parents whose children are online become reachable via direct URL but not from the menu.rex_yrewrite::getCurrentDomain()->getMountId() for "below this domain's root".getChildren() without the $ignore_offlines = true flag in a public template, then leaking offline categories into navigation.Creates, edits, and optimizes skills for Claude Code, including drafting, evaluating with test prompts, iterating on performance, and improving skill descriptions for better triggering accuracy.
npx claudepluginhub friendsofredaxo/claude-marketplace --plugin redaxo-structure