3

I have created a pseudo field with this code:

/**
 * Implements hook_entity_extra_field_info().
 */
function MYMODULE_entity_extra_field_info() {

  $extra['node']['my_bundle']['display']['my_pseudo_field'] = [
    'label'       => t('Pseudo Label'),
    'description' => t('Pseudo description'),
    'weight'      => 100,
    'visible'     => TRUE,
  ];

  return $extra;
}

/**
 * Implements hook_ENTITY_TYPE_view().
 */
function MYMODULE_node_view(array &$build, EntityInterface $entity, EntityViewDisplayInterface $display, $view_mode, $langcode = 'fr') {

  if ($display->getComponent('my_pseudo_field')) {

    $build['my_pseudo_field'] = [
      '#markup' => 'This is my pseudo content',
    ];
  }
}

Everything is working fine, except that I couldn't add it in a view.

  • Unfortunately this is not possible. You need a views field plugin like it exists for real (non-pseudo) entity fields and then add it in hook_views_data() to the node definition. – 4k4 Feb 3 '18 at 15:40
  • Thanks you 4k4. I assume I can create a computed field then add it in hook_views_data() – Baud Feb 3 '18 at 17:13
  • I meant a plugin, an example would be the path field of a node. The markup is placed in render() of the plugin class, see api.drupal.org/api/drupal/… – 4k4 Feb 3 '18 at 17:50
  • I have followed your idea and I could end up with a solution... you can see it below. One thing is still missing : the sort in the view doesn't works. – Baud Feb 4 '18 at 18:20
  • I think the best trade-off in this situation is to configure your view to display full nodes and choose a (newly added) dedicated view mode for display. Instead of letting your view display single fields. Displaying full nodes view modes will also display pseudo fields. – leymannx Oct 24 '18 at 19:24
4

With the help of 4k4, I could solve my problem... I will start by explain my first try, which could be a good solution in some cases, then I will explain/show how I finally end up.

Firstly I created my computed field in hook_entity_base_field_info_alter [Note that this is not a pseudo field this time]

function my_module_entity_base_field_info_alter(&$fields, EntityTypeInterface $entity_type) {
    if ($entity_type->id() === "node") {
      ...field is created here... will show you how to after
    }
}

Secondly, we need to create a class (that will perform the calculation) for the computed field (will show you after)

Thirdly, we can display this field in a view simply by implementing hook_views_data_alter... This is the beauty of this solution: the view integration is made very quickly. Here is the how the hook is implemented

function my_module_views_data_alter(array &$data) {
  $data['node']['my_computed_field'] = [
    'title' => t('My computed field'),
    'field' => [
      'id' => 'field',
      'default_formatter' => 'string',
      'field_name' => 'my_computed_field',
    ],
  ];
}

What was not good in my case, is the fact that my computed field does appears in all bundles (even if I use setTargetBundle('my_bundle') like showed below).

Thus I changed a little bit my computed field process creation:

1-Create the computed field in hook_entity_bundle_field_info_alter ... not in _entity_base_field_info_alter (As you can see, it is a float field... youn could use a string)

function my_module_entity_bundle_field_info_alter(&$fields, EntityTypeInterface $entity_type, $bundle)
{
    if ($entity_type->id() == 'node' && $bundle == 'my_bundle' ) {
        $fields['my_computed_field'] = BaseFieldDefinition::create('float')
        ->setName('my_computed_field')
        ->setLabel(t('Nombre de litres total'))
        ->setComputed(TRUE)
        ->setTargetEntityTypeId('node')
        ->setTargetBundle('my_bundle')
        ->setClass('\Drupal\my_module\MyComputedFieldItemList')
        ->setDisplayOptions('view', [
            'type' => 'float',
            'weight' => 0,
        ])
        ->setDisplayConfigurable('view', TRUE);
    }

}

Note: if someone know the purpose of ->setTargetBundle('my_bundle') I would be glad to read anything on it as I added it without knowing if it is mandatory/usefull.

2-Extend FieldItemList (MyComputedFieldItemList) [use the same name as in ->setClass('\Drupal\my_module\MyComputedFieldItemList')]

The main point here is the protected function computeValue that will use $node to compute the value.

<?php
namespace Drupal\my_module;

use Drupal\Core\Field\FieldItemList;
use Drupal\Core\TypedData\ComputedItemListTrait;

/**
 * Item list for a computed field that displays the total liter for a soup.
 *
 */
class MyComputedFieldItemList extends FieldItemList
{

    /**
     *
     * {@inheritdoc}
     */
    use ComputedItemListTrait;

    protected function computeValue()
    {
        $node = $this->getEntity();
        if (! $node->isNew()) {
            $this->list[0] = $this->createItem(0, my_module_function_to_compute($node));
        }
    }
}

At this stage, you have a computed field that you can display in "my_bundle"... Lets go to the view integration

3-Alter hook_views_data_alter to tell views that you have a new computed field linked to a node. The main point here is the id my_computed_field

function my_module_views_data_alter(&$data)
{
    $data['node']['my_computed_field'] = array(
        'title' => t('My computed result'),
        'group' => t('Content'),
        'field' => array(
            'title' => t('My computed result'),
            'help' => t('bla bla.'),
            'id' => 'my_computed_field',
        ),
    );
}

4-Thanks to 4k4, I understood that you need to create a plugin class that will have a render function to show the result in the view. The second important thing here is the @ViewsField("my_computed_field") annotation that needs to be the same as the id in step 3

<?php
/**
 * @file
 * Definition of Drupal\my_module\Plugin\views\field\ComputeField
 */

namespace Drupal\my_module\Plugin\views\field;

use Drupal\views\Plugin\views\field\FieldPluginBase;
use Drupal\views\ResultRow;

/**
 * Field handler to flag the node type.
 *
 * @ingroup views_field_handlers
 *
 * @ViewsField("my_computed_field")
 */
class ComputeField extends FieldPluginBase {

    /**
     * @{inheritdoc}
     */
    public function query() {
        // Leave empty to avoid a query on this field.
    }

    /**
     * @{inheritdoc}
     */
    public function render(ResultRow $values) {
        $node = $values->_entity;
        if ($node->bundle() == 'my_bundle') {
            return my_module_function_to_compute($node);
        }
    }
}

Now, everything runs fine:

  • The field could be displayed (or not) in any display mode and you can change the settings in the UI as any regular field
  • The field could be inserted in a node view
  • There is a function my_module_function_to_compute($node) that you can put in your module. This function will compute the result of your field with the entity as argument (node here)

One thing is missing: the sort (in the view) doesn't work... I have to dig into it

  • After some digging, it seems that we can't sort a computed field... I would be happy to get another opinion. – Baud Feb 5 '18 at 10:08

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.