Access Computed Field in Views

Created on 20 March 2023, about 2 years ago

I know this was talked about in another issue, but I wanted to make a separate post because it wasn't actually related to that issue and others might be looking as well.

I looked into the patch discussed at https://www.drupal.org/project/drupal/issues/2981047 📌 Allow adding computed bundle fields in Views Fixed but it's very confusing if any of that will work on a 9.5.x site. Is there a way to get the Computed Field to be used in Views? I tried a test using the example test plugins and what I read in the README, but it didn't work.

💬 Support request
Status

Active

Version

4.0

Component

Code

Created by

🇺🇸United States wxman

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

Merge Requests

Comments & Activities

  • Issue created by @wxman
  • 🇬🇧United Kingdom joachim

    📌 Allow adding computed bundle fields in Views Fixed would allow bundle fields to be output. Base fields apparently already can be output.

    However, currently no kind of computed field is picked up automatically by Views data.

    I'm pondering how best to fix this.

    Probably a core patch for base fields & module code for bundle fields.

  • 🇬🇧United Kingdom joachim

    I've made a core patch that declares base computed fields to views: 📌 Automatically declare computed base fields to Views Needs work .

  • 🇺🇸United States wxman

    joachim - I checked on #3 and am I missing seeing the patch? I don't have a lot of experience with core patches so maybe I'm just missing it?

  • 🇬🇧United Kingdom joachim

    It's a merge request rather than a patch.

  • 🇬🇧United Kingdom joachim

    I can report that computed fields added in the UI work fine with the patch from 📌 Allow adding computed bundle fields in Views Fixed -- though you need to declare them in code as well.

  • 🇺🇸United States wxman

    @joachim I have set up two test sites, one running D9.x and the other 10.x. The 9.x site I installed Version: 4.0.0-alpha3 of Computed Field, and it's working as designed, except for not being accessible in Views.
    I have applied patch: https://git.drupalcode.org/project/drupal/-/merge_requests/2511.patch.
    Above you say "-- though you need to declare them in code as well". Where do I declare the field in code? If I figure that out is that going to make it able to be used in Views?

  • 🇺🇸United States wxman

    Do I add that to a new custom module, or to the custom module I made at /web/modules/custom/buy_amazon?
    /web/modules/custom/buy_amazon/src/Plugin/ComputedField/Computedbuyamazon.php

  • 🇬🇧United Kingdom joachim

    It makes sense to put it in the same module as your computed field plugin, as both are required to make the field work.

  • 🇺🇸United States wxman

    I guess I'm going to have to wait till it gets fixed in Views. No matter what I try it still will not show in my View.

  • 🇦🇹Austria maxilein

    I could not wrap my head around building a plugin. Since I needed a solution a went back to a more traditional approach:

    1. create a core field

    2. create code in mmodule_ ...

    mymodule_node_presave($entity) {
     switch ($entity->bundle()) {
        // Here you modify only your day content type
        case 'entity_machine_name':
            if (!empty($entity->field_name->value)) {
                       ... do some custom calculation stuff 
    
    

    3. Make the field readonly in the form edit using Readonly Field Widget

    It is not as elegant as computed_field, but to me it was less time consuming, because I just did not comprehend the requirements and options for a plugin.

  • 🇬🇧United Kingdom joachim

    > I just did not comprehend the requirements and options for a plugin.

    Could you explain more about the problem in a separate issue please? The plugins for computed fields are really pretty simply as plugins go, and there are examples in the module to draw from.

  • 🇦🇹Austria maxilein

    That is not a problem only of this module. It is a fundamental problem of Drupal. That's why I try to help with explanations wherever I can.
    It may be pretty obvious for someone who programs Drupal for years. I could not get a proper article that leads me step by step. The explanations are extremely short.

    I try to avoid customizations, for easier upgrades. I do not want to read the source code in order to understand the basic purpose or principle of a module. At least not until I decided that a module could fit my purpose.

    In this case: if I have to do coding anyway, why shouldn't I do in a way that seems less complex?
    No offense!

  • 🇬🇧United Kingdom joachim

    I really REALLY suggest you all use Module Builder. It will generate the plugin class for you.

  • 🇦🇹Austria maxilein

    joachim, please don't get me wrong. You are outstanding in all your efforts! And documenting much better than most of Drupal developers!

  • 🇬🇧United Kingdom joachim

    Thanks! :)

    I've started some docs - https://www.drupal.org/docs/extending-drupal/contributed-modules/contrib...

    Incomplete, but will add to them when I have time.

  • 🇺🇸United States wxman

    Here is the module I made to go with my "computed_buy_amazon" computer field. It's at: buy_amazon\src\Plugin\ComputedField\ComputedField.php

    namespace Drupal\buy_amazon\Plugin\ComputedField;
    
    use Drupal\Component\Datetime\TimeInterface;
    use Drupal\computed_field\Field\ComputedFieldDefinitionWithValuePluginInterface;
    use Drupal\computed_field\Plugin\ComputedField\ComputedFieldBase;
    use Drupal\Core\Cache\CacheableMetadata;
    use Drupal\Core\Entity\EntityInterface;
    use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
    use Symfony\Component\DependencyInjection\ContainerInterface;
    
    /**
     * @ComputedField(
     *   id = "buy_amazon_computedbuyamazon",
     *   label = @Translation("Computed buy Amazon"),
     *   field_type = "text",
     *   attach = {
     *     "scope" = "base",
     *     "field_name" = "buy_amazon_computedbuyamazon",
     *     "entity_types" = {
     *       "entity_test" = {},
     *     },
     *   }
     * )
     */
    class Computedbuyamazon extends ComputedFieldBase {
    
      /**
       * {@inheritdoc}
       */
      public function ComputeValue(EntityInterface $host_entity, ComputedFieldDefinitionWithValuePluginInterface $computed_field_definition): array {
    	if(!empty($host_entity->field_isbn10->value)) {
    		$ISBN = $host_entity->field_isbn10->value;
    	}else{
    		if(!empty($host_entity->field_amazon_asin->value)) {
    			$ISBN = $host_entity->field_amazon_asin->value;
    		}else{
    			$ISBN = '';
    		}
    	}
    	if(!empty($ISBN)) {
    		$value = '<a href="http://www.amazon.com/gp/product/'.$ISBN.'" alt="" style="border:none !important; margin:0px !important;" />';
            return [
                0 => [
                'value' => $value,
                'format' => 'full_html',
                ],
            ];    
    	}
      }
    
    }
    

    I'm guessing it's somewhere in here I need to add a hook to make it seen in Views? I tried following the Drupal site information but nothing worked.

  • 🇬🇧United Kingdom joachim

    No, the hook goes in the .module file.

    Again, again - USE MODULE BUILDER. It will put all the things in the right places for you.

    BTW, I don't think this will work:

    > if(!empty($host_entity->field_isbn10->value)) {

    IIRC you need to use $entity->field->isEmpty()

  • 🇬🇧United Kingdom joachim

    An example of how to declare the field to Views in hook_views_data(), from my comment on 📌 Allow adding computed bundle fields in Views Fixed :

      $data['node_field_data']['test_2981047_base'] = [
        'title' => t('Test computed base field 2981047'),
        'entity field' => 'test_2981047_base',
        'field' => [
          'id' => 'field',
        ],
      ];
    
      $data['node_field_data']['test_2981047_bundle'] = [
        'title' => t('Test computed bundle field 2981047'),
        'entity field' => 'test_2981047_bundle',
        'field' => [
          'id' => 'field',
        ],
      ];
    
  • 🇬🇧United Kingdom joachim
     *     "entity_types" = {
     *       "entity_test" = {},
     *     },
    

    Do you actually want to attach this field to the 'entity_test' entity type?

  • 🇮🇹Italy MarcoPBazz

    An example of how to declare the field to Views in hook_views_data(), from my comment on #2981047: Allow adding computed bundle fields in Views:

    It's working form me (Drupal 9.5x + computed 4.0.0-alpha2) as I can show my computed field, but if I try to filter but I get "missing or broken handler for this element" as message.

    Here's my code in my .views.inc file

    function mymodule_views_data_alter(&$data) {
    
      $data['node_field_data']['myfield'] = [
        'title' => 'Myfield title',
        'entity field' => 'myfield',
        'field' => [
          'id' => 'field',
        ],
        'filter' => [
          'title' => t('Myfield title'),
          'field' => 'id',
          'id' => 'myfield',
        ],
      ];
    
    }

    What am I missing?

  • 🇬🇧United Kingdom joachim

    You can't filter by a computed field, as there is no data in the database for SQL to query.

  • 🇺🇸United States mlncn Minneapolis, MN, USA

    Curious about the difference between joachim's example of how to declare the field to Views in hook_views_data() 💬 Access Computed Field in Views Active versus that in the change record associated with the same issue, Computed fields can now be displayed by Views , which would seem to suggest something looking more like this:

      /**
       * Implements hook_views_data_alter().
       */
      #[Hook('views_data_alter')]
      public function addComputedFieldChangedFields(&$data) {
        $data['node']['computed_example'] = [
          'title' => t('Computed field example'),
          'field' => [
            'id' => 'field',
            'default_formatter' => 'string',
            'field_name' => 'computed_example',
          ],
        ];  
      }
    }
    

    'node' instead of 'node_field_data', no 'entity field' line.

    Confirmed that i can get it to show up using Joachim's approach; here is the full thing in the object-oriented hook implementation available from Drupal 11.1 onward:

    
    declare(strict_types = 1);
    
    namespace Drupal\example\Hook;
    
    use Drupal\Core\Hook\Attribute\Hook;
    
    class ViewsDataAlter {
    
      /**
       * Implements hook_views_data_alter().
       */
      #[Hook('views_data_alter')]
      public function addComputedFieldChangedFields(&$data) {
        $data['node_field_data']['computed_example'] = [
          'title' => t('Computed field example'),
          'entity field' => 'computed_example',
          'field' => [
            'id' => 'computed_example',
          ],
        ];  
      }
    }
    

    However i am unable to test that it works beyond showing up in Views due to i think an unrelated error: 🐛 Call to undefined method Drupal\computed_field\Field\FieldStorageDefinition::getThirdPartySetting() Active

  • 🇺🇸United States mlncn Minneapolis, MN, USA

    With the code above and a computed_example field that is otherwise working, while i can add my field to a view it does not work, and indeed warns immediately:

    The handler for this item is broken or missing. The following details are available:

    Installing the appropriate module may solve this issue. Otherwise, check to see if there is a module update available.

    That is, Views knows my computed field has a broken or missing handler, but it cannot provide any details about it.

    Suggestions?

  • 🇬🇧United Kingdom joachim

    > 'node' instead of 'node_field_data',

    node is the base table, node_field_data is the field data table. It may be that one of those is the old way, I'm not sure. Check how Views declares config fields to views data.

    > no 'entity field' line.

    IIRC Views field handler plugins expect this property to be there, to tell it the name of the field.

    > The handler for this item is broken or missing. The following details are available:

    Can you debug to see what handler it's trying to use?

  • 🇺🇸United States mlncn Minneapolis, MN, USA

    Inside $this Drupal\views\Plugin\views\field\Broken, configuration, id = "computed_changed_shortterm_fields"

    (In the report above i replaced "computed_changed_shortterm_fields" with "computed_example" but other than that it is identical.)

    And before handlePluginNotFound, when PluginManagerBase does createInstance, the $plugin_id it has is "computed_changed_shortterm_fields" with $configuration:

    id = "computed_changed_shortterm_fields"
    group = TranslatableMarkup "String"
    title = TranslatableMarkup "Computed changed short-term fields"
    entity_type = "node"
    entity field = "computed_changed_shortterm_fields"

    What is the plugin expected to be?

    Again, the computed field itself with ID "computed_changed_shortterm_fields" is working, but including that code here for completeness:

    <?php
    
    namespace Drupal\hw_household\Plugin\ComputedField;
    
    use Drupal\computed_field\Attribute\ComputedField;
    use Drupal\computed_field\Field\ComputedFieldDefinitionWithValuePluginInterface;
    use Drupal\computed_field\Plugin\ComputedField\ComputedFieldBase;
    use Drupal\computed_field\Plugin\ComputedField\SingleValueTrait;
    use Drupal\Core\Entity\EntityInterface;
    use Drupal\Core\StringTranslation\TranslatableMarkup;
    
    /**
     * Computed field which outputs short-term changed fields as a string.
     */
    #[ComputedField(
      id: "hw_household_changed_shortterm_fields",
      label: new TranslatableMarkup("Changed short-term fields"),
      field_type: "string",
    )]
    class ChangedShortTermFields extends ComputedFieldBase {
    
      use SingleValueTrait;
    
      /**
       * {@inheritdoc}
       */
      public function singleComputeValue(EntityInterface $entity, ComputedFieldDefinitionWithValuePluginInterface $computed_field_definition): mixed {
        $revision_id = \Drupal::service('revision_summary.compare_revisions')->latestRevisionIdWithChangedField($entity, 'field_date_stamp');
        $fields = \Drupal::service('revision_summary.compare_revisions')->listChangedFields($entity, $revision_id);
    
        return implode(',', array_values($fields));
      }
    
    }
    

    (Relying on Revision Summary module which i've contributed, though it turns out to really be using Diff module, remixed slightly.)

  • 🇬🇧United Kingdom joachim

    Is this bit:

    > id = "computed_changed_shortterm_fields"

    what you've declared to views data? 'id' is the ID of the Views field plugin to handle the field. And you've not defined that plugin. IIRC you can just use the plugin for the specific field type -- see the core issue for declaring bundle fields to views.

  • 🇺🇸United States mlncn Minneapolis, MN, USA

    I'm looking at the commit that allowed adding computed bundle fields in views via 📌 Allow adding computed bundle fields in Views Fixed and before i start trying things at random, are we talking for "id":

    • field (looking in core, this is "EntityField" class at web/core/modules/views/src/Plugin/views/field/EntityField.php)
    • computed_string_field
    • … i don't think computed_bundle_field or something from core like standard, custom, or markup would apply, but maybe almost anything valid works?
  • 🇬🇧United Kingdom joachim

    > field (looking in core, this is "EntityField" class at web/core/modules/views/src/Plugin/views/field/EntityField.php)

    That one.

    computed_string_field and computed_bundle_field are not a views field plugin IDs, they are just the name of entity fields in a test.

  • 🇺🇸United States mlncn Minneapolis, MN, USA

    Sorry finally back to this and it is still bedeviling me:

      #[Hook('views_data_alter')]
      public function addComputedFieldChangedFields(&$data) {
        $data['node_field_data']['hw_household_changed_shortterm_fields'] = [
          'title' => t('Computed changed short-term fields'),
          'entity field' => 'field',
          'field' => [
            'id' => 'computed_changed_shortterm_fields',
          ],
        ];
      }
    }
    

    Tried multiple variations for the field ID, including node.household.computed_changed_shortterm_fields and household.computed_changed_shortterm_fields.

    Before trying computed_field.computed_field.node.household.computed_changed_shortterm_fields, the full config file path for the field, and for all i know that would have worked, i dove into how views plugins work and understanding it from the perspective of writing a plugin that could be configured to show any (simple) computed field proved easy to understand for me, and the result is this merge request.

    It works great for my purposes and i would bet a good 80% of use cases.

    Right now it returns the computed field for the entity with zero configuration that is theoretically possible for the view mode and bundle it was placed on. If anybody needs it, i am convinced i could figure out how to let a specific display configuration be chosen, or load those configuration options in the Views field settings, long before i could figure out how to do the cursed views data alter for a single custom computed field.

    The views data alter that works for all computed fields in the merge request works great, and with the Views field plugin unlocks this for others without further custom code.

  • Pipeline finished with Success
    17 days ago
    Total: 332s
    #500015
  • 🇬🇧United Kingdom joachim

    I don't understand this MR.

    IIRC the problem is with declaring the fields to view. Why is a separate handler needed?

  • 🇬🇧United Kingdom joachim

    Yup, I just tried it, and you don't need a special field plugin. I don't understand what your MR does -- why are you selecting computed field plugins, which aren't even necessarily going to be attached to the entities shown in the view?

    All you need for a config computed field is this:

      /**
       * Implements hook_views_data_alter().
       */
      #[Hook('views_data_alter')]
      public function viewsDataAlter(array &$data) {
        // My computed field has the field machine name 'computed_computed_bundle_field'. This key is arbitrary, but using the field name keeps it tidy.
        $data['node_field_data']['computed_computed_bundle_field'] = [
          'title' => t('computed_computed_bundle_field field'),
          'help' => t('Somecomputed_computed_bundle_fielder'),
    
          'field' => [
            // This is the field plugin to use for ANY entity field.
            'id' => 'field',
            // I can't remember which of these is needed or if both are.
            'entity field' => 'computed_computed_bundle_field',
            'field_name' => 'computed_computed_bundle_field',
          ],
        ];
      }
    
  • Pipeline finished with Success
    15 days ago
    Total: 148s
    #501211
  • 🇺🇸United States mlncn Minneapolis, MN, USA

    Started documentation to save new adopters my trouble!

    https://www.drupal.org/docs/extending-drupal/contributed-modules/contrib...

    That precise code does work, 'entity field' not needed.

    Part of the problem for me in earlier attempts might have been not picking up that the field name needed is not the in-code ID of the computed field (plugin) but the machine name produced when you add the computed field to a content type / bundle via configuration. (Obviously i'm quite thick, but the fact that computed fields start with computed_ rather than field_ and the tests used test_ helped obfuscate this.)

    My later attempts did not work either because i was misunderstanding 'entity field' to communicate the field plugin (`field`) and the field key to communicate only the field name; turns out what is essential is that field_name and id both be in the field array.

    As for my merge request: It does indeed allow you to use computed field without ever attaching it to the given content type. I don't consider that to be a deal-breaker, though, as it allows adding to the view without custom code, and computed fields themselves work the same way— you need to know when you configure them that they make sense for a particular content type (bundle), or it probably won't actually work. Being able to do computed fields in Views only without them being on the entity could be a minor feature in some use cases.

    Currently the computed field views plugin would only work for computed fields with no configuration, as noted, though i think exposing computed field's configuration in the view field configuration could work the same as on regular bundle display field configuration.

  • 🇬🇧United Kingdom joachim

    > As for my merge request: It does indeed allow you to use computed field without ever attaching it to the given content type. I don't consider that to be a deal-breaker, though, as it allows adding to the view without custom code, and computed fields themselves work the same way

    Yeah but that isn't at all how Views works. Views data declares fields (and which plugin they use), not plugins where you have to select the field in the UI.

Production build 0.71.5 2024