I want to know if there is any possible way to alter the response generated by the JSON API endpoint. I have added the flag module. I want to send flag count with the node object. I tried adding a custom property in node object but it gets removed in the response. I can alter the existing property value but can't add a custom property.

For reference: http://drupal.stackexchange.com/questions/224351/is-there-any-way-to-alt...

Comments

sumitmadan created an issue. See original summary.

e0ipso’s picture

Status: Active » Fixed

Please see how `jsonapi_entity_base_field_info` adds a computed property to the `file` entities. That should do.

If you don't want to create a *virtual* field, you can add a normalizer that only acts for that specific content type as a service. You'll need to use a higher priority than `serializer.normalizer.entity.jsonapi` (set to 21). Your custom normalizer can inherit from `Drupal\jsonapi\Normalizer\ContentEntityNormalizer` and add your custom stuff in `getFields`.

wim leers’s picture

It's the Flag module's responsibility to provide a normalizer (which would work both fore rest and jsonapi).

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.

danberk4’s picture

I'm trying to implement e0ipso's solution by adding a normalizer, but i keep getting the error below. Any suggestions?

Recoverable fatal error: Argument 1 passed to Drupal\my_api\Normalizer\EventNodeEntityNormalizer::__construct() must be an instance of Drupal\jsonapi\LinkManager\LinkManager, instance of Drupal\Core\Entity\EntityManager given,

My code

namespace Drupal\my_api\Normalizer;

use Drupal\jsonapi\Normalizer\ContentEntityNormalizer;
use Drupal\jsonapi\ResourceType\ResourceType;

/**
 * Converts the Drupal entity object structures to a normalized array.
 */

class EventNodeEntityNormalizer extends ContentEntityNormalizer {

  /**
   * {@inheritdoc}
   */
  public function getFields($entity, $bundle, ResourceType $resource_type) {
    return parent::getFields($entity, $bundle, $resource_type); // TODO: Change the autogenerated stub
  }
}
danberk4’s picture

Fixed my issue. Make sure you are returning the following arguments in your normalizer service

arguments: ['@jsonapi.link_manager', '@jsonapi.resource_type.repository', '@entity_type.manager']

e0ipso’s picture

Thanks for taking the time to report back to the issue queue.

jorisInsign’s picture

Hi @e0ipso,

I found your solution with hook_entity_base_field_info().
It work great for a single value.

But I can't figure how to make it work for multi-values...

Here is my tests files
my_module.module :


function my_module_entity_base_field_info(\Drupal\Core\Entity\EntityTypeInterface $entity_type) {
  $fields = [];

  if ($entity_type->id() === 'my_entity') {
    $fields['test_single_value'] = \Drupal\Core\Field\BaseFieldDefinition::create('string')
      ->setLabel(t('test single value'))
      ->setComputed(TRUE)
      ->setCustomStorage(TRUE)
      ->setClass('Drupal\my_module\Field\MyFields');

    $fields['test_multi_value'] = \Drupal\Core\Field\BaseFieldDefinition::create('string')
      ->setLabel(t('test multi value'))
      ->setComputed(TRUE)
      ->setCustomStorage(TRUE)
      ->setClass('Drupal\my_module\Field\MyFields');
  }

  return $fields;
}

and my MyFields.php :

<?php

namespace Drupal\my_module\Field;

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

/**
 * Extends core URL field functionality.
 *
 * @internal
 */
class MyFields extends FieldItemList {

  use ComputedItemListTrait;

  /**
   * Initialize the internal field list with the modified items.
   */
  protected function computeValue() {

    $list = [];

    if ($this->getName() === 'test_single_value') {
      $list[] = $this->createItem(0, 'test');
    }

    elseif ($this->getName() === 'test_multi_value') {
      $list[0] = $this->createItem(0, 'test 0');
      $list[1] = $this->createItem(1, 'test 1');
      $list[2] = $this->createItem(2, 'test 2');
    }


    $this->list = $list;
  }

}

But at the end, I only got first value for the second field... :

"test_single_value": "test",
"test_multi_value": "test 0",

Can you point me my errors ? or if you have a guide somewhere for this purpose ? I can't find any documentation/tutorial about this...

Thanks !

brownrl’s picture

The computed field solution in the end for our app covered 100% of what we needed. What we do here is take all the file or image fields and make computed fields to expose direct urls for download or display. With some clever code you can get to the point where you just disable the file endpoint. The normalizer route also works but if you need to go that far we agreed here that it would just be best to make a custom json/resource.

To get the multi value thing to work I had to play with the "cardinality" of the computed field.

        // In the field_info hook
        $field->setCardinality(-1);

Then the system says "Ok, this can spit out more than one value."

e0ipso’s picture

It's great to hear that the recommendations are proving to be useful.

One friendly reminder, the recommendation is to use JSON RPC methods (using the Drupal module) instead of custom endpoints.

ash2303’s picture

StatusFileSize
new1.74 KB

I have been trying to add custom data (like edit link) in JSON API response as suggested in #5. As parent is returning object of type EntityNormalizerValue and it is not possible to add field/custom value to it. Any help is most welcome. I am attaching my code for ContentEntityNormalizer.

jiisuominen’s picture

Hey!

I'm trying to add user's roles directly to the User entity, because of simplicity in our app, but also because I'm having some issues with roles -relationships..

I'm getting some mixed results, and can't find straightforward documentation for this use case.

NULL values to be exact:

...
"default_langcode": true,
"user_roles": [
    null,
    null
 ]
...

So I'm guessing there's some normalizing happening that I cannot figure out. I can see the values with debugger in PHPSTORM.

Anyways, here's my field definition:

function MODULENAME_entity_base_field_info(\Drupal\Core\Entity\EntityTypeInterface $entity_type) {
  if ($entity_type->id() === 'user') {
    $fields = [];
    
    $fields['user_roles'] = \Drupal\asiakas\FieldStorageDefinition::create('string')
      ->setName('user_roles')
      ->setLabel(t('Käyttäjäroolit'))
      ->setDescription(t('This is my computed property.'))
      ->setComputed(TRUE)
      ->setReadOnly(TRUE)
      ->setInternal(FALSE)
      ->setDisplayOptions('view', [
        'label' => 'hidden',
        'region' => 'hidden',
        'weight' => -5,
      ])
      ->setDisplayOptions('form', [
        'label' => 'hidden',
        'region' => 'hidden',
        'weight' => -5,
      ])
      ->setCardinality(-1)
      ->setCustomStorage(TRUE)
      ->setClass(\Drupal\asiakas\UserRolesComputedList::class);
    
    return $fields;
  }
}

The configuration of this field was/is pretty much a guessing game for me, so there's probably something wrong here, but I just can't figure out correct combo for computed field. I started with only setComputed -value in the base field definition and added line by line to see some different results. Nulls are all I get with these..

And here's my method for the values:

  protected function computeValue(){
  
    $roles = $this->getEntity()->getRoles();
    
    foreach($roles as $delta => $value){
      $this->list[$delta] = $this->createItem($delta, $value);
    }
  }

I pretty much copied my code from this article.

Cheers,
Janne

jiisuominen’s picture

Just came to say that one needs to check that all enhancers are off when you try to develop custom stuff. So yeah, we had JSON field enhancer turned on for the computed field and that nulled all the values. Makes sense.

Cheers,
Janne

ash2303’s picture

I was able to add computed field. But I was specifically looking to alter jsonapi response.

Thanks,
Ashish