
— Jeff Greenberg, The Accidental Coder
That concludes the six-part Unmanaged Files in Drupal series: from raw file discovery to fully themed, category-aware randomization—no managed-file overhead, pure performance and flexibility.
Folder Structure Example
- public://segregated_maps/
- africa
- antarctica
- asia
- australia
- caribbean
- central america
- europe
- mideast
- north america
- pacific islands
- south america
This tutorial concludes the Unmanaged Files in Drupal series. In Part 1 we explored what unmanaged files are and when to use them. Part 2 built the foundation for our custom module and introduced the first file handler. Part 3 rendered unmanaged files dynamically within a custom block. Part 4 extended that output to Twig templates, and Part 5 introduced random selection logic. In this final installment, Part 6, we make that randomness category-aware—selecting three distinct images from separate subfolders within public://segregated_maps, ensuring each category is represented only once.
RandomCategoryFileHandler.php
<?php
namespace Drupalunmanaged_filesService;
use DrupalCoreFileFileSystemInterface;
class RandomCategoryFileHandler {
protected FileSystemInterface $fileSystem;
protected string $basePath;
public function __construct(FileSystemInterface $file_system) {
$this->fileSystem = $file_system;
$this->basePath = 'public://segregated_maps';
}
public function getCategoryConstrainedFiles(int $limit = 3): array {
$selected = [];
$base = $this->fileSystem->realpath($this->basePath);
if (!$base || !is_dir($base)) {
return $selected;
}
$dirs = glob($base . '/*', GLOB_ONLYDIR);
if (empty($dirs)) {
return $selected;
}
shuffle($dirs);
foreach ($dirs as $dir) {
$files = glob($dir . '/*.{jpg,jpeg,png,gif,webp}', GLOB_BRACE);
if (!empty($files)) {
$selected[] = $files[array_rand($files)];
}
if (count($selected) >= $limit) {
break;
}
}
shuffle($selected);
return $selected;
}
public function getRenderableCategoryConstrainedFiles(int $limit = 3): array {
$real_public = $this->fileSystem->realpath('public://');
$base_url = '/sites/default/files';
$files = $this->getCategoryConstrainedFiles($limit);
$renderable = [];
foreach ($files as $file) {
$url = str_replace($real_public, $base_url, $file);
$renderable[] = [
'#theme' => 'image',
'#uri' => $url,
'#alt' => 'Random map image',
'#attributes' => ['loading' => 'lazy'],
];
}
return $renderable;
}
}
RandomCategoryFileHandler.php




