Drupal 8 - Entity API - Part 1

20 March 2017

Entities are awesome!

Leveraging the Entity API lets us create more lightweight and flexible solutions; they provide a unified way to work with different data units in Drupal - that’s why this post is about Entity API in D8. Entity API is already in core - we have all the necessary tools available inside the Admin UI to create and manage website data. However, the Entity API can also be easily extended to create custom entities.

How to create your own entity in D8?

Here is a very basic sample of creating a custom bbd_team_member entity. Enjoy! Create a new module called  bbd_team_member and bbd_team_member.info.yml within it.

name: BBD Team
description: Provides custom bbd_team_member entity
type: module
core: 8.x

Visit /admin/modules - you should already be able to install the BBD Team module.  Create a src directory - this directory contains all the object-oriented code (classes, interfaces etc) in Drupal 8. Next, create an Entity directory within it and a file with the class for our entity src/Entity/BBDTeamMember.php with the following code:

<?php
/**
 * Description of BBDTeamMember
 *
 * @author Olga
 */
namespace Drupal\bbd_team_member\Entity;

use Drupal\Core\Entity\ContentEntityBase;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Field\BaseFieldDefinition;
use Drupal\Core\Field\FieldStorageDefinitionInterface;

/**
 * @ContentEntityType(
 *   id = "bbd_team_member",
 *   label = @Translation("Team Member"),
 *   label_singular = @Translation("team member"),
 *   label_plural = @Translation("team members"),
 *   label_count = @PluralTranslation(
 *     singular = "@count team member",
 *     plural = "@count team members"
 *   ),
 *   base_table = "bbd_team_member",
 *   entity_keys = {
 *     "id" = "id",
 *     "uuid" = "uuid",
 *     "label" = "title",
 *   }
 * )
 */
class BBDTeamMember extends ContentEntityBase {
}

Let's have fun and add some fields.

public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
  // Get field definitions for 'id' and 'uuid' from the parent.
  $fields = parent::baseFieldDefinitions($entity_type);
     
  $fields['title'] = BaseFieldDefinition::create('string')
     ->setLabel(t('Title'))
     ->setRequired(TRUE)
     ->setDisplayOptions('view', [
        'label' => 'hidden',
        'weight' => 1,
     ])
     ->setDisplayOptions('form', ['weight' => 1]);
  
  $fields['image'] = BaseFieldDefinition::create('image')
      ->setLabel(t('Image'))
      ->setRequired(TRUE)
      ->setDisplayOptions('view', [
        'label' => 'hidden',
        'weight' => 2,
      ])
      ->setDisplayOptions('form', ['weight' => 2]);

  $fields['position'] = BaseFieldDefinition::create('string')
      ->setLabel(t('Position'))
      ->setRequired(TRUE)
      ->setDisplayOptions('view', [
        'label' => 'inline',
        'weight' => 3,
      ])
      ->setDisplayOptions('form', ['weight' => 3]);
  
  return $fields;
}

Run the drush entity-updates. Right, now we can see the ‘bbd_team_member’ table in the database with the appropriate fields. Create an interface src/Entity/BBDTeamMemberInterface.php with the following code:

<?php

/**
 * Description of BBDTeamMember Interface
 *
 * @author Olga
 */

namespace Drupal\bbd_team_member\Entity;

use Drupal\Core\Entity\ContentEntityInterface;

interface BBDTeamMemberInterface extends ContentEntityInterface {

  /**
   * @return string
   */
  public function getTitle();

  /**
   * @param string $title
   *
   * @return $this
   */
  public function setTitle($title);
 
  /**
   * @return string
   */
  public function getPosition();
  
  /**
   * @param string $position
   *
   * @return $this
   */
  public function setPosition($position);

And update the class:

public function getTitle() {
    return $this->get('title')->value;
  }

  public function setTitle($title) {
    return $this->set('title', $title);
  }

  public function getPosition() {
    return $this->get('position')->value;
  }

  public function setPosition($position) {
    return $this->set('position', $position);
  }

You’ll want to see some results of our coding in the admin panel, won’t you? To add a route we need to add the following lines to the annotation.

handlers = {
  "view_builder" = "Drupal\Core\Entity\EntityViewBuilder",
  "route_provider" = {
    "html" = "Drupal\Core\Entity\Routing\DefaultHtmlRouteProvider",
  },
  "form" = {
    "add" = "Drupal\Core\Entity\ContentEntityForm",
    "delete" = "Drupal\Core\Entity\ContentEntityDeleteForm",
  },
  "views_data" = "Drupal\views\EntityViewsData",
},
links = {
  "canonical" = "/bbd-team-member/{bbd_team_member}",
  "add-form" = "/admin/content/bbd-team-member/add",
  "delete-form" = "/admin/content/bbd-team-members/manage/{bbd_team_member}/delete",
},
admin_permission = "administer bbd_team_members",

We can easily create a “bbd_team_member” from the admin panel, using admin/content/bbd-team-member/add. Don’t forget to run the drush entity-updates and drush cr all.

Add team member form

Quite nice, isn’t it? However, after the member has been created we get ‘Add team member’ page again. It’s a bit confusing - I want to see a notification that the member was created successfully and be able to see the created entity, just like nodes behave. Let’s do it right away.

Create a Form directory within crs, and add BBDTeamMemberForm.php with this code:

<?php
namespace Drupal\bbd_team_member\Form;

use Drupal\Core\Entity\ContentEntityForm;
use Drupal\Core\Form\FormStateInterface;

class BBDTeamMemberForm extends ContentEntityForm {

  public function save(array $form, FormStateInterface $form_state) {
    parent::save($form, $form_state);

    $entity = $this->getEntity();
    $entity_type = $entity->getEntityType();

    $arguments = [
      '@entity_type' => $entity_type->getLowercaseLabel(),
      '%entity' => $entity->label(),
      'link' => $entity->toLink($this->t('View'), 'canonical')->toString(),
    ];

    $this->logger($entity->getEntityTypeId())->notice('The @entity_type %entity has been saved.', $arguments);
    drupal_set_message($this->t('The @entity_type %entity has been saved.', $arguments));

    $form_state->setRedirectUrl($entity->toUrl('canonical'));
  }
}

And change the announcement - we can already use our custom class:

"add" = "Drupal\bbd_team_member\Form\BBDTeamMemberForm",
"edit" = “Drupal\bbd_team_member\Form\BBDTeamMemberForm",

Cheers! That’s what I want to see!

Team member entity

Let’s get a list of team members; fortunately Views are already part of Drupal 8 core. If you want to add views support to our entity just add this code to announcement:

"views_data" = "Drupal\views\EntityViewsData",

Keep in mind, don’t forget about cache ;)

Now we can just create new view showing ‘Team Member’ entities and use the limitless possibilities of views :)

Team members view

This is only a basic example. Entity API is more powerful than we may expect and lots of exciting features are still ahead. Stay tuned

Smiley face