Conversation
ericges
force-pushed
the
feat/filter-types
branch
from
May 28, 2026 14:14
374d0e0 to
43c7da1
Compare
ericges
force-pushed
the
feat/filter-types
branch
from
July 13, 2026 11:51
43c7da1 to
bb30f1f
Compare
ericges
added a commit
that referenced
this pull request
Jul 13, 2026
Rewrites the developer documentation for the v0.2 filter subsystem rework: - Delete pages for removed subsystems: DCA callbacks (callbacks.mdx), filter invokers (spec/invoker.md), and the six removed filter-element contracts (form-data, form-type-options, hydrate-form, intrinsic-value, runtime-value, palette). - New pages: dev/filter-types (custom filter types guide), dev/dca-builder (backend DCA building via DcaContract/DcaBuilder/ElementDcaEvent), dev/contracts/dca-contract, spec/filtering (filter pipeline architecture), reference/filter-types (built-in type catalog), migrating-from-v0.1, removed-in-v0.2, and an unlisted preflight-v0.2 release checklist. - Rewrite dev/filter-elements around the buildForm/buildFilter/buildDca/ configureOptions lifecycle, dev/events for the new event set and named aliases (.building/.built/.form_built/.dca), spec/specifications around the immutable Filter value object and the keyed addFilter() API, and dev/contracts/index for the new contract layout. - Update attribute docs (#[AsFilterElement] without palette/formType/method, #[AsListType] without palette), engine-mods examples to SimpleEquationFilterElement::define() + addFilter(), Twig docs for the new flare_make_filter() helper, and reference tables for the new Filter\Element namespace and per-element form controls. - flare_enclosure_files is documented as removed (code removal tracked on the preflight page). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
ericges
force-pushed
the
feat/filter-types
branch
3 times, most recently
from
July 19, 2026 13:11
ae0b057 to
5940ad6
Compare
ericges
force-pushed
the
feat/filter-types
branch
2 times, most recently
from
September 2, 2026 13:15
b86320a to
fdb7d89
Compare
…e` to modularize filter handling
…pleEquationFilterType` for extensible filter handling in FlareBundle
…ith assertion style configuration
…in configuration files
…r for improved clarity
Filter elements now own their entire lifecycle: form building on native Symfony FormBuilderInterface sub-builders, config schema + DCA-row translation via ConfigContract (OptionsResolver), data-to-options transformation in buildFilter(builder, context, data), and backend DCA configuration via DcaContract::configureDca() on both tl_flare_filter and tl_flare_list. - Replace ConfiguredFilter/ConfiguredFilterCollection with immutable Filter DTO held as plain keyed array on ListSpecification - Remove FilterInvocation, FilterFormBuilder, Hydrate/FormData/ Intrinsic/RuntimeValue/FormTypeOptions contracts, and the entire AsFlareCallback/palette machinery (PaletteContract, PaletteEvent, callback registry, MethodInjector) - Add standalone filter channels: Filter::fromCallback()/fromType() and flare_make_filter() Twig function - Nest filter GET params as ?list[alias][value]=x (BC break) - Add unit tests for Filter, FilterConfigResolver, ListSpecification
Restructure the filter subsystem under the Filter\ namespace and align class/interface names with their roles. No behavior changes. - Move filter elements to Filter\Element\ and collectors to Filter\Collector\; suffix element classes consistently (PublishedFilterElement, SearchKeywordsFilterElement, ...) - Rename ConfigContract -> FilterElementOptionsInterface (configureConfig -> configureOptions) and FilterConfigResolver -> Filter\OptionsResolver\FilterOptionsResolver - Rename DcaContract::configureDca -> buildDca; introduce DcaBuilderInterface and DcaFieldBuilderInterface - Rename Form\Type\DateRangeFilterType -> DateRangeFormType, resolving the name collision with the query-side filter type - Drop unused ListItemProviderConfig; update translations - Add unit tests for Filter, FilterOptionsResolver, and ListSpecification; restore FilterFormListener named dispatch
… and ListSpecification classes <!-- This aligns with the existing commit style from the history while mentioning the main action and specific additions. -->
…lter lifecycle and element handling.
…sChoiceFilterElement`
…ent` and update references Revised all occurrences of `AbstractFilterFilterElement` to streamline naming conventions and align with the filter subsystem structure.
…d update references Aligned filter element naming with established conventions (`ArchiveElement` -> `ArchiveFilterElement`, etc.) and adjusted translations accordingly.
…Test` and update method/exception references
Moved `FilterOptionsResolver` and `FilterElementResolver` to `Filter\Resolver\` namespace, adjusted imports and references accordingly.
Introduce `ConfigBuilder` (fluent canonical-config accumulator), `TransformerBuilder`
(source-class to transformer map), the `TransformerContract` (`configureTransformers()`),
and `FilterTransformerResolver` (per-element-class memoized transformer execution).
Transformer maps are extensible via `FilterTransformerEvent`, re-dispatched as
`flare.filter_element.{type}.transformers`.
…rogrammatic sugar - Elements now implement `configureTransformers()` (via `AbstractFilterElement`, which registers `transformFilterModel(FilterModel, ConfigBuilder)` for the FilterModel source); `FilterElementOptionsInterface` and `configFromRow()` are gone. `FilterOptionsResolver` checks the generic `OptionsInterface`. - `ListModelFilterCollector` translates via `FilterTransformerResolver`; elements without a matching transformer keep the raw-row passthrough. - New `FilterContextFactory` dedupes the identical `FilterContext` construction in `FilterFormFactory` and `FilterExecutor`. - Removed programmatic sugar: all static `define()` factories, `Filter::fromType()`, `Filter::fromCallback()`, `CallbackFilterElement`, and the `flare_make_filter` Twig function. Internal call sites construct `new Filter(element:, config:)` directly. A proper engine-extending API is a follow-up.
… options
- `Lists\ListSpec`: immutable list DTO (type, dc, filters, canonical config, source)
with `with*()` modifiers, `getAutoItemField()` (validated), and a config-based `hash()`.
- `ListBuilder` owns the list build lifecycle: type's `BuildListContract::buildList()`
hook, `ListBuildEvent` (named dispatch `flare.list.{type}.build`), base + type
transformer config assembly, schema resolution via `ListOptionsResolver`.
- `BaseListOptions`: framework-owned base schema/translation for tl_flare_list columns,
applied unconditionally; `genericPageMeta` replaces the dynamic `eval_generic_page_meta`.
- `AbstractListType` now implements `ListTypeInterface`, `OptionsInterface`, and
`TransformerContract` (`transformListModel()` override point).
- `ListBuilderFactory` replaces `ListSpecificationFactory` (old path still in place
until the consumer sweep).
…ryCache` utility Made `PaginatorConfig` non-nullable in `InteractiveContext` for improved null safety. Introduced a new `EntryCache` utility for handling cached entries in a more structured manner. Replaced positional entry lookups with ID-based indexing. Updated `ValidationLoader` and removed stale `entryCache` logic from `ValidationContextFactory`. Adjusted `composer.json` to require `doctrine/dbal` `^3.6 || ^4.0`. Refined `count()` in `PaginatorConfig` to return `1` by default if `totalItems` is unknown.
…olution mechanics Implemented detection for duplicate filter aliases in `ListModel` and introduced backend error messages for better user feedback. Refactored `ListDriver` resolution with a dedicated `ListDriverResolver` class to centralize logic. Updated `ListSpecBuilder`, `ListSpecFactory`, and related classes to use the resolver. Enhanced backend filter info template to display alias conflicts. Added corresponding translations and adjusted `ElementDcaListener` for alias check logic.
…codebase Aligned all `FlareException` instantiations to use the `method: __METHOD__` parameter for consistency. Updated exception messages and adjusted formatting where necessary to ensure compliance with the revised standard.
…rTransformerResolver` readability Added `has()` and `unset()` methods in `ConfigBuilder` for better configuration management. Refactored `FilterFactory::resolveElement()` and `FilterTransformerResolver` for improved code clarity and structure.
…in contexts Introduced the `LazyPage` utility for lazy-loading `PageModel` instances, replacing closure-based logic for jump-to pages. Updated `InteractiveContext`, `ValidationContext`, and `ReaderUrlConfigCreatorTrait` to use `LazyPage` for improved readability and consistency.
…elated tests Updated `ListSpecFactory` and its test suite to use `ListDriverResolver` for resolving drivers. Adjusted `InteractiveContext` return type to `self` for consistency. Introduced new dependency injection in `ListSpecBuilderTest` for improved driver resolution handling. Added initializations in `ElementDcaListener` for clearer variable declarations.
…LazyPage` Replaced `formActionPage` with `LazyPage` for improved lazy-loading capabilities. Updated `FilterFormFactory` and associated interfaces to use `createFormActionUrl()`. Removed redundant `resolveFormAction()` method for cleaner code. Adjusted tests to reflect the changes.
Moved `FormContextInterface`, `PaginatedContextInterface`, and `SortableContextInterface` from the `Interface` namespace to `Context` for consistency. Updated all associated imports and references across the codebase.
The fix from #35 added these arms to `SimpleEquationElement::__invoke()` on `main`. This branch had already moved that `match` into `SimpleEquationFilterType::buildQuery()`, so the rebase dropped them.
ericges
force-pushed
the
feat/filter-types
branch
from
September 7, 2026 15:29
429b40c to
02eb1fc
Compare
…ntime data handling Added `FilterData` to encapsulate filter runtime data as an immutable object, replacing array-based values. Updated `buildFilter()` across filter elements and contexts to require `FilterData` instead of raw arrays. Refactored methods in `InteractiveProjector`, `AggregationContext`, and `ValidationContext` to align with the new filter data model. Enhanced tests for compatibility with `FilterData` and ensured backward compatibility where appropriate.
Deleted combined audit files as they are no longer relevant or actively maintained. This cleanup reduces clutter and ensures the repository contains only actionable and current resources.
…y-based form handling Implemented a registry system to split filter elements' matching logic from their presentation logic. Added `FilterFormInterface` and revised `FilterElementInterface` to enable independent, per-instance form handling. Migrated intrinsic filters and related configuration to the new `formVariant` model, replacing the `intrinsic` boolean. Updated value handling with type-safe value objects and enhanced DCA palette composition for clearer backend structure.
…hip rules Refined documentation with a clearer `ChoiceSourceContract` design, introducing a second method (`valueFromChoiceKeys()`) to handle element-defined choice keys. Documented `ArchiveFilterElement` adaptations, config re-ownership details, and resolved decisions for intrinsic branching, shared translations, and value object usage. Added sections on decode divisions, module-specific behaviors, and open design questions.
Resolve the `FilterForm` name collision: the per-filter presentation strategy takes the name, and the whole-form machinery becomes `FilterSet`. The naming axis is multiplicity within *Filter*, not filter-versus-list — a form belongs to filtering, the list is exclusively output. - §2.1 defines the four layers (`FilterSet`, `FilterForm`, `FilterFormBuilder`, mount) and the `src/Filter/Form/` vs. `src/Form/` split - §2.2 carries the rename table and the rejected names with their reasons - §12 gains Phase 0: rename plus the new `FilterSet` object, no behaviour or schema change, so step 1 contains only design - §8 moves the decode loop from `InteractiveProjector::collectFilterData()` to `FilterSet::decode()`, which is why the aggregate is an object - `$field` becomes `$mount` throughout (§1.2, §3.1, §3.4, §7.2) — the mounted node is a group, not a field, in the compound case - §14.4 records the unresolved tension between `FilterSet` and `ListSpec::$filters`
…e FilterMount
Implements step 0 of SPEC_FILTER_FORMS.md §12 (§2.1/§2.2). `FilterForm` is the name the
upcoming per-filter presentation strategy needs, but it was occupied by the whole-form
machinery. This frees it and gives the decode loop of step 1 a home to move into.
Renames per §2.2:
- Filter\Factory\FilterFormFactory -> Filter\Factory\FilterSetFactory
- Event\FilterFormBuildEvent -> Event\FilterSetBuildEvent
- Event\FilterElementFormBuiltEvent -> Event\FilterFormBuiltEvent
- NamedDispatch\FilterFormListener -> NamedDispatch\FilterSetListener
- flare.form.{name}.build -> flare.filter_set.{name}.build
- flare.filter_element.{type}.form_built -> flare.filter_form.{type}.built
The second alias is not in §2.2's table (the spec renames the event class but is silent on
its alias); it is recorded there now. Its handler moves out of FilterElementListener into a
new FilterFormListener, occupying the path the FilterSetListener rename frees. `{type}` there
is still the element type — the alias names the concern, not the key.
FilterSetFactory::create() now returns a FilterSet holding the root form plus a mount<->filter
map of FilterMount (filter, alias, FilterContext), keyed by the filter's key within
ListSpec::$filters. Only filters that actually mount get an entry. Mounts resolve lazily
against the root form, because form children may legally be added or removed by a PRE_SUBMIT
listener while the request is handled. Local variables follow §12.0: root builder `$root`,
per-filter collector `$builder`, mounted node `$mount`.
FilterMount::$filter and $context overlap deliberately; only $alias carries information the
context does not — the valid-form-name invariant established before mounting.
InteractiveProjector gains a protected createFilterSet() that hands the request to the root
form; createForm() keeps its signature and delegates. collectFilterData() is untouched — step
1 replaces it with FilterSet::decode(). InteractiveView still takes a FormInterface, so the
list-view template is unaffected.
No behaviour change beyond the two dispatch aliases; no schema change; no new contract.
BREAKING CHANGE: the service id of the renamed factory, the four event/listener class names,
and the `flare.form.{name}.build` and `flare.filter_element.{type}.form_built` dispatch
aliases. The `flare.form.date_range.*` validator translation keys are unrelated and unchanged.
Answers SPEC_FILTER_FORMS.md §14.1, which asked for §9's "value objects need no hashing interface" claim to be measured against ListSpec::hash() before step 1 commits to it. Reaches the real hashing path with no new production code, via Filter::$data. §9's core claim holds: equal-but-distinct readonly value objects of scalars, arrays, enums and nested value objects hash identically and survive a serialize() round trip. Every hazard §9 names is confirmed (DateTimeImmutable timezone representation, array order, model-like mutation state, closures throwing). One finding §9 does not anticipate: serialize() is not a pure value function over an object graph. A repeated object is emitted as a back-reference (r:N;), so the hash differs depending on whether two filters share one value instance or hold two equal ones. Today's code is immune only because Filter::fingerprint() flattens through FilterData::toArray(); §8's plan to move the hashing role onto the value object removes that flattening. Recorded in §14.1. Committed separately so deleting the probe after step 1 is a clean revert. The finding in §14.1 is meant to outlive it.
…handling Introduced `FilterFormInterface` for registry-based form management, decoupling filter elements from their forms. Added immutable filter value objects (`BoolValue`, `ChoiceValue`, `DateRangeValue`, etc.) to ensure consistency and enforce type safety. Enhanced palette configuration with the new `formVariant` model for better backend clarity. Implemented comprehensive tests to validate flattening, normalization, and runtime compliance of value objects.
…ng and step clarifications
Replaces all occurrences of `FilterType` with `FilterLogic` to better reflect its purpose of encapsulating logical filtering operations rather than type definitions. Updates dependencies, registry mappings, tests, and documentation to adhere to the new naming convention. Introduced `FilterConditionsBuilder` and `FilterConditions` to replace `FilterQueryBuilder` and `FilterQuery`, aligning with the broader terminology shift. Includes minor adjustments to exception handling and logging strings for clarity.
…c` to `Logic` Replaced all occurrences of `FilterBuilder` with `LogicSequencer` to improve conceptual clarity, aligning it with its purpose of sequencing logical operations. Similarly, renamed `FilterLogic` and related classes to `Logic` with corresponding updates in tests, events, registry mappings, and documentation. Includes necessary refactors in method and property names to reflect this change across the codebase.
Updated terminology to improve alignment with evolving functional purposes. Renamed `FilterSet` to `FormHarness` across the codebase, including factories, events, namespaces, and documentation. Adjusted method signatures and type declarations where relevant. Ensured all references to `FilterSetFactory`, `FilterSetBuildEvent`, and `FilterMount` now reflect the updated `FormHarness` naming convention. Includes updates to specs, schema references, and tests.
…erFormInterface` doc comments Added `AbstractFilterForm` as a base class for filter forms aligning with `FilterFormInterface`. Refined `FilterFormInterface` PHPDoc for clarity and simplified method parameters (`$mount` to `$form`). Adjusted tests to reflect changes in method signatures (`decode`).
…cate `LogicStep` and `FilterExecutor` Rebranded all `Logic` classes and interfaces as `Predicate` to improve naming consistency and clarity across the codebase. Updated dependencies, tests, events, and registry mappings accordingly. Removed `LogicStep` and `FilterExecutor` entirely, along with their usages, as part of a broader simplification. Adjusted `ListSpec` filters to omit alias generation, enforcing cleaner filter registration logic. Includes fixes and updates to relevant PHPDoc, method signatures, and event dispatch aliasing (`LogicSequencerInterface` → `FormulaBuilderInterface`).
…ormalization Removed outdated attributes and validations (`default`, redundant PHPDoc) from filter forms, improving clarity. Updated constructors and methods of value classes (`BoolValue`, `DateRangeValue`, etc.) for consistency, immutability, and type safety. Introduced `normalizeTimestamp()` in `CalendarCurrentPredicate` and improved `DateTimeImmutable` usage in `DateRangeValue`. Adjusted references and registry mappings for streamlined behavior across filter forms.
…line config handling Decoupled alias generation, moved config resolution logic from FilterContextBuilderFactory and FilterContext into FilterFactory using FilterOptionsResolver. Updated constructors and methods across affected classes for improved consistency. Enhanced PHPDoc with templates and refined method signatures for type safety and clarity.
…dling in filter forms
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This pull request introduces significant refactoring and cleanup to the codebase, focusing on backend configuration, dependency injection, and code quality. The main changes include the removal of several legacy or redundant contracts, the introduction of a new DCA (Data Container Array) configuration API, and updates to service definitions and code quality settings.
Backend configuration and DCA API:
Introduced a new DCA configuration API with
DcaBuilder,DcaBuilderInterface, andDcaContextto standardize and streamline backend configuration for filter elements and list types. This includes the newDcaContractinterface for elements that configure their Contao backend appearance. (src/DataContainer/Builder/DcaBuilder.php[1]src/DataContainer/Builder/DcaBuilderInterface.php[2]src/DataContainer/Builder/DcaContext.php[3]src/Contract/DcaContract.php[4]Removed the legacy
PaletteContractand related configuration classes, as well as several filter element contracts, in favor of the new DCA API. (src/Contract/PaletteContract.php[1]src/Contract/Config/PaletteConfig.php[2]src/Contract/Config/ListItemProviderConfig.php[3]src/Contract/FilterElement/FormDataContract.php[4]src/Contract/FilterElement/FormTypeOptionsContract.php[5]src/Contract/FilterElement/HydrateFormContract.php[6]src/Contract/FilterElement/IntrinsicValueContract.php[7]src/Contract/FilterElement/RuntimeValueContract.php[8]Dependency injection and service configuration:
services.yamlto exclude new directories (DataContainer/Builder, a filter element file) from autowiring and removed the now-unneededFilterInvokerResolverservice definition. (config/services.yaml[1] [2] [3]Code quality and static analysis:
mago.tomlto restrict source paths to onlysrc/, add an assertion style rule, and improve static analysis configuration. (mago.toml[1] [2]Collection and utility classes:
AbstractCollectionandFilterDefinitionCollectionclasses, likely as part of the refactoring and simplification. (src/Collection/AbstractCollection.php[1]src/Collection/FilterDefinitionCollection.php[2]These changes collectively modernize the backend configuration approach, remove dead code, and improve maintainability.This pull request introduces a significant refactor of the filter system, focusing on renaming and replacing the
FilterDefinitionconcept withConfiguredFilter, and removing the custom filter invoker attribute/registration system. It also cleans up configuration and code style, and removes now-unnecessary files related to filter invoker registration.Key changes:
Filter System Refactor
Replaced all uses of
FilterDefinitionwithConfiguredFilterthroughout the codebase, including type hints, method signatures, and documentation. The collection class was renamed fromFilterDefinitionCollectiontoConfiguredFilterCollection, with corresponding updates to methods and type annotations. (src/Collection/ConfiguredFilterCollection.php, [1] [2] [3] [4] [5] [6];src/Contract/FilterElement/HydrateFormContract.php, [7];src/Contract/FilterElement/IntrinsicValueContract.php, [8] [9];src/Contract/FilterElement/RuntimeValueContract.php, [10] [11];src/DataContainer/FilterContainer.php, [12] [13] [14] [15]Removed the
AsFilterInvokerattribute and the compiler passRegisterFilterInvokersPass, effectively eliminating custom filter invoker registration via attributes. Related service configuration and autoconfiguration were also cleaned up. (src/DependencyInjection/Attribute/AsFilterInvoker.php, [1];src/DependencyInjection/Compiler/RegisterFilterInvokersPass.php, [2];config/services.yaml, [3];src/DependencyInjection/HeimrichHannotFlareExtension.php, [4] [5]Configuration and Code Style
Updated
mago.tomlto only include thesrc/directory in the source paths and added a rule for assertion style. (mago.toml, [1] [2]Minor code cleanup, such as removing unnecessary comments and ignoring obsolete PHPStan directives. (
src/DependencyInjection/Configuration.php, src/DependencyInjection/Configuration.phpL17)These changes modernize and streamline the filter system, making it more consistent and easier to maintain by relying on the new
ConfiguredFiltermodel and removing legacy invoker registration code.