PHP · TUTORIAL

Build PHP Recommendations That Exclude the Current Page

Select related pages from a trusted directory, exclude the page being viewed, and handle small categories without duplicate links.

A recommended-content block should help visitors find somewhere useful to go next. Showing the page they are already reading wastes one of those opportunities. On a directory-based website, PHP can assemble suggestions from neighboring article folders, remove the current page, and select a few remaining candidates. The selection code becomes easier to maintain when discovery, filtering and rendering each have a clear job.

Imagine a category containing twelve game pages. You want eight cards below every game, selected from the same category. The current game must never appear, and a smaller category should show fewer cards rather than repeat entries. This is a practical directory recipe for sites whose published articles each have their own folder and index.php, not a personalized recommendation engine.

Start from a trusted category and current slug

The two most important inputs are the category you intend to scan and the slug of the current article. Ideally, your page configuration supplies both values. If you derive them from a URL, validate them against your site structure first. Never append an arbitrary query parameter to a filesystem path and assume that the resulting folder contains public content.

$category = 'action-games';
$currentSlug = 'gravity-guy';
$allowedCategories = ['action-games', 'puzzle-games'];
if (!in_array($category, $allowedCategories, true)) {
    throw new InvalidArgumentException('Unknown category');
}
$categoryDirectory = $_SERVER['DOCUMENT_ROOT'] . '/' . $category;

This example assumes the document root contains those public category folders. An installation in a subdirectory or a server with different path mappings needs explicit configuration. A known absolute content root is usually simpler than trying to infer deployment details inside the recommendation include.

Discover folders, then decide which are eligible

A directory can contain helper folders as well as articles. Check that each candidate has the expected entry file and an acceptable slug. Exclude symbolic links in this basic recipe so the scan does not wander into linked content outside its intended layout. If your publishing process uses a manifest, that manifest is a stronger source of eligibility than file existence alone.

$directories = glob($categoryDirectory . '/*', GLOB_ONLYDIR);
$candidates = [];
foreach ($directories === false ? [] : $directories as $directory) {
    if (is_link($directory)) {
        continue;
    }
    $slug = basename($directory);
    if (!preg_match('~^[a-z0-9-]+$~D', $slug)) {
        continue;
    }
    if ($slug === $currentSlug || !is_file($directory . '/index.php')) {
        continue;
    }
    $candidates[] = $slug;
}

The exclusion takes place before random selection. Selecting first and removing afterward might leave seven results when eight alternatives were available. Filtering first also makes the candidate list inspectable: during development, you can confirm that the current slug has disappeared before worrying about presentation.

Shuffle once and take the first available entries

There is no need to repeatedly choose random positions and track which ones have already been used. Shuffle the eligible list once, then take a slice. Because each folder contributed one candidate, slicing cannot introduce duplicates. The requested count is an upper limit rather than a promise that every category contains enough material.

shuffle($candidates);
$selected = array_slice($candidates, 0, 8);

Random ordering is useful for variety, but it does not measure relevance beyond membership in the same category. If some articles are much more helpful than others, start with a curated set instead. You can still rotate a smaller selection from that set while maintaining editorial control over what the component recommends.

Keep titles and images in published metadata

Turning a hyphenated slug into a title is convenient for a prototype, but it produces awkward capitalization for product names, acronyms and numbered games. Store the display title and image path in a small content manifest. The directory scan can discover pages while the manifest decides which ones have complete public card data.

$titles = [
    'gravity-guy' => 'Gravity Guy',
    'space-runner' => 'Space Runner',
];
foreach ($selected as $slug) {
    $title = $titles[$slug] ?? ucwords(str_replace('-', ' ', $slug));
    $href = '/' . $category . '/' . rawurlencode($slug) . '/';
    echo '<a href="' . htmlspecialchars($href, ENT_QUOTES, 'UTF-8') . '">';
    echo htmlspecialchars($title, ENT_QUOTES, 'UTF-8');
    echo '</a>';
}

The fallback title is explicitly a fallback. For a production card grid, require complete metadata before adding a page to the candidates. Use the same record for the image alt text, visible caption and destination. That avoids a card whose picture belongs to one game while its link opens another.

Handle empty and small categories deliberately

If no eligible candidates remain, hide the recommendation section or link to the category hub. Do not display an empty heading with blank space underneath. With three candidates, render three cards and let the responsive grid adapt. Trying to fill eight positions by duplicating entries gives visitors less choice while making the page look unfinished.

Caching changes how often a shuffled list actually changes. A full-page cache may keep one selection for hours, even though the PHP code chooses randomly when it runs. Decide whether that behavior is acceptable before adding cookies or per-visitor variation. For many editorial sites, a stable selection during a cache window is perfectly useful.

Verify the component with real category shapes

Check a category containing one article, several articles and more than eight articles. Add a helper folder without an entry file and confirm it is omitted. Make sure the current slug is compared using your canonical naming convention. If one URL uses uppercase characters and another lowercase, fix that routing inconsistency rather than hiding it with broad string transformations.

For a larger site, replace repeated directory scans with a generated manifest that includes publication status, title and category. The exclusion and selection steps can stay nearly identical. Continue with random PHP array selections and extracting category segments. Function details are documented in the PHP manuals for glob() and shuffle().