Files
VDI-Starter/lib/class-breadcrumbs.php
2025-08-22 15:40:01 -05:00

397 lines
14 KiB
PHP

<?php
namespace BasicWP;
/**
* Class Breadcrumbs
*
* This class is responsible for generating and managing breadcrumb navigation
* for the WordPress theme. Breadcrumbs provide a navigational aid that helps
* users understand their location within the website's hierarchy.
*
* @package Basic-WP
* @subpackage Breadcrumbs
*/
class Breadcrumbs {
/**
* Generates the breadcrumb navigation for the website.
*
* This method is responsible for creating and returning the breadcrumb
* trail, which helps users navigate the website by showing their current
* location within the site's hierarchy.
*
* @return array An array of breadcrumb items, where each item is typically an
* associative array containing 'title' and 'url' keys.
*/
public static function generate() {
global $post;
$breadcrumbs = array();
// Always start with Home
$breadcrumbs[] = self::getHomeBreadcrumb();
if ( is_front_page() ) {
// Front Page - do nothing
return $breadcrumbs;
} elseif ( is_home() ) {
// Blog Index - add the Blog Posts Index page title
$breadcrumbs[] = self::getBlogPostsIndexBreadcrumb();
} elseif ( is_singular( 'post' ) ) {
// Single Post - add the category and post title
$breadcrumbs = array_merge( $breadcrumbs, self::getSinglePostBreadcrumbs() );
} elseif ( is_singular() && ! is_page() ) {
// Single Custom Post Type - add the post type archive and post title
$breadcrumbs = array_merge( $breadcrumbs, self::getCustomPostTypeBreadcrumbs() );
} elseif ( is_page() ) {
// Static Page - add the parent pages if any, and the page title
$breadcrumbs = array_merge( $breadcrumbs, self::getStaticPageBreadcrumbs( $post ) );
} elseif ( is_category() || is_tag() || is_tax() ) {
// Taxonomy Archive - add the taxonomy term name
$breadcrumbs[] = self::getTaxonomyArchiveBreadcrumb();
} elseif ( is_post_type_archive() ) {
// Post Type Archive - add the post type name
$breadcrumbs[] = self::getPostTypeArchiveBreadcrumb();
} elseif ( is_day() ) {
// Daily Archive - add the month and day links
$breadcrumbs = array_merge( $breadcrumbs, self::getDateArchiveBreadcrumbs() );
} elseif ( is_month() ) {
// Monthly Archive - add the month link
$breadcrumbs[] = self::getMonthArchiveBreadcrumb();
} elseif ( is_year() ) {
// Yearly Archive - add the year link
$breadcrumbs[] = self::getYearArchiveBreadcrumb();
} elseif ( is_author() ) {
// Author Archive - add the author name
$breadcrumbs[] = self::getAuthorArchiveBreadcrumb();
} elseif ( is_search() ) {
// Search Results - add the search query
$breadcrumbs[] = self::getSearchBreadcrumb();
} elseif ( is_404() ) {
// 404 Not Found - add a 404 message
$breadcrumbs[] = self::get404Breadcrumb();
}
return $breadcrumbs;
}
/** Generates the breadcrumb for the home page.
*
* This method is responsible for creating the breadcrumb
* link or label that represents the home page in the breadcrumb
* navigation trail.
*
* @return string The HTML or text representation of the home breadcrumb.
*/
private static function getHomeBreadcrumb() {
return array(
'url' => home_url(),
'label' => 'Home',
);
}
/** Generates the breadcrumb for the blog posts index page.
*
* This method is responsible for creating a breadcrumb entry
* that represents the blog posts index page in the breadcrumb trail.
*
* @return array An associative array representing the breadcrumb for the blog posts index page.
*/
private static function getBlogPostsIndexBreadcrumb() {
return array( 'label' => get_the_title( get_option( 'page_for_posts' ) ) );
}
/** Generates the breadcrumb trail for a single post.
*
* This method is responsible for creating the breadcrumb navigation
* specific to single post views. It typically includes links to the
* homepage, category (or categories) the post belongs to, and the
* post title itself.
*
* @return array An array representing the breadcrumb trail, where each
* element is a breadcrumb item (e.g., URL and label).
*/
private static function getSinglePostBreadcrumbs() {
$breadcrumbs = array();
$categories = get_the_category();
$breadcrumbs[] = array(
'url' => get_permalink( get_option( 'page_for_posts' ) ),
'label' => get_the_title( get_option( 'page_for_posts' ) ),
);
if ( ! empty( $categories ) ) {
$cat = $categories[0];
$breadcrumbs[] = array(
'url' => get_category_link( $cat ),
'label' => $cat->name,
);
}
$breadcrumbs[] = array( 'label' => get_the_title() );
return $breadcrumbs;
}
/** Generates breadcrumbs for custom post types.
*
* This method is responsible for creating breadcrumb navigation
* specific to custom post types. It retrieves and formats the
* breadcrumb trail based on the custom post type's hierarchy
* and structure.
*
* @return array An array representing the breadcrumb trail for the custom post type.
*/
private static function getCustomPostTypeBreadcrumbs() {
$breadcrumbs = array();
$postType = get_post_type_object( get_post_type() );
if ( $postType && ! in_array( get_post_type(), array( 'post', 'page' ), true ) ) {
$breadcrumbs[] = array(
'url' => get_post_type_archive_link( $postType->name ),
'label' => $postType->labels->name,
);
}
$breadcrumbs[] = array( 'label' => get_the_title() );
return $breadcrumbs;
}
/** Generates breadcrumbs for a static page.
*
* This method is responsible for creating breadcrumb navigation
* for static pages in a WordPress theme. It utilizes the provided
* post object to determine the breadcrumb structure.
*
* @param WP_Post $post The WordPress post object representing the static page.
* @return array An array of breadcrumb items, where each item is typically
* an associative array containing 'title' and 'url' keys.
*/
private static function getStaticPageBreadcrumbs( $post ) {
$breadcrumbs = array();
if ( $post->post_parent ) {
$ancestors = array_reverse( get_post_ancestors( $post->ID ) );
foreach ( $ancestors as $ancestor ) {
$breadcrumbs[] = array(
'url' => get_permalink( $ancestor ),
'label' => get_the_title( $ancestor ),
);
}
}
$breadcrumbs[] = array( 'label' => get_the_title() );
return $breadcrumbs;
}
/** Generates a breadcrumb for taxonomy archive pages.
*
* This method is responsible for creating breadcrumb navigation
* specific to taxonomy archive pages, providing users with a clear
* path to navigate back to the taxonomy or related sections.
*
* @return array An array representing the breadcrumb trail for the taxonomy archive.
*/
private static function getTaxonomyArchiveBreadcrumb() {
$term = get_queried_object();
return $term && isset( $term->name ) ? array( 'label' => $term->name ) : array();
}
/** Generates a breadcrumb for the archive page of a custom post type.
*
* This method is responsible for creating a breadcrumb link that points
* to the archive page of a specific custom post type. It is typically used
* in breadcrumb navigation to provide users with a way to navigate back
* to the archive page of the post type they are currently viewing.
*
* @return array An array containing the breadcrumb data for the custom post type archive.
*/
private static function getPostTypeArchiveBreadcrumb() {
$postType = get_post_type_object( get_post_type() );
return $postType ? array( 'label' => $postType->labels->name ) : array();
}
/** Generates breadcrumbs for date-based archives.
*
* This method is responsible for creating breadcrumb navigation
* for WordPress date archive pages, such as year, month, or day archives.
*
* @return array An array representing the breadcrumb trail for the date archive.
*/
private static function getDateArchiveBreadcrumbs() {
return array(
array(
'url' => get_month_link( get_query_var( 'year' ), get_query_var( 'monthnum' ) ),
'label' => get_the_date( 'F Y' ),
),
array( 'label' => get_the_date() ),
);
}
/** Generates a breadcrumb for a monthly archive page.
*
* This method is responsible for creating a breadcrumb
* specific to WordPress monthly archive pages. It typically
* includes the year and month as part of the breadcrumb trail.
*
* @return string The HTML markup for the monthly archive breadcrumb.
*/
private static function getMonthArchiveBreadcrumb() {
return array( 'label' => get_the_date( 'F Y' ) );
}
/** Generates a breadcrumb for the year-based archive page.
*
* This method is responsible for creating a breadcrumb link
* specific to the year archive in a WordPress site. It is typically
* used to provide navigation for users when they are viewing posts
* filtered by a specific year.
*
* @return string The HTML markup for the year archive breadcrumb.
*/
private static function getYearArchiveBreadcrumb() {
return array( 'label' => get_the_date( 'Y' ) );
}
/** Generates the breadcrumb for the author archive page.
*
* This method is responsible for creating a breadcrumb trail
* specific to the author archive, providing navigation context
* for pages that display posts by a particular author.
*
* @return string The HTML markup for the author archive breadcrumb.
*/
private static function getAuthorArchiveBreadcrumb() {
$author = get_queried_object();
return array( 'label' => 'Author: ' . $author->display_name );
}
/** Generates the breadcrumb for search results.
*
* This method is responsible for creating a breadcrumb trail
* specific to search result pages. It helps users navigate back
* to the search context or other parts of the site.
*
* @return string The HTML markup for the search breadcrumb.
*/
private static function getSearchBreadcrumb() {
return array( 'label' => 'Search: ' . get_search_query() );
}
/** Generates the breadcrumb trail for a 404 error page.
*
* This method is responsible for creating a breadcrumb structure
* specifically for 404 error pages, providing users with a navigational
* context when a requested page is not found.
*
* @return array An array representing the breadcrumb trail for the 404 page.
*/
private static function get404Breadcrumb() {
return array( 'label' => '404 Not Found' );
}
/** Renders the breadcrumb navigation.
*
* This method generates and outputs the breadcrumb trail for the current page.
*
* @return void
*/
public static function render() {
$items = self::generate();
$metadata = array(
'@context' => 'https://schema.org',
'@type' => 'BreadcrumbList',
'itemListElement' => array(),
);
ob_start();
?>
<nav aria-label="Breadcrumbs" class="flex items-center py-2 -mx-2 leading-none" vocab="https://schema.org/" typeof="BreadcrumbList">
<?php foreach ( $items as $index => $item ) : ?>
<?php
$ldItem = array(
'@type' => 'ListItem',
'position' => $index + 1,
'name' => htmlspecialchars( $item['label'], ENT_QUOTES, 'UTF-8' ),
);
$isActive = ( $index === count( $items ) - 1 );
$activeClass = $isActive ? ' active' : '';
?>
<span class="p-2 breadcrumb-item<?php echo esc_attr( $activeClass ); ?>"
<?php
if ( $isActive ) :
?>
aria-current="page"<?php endif; ?>>
<?php if ( ! empty( $item['url'] ) ) : ?>
<a href="<?php echo esc_url( $item['url'] ); ?>">
<?php if ( $index === 0 ) : ?>
<svg class="flex-shrink-0 w-5 h-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true" aria-label="Home">
<path d="M10.707 2.293a1 1 0 00-1.414 0l-7 7a1 1 0 001.414 1.414L4 10.414V17a1 1 0 001 1h2a1 1 0 001-1v-2a1 1 0 011-1h2a1 1 0 011 1v2a1 1 0 001 1h2a1 1 0 001-1v-6.586l.293.293a1 1 0 001.414-1.414l-7-7z" />
</svg>
<span class="sr-only"><?php echo esc_attr( $item['label'] ); ?></span>
<?php else : ?>
<?php echo esc_attr( $item['label'] ); ?>
<?php endif; ?>
</a>
<?php $ldItem['item'] = $item['url']; ?>
<?php else : ?>
<?php echo esc_attr( $item['label'] ); ?>
<?php endif; ?>
</span>
<?php $metadata['itemListElement'][] = $ldItem; ?>
<?php if ( ! $isActive ) : ?>
<svg class="flex-shrink-0 w-5 h-5 text-light" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
<path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clip-rule="evenodd" />
</svg>
<?php endif; ?>
<?php endforeach; ?>
</nav>
<script type="application/ld+json"><?php echo esc_js( wp_json_encode( $metadata ) ); ?></script>
<?php
echo wp_kses(
ob_get_clean(),
array(
'a' => array(
'class' => array(),
'href' => array(),
'title' => array(),
'rel' => array(),
'target' => array(),
'aria-label' => array(),
'aria-current' => array(),
),
'nav' => array(
'aria-label' => array(),
'class' => array(),
'vocab' => array(),
'typeof' => array(),
),
'span' => array(
'class' => array(),
'aria-current' => array(),
),
'svg' => array(
'class' => array(),
'xmlns' => array(),
'viewBox' => array(),
'fill' => array(),
'aria-hidden' => array(),
'aria-label' => array(),
),
'path' => array(
'd' => array(),
'fill-rule' => array(),
'clip-rule' => array(),
'fill' => array(),
'xmlns:xlink' => array(),
),
'script' => array(
'type' => array(),
'src' => array(),
),
)
);
}
}