0

I'm making decently complex system, where many entities are referencing each other, and I don't want to allow any broken references. That is why I would like to implement validation at entity deletion, but where should I actually put it?

I know hook_form_validate runs on deletion, but it is hard to know if it has been invoked by deleting operation. I guess I can also do proper check in access callback of entity, however it isn't about access but validation, and I think that would be ugly workaround, as those are two different things. What would be a good place to put, a validation mechanism for deletion that would query the database to know if deletion of entity is possible?

  • Are you using entity reference module to create the references or something else? – rooby Oct 25 '14 at 13:02
  • Yes I'm using entityreference. – Łukasz Zaroda Oct 25 '14 at 13:08
  • I don't believe you can prevent an entity being deleted in Drupal 7. You can do it in the form layer however there are other ways to delete entities that wouldn't be caught by that solution. Drupal 8 has hook_entity_pre_delete() but there is not an equivalent for Drupal 7 that I am aware of. Related: drupal.stackexchange.com/q/123729/10729 – rooby Oct 25 '14 at 14:54
  • Actually even that drupal 8 predelete hook wouldn't help. – rooby Oct 25 '14 at 14:58
  • If you can be 100% sure that no one will be deleting them any other ways than via a specific form submission then a form validation would be ok, but it's still a little risky in terms of ending up with broken references. – rooby Oct 25 '14 at 15:00
0

There are a few options that may mean you don't have to code anything:

I would go with one of those if possible instead of writing custom code.

Seeing as you do want to run custom code though I would suggest reading up on how those solutions work and implementing something similar, but with your own custom logic.

For example in the issue in that first link they discuss 3 different ways of doing the deletions and some of the pros and cons of certain possible solutions, so I think that information would be useful to you in implementing your own.

Although I don't know your exact requirements I would suggest that doing this at the validation stage is not a good idea because it means it is possible for you to delete referencing entities at validation stage and then something could stop the deletion of the referenced entities after your code runs and you will have deleted the reference when you shouldn't have.

It should be done later than the validation stage.

That patch issue suggests maybe hook_entity_delete(), which I think is a better place for it, however in that issue they also mention possible page timeouts if there are lots of references to delete.

The code in those 2 modules may also be useful to you but I haven't read those.

EDIT: just had a quick look an the reference_delete module also uses the entity delete hook.

  • I would rather like to do this in code because rules that I would like to use are pretty complex. It's not only about having broken reference, but entity that have reference 1. NEEDS to have one, 2. it NEEDS to work. Which means nulling values of references is not an option. User will need to be informed that he need to handle some other entities before the deletion will be possible. – Łukasz Zaroda Oct 25 '14 at 13:18
  • Thanks for help, but can I actually stop deleting process from hook_entity_delete? – Łukasz Zaroda Oct 25 '14 at 13:44
  • Nope, but your question doesn't say anything about doing that. It just says you don't want broken references. If you make sure to remove any references on deletion then you don't have to stop the process. I'm thinking from the side of deleting references to deleted entities. If you have requirements that you want to stop users from deleting entities that are being referenced you should add that to your question. – rooby Oct 25 '14 at 13:49
  • Sorry if I wasn't too clear but I assumed that the definition of validation is to allow something or stop something from happening . hook_entity_delete unfortunately only allows to act when event happens, but cannot act on the event. However now I wonder if "delete()" method of entity controller would be a good place to do this... Will need to check. – Łukasz Zaroda Oct 25 '14 at 13:55
0

You will need to extend the EntityAPIController in your entity. Here is an example of my hook_entity_info and then the method that handles deletion of of your entity:

/**
 * Implements hook_entity_info()
 *
 * @return array
 */
function iplist_entity_info() {
  $info = array();

  $info['iplist'] = array(
    'label' => t('IP Address List'),
    'base table' => 'iplistentity_iplist',
    'entity keys' => array(
      'id' => 'id',
      'label' => 'name',
    ),
    'uri callback' => 'entity_class_uri',
    'entity class' => 'IpListEntity',
    'controller class' => 'IpListEntityController',
    'admin ui' => array(
      'path' => 'admin/iplist',
      'controller class' => 'IpListEntityUIController',
      'menu wildcard' => '%iplist',
      'file' => 'iplist.admin.inc',
    ),
    'module' => 'iplist',
    'access callback' => 'iplist_access_callback',
    'fieldable' => TRUE,
    'bundles' => array(
      'iplist' => array(
        'label' => t('IP Address List'),
        'admin' => array(
          'path' => 'admin/iplist',
          'access arguments' => array('administer iplists'),
        ),
      ),
    ),
    'views controller class' => 'EntityDefaultViewsController',
  );

  return $info;
}

class IpListEntityController extends EntityAPIController {
  /**
   * IP list save
   *
   * @param $entity
   * @param DatabaseTransaction $transaction
   * @return bool|int
   */
  public function save($entity, DatabaseTransaction $transaction = NULL) {

    if (isset($entity->is_new)) {
      $entity->created_at = REQUEST_TIME;
    }

    $entity->updated_at = REQUEST_TIME;

    return parent::save($entity, $transaction);
  }

  /**
   * IPList delete controller
   *
   * @param $ids
   * @param DatabaseTransaction $transaction
   */
  public function delete($ids, DatabaseTransaction $transaction = NULL) {
    $entities = $ids ? $this->load($ids) : FALSE;
    if (!$entities) {
      // Do nothing, in case invalid or no ids have been passed.
      return;
    }
    $iplist_single = array_pop($entities);
    $iplistentity = self::loadByEntityWrapper($iplist_single);
    $iterator = $iplistentity->field_ip_address->value();

    foreach($iterator as $ip)
    {
      IpEntityController::deleteByWrapper($ip);
    }

    return parent::delete($ids, $transaction);
  }
}

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.