0

I'm kind of new to Drupal 8 development and object-oriented PHP in general. I've been trying to make a simple custom block module, which includes a service with simple dependency injection. However, I can't seem to figure out why the construct method does not receive an appropriate parameter - been staring at the code for hours. My code:

event_countdown.services.yml

services:
  event_countdown.date_calculator:
    class: Drupal\event_countdown\DateCalculator

event_countdown.routing.yml

event_countdown.content:
  defaults:
    _title: 'Event Countdown'
  requirements:
    _permission: 'access content'
  options:
    no_cache: 'TRUE'

src/DateCalculator.php

<?php

namespace Drupal\event_countdown;

class DateCalculator {

  // forms the block's output based on given date
  public function daysUntilEventStarts($date) {
    // get difference in days between now and the given date
    $difference = $this->getDifferenceInDaysFromCurrentDate($date);
      // if event date is in the future
      if($difference >= 1) {
        return "Days left until the event starts: " . $difference;
      // if event date and current date are on the same day
      } else if($difference == 0) {
        return "The event is in progress.";
      // if event date is in the past
      } else {
        return "The event has already ended.";
      }
  }

  // calculates difference in days between now and given date
  public function getDifferenceInDaysFromCurrentDate($date) {
    // current time
    $now = time();
    // event datetime field converted to timestamp
    $event_date = strtotime($date);
    // timestamp difference
    $difference = $event_date - $now;
    // timestamp difference rounded down to days
    return round($difference / (60 * 60 * 24));
  }

}

src/Plugin/Block/EventCountdownBlock.php

<?php

namespace Drupal\event_countdown\Plugin\Block;

use Drupal\Core\Block\BlockBase;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\event_countdown\DateCalculator;


/**
 * Provides an event countdown block.
 *
 * @Block(
 *   id = "event_countdown_block",
 *   admin_label = @Translation("Event countdown block"),
 *   category = @Translation("Event countdown block"),
 * )
 */

class EventCountdownBlock extends BlockBase {

  protected $dateCalculator;

   public function __construct(DateCalculator $dateCalculator) {
     $this->dateCalculator = $dateCalculator;
   }

   public static function create(ContainerInterface $container) {
     return new static($container->get('event_countdown.date_calculator'));
   }

   public function build() {

      // get current node based on route
      $node = \Drupal::routeMatch()->getParameter('node');

      //check whether node type equals 'event' - if it does, get text output, otherwise display an error message
      if($node->getType() == "event") {
        // get datetime value from node
        $date = $node->field_event_date->value;
        // call service method to calculate difference in days
        $output = $this->dateCalculator->daysUntilEventStarts($date);
      } else {
        // display error
        $output = "Woops! This block is intended only for event pages.";
      }

      return array(
        // output
        '#markup' => $output,
        // prevent block caching
        '#cache' => [
          'max-age' => 0,
          ],
      );
  }

}

The error I get in Drupal logs is:

Recoverable fatal error: Argument 1 passed to Drupal\event_countdown\Plugin\Block\EventCountdownBlock::__construct() must be an instance of Drupal\event_countdown\DateCalculator, array given, called in D:\drupal8\devdesktop\drupal-8.5.1\core\lib\Drupal\Core\Plugin\Factory\ContainerFactory.php on line 25 and defined in Drupal\event_countdown\Plugin\Block\EventCountdownBlock->__construct() (line 24 of D:\drupal8\devdesktop\drupal-8.5.1\modules\custom\event_countdown\src\Plugin\Block\EventCountdownBlock.php) #0 D:\drupal8\devdesktop\drupal-8.5.1\core\includes\bootstrap.inc(582): _drupal_error_handler_real(4096, 'Argument 1 pass...', 'D:\\drupal8\\devd...', 24, Array) #1

I would appreciate any sort of help. Thank you!

1

Plugins aren't naturally container-aware, so your create method is never called. The default plugin factory passes a configuration array as the first argument to your constructor, hence the error.

You can make the plugin container-aware by implementing ContainerFactoryPluginInterface. For example:

class EventCountdownBlock extends BlockBase implements ContainerFactoryPluginInterface {

  public function __construct(array $configuration, $plugin_id, $plugin_definition, DateCalculator $dateCalculator) {
    parent::__construct($configuration, $plugin_id, $plugin_definition);
    $this->dateCalculator = $dateCalculator;
  }

  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
    return new static(
      $configuration,
      $plugin_id,
      $plugin_definition,
      $container->get('event_countdown.date_calculator')
    );
  }
  • Ah, everything makes so much more sense now. Your suggestion worked, thank you very much :) – jhacin Apr 19 '18 at 15:32

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.