8

I have a multi-valued taxonomy reference field named field_classrooms in a node type. I use below code to delete the value in the field.

$node = Drupal\node\Entity\Node::load('1512');
unset($node->field_classrooms[0]);
$node->save();

I can't figure out what I am doing wrong here.

9 Answers 9

12

Maybe something like this could help you out

// Get the list of the associated classrooms ...
$classrooms = $node->get('field_classrooms')->getValue();
// ... if there are any associated classrooms ...
if(!empty($classrooms)) {
  foreach($classrooms as $classroom) {
    // ... get the key of the classroom
    $key = array_search(current($classroom), array_column($classrooms, key($classroom)));
    // ... remove the relationship ...
    $node->get('field_classrooms')->removeItem($key);
  }
  // ... and save the node.
  $node->save();
}

If you want to remove a single classroom from the list and you already know the tid

private function removeClassroomFromNode($node, $tid) {
  if(null != $node) {
    // ... get the list of associated classrooms
    $classrooms = $node->get('field_classrooms')->getValue();
    // ... get the key of the classroom
    $key = array_search($tid, array_column($classrooms, 'target_id'));
    // ... remove the classroom
    $node->get('field_classrooms')->removeItem($key);
    // ... save the node.
    $node->save();
  }
}
3
  • 1
    Thanks for the response. I tried removeItem instead of unset but still it doesn't seem to remove the value. It is a multi value field btw. Commented May 23, 2018 at 9:21
  • 1
    The removeItem() method uses unset() api.drupal.org/api/drupal/…
    – Rockabelly
    Commented May 23, 2018 at 11:18
  • So unnecessarily complex ... Drupal is something else...
    – AlxVallejo
    Commented Aug 17, 2023 at 21:22
4

You can use below code for removing items in the reference field in Drupal 8 :

/** @var \Drupal\Core\Entity\ContentEntityBase $entity */
$entity = \Drupal::entityTypeManager()->getStorage('node')->load($nid);

$tids = $entity->get('field_classrooms')->getValue();

$updatedTerms = [];
foreach($tids as $term) {
  if ($term['target_id'] != 35)
    $updatedTerms[] = ['target_id' => $term['target_id']];      
}

$entity->field_notifications = $updatedTerms;

$entity->save();
3

You can remove the first item of a list just calling

$node->get('field_classrooms')->removeItem(0);
1
  • Ya but you're shooting in the dark - how do you know which index to delete?
    – AlxVallejo
    Commented Aug 17, 2023 at 21:38
3

You can simply set to empty

$node->set('field_classrooms', []);
2

You can remove a particular referenced entity by id with this code:

    $entity->get('my_field')->setValue(array_filter($entity->get('my_field')->getValue(), function ($value) use ($id) {
      return ($value['target_id'] != $id);
    }));
2

Another way of bulk or selective delete is elegant filter() method.

Example:

use Drupal\Core\TypedData\TypedDataInterface;

$nodeStorage = \Drupal::service('entity_type.manager')->getStorage('node');
/**
 * @var \Drupal\node\Entity\Node $node
 */
$node = $nodeStorage->load($nid);
$classrooms = $node->get('field_classrooms');
$classrooms->filter(function (TypedDataInterface $item) {
  // You can do some operations on field item like: $item->getValue();
  // Returning TRUE in an iteration will remove the item.
  return TRUE;
});
$node->save();
1
  • Thanks! This was the winner for me because the filter() function lets you remove more than one entity reference in one go. If you try to do this with a loop you always end up with array indexing errors.
    – ocean
    Commented Feb 13, 2023 at 3:20
1

Meanwhile you can take help from this code -

$node = \Drupal\node\Entity\Node::load('1512');
// Removes '0' index element.
$node->set('field_classrooms', array_slice($node->get('field_classrooms')->getValue(), 1));
$node->save();
0

the item is removed based on the index number of the list, so you need to figure out the index you want to remove,

1.- get all the referenced items

$array_of_referenced_items = $node->get('field_SOMETHING_ref')->getValue();

2.- search on the array for the index of the item you are trying to remove.

$index_to_remove = array_search(['target_id' => ID_TO_SEARCH], $array_of_referenced_items);

3.- Remove the reference by the index.

$node->get('field_SOMETHING_ref')->removeItem($index_to_remove);

0

Drupal 10:

This is a cool thread and I think I can add some value to it. The problem is, that you will have an issue with the indexes, when you use the removeItem method in a loop: The delta will change because you use removeItem inside the loop.

