I've only recently discovered this module, so forgive me if this doesn't make sense.

Would the core Book module be considered something a dependency could be created on? Like if a node was part of a Book, the dependencies would be it's children?

Comments

danielb’s picture

Some people might consider it the other way around, a book page has a dependency which is it's direct parent. Some people might want both and then you'd be in circular territory. Such an implementation might even require an interface where a user picks which way the dependency goes?

danielb’s picture

I've come up with a solution, however it relies on changes in this issue: #1590312: Collect useful data in $dependency property
I assume a patch wouldn't pass tests otherwise.

<?php

/**
 * Implements hook_entity_dependenies().
 */
function book_entity_dependencies($entity, $entity_type) {
  if ($entity_type == 'node' && !empty($entity->book)) {
    $dependencies = array();

    // Book page's immediate parent.
    entity_dependency_add($dependencies, $entity, 'node', array(array('book', 'plid')));

    // Book page's immediate children.
    $flat = book_get_flat_menu($entity->book);
    $children = array();
    if ($entity->book['has_children']) {
      // Walk through the array until we find the current page.
      do {
        $link = array_shift($flat);
      } while ($link && ($link['mlid'] != $entity->book['mlid']));
      // Continue through the array and collect the links whose parent is this page.
      while (($link = array_shift($flat)) && $link['plid'] == $entity->book['mlid']) {
        $matches = array();
        if (preg_match('/^node\/([\d]+)$/', $link['href'], $matches)) {
          $dependencies[] = array(
            'type' => 'node',
            'id' => $matches[1],
          );
        }
      }
    }

    return $dependencies;
  }
}

?>

The key to book relationships is $node->book['plid'] which is a book page's parent nid. There is no converse property that gives child pages.
However, if the dependency goes both ways, the relationship will hold because the child pages will always correctly track their parent dependency.

danielb’s picture

It seems that I am wrong, the plid is not the parent node, it's the parent node's menu link id. Bugger.

danielb’s picture

The problem can be solved by querying the book table, but this is no longer a candidate for testing my nested properties support.

danielb’s picture

Here's a version that works, but yeah there's no support for identifying the property that signifies the relationship, since it requires complex calculations. Basically with both the parent dependency and child dependency any book page will pull up all the other pages of the book as a dependency. That might not be the desired behaviour, but here's all the logic for it anyway.

<?php
/**
 * Implements hook_entity_dependenies().
 */
function book_entity_dependencies($entity, $entity_type) {
  if ($entity_type == 'node' && !empty($entity->book)) {
    $dependencies = array();

    // Book page's immediate parent.
    if (!empty($entity->book['plid'])) {
       $parent_nid = db_query(
         'SELECT nid FROM {book} WHERE mlid = :mlid',
         array(':mlid' => $entity->book['plid'])
       )->fetchField();
       $dependencies[] = array(
         'type' => 'node',
         'id' => $parent_nid,
       );
    }

    // Book page's immediate children.
    $flat = book_get_flat_menu($entity->book);
    $children = array();
    if ($entity->book['has_children']) {
      // Walk through the array until we find the current page.
      do {
        $link = array_shift($flat);
      } while ($link && ($link['mlid'] != $entity->book['mlid']));
      // Continue through the array and collect the links whose parent is this page.
      while (($link = array_shift($flat)) && $link['plid'] == $entity->book['mlid']) {
        $matches = array();
        if (preg_match('/^node\/([\d]+)$/', $link['href'], $matches)) {
          $dependencies[] = array(
            'type' => 'node',
            'id' => $matches[1],
          );
        }
      }
    }

    return $dependencies;
  }
}
?>
danielb’s picture

I now realise there is an additional relationship each book page has to the book root page ($node->book['bid']) and while it may seem redundant to include, it is a legit relationship that should be handled.

danielb’s picture

Status: Postponed » Active

The $node->book['bid'] is just a node id, so that's easy.

According to http://api.drupal.org/api/drupal/modules%21menu%21menu.module/function/m...
there should at least sometimes be a $node->menu['mlid']
so I've added data to indicate that via a relationship key, which I guess should be documented so other hooks can do the same thing. The relationship key holds an array with keys "key" which is the assumed parent array path to the key in the dependent entity, and the value is the value of that key in the relationship stored in the current entity at 'property'. When present this would override the assumption that the 'property' leads us to an entity id. This isn't used anywhere, it's just more useful data that could be exploited with custom code to fix relationships between entities and their dependencies upon import.

<?php
/**
 * Implements hook_entity_dependenies().
 */
function book_entity_dependencies($entity, $entity_type) {
  if ($entity_type == 'node' && !empty($entity->book)) {
    $dependencies = array();

    // Book page's root book node.
    if (!empty($entity->book['bid'])) {
       $dependencies[] = array(
         'type' => 'node',
         'id' => $entity->book['bid'],
         'property' => array(array('book', 'bid')),
       );
    }

    // Book page's immediate parent.
    if (!empty($entity->book['plid'])) {
       $parent_nid = db_query(
         'SELECT nid FROM {book} WHERE mlid = :mlid',
         array(':mlid' => $entity->book['plid'])
       )->fetchField();
       $dependencies[] = array(
         'type' => 'node',
         'id' => $parent_nid,
         'property' => array(array('book', 'plid')),
         // Recognise the relationship is not done through the entity id key.
         'relationship' => array(
           'key' => array('menu', 'mlid'),
           'value' => $entity->book['plid'],
         ),
       );
    }

    // Book page's immediate children.
    $flat = book_get_flat_menu($entity->book);
    $children = array();
    if ($entity->book['has_children']) {
      // Walk through the array until we find the current page.
      do {
        $link = array_shift($flat);
      } while ($link && ($link['mlid'] != $entity->book['mlid']));
      // Continue through the array and collect the links whose parent is this page.
      while (($link = array_shift($flat)) && $link['plid'] == $entity->book['mlid']) {
        $matches = array();
        if (preg_match('/^node\/([\d]+)$/', $link['href'], $matches)) {
          $dependencies[] = array(
            'type' => 'node',
            'id' => $matches[1],
          );
        }
      }
    }

    return $dependencies;
  }
}
?>
danielb’s picture

Status: Active » Postponed

I'm going to try and put this practical use now for exporting/importing books between sites. Obviously the nids will change, so the hope is the extra data provided will give enough feedback about dependencies to be able to handle the situation. Might take me a while to conjure up this practical usage.
In any case the suggested code here can't be turned into a patch until this is solved: #1590312: Collect useful data in $dependency property

Status: Active » Postponed