Dialogue on how to implement a 4.0 plugin - with partial code

Created on 27 February 2023, over 1 year ago
Updated 8 February 2024, 5 months ago

I have a field that links a book to Amazon that I use Computed Field for. I gave the 4.0.0-alpha1 release a try today. After using composer to upgrade I ended up with these errors:

In Template.php line 419:

  An exception has been thrown during the rendering of a template ("Unable to determine class for field type 'computed_string' found in the 'field.storage.node.field_buy_amazon' configurat
  ion") in "__string_template__87e29eef9614b151fe27cc2611af6447" at line 1.


In FieldStorageConfigStorage.php line 167:

  Unable to determine class for field type 'computed_string' found in the 'field.storage.node.field_buy_amazon' configuration


In DiscoveryTrait.php line 53:

  The "computed_string" plugin does not exist. Valid plugin IDs for Drupal\Core\Field\FieldTypePluginManager are: comment, computed_render_array, datetime, entity_reference_revisions, file
  , file_uri, fivestar, blockbuilder, image, layout_section, link, metatag, list_integer, list_float, list_string, path, redirect_source, simplenews_subscription, simplenews_issue, social_
  media_links_field, text_long, text, text_with_summary, webform, decimal, float, boolean, changed, string, string_long, integer, timestamp, uuid, map, password, uri, entity_reference, ema
  il, created, language

I searched but nobody else reported this. Any ideas?

💬 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

Comments & Activities

Not all content is available!

It's likely this issue predates Contrib.social: some issue and comment data are missing.

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

    Those field types are gone in 4.x.

    There's no upgrade path unfortunately -- it's not something I have time to work on, and I don't know the 3.x version at all.

    Basically though, you can either delete those fields and re-create them, or change the field type -- which isn't easy. IIRC chx released a module to do that but I can't find it.

  • 🇺🇸United States wxman

    Let me get this straight.
    What I made will no longer work at all, including the custom modules I made to work with it?
    If I upgrade to version 4 do I just delete all the compute fields and remake them the same way?

  • 🇬🇧United Kingdom joachim

    You can stick with version 3 - that will carry on working as before.

    For version 4, code in the old hooks can be moved to plugins with minimal changes. You can then either create the fields in the admin UI, or define the plugins to automatically create the fields.

  • 🇺🇸United States wxman

    I want to be ready for Drupal 10 so I guess I need to figure out version 4. My fields are fairly simple using the custom modules to create a link to Amazon book page using an entered ISBN number. That sound like it should work with version 4? Maybe I should figure out a way to try 4 but leave a way to go back.

  • 🇬🇧United Kingdom joachim

    You should be able to do whatever you did in a hook for version 3 in a plugin for version 4.

    > Maybe I should figure out a way to try 4 but leave a way to go back.

    Make a test copy of your site :)

  • 🇬🇧United Kingdom joachim

    > I want to be ready for Drupal 10

    I'll commit patches for 3.x that are Drupal 10 updates.

  • 🇺🇸United States wxman

    I fixed the $entity variable, cleared all the caches, and checked to make sure Computed Field is enabled, it still only shows 'Reverse entity reference' in the computed value source drop-down box.

  • 🇬🇧United Kingdom joachim

    Is your custom module enabled?

  • 🇺🇸United States wxman

    Ok I think maybe this could be the problem. All I did was install Computed Field, then used Module Builder to generate that one php file I added to the plug in. I don't make a custom module for this. I thought it was only using a plug in.

  • 🇬🇧United Kingdom joachim

    > All I did was install Computed Field, then used Module Builder to generate that one php file I added to the plug in. I don't make a custom module for this. I thought it was only using a plug in.

    I don't understand. Where did you put the plugin file?

  • 🇩🇰Denmark Uv516 Denmark

    I have tried to make a module/plugin to CF with MB.
    My module and CF is installed/enabled.
    When I append a CF, I only see 'Reverse ...' as wxman.
    Should I do something more before I can see the plugin in CF?

  • 🇬🇧United Kingdom joachim

    You need to clear all caches -- best way is 'drush cr'.

  • 🇺🇸United States wxman

    Answer #26. I made that one php file and put it in the Computed Field folder in
    src/Plugin/ComputedField/Computedbuyamazon.php

  • 🇬🇧United Kingdom joachim

    > Answer #26. I made that one php file and put it in the Computed Field folder in
    src/Plugin/ComputedField/Computedbuyamazon.php.

    That won't work because the namespace in the PHP class file will be wrong. The PHP file won't be picked up. ALSO -- ***don't hack core or contrib***!!!!!!!

    > Was I supposed to generate a whole module? I thought all we had to do is add a plug-in to Computed Field.

    You create a custom plugin in a custom module. Don't ever change core or contrib code (unless you're patching).

    Can I also check -- have you both made the right *type* of plugin?

  • 🇺🇸United States wxman

    I thought it didn't seem right to add to the module but that's what it sounded like you were saying. Sorry.

    I always clear caches when I do anything.

    When I used MB I only set the computed_field Plugin.

  • 🇺🇸United States wxman

    This time I used MB to make a whole new module for the plugin.
    The only setting I used was selecting Plugins 1 type as computed_field, I Named it: computedbuyamazon, and I didn't set any hooks.
    After clearing the caches I found in the listed modules, so I enabled it.
    When I go to my test content type, and try to add a Computed Field, it won't let me. It still only shows 'Reverse entity reference' in the computed value source drop-down box. I guess I'm just not getting what is needed for a plugin to work for my simple URL field.

  • 🇩🇰Denmark Uv516 Denmark

    I have exactly the same problem.

  • 🇩🇰Denmark Uv516 Denmark

    I have listed what I have done in Module Builder before I tried to create a Computed Field, see "Computed Field.pdf"
    The result is shown in the file "cbm_cf.zip".
    @joachim: Will you try a look and tell os, what I/we is missing.

  • 🇬🇧United Kingdom joachim

    @Uv516 Your PDF shows that you haven't added a plugin, you've added a Plugin **type**.

  • 🇩🇰Denmark Uv516 Denmark

    Now I have added a plugin (computed_field).
    Se the files.
    Create a new CF in my testnode: My new plugin is not shown!

    @joachim: Could you make a small, educational manual that we can follow. I think it would make a big difference.
    I think it can be done in minutes, instead of us spending days trying to find a solution (while consuming your time).

  • 🇬🇧United Kingdom joachim

    > @joachim: Could you make a small, educational manual that we can follow. I think it would make a big difference.

    Have you read the README?

     *   field_type = "TODO: replace this with a value",
     *   no_ui = "TODO: replace this with a value",
     *   attach = {
     *     "TODO" = "array values",
     *   },
    

    The TODOs are there for your attention. The README (and also the annotation class, and the example plugins in the tests) tell you what those properties do.

    You should delete all the methods other than computeValue() -- Module Builder is over-eager at giving you all the methods in case you want to override things.

        // Returns the value for a computed field.
        $host_entity = 'Hello World';
    

    Read the comment! You need to return a value! Just zapping $host_entity will have no effect!

  • 🇺🇸United States wxman

    @Uv516 made a lot more than mine. I still can't get it to show up even after enabling the module, and clearing caches.

  • 🇬🇧United Kingdom joachim

    Your code in computeValue() looks ok, but:

    > * field_type = "TODO: replace this with a value",

    You have to set the field type.

    You also have to delete all the empty methods, otherwise they will prevent the base class from doing its work.

    Look at the file src/Plugin/ComputedField/TestString.php for an example.

  • 🇩🇰Denmark Uv516 Denmark

    My first success (with modifications).
    I have made my plugin with MB and compared with @joachim's "TestString.php".

    "TestString.php" has:

    /**
     * Computed field which outputs a simple string.
     *
     * @ComputedField(
     *   id = "test_string",
     *   label = @Translation("Test string"),
     *   field_type = "string",
     * )
     */
    

    My plugin has:

    /**
     * TODO: class docs.
     *
     * @ComputedField(
     *   id = "cbm_cf3_cf3a",
     *   label = @Translation("Cf3a"),
     *   field_type = "long_text", // Try something else
     *   no_ui = "TODO: replace this with a value", // I change this value to FALSE to show up in node.
     *   attach = {
     *     "TODO" = "array values", // What is this for? Which values could I set?
     *   },
     * )
     */
    

    "TestString.php" has:
    use Drupal\computed_field\Plugin\ComputedField\SingleValueTrait;
    and
    use SingleValueTrait;
    I don't have this... - What is it for? - Should I need it?

    "TestString.php" has:

      public function singleComputeValue(EntityInterface $host_entity, ComputedFieldDefinitionWithValuePluginInterface $computed_field_definition): mixed {
        return 'cake!';
      }
    

    My plugin has:

      public function computeValue(EntityInterface $host_entity, ComputedFieldDefinitionWithValuePluginInterface $computed_field_definition): array {
        // Returns the value for a computed field.
        return ['cake']; // Need to be an array (of long_text). @joachim's function is "mixed" and return a string; - HOW is this made in MB?
      }
    

    Notice that my plugin has "computeValue" and not "singleComputeValue" and my type is array instead of mixed. How to control it in MB?

    My CF is working!
    But I still have many questions about both CF and MB...

    @wxman: It is similar to "test_computed_field_plugins" (from CF), but you also have a module-file. It is not needed.
    Is your plugin made with MB or just a copy of "test_computed_field_plugins"?

  • 🇬🇧United Kingdom joachim

    > "TestString.php" has:

    The TestString class has the bare minimum needed to function.

    > * no_ui = "TODO: replace this with a value", // I change this value to FALSE to show up in node.

    That works, but you can also just omit that property completely and it defaults to FALSE.

     *   attach = {
     *     "TODO" = "array values", // What is this for? Which values could I set?
     *   },
    

    That's explained in the README. It allows your computed field plugin to automatically define itself as fields, without needing to use the admin UI. You can just omit that if you want to use the UI.

    > computeValue" and not "singleComputeValue

    You can use either.

    - computeValue lets you return a multi-valued field.
    - singleComputeValue lets you return just a single scalar value. It makes your code a bit easier to read, that's all. For it to work you need the SingleValueTrait. It's just there as a convenience, and MB doesn't know about it.

  • 🇩🇰Denmark Uv516 Denmark

    @joachim: Thank you for your very quick response.

    I think I have reached the goal of plugins for CF. After all, there are some things to do after the plugin is created with MB. It is not all that is described (well enough) in the Readme.
    I need several different Computed Fields and thus also several different plugins. Furthermore, it is very rare that I reuse a computed field and therefore it will probably also be rare that I reuse a plugin.
    On one of my sites I have more than 200 unique computed fields, so there will be something to upgrade from version 3.x to 4.x.
    I have tried to make a session of CF with multiple fields. It looks like with one session/module I can have all the CF plugins I want.
    Isn't it correct?

    For a simple series of plugins for CF, I'd like to make a tutorial that's a bit more educational than the Readme. What do you say to that, @joachim?

    Is it correct that CF never stores values in the database but calculates them for each call?
    What is possibly the setting for CF to store values in the database?

  • 🇬🇧United Kingdom joachim

    > It is not all that is described (well enough) in the Readme.

    Feel free to file issues with suggestions for improvements of the README and code docs :)

    > I have tried to make a session of CF with multiple fields. It looks like with one session/module I can have all the CF plugins I want.

    Yes, you can have as many plugins as you like in one module.

    > For a simple series of plugins for CF, I'd like to make a tutorial that's a bit more educational than the Readme.

    Sure. Though I've not worked out how contrib module docs work since the d.org docs system changed.

    > Is it correct that CF never stores values in the database but calculates them for each call?

    Yes. But the computed value is cached with the rendered entity by default.

  • 🇬🇧United Kingdom joachim

    > What is possibly the setting for CF to store values in the database?

    It occurs to me it would be fairly easy to do that with hooks -- implement both the update and insert hooks, and just copy the value from the computed field to a normal stored field.

  • 🇺🇸United States wxman

    I think I'm getting there. Here is my latest try for the plugin php file:

    namespace Drupal\buy_amazon\Plugin\ComputedField;
    
    use Drupal\Core\Cache\CacheableMetadata;
    use Drupal\Core\Entity\EntityInterface;
    use Drupal\Core\Entity\EntityTypeInterface;
    use Drupal\computed_field\Field\ComputedFieldDefinitionWithValuePluginInterface;
    use Drupal\computed_field\Plugin\ComputedField\ComputedFieldBase;
    
    /**
     * @ComputedField(
     *   id = "buy_amazon_computedbuyamazon",
     *   label = @Translation("Computed buy Amazon"),
     *   field_type = "string",
     * )
     */
    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.'/ref=as_li_tf_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN='.$ISBN.'&linkCode=as2&tag=MYID"  target="_blank">Amazon.com</a><img src="https://www.assoc-amazon.com/e/ir?t=MYID&l=as2&o=1&a='.$ISBN.'" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" />';
    		return $value;
    	}
      }
    
    }
    

    When I go to add a computed field the Computed value source now does show "Computed buy Amazon". I save the field, and tried to add content. When I hit save I got:
    TypeError: Drupal\buy_amazon\Plugin\ComputedField\Computedbuyamazon::computeValue(): Return value must be of type array, string returned in Drupal\buy_amazon\Plugin\ComputedField\Computedbuyamazon->computeValue() (line 35 of /var/www/vhosts/graphixcreate.com/newsite/web/modules/custom/buy_amazon/src/Plugin/ComputedField/Computedbuyamazon.php)

    At least I think I'm almost there.

  • 🇩🇰Denmark Uv516 Denmark

    return [$value] (as array)
    instead of return $value.
    See #40

  • 🇬🇧United Kingdom joachim

    You're nearly there.

    > public function computeValue(EntityInterface $host_entity, ComputedFieldDefinitionWithValuePluginInterface $computed_field_definition): array

    This must return an array. Read its docs:

       * @return array
       *   An array of field values, indexed by the numeric field delta.
    

    As I said above, if you want the ease of returning a string, use SingleValueTrait. That's in the docs too:

       * Use SingleValueTrait to implement this method if a plugin is returning only
       * a single value for a field.
    
  • 🇺🇸United States wxman

    I'm down to saving it without errors, so big step.

    namespace Drupal\buy_amazon\Plugin\ComputedField;
    
    use Drupal\Core\Cache\CacheableMetadata;
    use Drupal\Core\Entity\EntityInterface;
    use Drupal\Core\Entity\EntityTypeInterface;
    use Drupal\computed_field\Field\ComputedFieldDefinitionWithValuePluginInterface;
    use Drupal\computed_field\Plugin\ComputedField\ComputedFieldBase;
    use Drupal\computed_field\Plugin\ComputedField\SingleValueTrait;
    
    /**
     * @ComputedField(
     *   id = "buy_amazon_computedbuyamazon",
     *   label = @Translation("Computed buy Amazon"),
     *   field_type = "string",
     * )
     */
    class Computedbuyamazon extends ComputedFieldBase {
        
          use SingleValueTrait;
    
      /**
       * {@inheritdoc}
       */
      public function singleComputeValue(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.'/ref=as_li_tf_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN='.$ISBN.'&linkCode=as2&tag=MYID"  target="_blank">Amazon.com</a><img src="https://www.assoc-amazon.com/e/ir?t=MYID&l=as2&o=1&a='.$ISBN.'" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" />';
    		return [$value];
    	}
      }
    
    }
    

    If I add a test content using this, it works without any errors, but it also doesn't show any of the $value. If I change '$computed_field_definition): array {' to '$computed_field_definition): mixed{' and 'return $value, It will show the entire HTML '<a href="http://www.amazon.com/gp/product/'.$ISBN.'/ref=as_li_tf_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN='.$ISBN.'&linkCode=as2&tag=MYID" target="_blank">Amazon.com</a><img src="https://www.assoc-amazon.com/e/ir?t=MYID&l=as2&o=1&a='.$ISBN.'" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" />' with the correct values plugged in.

    The ideal situation is for it to render the HTML so just the 'Amazon.com' text shows with it linked.

  • 🇬🇧United Kingdom joachim

    > singleComputeValue

    If you use that, then you have to return a single value, not an array.

  • 🇺🇸United States wxman

    If I change it back to mixed, and change $value, then my output code looks like this:

    <div class="node__content">
        <div class="field field--name-field-amazon-asin field--type-string field--label-above">
            <div class="field__label">ASIN</div>
            <div class="field__item">B06XNJW99V</div>
        </div>
    
        <div class="field field--name-field-isbn10 field--type-string field--label-above">
            <div class="field__label">ISBN10</div>
            <div class="field__item">0316371246</div>
        </div>
    
        <div class="field field--name-computed-buy-amazon field--type-string field--label-above">
            <div class="field__label">Buy Amazon</div>
            <div class="field__item">&lt;a href="http://www.amazon.com/gp/product/0316371246/ref=as_li_tf_tl?ie=UTF8&amp;camp=1789&amp;creative=9325&amp;creativeASIN=0316371246&amp;linkCode=as2&amp;tag=MYID"  target="_blank"&gt;Amazon.com&lt;/a&gt;&lt;img src="https://www.assoc-amazon.com/e/ir?t=MYID&amp;l=as2&amp;o=1&amp;a=0316371246" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" /&gt;</div>
        </div>
    </div>
    

    The manage display only lists the display as plain text. Do I need to code it to display a different type?

  • 🇬🇧United Kingdom joachim

    Ah, you probably have the wrong field type. You probably want 'text' instead of 'string'.

  • 🇬🇧United Kingdom joachim

    For Views, I think you'll need this patch to core: https://www.drupal.org/project/drupal/issues/2981047 📌 Allow adding computed bundle fields in Views Fixed

    Alternatively, use the 'attach' plugin property and declare your field as a base field -- see README for details.

  • 🇺🇸United States wxman

    I had already tried the field type 'text' and it still didn't show correctly. It showed the same output with the only change being that it automatically tried to turn the https parts into links.

    All I want is it to show the words "Amazon.com" with that being linked using the computed field generated URL

    <a href="http://www.amazon.com/gp/product/'.$ISBN.'/ref=as_li_tf_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN='.$ISBN.'&linkCode=as2&tag=MYID"  target="_blank">Amazon.com</a><img src="https://www.assoc-amazon.com/e/ir?t=MYID&l=as2&o=1&a='.$ISBN.'" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" />
    

    Using version 3 of Computed Field I only needed the hook in my custom module and then it displayed perfectly. I really want this to work with version 4 if possible, but I'm obviously missing something.

  • 🇬🇧United Kingdom joachim

    I had a fiddle with this to figure it out. The field type does need to be text, but we also need to set the text format, and that's something I hadn't considered at all.

    I got it to work like this:

    - implement computeValue (that is, remove the trait)
    - return this:

        return [
          0 => [
            'value' => $value,
            'format' => 'full_html',
          ],
        ];
    

    I'll add test coverage for that with a sample plugin.

  • 🇺🇸United States wxman

    You are a genius! That's what I was trying to figure out how to fit it in. I knew it was a format type problem, but I just couldn't find how to add it. Here is my final plugin code if it helps anyone else.

    namespace Drupal\buy_amazon\Plugin\ComputedField;
    
    use Drupal\Core\Cache\CacheableMetadata;
    use Drupal\Core\Entity\EntityInterface;
    use Drupal\Core\Entity\EntityTypeInterface;
    use Drupal\computed_field\Field\ComputedFieldDefinitionWithValuePluginInterface;
    use Drupal\computed_field\Plugin\ComputedField\ComputedFieldBase;
    
    /**
     * @ComputedField(
     *   id = "buy_amazon_computedbuyamazon",
     *   label = @Translation("Computed buy Amazon"),
     *   field_type = "text",
     * )
     */
    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.'/ref=as_li_tf_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN='.$ISBN.'&linkCode=as2&tag=MYID"  target="_blank">Amazon.com</a><img src="https://www.assoc-amazon.com/e/ir?t=MYID&l=as2&o=1&a='.$ISBN.'" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" />';
            return [
                0 => [
                'value' => $value,
                'format' => 'full_html',
                ],
            ];    
    	}
      }
    }
    

    Thanks for all your hard work and patience.

  • 🇺🇸United States wxman

    Just for a test I changed this to see if it would show in Views.

    /**
     * @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" = {},
     *     },
     *   }
     * )
     */
    

    I based it on your test modules but it still doesn't show the field up in Views. I'll try and figure out the patch you talked about and hopefully that will work.

  • 🇩🇰Denmark Uv516 Denmark

    I have tried to show the CF i Views without luck.

    I have run dpm($entity) from hook_entity_presave.
    In the test node I have one CF and one text field (long formatted).
    The CF is named "computed_cf_lang_tekst"
    The text field is named "field_langt_tekstfelt"
    Note that the field "field_langt_tekstfelt" does NOT appear under #values. Is this an issue?
    In the field field_langt_tekstfelt->x-default->list[0] there is some data (see below). In the field computed_cf_lang_tekst->x-default->list this is empty. Is this an issue?

    #entityTypeId: "node"
    #enforceIsNew: null
    #typedData: Drupal\Core\Entity\Plugin\DataType\EntityAdapter {#1906 ▶}
    #cacheContexts: []
    #cacheTags: []
    #cacheMaxAge: -1
    #_serviceIds: []
    #_entityStorages: []

      #values: array:22 [▼
        "nid" => array:1 [▶]
        "vid" => array:1 [▶]
        "type" => array:1 [▶]
        "uuid" => array:1 [▶]
        "langcode" => array:1 [▶]
        "revision_uid" => array:1 [▶]
        "revision_timestamp" => array:1 [▶]
        "revision_log" => array:1 [▶]
        "revision_default" => array:1 [▶]
        "isDefaultRevision" => array:1 [▶]
        "status" => array:1 [▶]
        "uid" => array:1 [▶]
        "title" => array:1 [▶]
        "created" => array:1 [▶]
        "changed" => array:1 [▶]
        "promote" => array:1 [▶]
        "sticky" => array:1 [▶]
        "default_langcode" => array:1 [▶]
        "revision_translation_affected" => array:1 [▶]
        "body" => array:1 [▶]
        "field_langt_tekstfelt" => array:1 [▶]
        "original" => Drupal\node\Entity\Node {#2160 ▶}
    #fields: array:22 [▼
        "revision_log" => array:1 [▶]
        "changed" => array:1 [▶]
        "body" => array:1 [▶]
        "created" => array:1 [▶]
        "field_langt_tekstfelt" => array:1 [▶]
        "langcode" => array:1 [▶]
        "path" => array:1 [▶]
        "promote" => array:1 [▶]
        "status" => array:1 [▶]
        "sticky" => array:1 [▶]
        "title" => array:1 [▶]
        "uid" => array:1 [▶]
        "vid" => array:1 [▶]
        "type" => array:1 [▶]
        "nid" => array:1 [▶]
        "uuid" => array:1 [▶]
        "revision_timestamp" => array:1 [▶]
        "revision_uid" => array:1 [▶]
        "default_langcode" => array:1 [▶]
        "revision_default" => array:1 [▶]
        "revision_translation_affected" => array:1 [▶]
        "computed_cf_lang_tekst" => array:1 [▶]
    #fieldDefinitions: array:22 [▼
        "nid" => Drupal\Core\Field\BaseFieldDefinition {#1145 ▶}
        "uuid" => Drupal\Core\Field\BaseFieldDefinition {#1167 ▶}
        "vid" => Drupal\Core\Field\BaseFieldDefinition {#1170 ▶}
        "langcode" => Drupal\Core\Field\BaseFieldDefinition {#1173 ▶}
        "type" => Drupal\Core\Field\BaseFieldDefinition {#1176 ▶}
        "revision_timestamp" => Drupal\Core\Field\BaseFieldDefinition {#1178 ▶}
        "revision_uid" => Drupal\Core\Field\BaseFieldDefinition {#1182 ▶}
        "revision_log" => Drupal\Core\Field\BaseFieldDefinition {#1186 ▶}
        "status" => Drupal\Core\Field\BaseFieldDefinition {#1190 ▶}
        "uid" => Drupal\Core\Field\BaseFieldDefinition {#1195 ▶}
        "title" => Drupal\Core\Field\BaseFieldDefinition {#1199 ▶}
        "created" => Drupal\Core\Field\BaseFieldDefinition {#1202 ▶}
        "changed" => Drupal\Core\Field\BaseFieldDefinition {#1206 ▶}
        "promote" => Drupal\Core\Field\Entity\BaseFieldOverride {#1241 ▶}
        "sticky" => Drupal\Core\Field\BaseFieldDefinition {#1215 ▶}
        "default_langcode" => Drupal\Core\Field\BaseFieldDefinition {#1220 ▶}
        "revision_default" => Drupal\Core\Field\BaseFieldDefinition {#1226 ▶}
        "revision_translation_affected" => Drupal\Core\Field\BaseFieldDefinition {#1232 ▶}
        "path" => Drupal\Core\Field\BaseFieldDefinition {#1238 ▶}
        "computed_cf_lang_tekst" => Drupal\computed_field\Entity\ComputedField {#1242 ▶}
        "body" => Drupal\field\Entity\FieldConfig {#1243 ▶}
        "field_langt_tekstfelt" => Drupal\field\Entity\FieldConfig {#1244 ▶}

    -------------

     "field_langt_tekstfelt" => array:1 [▼
          "x-default" => Drupal\Core\Field\FieldItemList {#2102 ▼
            #definition: Drupal\field\Entity\FieldConfig {#1244 ▶}
            #name: "field_langt_tekstfelt"
            #parent: Drupal\Core\Entity\Plugin\DataType\EntityAdapter {#1906 ▶}
            #_serviceIds: []
            #_entityStorages: []
            #stringTranslation: null
            #typedDataManager: Drupal\Core\TypedData\TypedDataManager {#218 ▶}
            #list: array:1 [▼
              0 => Drupal\text\Plugin\Field\FieldType\TextLongItem {#2107 ▼
                #definition: Drupal\Core\Field\TypedData\FieldItemDataDefinition {#1426 ▼
                  #definition: array:2 [▶]
                  #typedDataManager: Drupal\Core\TypedData\TypedDataManager {#218 ▶}
                  #fieldDefinition: Drupal\field\Entity\FieldConfig {#1244}
                }
                #name: 0
                #parent: Drupal\Core\Field\FieldItemList {#2102}
                #_serviceIds: []
                #_entityStorages: []
                #stringTranslation: null
                #typedDataManager: Drupal\Core\TypedData\TypedDataManager {#218 ▶}
                #values: array:2 [ …2]
                #properties: array:2 [ …2]
              }
            ]
    computed_cf_lang_tekst" => array:1 [▼
          "x-default" => Drupal\computed_field\Field\ComputedFieldClass {#2152 ▼
            #definition: Drupal\computed_field\Entity\ComputedField {#1242 ▶}
            #name: "computed_cf_lang_tekst"
            #parent: Drupal\Core\Entity\Plugin\DataType\EntityAdapter {#1906 ▶}
            #_serviceIds: []
            #_entityStorages: []
            #stringTranslation: null
            #typedDataManager: Drupal\Core\TypedData\TypedDataManager {#218 ▶}
            #list: []
            #langcode: "da"
            #valueComputed: false
          }
  • 🇺🇸United States wxman

    I looked into the link to the topic about the core patch to fix this. I had to post a question there because the topic has become pretty long and I can't figure out if the patch will work on a 9.5.x site.

  • 🇺🇸United States wxman

    "Alternatively, use the 'attach' plugin property and declare your field as a base field -- see README for details."

    Is there anything else, other than what I posted in #56 that needs to be changed to possibly make this work? I tried to base it on what the README said, and the test example.

  • 🇬🇧United Kingdom joachim

    Ah, so the title of 📌 Allow adding computed bundle fields in Views Fixed made me think that computed base fields were supported.

    However, the work done in #2852067: Add support for rendering computed fields to the "field" views field handler is only to do with their output, not with registering them with views data!

  • 🇦🇹Austria maxilein

    Thank you all for having his detailed discussion here! It helps a lot - I have all the same questions...
    Thank you for the screenshots in the pdf in #36.
    All this details help a lot.
    I would gladly like to help make a more detailed documentation.

    Any news on how to make it show up in views?

  • 🇦🇹Austria maxilein

    To answer my question: continued here: https://www.drupal.org/project/drupal/issues/3349739 📌 Automatically declare computed base fields to Views Needs work

  • 🇦🇹Austria maxilein

    Since there is no upgrade path and a newbie would not filter by the old version ...

  • 🇦🇹Austria maxilein

    I renamed the issue, since it helped me understand plugin-approach. I think it can help others a lot, too. And now it is easier to find. Because the string error does not indicate the general value of this issue. Hope this is ok.

  • First commit to issue fork.
  • 🇨🇦Canada AvO WebWorks Ottawa, Ontario, Canada

    I just wanted to say how helping this thread is in terms of figuring out how to use the latest version (4) of this module. Thank you @Joachim and @wxman for your time in posting your process!

  • 🇦🇹Austria maxilein

    @AvO please, feel free to add things that you feel were missing. Drupal needs many more threads like this to survive the competition of CMSes.

  • 🇬🇧United Kingdom joachim

    I've added documentation here: https://www.drupal.org/node/3366477

    Please add to it if there's anything I've missed out.

  • 🇺🇸United States wxman

    @AvO Thanks for the thought! I'm ready to upgrade most of our sites, but they keep failing for a variety of reasons, none caused by computed field at least.

  • 🇺🇸United States spivey

    Are there any examples of how to perform calculations? I believe I'm following everything with regards to the custom module and plugins but I cannot find examples of finding the difference between to values. Essentially I have a "start" date with time and a "end" date with time and need to return the difference between the two. Some instances I just need the number of days, others it needs to be in hours.

  • 🇺🇸United States wxman

    @spivey do you mean like shown here: https://www.php.net/manual/en/datetime.diff.php

  • 🇺🇸United States spivey

    @spivey do you mean like shown here: https://www.php.net/manual/en/datetime.diff.php

    If that's the best way to do it, sure. I'm not quite sure how or where to enter that. I'm coming from ComputedField in D6, where the code was a little easier for me to adapt.

  • 🇬🇧United Kingdom joachim

    > I'm not quite sure how or where to enter that

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

  • 🇺🇸United States spivey

    You will now need to examine the generated plugin file. Some methods may be removed depending on your use case.

    The code that produces the value for the field should go in the computeValue() method. If your field is single-valued, you can use the SingleValueTrait to simplify the return value.

    I've been over that several times; which is why I came here to ask if there are any examples of calculations being performed. Is there anything like that in the test directories? From what I can see there's only samples of variables being declared or strings generated.

  • 🇬🇧United Kingdom joachim

    The examples in the test modules tend to just return constant values to keep the test simple. But you can put anything in the computeValue() method.

  • 🇺🇸United States spivey

    I'm still completely lost. I'm guessing the custom code needs to go here:

    public function computeValue(EntityInterface $host_entity, ComputedFieldDefinitionWithValuePluginInterface $computed_field_definition): array {
        // Returns the value for a computed field.

    Coming from D6 I had this in the PHP computed code block:
    $node_field[0]['value'] = date_difference($node->field_calltime[0]['value'],$node->field_calltime[0]['value2'], 'hours',DATE_DATETIME);

    Formatted with:
    $display = $node_field_item['value'];

    fieldcalltime[0] 'value' and 'value2' are the 2 values store in the date range field 'field_calltime'. Does this translate at all into ComputedField version 3?

  • 🇺🇸United States spivey

    If this isn't a valid support request do I need to reach out to a developer or contracted help?

  • 🇬🇧United Kingdom joachim

    > $node_field[0]['value']

    This sort of code for working with field values is different on D8/9/10.

  • 🇺🇸United States spivey

    This sort of code for working with field values is different on D8/9/10.

    Which is why I'm requesting help. The documentation for the D6 version was much more compete with code examples.

  • @wxman were you successful in getting computed fields to work in views?

    Thanks to this discussion I have computed fields working, but I'm stumped on the views portion.

  • 🇬🇧United Kingdom joachim

    See the README for details about Views. There are some limitations.

  • I'm not sure how to apply this from the README:

    "Automatically declare computed base fields to Views: https://www.drupal.org/project/drupal/issues/3349739 📌 Automatically declare computed base fields to Views Needs work "

  • 🇺🇸United States wxman

    @mts11 Yes I was able to get Computed Fields 4.0.0-alpha6 to work with Views after I finally upgraded to Drupal 10. My plug-ins are not the best design, but they do the job well enough. I was getting empty array errors because sometimes the fields it was drawing the data from were empty. I had to fake it by checking to see if there is data, and if none, it passed a message saying there was no data. I would rather it just showed blank. If anyone is interested, I can post what I made here. They are fairly simple examples though.

  • @wxman I'm on Drupal 10 and have the fields working as expected on nodes. When I create a View, however, I don't see any of them in the "Add fields" interface.

    Maybe I'm missing something here:

     *   attach = {
     *     "scope" = "",
     *     "field_name" = "",
     *     "entity_types" = {
     *       "" = {},
     *     },
     *   },
    
  • 🇬🇧United Kingdom joachim

    > I'm not sure how to apply this from the README:

    See https://www.drupal.org/docs/develop/using-composer/manage-dependencies#p...

    > Maybe I'm missing something here:

    You need to fill in the values in that array!

  • 🇺🇸United States wxman

    @mts11 Did you add the patch to composer.json

    "patches": {
        "drupal/core": {
            "computed base fields to work in Views": "https://git.drupalcode.org/project/drupal/-/merge_requests/4224.diff"
         }
    },
    

    Also, did you make a *.views.inc file as well?

    One of mine generates a simple URL link to an online bookstore. The Computed Field is called 'computed_buy_booksamillion'. It takes the ISBN number entered in another text field, and inserts it into the URL. The plug-in I made I called 'buy_booksamillion'. Along with the *.yml file I added 'buy_booksamillion.views.inc':

    /**
     * @file
     * Allows the computed field Buy Booksamillion(computed_buy_booksamillion) to be used in views.
     * Works with Computed Field ver 4.x
     */
    
    /**
     * Implements hook_views_data().
     */
    function buy_booksamillion_views_data() {
    
      $data['node_field_data']['computed_buy_booksamillion'] = [
        'title' => t('Buy Booksamillion'),
        'entity field' => 'computed_buy_booksamillion',
        'field' => [
          'id' => 'field',
        ],
      ];
    
      return $data;
    }
    

    Then under buy_booksamillion/src/Plugin/ComputedField/Computedbuybooksamillion.php:

    namespace Drupal\buy_booksamillion\Plugin\ComputedField;
    
    use Drupal\Core\Cache\CacheableMetadata;
    use Drupal\Core\Entity\EntityInterface;
    use Drupal\Core\Entity\EntityTypeInterface;
    use Drupal\computed_field\Field\ComputedFieldDefinitionWithValuePluginInterface;
    use Drupal\computed_field\Plugin\ComputedField\ComputedFieldBase;
    use Drupal\Component\Plugin\ConfigurableInterface;
    use Drupal\Core\Plugin\PluginFormInterface;
    
    /**
     * TODO: class docs.
     *
     * @ComputedField(
     *   id = "buy_booksamillion_Computedbuybooksamillion",
     *   label = @Translation("ComputedBuyBooksaMillion"),
     *   field_type = "text",
     *   attach = {
     *     "TODO" = "array values",
     *   },
     * )
     */
    class Computedbuybooksamillion extends ComputedFieldBase {
    
      /**
       * {@inheritdoc}
       */
      public function computeValue(EntityInterface $host_entity, ComputedFieldDefinitionWithValuePluginInterface $computed_field_definition): array {
        // Returns the value for a computed field.
        if (!empty($host_entity->field_isbn_13_hc->value)) {
            $ISBN = $host_entity->field_isbn_13_hc->value;
        } else if (!empty($host_entity->field_isbn_13_pb_->value)) {
            $ISBN = $host_entity->field_isbn_13_pb_->value;
         } else if (!empty($host_entity->field_isbn_13_au_->value)) {
            $ISBN = $host_entity->field_isbn_13_au_->value;
        } else {
    		$ISBN = '';
        }
        
    	if(empty($ISBN)) {
    		$value = '<div>Not Available at booksamillion.com</div>';
            return [
                0 => [
                'value' => $value,
                'format' => 'full_html',
                ],
            ];    
    	} 
    
    	if(!empty($ISBN)) {
    		$value = '<a href="http://www.booksamillion.com/p/Catherine-Coulter/'.$ISBN.'?id=6378422423141" target="_blank">Books-A-Million</a>';
            return [
                0 => [
                'value' => $value,
                'format' => 'full_html',
                ],
            ];    
    	} 
      }
    
    }
    

    Like I said, the whole thing is just to insert the ISBN into the URL and display it as a link. I know it's not very elegant, but it works for now. It kept throwing empty array errors if none of the ISBN fields were filed in, so I added the if(empty) bit to pass the Not Available link. I'd rather it just didn't show anything if there is no data, but it works for now. I hope this helps you a bit.

  • 🇬🇧United Kingdom joachim
     *   attach = {
     *     "TODO" = "array values",
     *   },
    

    You need to either fill this in or remove it!

  • You folks rock! Thank you so much.

    1) I had only applied the patch for bundle fields. I needed the base fields patch.
    2) I was missing the *.view.inc file - @wxman I repurposed your code for one of my computed fields and it's working.

    Presently I have multiple plugins (one for each computed field) in my custom module. Is it possible to include them all in a single *.view.inc file, or is better to split the plugins into separate modules?

  • 🇺🇸United States wxman

    @mts11 I gave up trying to put them in a single plug-in and opted to make separate ones.

    @joachim If I remove that it's going to allow it to pass empty if nothing is entered?

  • 🇬🇧United Kingdom joachim

    The 'attach' property needs to contain entity type IDs. If it says 'TODO' then it has no effect and probably is causing an error somewhere!

  • 🇩🇪Germany marcoka

    Thank you all for the infos so far. I am working on an example where i add multiple int fields that will result in a float value.
    The code works so far, but one thing confuses me. I don´t get it.

    If i output the value it is always rounded down.
    - I generate 5.3, the output is 5.0

    <?php
    
    namespace Drupal\computed_ratings\Plugin\ComputedField;
    
    use Drupal\Core\Cache\CacheableMetadata;
    use Drupal\Core\Entity\EntityInterface;
    use Drupal\Core\Entity\EntityTypeInterface;
    use Drupal\computed_field\Field\ComputedFieldDefinitionWithValuePluginInterface;
    use Drupal\computed_field\Plugin\ComputedField\ComputedFieldBase;
    use Drupal\computed_field\Plugin\ComputedField\SingleValueTrait;
    
    /**
     *
     *
     * @ComputedField(
     *   id = "computed_ratings_field",
     *   label = @Translation("Computed Ratings Field"),
     *   field_type = "float",
     * )
     */
    class ComputedRatingsField extends ComputedFieldBase {
    
      use SingleValueTrait;
    
      /**
       * {@inheritdoc}
       */
      public function singleComputeValue(EntityInterface $host_entity, ComputedFieldDefinitionWithValuePluginInterface $computed_field_definition): int {
         return (float) 5.3; 
      }
    }
    
  • 🇬🇧United Kingdom joachim

    Can you file new issues for both of these?

  • 🇩🇪Germany marcoka

    I think i already fixed the problems in code. The float problem, was my bad code. The tokens work using hook_token.
    I will publish it in a second to a sandbox module.

  • 🇦🇹Austria maxilein

    @marcoka: thanks for sharing a complete example for 4.0!!!!

    Q: is it an alone standing module or a plugin for computed field?

  • 🇩🇪Germany marcoka

    It is a plugin for computed field. Also pay attention to the hardcoded fieldname!

  • @maxilein, @marcoka - I created about a dozen computed field plugins. Each lives in it's own custom module.

  • 🇦🇹Austria maxilein

    #mts11: forgive my ignorance ... where are they? Can I look at them?

  • @maxilein here's an example of my setup:

    My custom module lives at /web/modules/custom/computed_float

    In the module directory I have:

    1) computed_float.info.yml

    name: 'Computed Float'
    type: module
    description: 'Computes "computed_float" field value'
    package: Custom Computed Fields
    core_version_requirement: '^8 || ^9 || ^10'

    2) computed_float.views.inc

    <?php
    
    /**
     * @file
     * Allows the computed field Computed Float (computed_float) to be used in Views.
     */
    
    /**
     * Implements hook_views_data().
     */
    function computed_float_views_data() {
      
      $data['node_field_data']['computed_float'] = [
        'title' => t('Computed float'),
        'entity field' => 'computed_float',
        'field' => [
          'id' => 'field',
        ],
      ];
    
      return $data;
    }
    

    3) plugin directory /src/Plugin/ComputedField containing ComputedFloat.php

    <?php
    
    namespace Drupal\computed_float\Plugin\ComputedField;
    
    use Drupal\Core\Cache\CacheableMetadata;
    use Drupal\Core\Entity\EntityInterface;
    use Drupal\Core\Entity\EntityTypeInterface;
    use Drupal\computed_field\Field\ComputedFieldDefinitionWithValuePluginInterface;
    use Drupal\computed_field\Plugin\ComputedField\ComputedFieldBase;
    
    /**
     * TODO: class docs.
     *
     * @ComputedField(
     *   id = "computed_float",
     *   label = @Translation("Computed Float"),
     *   field_type = "float",
     * )
     */
    class ComputedFloat extends ComputedFieldBase {
    
      /**
       * {@inheritdoc}
       */
      public function computeValue(EntityInterface $host_entity, ComputedFieldDefinitionWithValuePluginInterface $computed_field_definition): array {
        
        if(!empty($host_entity->field_my_source_data->value)) {
          $sourceData = $host_entity->field_my_source_data->value;
          $myFloat = $sourceData;
          
        }
        
        return [$myFloat];
        
      }
    }
    
    
  • 🇦🇹Austria maxilein

    Thank you!

  • 🇩🇪Germany marcoka

    Just to be sure the views code "$data['node_field_data']['computed_float']"

    "computed_float" is not the name of the computed_field field you added using the ui?

    When i add my field to views i get an error. I also have not achived to make my formatter avaliable in views. Its just avaliable when i add the field using layoit builder.

  • @wxman do you see log messages related to your views.inc file along the lines of this?

    Deprecated function: Creation of dynamic property Drupal\computed_field\Field\FieldStorageDefinition::$schema is deprecated in Drupal\computed_field\Field\FieldStorageDefinition->getSchema()

  • 🇺🇸United States wxman

    @mts11 No I'm afraid I don't

  • It seems the log message is related to php 8.2. I switched to 8.1 and the log looks good.

  • 🇺🇸United States wxman

    @mts11 I just switched to php 8.2.x and now it's all over the place:

    Deprecated function: Creation of dynamic property Drupal\computed_field\Field\FieldStorageDefinition::$schema is deprecated in Drupal\computed_field\Field\FieldStorageDefinition->getSchema() (line 299 of modules/contrib/computed_field/src/Field/FieldStorageDefinition.php).
    
  • @wxman I've stuck with php 8.1

    @joachim I don't feel confident enough yet to write documentation. I've been wrestling with using computed fields in Views and Charts. With the 8.x version of computed fields I needed to use aggregation on the computed field and then charts would draw as expected. In 4.x, aggregation doesn't seem to be an option (but this makes sense, right? No database storage for computed field?), so I'm tinkering with field grouping and getting somewhat close.

  • 🇺🇸United States wxman

    @mts11 I tried the patch at https://www.drupal.org/project/computed_field/issues/3410696#comment-153... 💬 Deprecated function Creation of dynamic property error in php 8.2 Needs review and it works perfectly using php 8.2

  • 🇺🇸United States derekw

    I'm trying to implement a calculated field plugin of type entity_reference.

    EntityReferenceItem defaults to referencing entities of type node, but I want to reference taxonomy_term entities with my plugin.

    The only way I could find to change the target_type was by attaching my field as a base field, and using hook_entity_base_field_info_alter(). In the function below I am hard-coding the override to a specific entity type, but for versatility it could just loop through all fields looking for computed entity_reference field plugins.

    Is this the correct way?

    
    use Drupal\Core\Entity\EntityTypeInterface;
    use Drupal\Core\TypedData\DataDefinition;
    
    /**
     * 
     * Implements hook_entity_base_field_info_alter().
     * 
     * For computed entity reference field, override entity reference item definition
     * with settings from plugin getFieldDefinitionSettings()
     * 
     * @param ComputedFieldDefinition $fields
     * @param EntityTypeInterface $entity_type
     */
    function my_module_entity_base_field_info_alter(&$fields, EntityTypeInterface $entity_type){  
      if ($entity_type->id() == 'profile'){
        foreach($fields as $field_name=>$field){
          if ($field instanceof ComputedFieldDefinition 
            && $field->getType() == 'entity_reference'
            && $field_definition_settings = $field->getFieldValuePlugin()->getFieldDefinitionSettings()){     
              // Override field ItemDefinition with the settings from the plugin getFieldDefinitionSettings
              $fields[$field_name]->setItemDefinition(DataDefinition::createFromDataType('field_item:entity_reference')->setSettings($field_definition_settings));       
          }
        }              
      }
    }
    
  • 🇬🇧United Kingdom joachim

    Please file a new issue for a new problem.

Production build 0.69.0 2024