GrabDuck

Using the entity API in Drupal 8 | Lakshmi Narasimhan

:

There is a lot of literature about entities and their purpose in Drupal 7 context. Most of it has been adopted in Drupal 8 as well. In this post, I'll highlight the differences between D7 and D8 entities and how to use the entity API in 8.

Entities have their own classes in 8. Also, Drupal 8 introduces the concept of config entities. These are used to store user-created configuration if its more than a piece of text, boolean or integer. They differ from the usual entities in the following ways:

  • The are not revisionable
  • The don't support entity translation interface(TranslatableInterface), but can still be translated using config's translation API.
  • The don't have fields/are not fieldable.

The rule of the thumb is, any information pertaining to the structure and functionality of the site(image style, content types, filters), how content is being served(views, display modes) etc. are config entities.

Secondly, the data storage mechanism moved from being field-centric in 7 to entity centric in 8. This implies that all fields attached to an entity share the same storage backend, making querying a lot easier.

Entity validation is a separate API based on Symfony's validator component. This can be availed when adding entities through other means(ex. programmatically creating an entity instance) than by using user facing forms. Entity validation will be the demonstrated in another future post.

Creating and loading entities

To create a new entity object, use the entity_create. NOTE that this only creates an entity object and does not persist it.

$node = entity_create('node', array(
  'title' => 'New Article',
  'body' => 'Article body',
  'type' => 'article',
));

If you know what the entity class name is, you can use it directly.

$node = Node::create(array(
  'title' => 'New Article',
  'body' => 'Article body',
  'type' => 'article',
));

Entities can be loaded using similar functions, entity_load and <class_name>::load.

$node = entity_load('node', $id);

// same as above
$node  Node::load($id);

Entity save is done by calling the instance's save method.

$node->save();

Save works for both creating and updating an entity. An entity can be checked if it's being created for the first time using the isNew method.

use Drupal\node\Entity\Node;

  $data = file_get_contents('https://www.drupal.org/files/druplicon-small.png');
  $file = file_save_data($data, 'public://druplicon.png', FILE_EXISTS_RENAME);

  $node = Node::create([
    'type'        => 'article',
    'title'       => 'A new article',
    'field_image' => [
      'target_id' => $file->id(),
      'alt' => 'Drupal',
      'title' => 'Drupal logo'
    ],
  ]);
assert($node->isNew(), TRUE);
$node->save();
assert($node->isNew(), FALSE);

entity permissions can be checked using the access method.

$node->access($op);
// where $op is one of "view", "create", "update" or "delete"

Reading and updating entities

Entity properties can be modified using the set method.

$node->set("title", "A newer title");
$node->save();

Reading and updating entity fields follows a similar pattern to Entity Metadata Wrappers in 7, albeit more object oriented. Fields can be read as follows:

use Drupal\node\Entity\Node;

// text field
$node = Node::load(4);
$txt = $node->field_my_text->value;

// entity reference
$node = Node::load(3);
$tags = $node->field_tags->referencedEntities();

// link field
$uri = $node->field_my_link->uri;
$title = $node->field_my_link->title;
$options = $node->field_my_link->options;

The $tags contains all the term objects associated with that field.

Updating a text field is easy.

$node = Node::load(4);
$node->field_my_text = "updated text";
$node->save();

To update a node and add a set of terms,

use Drupal\node\Entity\Node;
use Drupal\taxonomy\Entity\Term;

$node = Node::load(4);
$term1 = Term::load(1);
$term2 = Term::load(2);
$node->field_tags->setValue([$term1, $term2]);
$node->save();

Link fields can be updated as follows,

// specific attributes can be updated.
$node = Node::load(4);
$node->field_my_link->uri = "https://lakshminp.com/writing-custom-authenticator-drupal-8";
$node->save();

// the whole field can also be updated.
$node = Node::load(4);
$node->field_my_link = ["uri" => "https://lakshminp.com/", "title" => "My Blog", "options" => ["target" => "_blank"]];
$node->save();

Entity field query in D8

Entity field query has been essentially rewritten in Drupal 8. It helps fetching entities which match given criteria without writing any SQL queries. Here's a simple query to fetch all published nodes of type article.

$query = \Drupal::entityQuery('node');
 $query->condition('status', 1);
 $query->condition('type', 'article');
 $entity_ids = $query->execute();

The $query query object is chainable, just like entity field query and returns an object of type QueryInterface. It is possible to query fields.

$query = \Drupal::entityQuery('node')
  ->condition('status', 1)
  ->condition('field_tags.entity.name', 'Chennai');
$nids = $query->execute();

We can give different comparison operators too.

$query = \Drupal::entityQuery('node')
  ->condition('status', 1)
  ->condition('field_my_link.uri', 'lakshminp.com', 'CONTAINS');
$nids = $query->execute();

You can specify a field delta value between the field name and column name, as in:

$query = \Drupal::entityQuery('node')
  ->condition('status', 1)
  ->condition('field_tags.1.entity.name', 'Mumbai');
$nids = $query->execute();

will fetch all the nodes whose 2nd tag name is "Mumbai".

It is possible to specify OR conditions and chain them.

$query = \Drupal::entityQuery('node')
  ->condition('status', 1);

$group = $query->orConditionGroup()
  ->condition('field_tags.entity.name', 'Mumbai');

$nids = $query->condition($group)->execute();

fetches all nids which are either published or have "Mumbai" in tags.

These nids can be further processed after fully loading the entity objects using entity_load_multiple.

// ...
$nids = $query->execute();
$nodes = entity_load_multiple('node', $nids);
foeach($nodes as $node) {
  //do something
}