Skip to content

Add Symfony 4-compatible element infrastructure#1367

Merged
werrolf merged 69 commits into
masterfrom
feature/symfony-4-element-infrastructure
Jun 29, 2021
Merged

Add Symfony 4-compatible element infrastructure#1367
werrolf merged 69 commits into
masterfrom
feature/symfony-4-element-infrastructure

Conversation

@werrolf

@werrolf werrolf commented Jun 25, 2021

Copy link
Copy Markdown
Contributor

Mapbender Elements inheriting from Mapbender\CoreBundle\Component\Element will inherently be incompatible with Symfony 4.

This pull adds new infrastructure to allow writing Elements that will work on Symfony 4.

Conformant Element classes must implement Mapbender\Component\Element\ElementServiceInterface (alternatively extend Mapbender\Component\Element\AbstractElementService, which implements the interface already).

Conformant Element classes must be registered as a service and tagged with mapbender.element. Use standard Symfony DI to pass services / global configuration parameters into the constructor. E.g.:

        <service id="mapbender.element.main_map" class="Mapbender\CoreBundle\Element\Map">
            <tag name="mapbender.element" />
            <argument type="service" id="doctrine" />
        </service>

NOTE: omitting the id is an error on Symfony 4.
NOTE: do not attempt injecting the full container as service.container. This is an error on Symfony 4.

Redundantly naming the class name of a tagged Element service in the return value of a MapbenderBundle::getElements method is discouraged.

API comparison

Element services retain some of the static API from legacy Component\Element. Namely the static methods getClassTitle, getClassDescription, getDefaultConfiguration, getFormTemplate, getType (=backend form type FQCN).

Non-static method getWidgetName now receives the Element entity as the first argument (same name).

Non-static method getRequiredAssets (renamed for clarity / signature sanity) receives the Element entity as the first argument, and functionally replaces both Component\Element::getAssets and (super legacy) static listAssets.

NOTE: Service-type Elements do not support legacy automatic bundle name amending ('file.js' => '@MagicallyInflectedBundle/Resources/public/file.js') for their asset requirements. References to required assets must be returned in properly qualified form. Magic bundle scope inflection of assets has been deprecated since Mapbender v3.0.8-beta1.

Non-static method getClientConfiguration (renamed for clarity / signature sanity) receives the Element entity as the first argument, and functionally replaces Component\Element::getPublicConfiguration

Non-static method getView receives the Element entity as the first argument, and must return either a StaticView (empty or trivially prerendered content) or a TemplateView, or a falsy PHP value. This functionally replaces Component\Element's getFrontendTemplatePath, getFrontendTemplateVars and render methods.

There are no longer any "utility methods" (getTitle, getId, getEntity, getConfiguration, getMapEngineCode). The Element entity is universally available as an argument.

Frontend markup rendering changes

Service-type Elements are rendered by the system, according to what they return from getView. Accessing the (twig) templating engine from inside the Element implementation is discouraged.

Service-type Element frontend templates should drop wrapping <div id="..." class="mb-element ..." ... tags. The outer tag is generated as appropriate for the enclosing region. E.g. <li id="..." class="mb-element toolBarItem"> is generated in footers / headers; divs elsewhere.
ElementView has a public $attributes array property that should be used to add any additional required attributes (e.g. title for tooltips, class to tie in extra CSS rules).
NOTE that the id attribute and class="mb-element" are added automatically, and should not be respecified.

There is no longer any predetermined set of variables injected into templates. Any variables required to render the Element markup via (twig) template must be explicitly placed into the TemplateView's $variables (public array property).

The getView method may return false to suppress frontend markup entirely. This is useful for Elements that, after dynamically inspecting configuration / Application circumstances, cannot reasonably function and should not render at all (e.g. control buttons with disabled target Elements; ViewManager with grants settings that disallow any interaction for the current user).

Http handling changes

Http request handling is frequently the most complex part of any Element, with many service / parameter dependencies. To reduce common initialization overhead, http handling is now inflected via the getHttpHandler method. getHttpHandler should return the (DI'ed) http handling service or a falsy PHP value if no requests are handled.

The handler must implement ElementHttpHandlerInterface, requiring a handleRequest method. handleRequest receives the Element entity and the (Symfony) Request object as arguments.

NOTE that the passed Request object is the canonical current request passed down from the Symfony controller. Element services should not attempt accessing the request_stack themselves.
NOTE that (in simple scenarios) an Element service may return $this; from getHttpHandler, as long as it implements the ElementHttpHandlerInterface itself (example: AboutDialog rewrite).

There is no longer any default http handler implementation. AbstractElementService returns null from getHttpHandler.

This functionally replaces the handleHttpRequest and (super legacy) httpAction methods on Component\Element.

Additional import processing

When cloning applications, or importing exported Applications, Elements that reference database objects in their configuration by id must adjust those ids (e.g. "layersets" are id references in the main map configuration; see main Map rewrite).

Affected Element services must implement the new ImportAware interface. This requires a method onImport. onImport receives the Element entity and a Mapper implementation as arguments.

This functionally replaces Component\Element's denormalizeConfiguration method.

Declaring Element replacements

The <tag name="mapbender.element" /> allows a replaces property, which must, if present, contain (a comma-separated list of) previous Element class FQCN(s).

This will indicate that the Element service will handle those other class names. This allows replacing Element legacy / undesired default, or even no longer existant Element implementations, that will only take effect if the defining configuration (=most likely the bundle containing it) is currently loaded.

E.g.

        <service id="project.element.print" class="ProjectBundle\Element\Print">
            <tag name="mapbender.element" 
              replaces="Mapbender\PrintBundle\Element\PrintClient,Mapbender\CoreBundle\Element\PrintClient">
            <!--- arguments ... -->
        </service>

Explicitly declaring canonical class name

To maintain compatibility with existing db contents ("class" column in mb_core_element), a "canonical" class name is set when adding or editing Elements inside an Application. This avoids hard errors after deactivating a bundle with a reimplemented Element class that changes the handling class name.

By default, the canonical class name is the first entry in the mapbender.element tag's replaces attribute if replaces is specified, otherwise it is the FQCN of the handling service class itself.

You may explicitly specify the canonical content of the mb_core_element table's "class" column. Canonical can be any string value (must not necessarily name an existing PHP class).

        <service id="mapbender.element.digitizer_testing" class="Mapbender\Digitizer\Element\DigitizerService">
            <tag name="mapbender.element" 
              canonical="Mapbender\DigitizerBundle\Element\Digitizer">
            <!--- arguments ... -->
        </service>

IOW, "replaces" maps the value in the mb_core_element "class" column to the PHP class implementing the Element logic, while "canonical" controls what is written into the mb_core_element "class" column.

werrolf added 30 commits June 24, 2021 10:11
…d attribute, provide mb-element, toolBarItem classes
…mponent\Element-unaware ElementEntityFactory)
werrolf added 27 commits June 24, 2021 10:11
…tend config and asset references (check all only for markup)
@werrolf werrolf merged commit 79d1949 into master Jun 29, 2021
@werrolf werrolf deleted the feature/symfony-4-element-infrastructure branch June 29, 2021 12:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant