Skip to content
Language:DeutschEnglish Last updated: 2026-08-19

Extension points

Archives have special requirements — their own reference code logic, a particular Word finding aid, a format normalisation. Anton answers this with one consistent pattern instead of forking the core per customer:

Tenant-specific behaviour is hooked in by naming a class via a setting — or by having a customer-specific subclass. The core remains unchanged.

Anyone who internalises that sentence will find most extension seams on their own. This page lists the most important ones.

Reference code generation

The setting identifier_generator contains either one of the built-in modes (standard, recordgroup_as_base, id_identifier, manual_identifiers) or the fully qualified class name of a custom generator. Anton instantiates it and calls getNewIdentifier(...):

$generator = setting('identifier_generator');
// e.g. 'Anton\Models\IdentifierGenerators\KaeNewIdentifier'
return (new $generator($this))->getNewIdentifier($level_of_description_id, …);

Custom generators are located under app/Models/IdentifierGenerators/ (KaeNewIdentifier, StopNewIdentifier as templates). This allows an archive to pull the location into the reference code or to run an entirely custom scheme without the standard generator knowing about it.

Exporters

The export layer is built along this seam. Anyone extending it follows two rules (see also CLAUDE.md, section Exporter layout):

  • Builders, DTOs and format logic belong under app/Services/Exporter/<Format>/, never in an HTTP controller.
  • A controller under app/Http/Controllers/Exporter/ is only a route handler.

Two concrete extension patterns:

TEI via a factory. custom_teiexporter is a setting that names an exporter class per entity (actors, places, keywords …). The TeiExporterFactory resolves it and otherwise falls back to the standard exporters. This gives an archive a special format (the Opera format of ZBZ, for example) without touching the standard.

Customer-specific subclasses. Word finding aids exist as AntonWordExport and variants derived from it (CasparwolfWordExport, GosteliWordExport, ArchivdatenWordExport); the DIP has a ZH variant (ZhCreateDip). The pattern: inherit from the standard, override what differs.

Maintain the export matrix as well

Anyone adding an export format or changing what an existing one outputs updates the export matrix in the same step — archives decide on that basis what counts as a backup. The rule is also in CLAUDE.md.

Media conversions

The standard conversions (web, thumb, poster) are in config/conversions.php. For anything different, a custom conversion script can be stored: media:conversions --conversion-script=… reads it from the tenant's scripts/ directory (customers/{slug}/scripts/). This allows an archive to run a normalisation that the core does not know about.

Forms and value lists

Not code but data — and yet the most important «extension» of all: fields, form sets and value lists are created by an archive via the interface or via seeders. Details under The form system. Seeders deliberately do not overwrite existing configurations.

Tenant assets

Beyond code and database, tenant-specific material is located under customers/{slug}/ — logo and favicons, the conversion scripts mentioned, paths for import and reset. That is the place for everything that belongs to the installation but not to the code.

The limit

The price of this pattern: customer classes and named settings are scattered across the tree. Before building a new special case as its own class, it is worth asking whether it could not be expressed as configuration — a form set, a value list, a setting. Configuration survives refactorings; a customer class has to be dragged along.