Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
96 commits
Select commit Hold shift + click to select a range
2f821a7
Refactor: Introduce `SimpleEquationFilterType` and `AbstractFilterTyp…
ericges Apr 28, 2026
50c9fad
Chore: Enable strict types in FilterType classes
ericges Apr 28, 2026
969ff16
Feat: Add initial filter builder and interfaces for FlareBundle
ericges Apr 30, 2026
9ae6a55
Feat: Introduce `FilterTypeInterface`, `AbstractFilterType`, and `Sim…
ericges Apr 30, 2026
a19dae5
refactor: update filter handling and introduce new filter types and e…
ericges May 1, 2026
6aee101
refactor: remove unused FilterFactoryInterface and update mago.toml w…
ericges May 1, 2026
a411148
refactor: update mago.toml by refining paths and fix missing newline …
ericges May 1, 2026
61b8b1e
refactor: enhance type handling in AbstractFilterElement and DcaHelpe…
ericges May 1, 2026
04066bb
Chore: Add PHPStan ignore for Symfony Config template defaults
ericges Jul 13, 2026
e0cd2b4
refactor!: element-owned filter architecture
ericges Jul 13, 2026
4c20aea
refactor: consolidate filter namespace and align naming conventions
ericges Jul 13, 2026
676ecaf
test: add comprehensive unit tests for Filter, FilterOptionsResolver,…
ericges Jul 13, 2026
0b2e8d3
Enable strict types, implement `CallbackChoiceLoader`, and improve fi…
ericges Jul 13, 2026
582935d
refactor: remove unused `DcaContract` implementation from `CodefogTag…
ericges Jul 13, 2026
7873a9a
refactor: rename `AbstractFilterFilterElement` to `AbstractFilterElem…
ericges Jul 13, 2026
3f06325
refactor: rename filter element classes to include `Filter` suffix an…
ericges Jul 13, 2026
12910a9
refactor: rename `FilterConfigResolverTest` to `FilterOptionsResolver…
ericges Jul 13, 2026
78d5555
refactor: restructure filter resolver namespace and update references
ericges Jul 14, 2026
3ace5c1
feat: add transformer machinery for canonical config translation
ericges Jul 14, 2026
b61fa4b
refactor: replace `configFromRow` with transformer cycle and remove p…
ericges Jul 14, 2026
25f7b86
feat: add Lists domain — `ListSpec` DTO, `ListBuilder`, per-type list…
ericges Jul 14, 2026
6995ec3
refactor: replace `ListSpecification` with immutable `ListSpec` built…
ericges Jul 14, 2026
92cf17e
refactor: rename `ConfigureQueryContract` to `BuildQueryContract`
ericges Jul 14, 2026
747d539
test: cover transformer machinery and Lists domain; update AGENTS.md
ericges Jul 14, 2026
9be4237
refactor: exact-class precedence in `TransformerBuilder::resolve()`; …
ericges Jul 14, 2026
92d80f6
refactor: rename `Lists` namespace to singular `List`
ericges Jul 14, 2026
4ce3c77
refactor: add missing imports, normalize namespaces, fix type names a…
ericges Jul 14, 2026
ef32f49
refactor: replace `TransformerBuilder` with `TransformerResolver`, in…
ericges Jul 14, 2026
1bc98c9
refactor: replace `TransformerBuilder` with `TransformerResolver` and…
ericges Jul 14, 2026
8a5de6e
test: align tests with `TransformerResolver` API and `(ConfigBuilder,…
ericges Jul 14, 2026
24e1324
fix mago lint findings
ericges Jul 14, 2026
2dfd634
add clarification comment for PHPStan ignore annotation
ericges Jul 14, 2026
d45a834
refactor: add `from_enabled`/`to_enabled` options to `DateRangeFilter…
ericges Jul 14, 2026
20a5f40
refactor: replace `intrinsicOnly` attribute with `IntrinsicContract` …
ericges Jul 14, 2026
4c28510
refactor: streamline ChoicesBuilder usage and remove redundant option…
ericges Jul 14, 2026
5a8e867
refactor: introduce `FilterFormBuilderInterface` to handle single-fie…
ericges Jul 15, 2026
8e347e7
refactor: move `FilterFormBuilder` and `FilterFormFactory` to `Filter…
ericges Jul 15, 2026
45ab8de
refactor: extract `Config\SchemaResolver`
ericges Jul 15, 2026
994d6c3
refactor: make `ListOptionsResolver` and `FilterOptionsResolver` `fin…
ericges Jul 15, 2026
8abf7cf
refactor: extract `List\Resolver\ListTransformerResolver`
ericges Jul 15, 2026
2092710
feat: dispatch `ListTransformerEvent` from `ListTransformerResolver`
ericges Jul 15, 2026
5d9cb33
refactor: consolidate `ListType` to `ListDriver` terminology and upda…
ericges Jul 15, 2026
e228fc6
refactor: rename `ListType` to `ListDriver` in translation files
ericges Jul 15, 2026
a8e0fed
refactor: fix usages missed in the ListDriver/ListSpecBuilder rename,…
ericges Jul 15, 2026
a4fad6e
refactor: rename `ListType` to `ListDriver` across the codebase
ericges Jul 15, 2026
f30ec8d
refactor: centralize `ListDriverReference` handling, replace inline c…
ericges Jul 15, 2026
aa025be
feat: add PHPUnit CI workflow and configuration
ericges Jul 15, 2026
2918dda
docs: improve README formatting and emphasize description
ericges Jul 15, 2026
110250c
docs: add security CI badge to README
ericges Jul 15, 2026
84d49bc
refactor: replace `type` with `driver` in ListSpec and rename `getDat…
ericges Jul 16, 2026
a23d08d
refactor: remove obsolete classes and interfaces related to ListDrive…
ericges Jul 17, 2026
fe32a52
refactor: rename `ListType` namespaces to `ListDriver` and update rel…
ericges Jul 17, 2026
333f09e
refactor: replace `getDataContainerName` method with `dc` property ac…
ericges Jul 17, 2026
ff72970
refactor: mark filter-related classes as `final` and enhance immutabi…
ericges Jul 17, 2026
f593ff5
refactor: replace `hasFilterOfType` with `hasFilterInstance` for impr…
ericges Jul 17, 2026
230d8fd
refactor: replace `DcaBuilder` with `DcaBuilderInterface` across all …
ericges Jul 17, 2026
977e43a
add `test` target to Makefile and update AGENTS.md to document its usage
ericges Jul 17, 2026
d8ad46f
refactor: streamline type handling across ListDriver and FilterElemen…
ericges Jul 18, 2026
63a0231
refactor: update tests to reflect recent Filter and ListSpec type han…
ericges Jul 18, 2026
fd667dc
refactor: replace getters and setters with public readonly properties…
ericges Jul 18, 2026
1c8d1bc
fix: model validation logic in `LinksToReaderTrait`
ericges Jul 18, 2026
6a816ec
refactor: remove redundant element parameter from Filter and adjust r…
ericges Jul 18, 2026
0f8ceec
refactor: replace `ChildDefinition` with `setAlias` for FilterElement…
ericges Jul 18, 2026
db6084f
refactor: enhance immutability and streamline type handling across mu…
ericges Jul 18, 2026
e7e22cc
refactor: improve headline processing and HTML tag handling in `Str`,…
ericges Jul 19, 2026
b07c88f
refactor: remove unused imports and redundant constructor dependencie…
ericges Jul 19, 2026
2f98cad
add combined audit findings documentation for tests, CI, security, ar…
ericges Jul 19, 2026
b80e41b
refactor: improve null safety in context constructors, introduce `Ent…
ericges Jul 20, 2026
2e37580
feat: add duplicate filter alias detection and improve ListDriver res…
ericges Jul 20, 2026
a0bedb2
refactor: standardize `FlareException` method parameter usage across …
ericges Jul 21, 2026
5ff459a
refactor: improve error handling and type consistency in List and Rea…
ericges Jul 21, 2026
76dc017
refactor: enhance `ConfigBuilder`, improve `FilterFactory` and `Filte…
ericges Sep 2, 2026
8dc132a
refactor: replace closures with `LazyPage` for jump-to page handling …
ericges Sep 2, 2026
e9872fd
refactor: integrate `ListDriverResolver` into `ListSpecFactory` and r…
ericges Sep 2, 2026
5d6655c
refactor: enable strict array index existence check in mago config
ericges Sep 2, 2026
cb00935
refactor: replace form action handling in `InteractiveContext` with `…
ericges Sep 2, 2026
3f4c3ca
refactor: remove redundant `Interface` namespace from context interfaces
ericges Sep 2, 2026
02eb1fc
fix: restore `IN`/`NOT_IN` operator arms in `SimpleEquationFilterType`
ericges Sep 7, 2026
4263611
feat: introduce immutable `FilterData` class to standardize filter ru…
ericges Sep 7, 2026
9d66878
chore: remove outdated and redundant audit files from `.audit` directory
ericges Sep 7, 2026
8255195
feat: decouple filter forms from filter elements, introducing registr…
ericges Sep 7, 2026
2ab8e80
docs: update filter form spec with revised capability port and owners…
ericges Sep 7, 2026
ce2ae7b
docs: establish filter form nomenclature and add rename phase to spec
ericges Sep 8, 2026
1490eb8
refactor!: rename the filter form aggregate to FilterSet and introduc…
ericges Sep 8, 2026
06fc013
test: probe serialize() stability of readonly value objects (throwaway)
ericges Sep 8, 2026
cbbad85
fix: correct template formatting and clarify doc comment in FilterSet
ericges Sep 8, 2026
0babe62
feat: add type-safe, immutable filter value objects and enhance form …
ericges Sep 8, 2026
e0c6950
docs: update SPEC_FILTER_FORMS.md with schema/data migration sequenci…
ericges Sep 8, 2026
21e9459
refactor!: rename `FilterType` to `FilterLogic` across the codebase
ericges Sep 8, 2026
7bf470f
refactor!: rename `FilterBuilder` to `LogicSequencer` and `FilterLogi…
ericges Sep 8, 2026
97e2931
refactor!: rename `FilterSet` and related elements to `FormHarness`
ericges Sep 8, 2026
ad71819
refactor!: introduce `AbstractFilterForm` base class and update `Filt…
ericges Sep 8, 2026
311db8b
refactor!: migrate from `Logic` terminology to `Predicate`, and depre…
ericges Sep 8, 2026
b2915d9
refactor!: simplify filter form handling and value classes, enhance n…
ericges Sep 9, 2026
2fb1a80
refactor!: integrate FilterOptionsResolver into FilterFactory, stream…
ericges Sep 9, 2026
6ed5586
refactor!: rename `FormHarnessFactory` namespace and refine alias han…
ericges Sep 9, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
51 changes: 51 additions & 0 deletions .github/workflows/phpunit.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
name: PHPUnit

on:
push:
branches-ignore:
- 'docs/**'
paths:
- '**.php'
- 'composer.json'
- 'phpunit.xml.dist'
workflow_dispatch: ~

jobs:
test:
name: Unit Tests
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.2'
tools: composer
coverage: none

- name: Get Composer cache directory
id: composer-cache
run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT

- name: Restore Composer cache
id: composer-cache-restore
uses: actions/cache/restore@v4
with:
path: ${{ steps.composer-cache.outputs.dir }}
key: composer-8.2-${{ hashFiles('composer.json') }}
restore-keys: composer-8.2-

- name: Install dependencies
run: composer update --no-progress --prefer-dist

- name: Save Composer cache
if: always() && steps.composer-cache-restore.outputs.cache-hit != 'true'
uses: actions/cache/save@v4
with:
path: ${{ steps.composer-cache.outputs.dir }}
key: composer-8.2-${{ hashFiles('composer.json') }}

- name: Run PHPUnit
run: vendor/bin/phpunit
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,5 @@ docs/build

###> Docs ###
docs/
todo.*
###< Docs ###
59 changes: 43 additions & 16 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ The core execution flow is:

```
ContentElement Controller
EngineFactory (consumes a ListSpecification from src/Specification/)
→ Engine
ListBuilderFactory::createFromListModel(...)->build() — builds the immutable ListSpec (src/List/)
EngineFactory → Engine
→ Context (Interactive / Validation / Aggregation) — src/Engine/Context/
→ Loader (src/Engine/Loader/) + Mods (src/Engine/Mod/)
→ Projector (Interactive / Validation / Aggregation / Export) — orchestrates query + filter execution
Expand All @@ -30,10 +30,30 @@ Note: Contexts and Projectors/Views are separate axes — (Export Context/Projec

The bundle follows standard Symfony Bundle architecture with deep Contao integration.

**Lifecycle taxonomy** — elements and list types own their lifecycle through two method families:
`configure*` methods are declarative, memoizable setup (`configureOptions` = OptionsResolver schema,
`configureTransformers` = source→canonical-config mappings); `build*` methods are per-invocation construction
(`buildDca`, `buildForm`, `buildContext`, `buildList`, `buildTableRegistry`/`buildBaseQuery`).

**Notable subsystems** (beyond the flow above):
- `src/Specification/` — `ListSpecification` / `FilterDefinition`, the declarative input to the engine
- `src/Filter/`, `src/FilterElement/`, `src/FilterCollector/` — filter definition and execution
- `src/Form/` — filter form building (FilterFormFactory etc.)
- `src/List/` — `ListSpec` (immutable list DTO: type, dc, filters, canonical config, source), `ListBuilder`
(build lifecycle: type's `buildList()` hook → `ListBuildEvent` → config assembly → schema resolution),
`BaseListOptions` (framework-owned base schema for tl_flare_list columns)
- `src/Filter/` — `Filter` DTO, elements (`Element/`), types (`Type/`), collector, resolvers
(`FilterOptionsResolver`, `FilterTransformerResolver`, `FilterElementResolver`), `FilterContextFactory`,
and the form aggregate: `FormHarnessFactory` builds a `FormHarness` (root form + mount↔filter map of
`FilterMount`s) per list × form context
- `src/Config/` — `ConfigBuilder` (fluent canonical-config accumulator; no cast helpers — transformers cast
declaratively off the typed model) and `TransformerResolver` (source class → transformer map)
- `src/Filter/Form/` — FLARE filter form implementations (peers of `src/Filter/Element/`) behind
`FilterFormInterface` (`buildForm()` + `decode()`); `src/Form/` — Symfony-level building blocks only
(`ChoicesBuilder`, `Form/Type/DateRangeFormType`)
- `src/Filter/Value/` — immutable filter value objects (`BoolValue`, `ChoiceValue`, `KeywordsValue`,
`DateRangeValue`, `ParentRefValue`), the typed channel between a form's `decode()` and an element's
`buildFilter()`. Each is `final readonly` with only public scalar/enum/nested-VO/array properties —
the containment rule `tests/Filter/Value/ValueObjectContainmentTest.php` enforces by reflection, so
`Util\Fingerprint::flatten()` can hash them without object identity leaking in. Deliberately not
services (excluded in `config/services.yaml`)
- `src/Reader/` — reader/detail-page URL generation (`ReaderUrlGenerator`)
- `src/InferPtable/` — parent-table inference for DCAs
- `src/Integration/` — optional integrations (Codefog Tags, Terminal42 ChangeLanguage), wired via `config/integrations/*.yaml`
Expand All @@ -46,21 +66,27 @@ The bundle follows standard Symfony Bundle architecture with deep Contao integra
- `src/Controller/ContentElement/ListViewController.php` / `ReaderController.php` — frontend controllers

**Extensibility via PHP 8 attributes** (compiler passes auto-register tagged services):
- `#[AsFilterElement(type: '...', palette: '...', formType: ...)]` — register a filter element
- `#[AsListType(type: '...', dataContainer: '...', palette: '...')]` — register a list type
- `#[AsFilterCallback(type, 'path.to.callback')]` — register a Contao DCA callback on a filter type
- `#[AsListCallback(type, 'path.to.callback')]` — register a Contao DCA callback on a list type
- `#[AsFilterInvoker]` — register a custom filter invocation handler

(`AsFilterCallback` and `AsListCallback` both extend the `@internal` base attribute `AsFlareCallback`.)
- `#[AsFilterElement(type: '...', isTargeted: ...)]` — register a filter element
- `#[AsFilterForm(name: '...', value: '...', requires: [...], default: ...)]` — register a filter form,
bound to a value class rather than to an element type (repeatable)
- `#[AsListDriver(type: '...', dataContainer: '...')]` — register a list driver

Attributes are in `src/DependencyInjection/Attribute/`, compiler passes in `src/DependencyInjection/Compiler/`.
Backend palettes/fields are declared in code via `DcaContract::buildDca(DcaBuilder, DcaContext)` (both
tl_flare_filter and tl_flare_list).

**Event system** — Events, some with aliased dispatch for targeted listening (`flare.form.{name}.build`, etc., implemented by the listeners in `src/EventListener/NamedDispatch/`). All events are in `src/Event/`. Prefer events over overriding services for customization.
**Event system** — Events, some with aliased dispatch for targeted listening
(`flare.form.{name}.build`, `flare.filter_form.{type}.built`, `flare.list.{type}.build`,
`flare.filter_element.{type}.transformers`, `flare.filter_element.{type}.dca` / `flare.list.{type}.dca`,
etc., implemented by the listeners in `src/EventListener/NamedDispatch/`). All events are in `src/Event/`.
Prefer events over overriding services for customization.

**Registry pattern** — Registries in `src/Registry/` map type names to implementations: `FilterElementRegistry`, `ListTypeRegistry`, `FilterInvokerRegistry`, `ProjectorRegistry`, `FilterCollectorRegistry`, `FlareCallbackRegistry`, `EngineModRegistry`.
**Registry pattern** — Registries in `src/Registry/` map type names to implementations: `FilterElementRegistry`, `FilterFormRegistry`, `ListDriverRegistry`, `FilterPredicateRegistry`, `ProjectorRegistry`, `EngineModRegistry`. `FilterFormRegistry` differs from the others: it holds
compile-time metadata as plain arrays and resolves the form services through a lazy
`container.service_locator`, so reading metadata (the `formVariant` options, the form election)
instantiates nothing.

**Query safety** — `FilterQueryBuilder` (`src/Query/FilterQueryBuilder.php`) enforces parameterized queries. `TableAliasRegistry` (`src/Query/TableAliasRegistry.php`) manages table aliases and JOINs safely.
**Query safety** — `FilterConditionsBuilder` (`src/Query/FilterConditionsBuilder.php`) enforces parameterized queries. `TableAliasRegistry` (`src/Query/TableAliasRegistry.php`) manages table aliases and JOINs safely.

**Contao DCA** — Backend form definitions in `contao/dca/tl_flare_*.php`. Templates in `contao/templates/`. Translations in `contao/languages/`.

Expand Down Expand Up @@ -91,8 +117,9 @@ Attributes are in `src/DependencyInjection/Attribute/`, compiler passes in `src/

## Testing & CI

* **There is currently no test suite**: no `tests/` directory, no `phpunit.xml`, no test CI workflow — even though PHPUnit is in `require-dev` and `tests/` is referenced in `autoload-dev` and `mago.toml`. Don't look for tests or invent a `make test` target.
* **Unit tests** live in `tests/` (PHPUnit 9, configured via `phpunit.xml.dist`); run them with `make test` (optionally passing phpunit args, e.g. `make test tests/SomeTest.php`).
* CI workflows in `.github/workflows/`:
* `phpunit.yaml` — PHPUnit test suite
* `phpstan.yaml` — PHPStan analysis
* `mago.yaml` — Mago lint (`--minimum-fail-level note`, PHP 8.2–8.5)
* `compatibility.yaml` — `composer validate` + dependency-resolution matrix (PHP 8.2–8.5 × Contao 4.13/5.x)
Expand Down
5 changes: 4 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.PHONY: help docs-setup docs-remove php composer semgrep-sec
.PHONY: help docs-setup docs-remove php composer semgrep-sec test

# Configuration
DOCS_DIR = docs
Expand All @@ -23,6 +23,9 @@ phpstan-pro: ## Run PHPStan static analysis in Pro GUI
semgrep-sec: ## Run Semgrep security scanner
docker compose run --rm semgrep-sec

test: ## Run PHPUnit test suite
docker compose run --rm php vendor/bin/phpunit $(filter-out $@,$(MAKECMDGOALS))

docs-setup: ## Setup the Docusaurus worktree environment locally
@if [ -d "$(DOCS_DIR)" ]; then \
echo "Directory $(DOCS_DIR) already exists."; \
Expand Down
Loading
Loading