[2.0.0-alpha3] Improved source plugins organization

Created on 14 June 2024, 12 days ago
Updated 15 June 2024, 10 days ago

Problem/Motivation

  1. We need a way to better organize source plugins, with a tagging-like mechanisms
  2. We need a way to better filter source plugins from a caller point of view, with a requirement-like mechanism

Proposed resolution

  1. We need a way to better organize source plugins with some tags.
    For example, all widgets would be tagged with "widget" or something.
    We would achieve this with a new attribute to source plugin definitions called "tags".
    In source plugin manager, in method "getDefinitionsForPropType",
    we will add a third argument called "$tag_filter" (keys are tags, value is true or false) that would allow to filter the returned definitions,
    including some tags or excluding some tags.
  2. We need a way to better filter source plugins from a caller point of view, using a requirement mechanism, implemented as a context matching (a context would have some specific values to be required).
    this would be a generalization of different attempts already made in other issues to let source plugins be available only in specific situations (cf. ui_patterns_views or ui_patterns_field_formatters source plugins)
    Basically how it would work : a specific context is defined in a source plugin definition, with a name like "context_requirements" or something like that. This context, declared in "context_definitions" plugin definition of source plugin, would be something like that :
    $context_requirements = new ContextDefinition('any');
    $context_requirements
          ->setRequired(TRUE)
          ->setLabel("context_requirements")
          ->addConstraint("ArrayRequiredValues", ["CUSTOM_VALUE"]);
    
    // The "RequiredValue" would be a new symfony contraint, checking  that some values ["x","y"] are inside an array (the array is the  context value
    

    In other words, the example here means a source plugin would be available if and only if the context "context_requirements" would be an array and contains the value CUSTOM_VALUE.
    to simplify the declaration of this, we would like to add an attribute called "context_requirements" in source plugin definition. The source plugin manager will read the "context_requirements" value (being an array),
    then dynamically create the context "context_requirements" with the appropriate values filled in the constraint.

Once both are done, we can have a source plugin like this (example here is with annotation)

/**
 * Plugin implementation of the source.
 *
 * @Source(
 *   id = "XXX",
 *   label = @Translation("XXX label"),
 *   description = @Translation("XX description."),
 *   prop_types = {
 *     "string"
 *   },
 *   tags = {
 *      "widget",
 *      "my_group_x"
 *   }
 *   context_requirements = {
 *      "my_requirement_value_y",
 *      "another_required_value_z"   
 *   }
 * )
 */
📌 Task
Status

Fixed

Version

2.0

Component

Code

Created by

🇫🇷France just_like_good_vibes PARIS

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Merge Requests

Comments & Activities

  • Issue created by @just_like_good_vibes
  • Status changed to Needs review 11 days ago
  • 🇫🇷France just_like_good_vibes PARIS

    ready for review

  • 🇩🇪Germany Christian.wiedemann

    You are a machine. Looks great and brutal fast. :)
    Just one thing:
    SourcePluginBase::addContextRequirementsValues is not clear for me. Why is the first argument contexts and not only the context which needs the "RequirementsValues". Maybe we create an own Context Class like "Drupal\Core\Plugin\Context\EntityContext"
    and Add a static Method $context = RequirementsValuesContext::create('key', ['value1']) and RequirementsValuesContext::attach($context, ['value1'])

  • Status changed to Needs work 11 days ago
  • 🇫🇷France just_like_good_vibes PARIS

    Thank you Christian for reviewing.
    about the method you mentioned, i just created it to factorize a piece of code that would:
    1: create the context when missing and adds the values provided
    2: overwrite the context if it already exists and adds the values provided to the already existing values.

    that's why i need the whole contexts array,
    from the caller point of view, this makes adding values very easy, one line of code. you provide the context array and the values you want to add to the requirements, and it does the job (the method may also allow to update implementation later maybe by hiding the key in the context)

    i put it as a static method in that class, but we could have put it elsewhere if we wanted.
    i am not so sure to introduce a new context class as you suggested,
    probably more elegant in terms of code, but unfortunately it adds even more code (& complexity ?) without really adding a new functionality.

    i wanted this MR to removes code at the end, i think this is achieved if you do no count comment lines.

    so i would rather keep it like this for now. what do you think?

  • Status changed to Needs review 11 days ago
  • 🇫🇷France just_like_good_vibes PARIS

    ok, i addressed your remark,
    created specific classed for context definition and context. It is quite nice actually...
    I also kept the method in SourcePluginBase to keep the management of requirements simple from consumer point of view.
    please review :)

  • 🇩🇪Germany Christian.wiedemann

    Sorry for the late answer. I was also fine with the solution before. But I think it is better now in sense of more clear. Let's merge!

  • 🇫🇷France pdureau Paris

    Hi guys,

    I am not sure i understand everything in this ticket, but i want to share than I love it, especially 3 stuffs:

    • the way you are presenting and organizing the work together
    • the way you extend Drupal\Core\Plugin\Context\Context and Drupal\Core\Plugin\Context\ContextDefinition
    • the removal of specific code in ui_patterns_field_formatters & ui_patterns_view and the addition of generic code in ui_patterns

    It would be great if every public method is covered by an interface

    • By implementing an already existing interface if possible (this is the "dream" target, it worth taking the time of investigating existing interfaces
    • Else, by extending or adding a custom interface

    For example, RequirementsContextDefinition::fromRequirements(array $requirements = [], ?string $label = NULL) : RequirementsContextDefinition is not covered by
    ContextDefinitionInterface

    SourcePluginBase::addRequirementsToContext(array $context, array $values): array; is not described by any interface AFAIK.

  • 🇫🇷France pdureau Paris

    Also, it would be great to document this, especially by updating the (now outdated) schema: https://app.diagrams.net/#G1DdZDq3CYjnkev1aT_kpyrX7oNkk_2kNv#%7B%22pageI...

  • 🇫🇷France pdureau Paris

    Also, it would be great to document this, especially by updating the (now outdated) schema: https://app.diagrams.net/#G1DdZDq3CYjnkev1aT_kpyrX7oNkk_2kNv#%7B%22pageI...

  • 🇩🇪Germany Christian.wiedemann

    Hi Piere, both functions are factory functions which are usually not covered by an interface.

    So for me
    1. RequirementsContextDefinition and RequirementsContext is fine without an interface. I like your proposal of an ?additional? factory method forRequirements(array $requirements = [], ?string $label = NULL) or forRequirements(array $requirements = [], ?string $label = NULL) .
    2. SourcePluginBase::addRequirementsToContext(array $context, array $values): array; could we maybe move also to the
    RequirementsContext class. I "think" factory should belong to the class which is created.
    RequirementsContext::attach(RequirementsContext::forRequirements(['field_property']), $contexts).

  • 🇩🇪Germany Christian.wiedemann

    Short talk to finalize this. Than we I can rebase my stuff today and also provide the MR for the attribute stuff.

  • 🇩🇪Germany Christian.wiedemann

    But lets merge. It would be easy to refactor that later.

  • 🇫🇷France pdureau Paris

    Sure. If you both agree, let's merge. I am just commenting, you are deciding here.

  • Status changed to Fixed 10 days ago
  • 🇫🇷France just_like_good_vibes PARIS

    thanks guys :)

Production build 0.69.0 2024