 [ ← blog / drupal ](/) Jul 06, 2018  ·  \#drupal  ·  4 min read 

# Filtering out invalid entity references in Drupal 8

Today I was working on a custom Drupal 8 form where I needed an option to purge existing entities and their references on a parent entity before running an import. It seemed pretty straightforward until I saw "ghost" values persisting on the parent entity's inline entity form. Here's my journey down the rabbit hole to fix broken entity reference values.



Today I was working on a custom Drupal 8 form where I needed an option to purge existing entities and their references on a parent entity before running an import. It seemed pretty straightforward until I saw "ghost" values persisting on the parent entity's inline entity form. Here's my journey down the rabbit hole to fix broken entity reference values.

The first thing I did was pretty straightforward. It's in a batch, so I grabbed a few entities and just deleted them, hoping magic would happen.

```php
$limit = 25;   
$price_list_item_ids = array_slice($price_list->getItemsIds(), 0, $limit);
$price_list_items = $price_list_item_storage->loadMultiple($price_list_item_ids);
$price_list_item_storage->delete($price_list_items);
```

That didn't work. When I would view the price list the references still existed, albeit broken.

So then I tried to run the filter empty items method, thinking "well, the value is empty." Fingers crossed, I did the following:

```php
$price_list->get('items')->filterEmptyItems();
```

No dice. So I dig in and see why this isn't working. The **\\Drupal\\Core\\Field\\FieldItemList::filterEmptyItems** method invokes **\\Drupal\\Core\\TypedData\\Plugin\\DataType\\ItemList::filter** and checks if the field item reports that it has an empty value.

```php
  /**
   * {@inheritdoc}
   */
  public function filterEmptyItems() {
    $this->filter(function ($item) {
      return !$item->isEmpty();
    });
    return $this;
  }
```

Well, it should be empty, right? *WRONG AGAIN*. The EntityReferenceItem class doesn't care if the reference is valid or not, only if it has values in its properties. In my case, there was always a **target\_id** set and sometimes **$this-&gt;entity** was populated from a previous access.

```php
  /**
   * {@inheritdoc}
   */
  public function isEmpty() {
    // Avoid loading the entity by first checking the 'target_id'.
    if ($this->target_id !== NULL) {
      return FALSE;
    }
    if ($this->entity && $this->entity instanceof EntityInterface) {
      return FALSE;
    }
    return TRUE;
  }
```

So I took another approach. I checked to see if validating the field would return validation constraints and let me remove field values that way. Running validate gave me a list of all the broken references... except it was near impossible to traverse them and discover what field value delta was incorrect.

```php
$price_list->get('items')->validate();
```

So then I backtracked a bit. I noticed that the filter method for the item list was public. The **filterEmptyItems** method wasn't working for me, so why not roll my own! And I did. And it failed.

```php
$items->filter(function (EntityReferenceItem $item) {
  return !$item->validate()->count();
});
```

Why? Because the **ValidReference** constraint is on the entity reference list class and not on the entity reference item class itself. There were never any violations at the field value level, only for the entire list of values.

Since I couldn't trust validations, I could trust that the computed **entity** field would be null when it was passed through the entity adapter. And that's how I got to my final version which removes all invalid entity reference values:

```php
// The normal filter on empty items does not work, because entity
// reference only cares if the target_id is set, not that it is a viable
// reference. This is only checked on the constraints. But constraints
// do not provide enough data. So we use a custom filter.
$price_list->get('items')->filter(function (EntityReferenceItem $item) {
  return $item->entity !== NULL;
});
$price_list->save();
```

Hopefully, this aids someone else going down a unique rabbit hole of programmatically removing entities and mending references.

 

  ![](/themes/custom/mglaman/images/headshot.webp)Thanks for reading

I write about Drupal, PHP, and the quiet infrastructure behind large sites — caching, config, and testing. New posts land every week or two.

 [Subscribe via RSS →](/rss.xml) [About me →](/about-me) 

 



 

  [← Older

Teaching my son debugging through Code Master

 ](/blog/teaching-my-son-debugging-through-code-master) [Newer →

Drupal 7, 8, and 9: deprecate the old to intake the new

 ](/blog/drupal-7-8-and-9-deprecate-old-intake-new)