The solution

The solution to this problem is a recursive function which takes care about the changed index after removeItem is called. in the following code we assume that you have your entity reference field ( \Drupal\Core\Field\EntityReferenceFieldItemListInterface ) stored in $my_order_order_item_list (the name is "My Order" plus the ER field "Order Item") and a list of entity-ids which should be compared (array $my_order_item_ids_all) with it.

Delete orphans on success (not before)

Also we save the entities we want to remove in an array ( @param array $entitiesToRemove )because we do a try-catch block where we save our My Order and when this was successful we delete the entities which do not belong to this entity anymore.

The method

/**
 * Removes entities from an entity reference list that are not in the specified list of IDs.
 *
 * This function uses recursion to safely remove items from the entity reference list
 * without causing index issues. It collects the entities to be removed in an array,
 * which can then be processed outside the loop.
 *
 * @param \Drupal\Core\Field\EntityReferenceFieldItemListInterface $my_order_order_item_list
 *   The entity reference field item list from which entities should be removed.
 * @param array $my_order_item_ids_all
 *   The array of IDs that should remain in the entity reference list.
 * @param array $entitiesToRemove
 *   The array that will be populated with entities to be removed.
 *
 * @return array
 *   The array of entities that were removed from the reference list.
 */
  private function removeEntitiesRecursively(&$my_order_order_item_list, $my_order_item_ids_all, &$entitiesToRemove) {


  // Flag to check if any item was removed
  $itemRemoved = false;

  foreach ($my_order_order_item_list as $delta => $value) {
    if (!in_array($value->target_id, $my_order_item_ids_all)) {
      if ($entity = $value->entity) {
        $entitiesToRemove[] = $entity;
      }
      $my_order_order_item_list->removeItem($delta);
      $itemRemoved = true;
      break; // Exit the loop to avoid index issues
    }
  }

  // If an item was removed, call the function again
  if ($itemRemoved) {
    $this->removeEntitiesRecursively($my_order_order_item_list, $my_order_item_ids_all, $entitiesToRemove);
  }

  // Return the list of entities to remove once no items are left to remove
  return $entitiesToRemove;
}

The method call

// now we delete all My Order Items which are not in the Request
$entitiesToRemove = $this->removeEntitiesRecursively($my_order_order_item_list, $my_order_item_ids_all, $entitiesToRemove);

Take care of orphans

After that we save our entity My Order and delete orphaned entities on success

try {
  $my_order->save();
} catch (\Exception $e) {
  // I used this in REST endpoint but you can throw whatever you want, it´s only an example :-)
  throw new BadRequestHttpException('The My Order data could not be updated. ' . $e->getMessage());
}
// after the My Order is saved, we physically delete the My Order Items
foreach ($entitiesToRemove as $entityToRemove) {
  $entityToRemove->delete();
}

Thoughts

This is the removeItem method:
https://api.drupal.org/api/drupal/core%21lib%21Drupal%21Core%21TypedData%21Plugin%21DataType%21ItemList.php/function/ItemList%3A%3AremoveItem/10
The index problem is the use of rekey inside removeItem
https://api.drupal.org/api/drupal/core%21lib%21Drupal%21Core%21TypedData%21Plugin%21DataType%21ItemList.php/function/ItemList%3A%3Arekey/10

In my opinion it´s not the best thing to rely on the index when you remove an item from an entity reference list and I remember there are some open issues regarding removeItem so take care about this, maybe something changes or become deprecated in Drupal 11 or whatsoever, but if it changes to let´s say ID based it will be easy to change the code so: I am using it (and the cool thing is, we do not have to save the parent entity every time we change the ER list)

The whole answer got a little more complex than requested but it covers problems that you might face when you are in need of manipulating an Entity Reference Field Item List.

Also I assumed that you want to delete the entities which were referenced by the list which is not always the case. The example is coming from eCommerce where you may have orders and order Items attached to it and as long those order Items does belong to a specific order, you want to delete those after you removed them from the list.

We can say that you need this approach in every case you would set the primary key of the referenced entity as foreign key to the parent entity.

What about added entities?

We talked about removing entities but what if we added (also) some entities? It´s the same as before: whether removeItem, nor appendItem will make us save the parent entity each time we use it, so you can expand your $my_order_order_item_list before you call the method like so:

$my_order_order_item_list->appendItem($my_order_item_id);

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.