Heratio Help Center article. Category: Technical.
Heratio AHG Framework - Technical Architecture
For Technical Discussion with Artifactual
Prepared by: The Archive and Heritage Digital Commons Group
Date: January 2026
1. Executive Summary
The Heratio AHG Framework is a non-invasive modernization layer that:
- Integrates Laravel Query Builder into Heratio 2.10 without modifying core files
- Provides database-driven plugin management replacing hardcoded configurations
- Maintains 100% backward compatibility with existing Heratio functionality
- Targets GLAM institutions with South African regulatory compliance (POPIA, NARSSA, PAIA, GRAP 103)
Key Principle: We enhance Heratio, we don't fork it.
2. Architectural Overview
3. How the Framework Connects to Heratio
3.1 Bootstrap Integration Point
The framework bootstraps via a single function added to Heratio's config:
// /config/ProjectConfiguration.class.php
class ProjectConfiguration extends sfProjectConfiguration
{
public function setup()
{
// 1. Load core Symfony plugins (unchanged)
$corePlugins = array(
'sfWebBrowserPlugin',
'sfThumbnailPlugin',
// ... standard Heratio plugins
);
// 2. NEW: Load database-driven plugins
$this->loadPluginsFromDatabase($corePlugins);
}
// NEW FUNCTION - Only addition to Heratio config
protected function loadPluginsFromDatabase($corePlugins)
{
// Initialize Laravel Query Builder
require_once dirname(__FILE__) . '/../atom-framework/bootstrap.php';
// Query atom_plugin table
$dbPlugins = DB::table('atom_plugin')
->where('is_enabled', 1)
->orderBy('load_order')
->pluck('name')
->toArray();
// Merge and enable
$allPlugins = array_unique(array_merge($corePlugins, $dbPlugins));
$this->enablePlugins($allPlugins);
}
}
3.2 Framework Bootstrap Sequence
4. Database Architecture
4.1 Plugin Management Table
CREATE TABLE atom_plugin (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255) NOT NULL UNIQUE, -- Plugin class name
class_name VARCHAR(255) NOT NULL, -- Configuration class
version VARCHAR(50),
description TEXT,
category VARCHAR(100) DEFAULT 'general', -- theme|ahg|sector|utility
is_enabled TINYINT(1) DEFAULT 0,
is_core TINYINT(1) DEFAULT 0, -- Cannot be disabled
is_locked TINYINT(1) DEFAULT 0, -- Cannot be modified
load_order INT DEFAULT 100, -- Plugin loading sequence
plugin_path VARCHAR(500),
settings JSON, -- Per-plugin configuration
enabled_at TIMESTAMP,
disabled_at TIMESTAMP,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
4.2 Dual-ORM Strategy
5. Plugin Architecture
5.1 Directory Structure
5.2 Plugin Loading Flow
6. CLI Interface
6.1 Command Structure
# Extension Management
php bin/atom extension:discover # Scan and list available extensions
php bin/atom extension:enable <name> # Enable a plugin
php bin/atom extension:disable <name># Disable a plugin (if not locked)
php bin/atom extension:list # Show enabled/disabled status
# Framework Management
php bin/atom framework:install # Initial setup
php bin/atom framework:update # Pull latest changes
php bin/atom framework:version # Display version info
# Version Release (Development)
./bin/release patch "message" # Bump patch version
./bin/release minor "message" # Bump minor version
6.2 Extension Enable Process
7. What We Don't Modify
7.1 Core Heratio Integrity
8. Repository Pattern
8.1 Extension Repository Example
<?php
// atom-framework/src/Repositories/BaseRepository.php
namespace AtomExtensions\Repositories;
use Illuminate\Database\Capsule\Manager as DB;
use Illuminate\Support\Collection;
abstract class BaseRepository
{
protected string $table;
protected string $primaryKey = 'id';
public function find(int $id): ?array
{
return DB::table($this->table)
->where($this->primaryKey, $id)
->first();
}
public function findWhere(array $conditions): Collection
{
$query = DB::table($this->table);
foreach ($conditions as $column => $value) {
$query->where($column, $value);
}
return $query->get();
}
public function create(array $data): int
{
return DB::table($this->table)->insertGetId($data);
}
public function update(int $id, array $data): bool
{
return DB::table($this->table)
->where($this->primaryKey, $id)
->update($data) > 0;
}
}
8.2 Plugin-Specific Repository
<?php
// atom-ahg-plugins/ahgLibraryPlugin/lib/Repositories/LibraryItemRepository.php
namespace ahgLibraryPlugin\Repositories;
use AtomExtensions\Repositories\BaseRepository;
use Illuminate\Support\Collection;
class LibraryItemRepository extends BaseRepository
{
protected string $table = 'atom_library_item';
public function findByCallNumber(string $callNumber): ?array
{
return DB::table($this->table)
->where('call_number', $callNumber)
->first();
}
public function getCheckedOutItems(): Collection
{
return DB::table($this->table)
->where('status', 'checked_out')
->orderBy('due_date')
->get();
}
}
9. Theme Integration
9.1 Bootstrap 5 Implementation
10. Deployment Model
10.1 Installation Process
10.2 Update Process
# Framework updates
cd atom-framework
git pull origin main
bash bin/install --update
# Plugin updates
cd atom-ahg-plugins
git pull origin main
# Individual plugin enable
php bin/atom extension:enable ahgNewPlugin
11. Technical Specifications
| Component | Technology |
|---|---|
| Base Platform | Heratio 2.10 |
| PHP Version | 8.3 |
| Framework ORM | Laravel Query Builder (Illuminate\Database) |
| Core ORM | Propel (Symfony 1.x) - unchanged |
| Database | MySQL 8 |
| Search | Elasticsearch 7.10 |
| Web Server | nginx |
| Theme | Bootstrap 5 |
| CLI | Symfony Tasks + Custom Commands |
12. ProjectConfiguration.class.php.template
<?php
class ProjectConfiguration extends sfProjectConfiguration
{
protected $pluginsLoaded = false;
public function setup()
{
$corePlugins = array(
'sfWebBrowserPlugin',
'sfThumbnailPlugin',
'sfJSONPlugin',
'sfInstallPlugin',
'qtAccessionPlugin',
// ... other core plugins
);
$this->loadPluginsFromDatabase($corePlugins);
}
protected function loadPluginsFromDatabase($corePlugins)
{
if ($this->pluginsLoaded) {
return;
}
$frameworkPath = dirname(__FILE__) . '/../atom-framework';
if (!file_exists($frameworkPath . '/bootstrap.php')) {
$this->enablePlugins($corePlugins);
return;
}
require_once $frameworkPath . '/bootstrap.php';
try {
$dbPlugins = \Illuminate\Database\Capsule\Manager::table('atom_plugin')
->where('is_enabled', 1)
->orderBy('load_order')
->pluck('name')
->toArray();
$allPlugins = array_unique(array_merge($corePlugins, $dbPlugins));
$this->enablePlugins($allPlugins);
} catch (\Exception $e) {
$this->enablePlugins($corePlugins);
}
$this->pluginsLoaded = true;
}
}
Document Version: 1.0.0
Last Updated: January 2026
The Archive and Heritage Digital Commons Group