I am new to Drupal but an experienced developer. I'm creating a DS field in which I will display a custom block chosen by the content editor. I have successfully gotten a referenced entity (two references deep) based on this answer How to get instance of referenced entity?.

Is there some data that will tell me what type of thing it is? Should I find out by looking in the UI at the block layout menu?

It would be simple to answer the question 'How do I get the block ID?' (answer: $bid = $campaignFundraisingBlock->id(); and I will fix that shortly) but the question I really want answered is 'How can I find out what type of data I have?'

The top answer (here) How can I programmatically display a block? made it clear that the type of block makes a difference in how you render it, so it seems important to be able to find out what type of data I have.

With the code (below) I am currently getting this error: Error: Cannot use object of type Drupal\block_content\Entity\BlockContent as array in Drupal\shpf_campaign\Plugin\DsField\ParentCampaignFundraisingBlock->build(), (which I will fix shortly).

Here's my code:

class ParentCampaignFundraisingBlock extends DsFieldBase {

  public function build() {
    $campaignStory = $this->entity();
    $fieldCampaignFundraiserOptIn = $campaignStory->get('field_campaign_fundraiser_opt_in')->getValue();
    $showFundraiserCta = FALSE;
    if (!empty($fieldCampaignFundraiserOptIn)) {
      $showFundraiserCta = $fieldCampaignFundraiserOptIn[0]['value'];
    }

    if ($showFundraiserCta) {
      $campaign = $campaignStory
        ->get('field_campaign_node_type')
        ->first()
        ->get('entity')
        ->getTarget()
        ->getValue();
      $campaignFundraisingBlock = $campaign
        ->get('field_campaign_fundraising_block')
        ->first()
        ->get('entity')
        ->getTarget()
        ->getValue();

  /* question: Do I have a renderable block in $campaignFundraisingBlock, or a type? How can I tell? */

      $bid = $campaignFundraisingBlock['values']->id;

  /* This next code assumes that the data I have is a block created in the interface (content block), but that seems like a big assumption that is not necessarily true. */

      $block = \Drupal\block_content\Entity\BlockContent::load($bid);
      $render = \Drupal::entityTypeManager()
        ->getViewBuilder('block_content')
        ->view($block);
      return $render;
    }
  }
}

Some background information: The block type is set to 'Featured CTA' and there are a bunch of blocks of that type available for content editors to choose from. The content editor chooses the block in the 'campaign' content type (it is an entity reference). The end user can fill out a form in which they tell a 'campaign story'. The end user can choose to include the campaign fundraiser or not. The campaign story has an entity reference to the campaign it is part of.

So, how do I find out what type of data I have?

有帮助吗?

解决方案

Do I have a renderable block in $campaignFundraisingBlock, or a type? How can I tell? How do I find out what type of data I have?

I will try to address the question how to determine the type of data in this scenario with entity references and the following block of code, which uses Entity and Typed Data APIs.

tl;dr $campaignFundraisingBlock should be an entity of whatever entity type(s) is referenced by the Entity Reference field "field_compaign_fundraising_block", and this entity can be passed to its View Builder dynamically to create the appropriate render array.

$campaignFundraisingBlock = $campaign
  ->get('field_campaign_fundraising_block')
  ->first()
  ->get('entity')
  ->getTarget()
  ->getValue();

I would break down the method calls to understand this.

$campaign seems to be the entity that is injected into the Display Suite Field Plugin. I don't know much about Display Suite, but it's an entity, so some of the basic entity methods are available to determine what it is:

EntityInterface::getEntityType can get an object described by its corresponding @EntityType (or @ConfigEntityType or @ContentEntitType) plugin. This can be useful to determine the property/field definitions the entity is capable of.

$typedData = $campaign
   ->get('field_campaign_fundraising_block')
   ->first()
   ->get('entity')

The get method here returns FieldItemListInterface, and the subsequent methods are really a part of the lower-level Typed Data API (See Entity API "implements" Typed Data API quotation mine). When we get an object that is an instance of TypedDataInterface, then we have some different methods for getting definitions.

The basic interface here is very limited, and, depending on what Typed Data Data Type is being represented, then it might implement additional interfaces.

TypedDataInterface::getDataDefinition provides Typed Data Definition class that describes this Typed Data Data Type. The DataDefinitionInterface::propertyDefinitions returns an associative array DataDefinitionInterface keyed by the property name, and the getDataType method returns the plugin ID/string of the data type its representing. In this case, for Typed Data Data Type representing an entity, it's EntityDataDefinition, which calls up to the Entity Type (back in the higher-level Entity API) to get its base and bundle field definitions.

$referencedEntity = $typedData
  ->getTarget()
  ->getValue();

The EntityReference::getTarget method (really weird in my opinion) gets either a cached version of the Typed Data representation of the entity or loads the entity and returns the Typed Data representation of it.

Calling getValue on a Typed Data Data Type will return the underlying data. So in the case of primitive data types, a string scalar might be returned, or in the case of complex data types, an entity or an associative array.

Typed Data Data Types do not necessarily return render arrays or have methods that return render arrays. The Entity API has that concept for entities. This is why the entity object returned from getValue can be passed to its view builder.

Unfortunately we can't instantiate a View Builder from the Entity object or EntityType object of an entity because of dependency injection, which is why we need the EntityTypeManager to do so. However I do know the entity type dynamically because getEntityTypeId method so I can dynamically return a render array for the correct entity:

$render = \Drupal::entityTypeManager()
  ->getViewBuilder($referencedEntity->getEntityTypeId())
  ->view($referencedEntity);

So in short, again, if you don't necessarily know what entity type you're dealing with, then you can use EntityInterface::getEntityTypeId from an entity object and pass that into getViewBuilder. And hopefully the explanation about Entity and Typed Data will help to realize why that huge method chain is necessary to return the entity object.

许可以下: CC-BY-SA归因
scroll top