1

I'm implementing a custom block visibility condition plugin and am unable to figure out my bugs in the conditional logic, because I can't get a dpm, var_dump, or debug call to work inside the evaluate() function.

I've setup a block using the condition plugin, and when i visit any URL, I'm not seeing any debug information. (Obviously, i've cleared caches, and have the relevant and necessary "development" configurations setup in settings.local.php to disable caching.

Here is my custom Condition plugin, the relevant part is the evaluate() part.

<?php

namespace Drupal\mymodule\Plugin\Condition;

use Drupal\Core\Condition\ConditionPluginBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Provides an 'Entity type' condition.
 *
 * Note that this condition does not work in node_preview and possibly in
 * node_revision contexts without the patch on core issue:
 * https://www.drupal.org/i/2890758
 *
 * @Condition(
 *   id = "entity_type",
 *   label = @Translation("Entity type"),
 * )
 */
class EntityType extends ConditionPluginBase implements ContainerFactoryPluginInterface {

  /**
   * An array of plugin definitions (empty array if no definitions were found). Keys are plugin IDs.
   *
   * @var mixed[]
   */
  protected $entityTypeDefinitions;

  /**
   * Creates a new EntityType instance.
   *
   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
   *   The entity type manager.
   * @param array $configuration
   *   The plugin configuration, i.e. an array with configuration values keyed
   *   by configuration option name. The special key 'context' may be used to
   *   initialize the defined contexts by setting it to an array of context
   *   values keyed by context names.
   * @param string $plugin_id
   *   The plugin_id for the plugin instance.
   * @param mixed $plugin_definition
   *   The plugin implementation definition.
   */
  public function __construct(EntityTypeManagerInterface $entity_type_manager, array $configuration, $plugin_id, $plugin_definition) {
    parent::__construct($configuration, $plugin_id, $plugin_definition);
    $this->entityTypeDefinitions = $entity_type_manager->getDefinitions();
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
    return new static(
      $container->get('entity_type.manager'),
      $configuration,
      $plugin_id,
      $plugin_definition
    );
  }

  /**
   * {@inheritdoc}
   */
  public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
    foreach ($this->entityTypeDefinitions as $type) {
      $options[$type->id()] = $type->getLabel();
    }

    $form['types'] = [
      '#title' => $this->pluginDefinition['label'],
      '#type' => 'checkboxes',
      '#options' => $options,
      '#default_value' => $this->configuration['types'],
    ];
    return parent::buildConfigurationForm($form, $form_state);
  }

  /**
   * {@inheritdoc}
   */
  public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
    $this->configuration['types'] = array_filter($form_state->getValue('types'));
    parent::submitConfigurationForm($form, $form_state);
  }

  /**
   * {@inheritdoc}
   */
  public function defaultConfiguration() {
    return ['types' => []] + parent::defaultConfiguration();
  }

  /**
   * {@inheritdoc}
   */
  public function evaluate() {
    if (empty($this->configuration['types']) && !$this->isNegated()) {
      return TRUE;
    }

    foreach ($this->configuration['types'] as $type) {
      if ($entity = $this->getContextValue($type)) {
        var_dump("found $type entity in context");
        return TRUE;
      }
      elseif($entity_id = \Drupal::routeMatch()->getParameter($type)) {
        var_dump("found $type entity in route parameter");
        return TRUE;
      }
      else {
        var_dump("couldn't find $type entity in context or route parameter");
      }
    }
    return FALSE;
  }

  /**
   * {@inheritdoc}
   */
  public function summary() {
    $types = $this->configuration['types'];
    if (empty($types)) {
      return;
    }

    foreach ($this->entityTypeDefinitions as $type) {
      if (in_array($type->id(), $types)) {
        $labels[] = $type->getLabel();
      }
    }

    if (count($labels) > 1) {
      $last = array_pop($labels);
      $labels = implode(', ', $labels);

      if (!empty($this->configuration['negate'])) {
        return $this->t('Entity type is not @types or @last', array('@types' => $labels, '@last' => $last));
      }
      return $this->t('Entity type is @types or @last', array('@types' => $labels, '@last' => $last));
    }
    $label = reset($labels);

    if (!empty($this->configuration['negate'])) {
      return $this->t('Entity type is not @type', array('@type' => $label));
    }
    return $this->t('Entity type is @type', array('@type' => $label));
  }

  /**
   * {@inheritdoc}
   */
  public function getCacheContexts() {
    $contexts = parent::getCacheContexts();
    $contexts[] = 'url';
    return $contexts;
  }

}
1

When you debug this with xdebug you see that the line

if ($entity = $this->getContextValue($type)) {

throws a ContextException(sprintf("The %s context is not a valid context.", $name)); and so the code stops before any debug message is printed out.

You don't need to check for a context if you don't indent to implement one for each entity type. A basic version of the plugin should work with a check for the route parameter alone:

  public function evaluate() {
    if (empty($this->configuration['types']) && !$this->isNegated()) {
      return TRUE;
    }
    foreach ($this->configuration['types'] as $type) {
      if ($entity_id = \Drupal::routeMatch()->getParameter($type)) {
        return TRUE;
      }
    }
    return FALSE;
  }

Because ContextException errors thrown in a custom ConditionPluginBase evaluate() function are caught upstream, you'd need to add a try {} catch () {} clause to properly debug this function.

  • While this solves my actual problem (4k4++) it doesnt answer the question of this post. (Why I cant run any commands that send debug data to screen from within ConditionBasePlugin::evaluate(). I'd still love to understand this better. – JamesWilson Mar 21 '18 at 13:33
  • 1
    because if you put an unknown context in getContextValue() this throws an exception and the debug data you send is later in evaluate(), so you don't see any debug data and you don't see the exception either, because it is catched internally and doesn't cause an error. – 4k4 Mar 21 '18 at 13:46
  • Doh. You're right and sorry. I knew that of course. Have clearly been looking at this for too long. – JamesWilson Mar 21 '18 at 14:41
  • 1
    @JamesWilson, about your proposed edit, only ContextException is catched, other exception are handled as usual. – 4k4 Mar 21 '18 at 15:01

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.