Storing books in Drupal 7 databases

Tags: 

The Book module is part of Drupal core. This article is about how Drupal 7 represents books in its database. You'll need to know if you want to write a custom module to mess with books.

The Book module is a small extension of the core menu system. The module doesn't add much data of its own. Understand the menu system, and you understand books.

I talked about the menu system in another article. Here's a recap.

Menus reprised

Here's a main menu from a test Web site:

Menu

Here's how it might look to users if we use the subtree menu module:

Submenu showing

How is the menu represented in the database? To start with, we need some nodes. Here they are in the node table:

Node table

The menu data is in the menu_links table:

Menu links table

Each row represents one menu entry. The primary key of the table is mlid, the menu item's id. mlid is an important field. Remember, each menu item has its own unique value for mlid.

The menu_name field tells Drupal which menu the entry is part of. All of the menu items here are in main-menu, as you can see from the first column.

Usually, each menu item is rendered as an <a> tag, as in <a href="path">text</a>. The link_path field shows the path. The link_title field shows the link's text.

How does Drupal know that there is a submenu, with two items (Evil unicorns and Good unicorns) under the Unicorns item? Check out the field plid, which shows the mlid of each item's parent item. Recall the mlid is the unique id of each menu item.

The first three items are at the top level of the main menu. They have no parent, so their plid is 0.

The last two items are children of the Unicorns item. Unicorns has an mlid of 330. So, the mlid of the two items is 330.

Just one detail left. Here's the menu again:

Menu

How does Drupal know that Welcome comes before Unicorns comes before Rainbows?

Here's the menu_links table again:

Menu links table

Look at the weight field, over on the right. The order is shown there.

Items are sorted alphabetically within weight. Suppose we have a menu with several items that have weights of 0, and several that have weights of 1. All the items in the 0 group will show before any items in the 1 group. Within each group, Drupal shows the items alphabetically.

That's how menus work. If you need more detail, see the menu article.

Menus to books

Suppose we use the Book module to create a book like this:

Reorder

The node ZB1 Front page is the root of the book. It has two children: ZB Chapter 1 front page and ZB Chapter 2 front page. Each of those has three pages under it.

Here is the node table:

Book node table

You can see the node ids on the left.

Here is the menu_links table for the book items:

Book menu links table

The structure is the same as it is for menus. Node 1 is the book's root. It has a plid of 0, since it has no parents.

Nodes 2 and 6 are the first nodes of each chapter. They have mlids of 321 and 325 respectively. They have the same parent, so their plids have the same value. What value? The plids contain the mlid of the book's root node.

The rest of the book items follow the same pattern. The three items in chapter 1 (nodes 3, 4, and 5) have the same parent. So do the items in chapter 2.

As you can see, the book module uses Drupal's menu system to represent each book's hierarchy.

Does the Books module add to the database?

Aye, it does. It adds one table, called book. Here it is, for the situation above:

Book book table

A site can have many books. The book table shows which book each node/mlid combination belongs to.

Look at the last field, bid. That stands for "book id." What value goes in there? The nid (node id) of the book's root node. It's 1 here, because that's the nid of the book's root node. Here's the node table again:

Book node table

The first row stores the first node I created, hence it has an nid of 1. That nid is stored as bid in book, to show which book menu items belong to.

Coding

Let's say we want to combine chapters 1 and 2. We want to put all of the nodes in chapter 2 at the end of chapter 1.

Here's some pseudocode:

//Find the mlid of chapter 1's opening page.
ch1_root_mlid = select mlid from menu_links
                  where title = 'ZB Chapter 1 front page'
//Find the mlid of chapter 2's opening page.
ch2_root_mlid = select mlid from menu_links
                  where title = 'ZB Chapter 2 front page'
//Find the highest weight used in chapter 1.
high_weight = select max(weight) from menu_links
                where plid = ch1_root_mlid
  //The where clause returns all the items in chapter 1,
  //that is, that have a parent id that is the same as
  //the id for the root of chapter 1.
//Get all the items in chapter 2.
ch2_items = select * from menu_links
              where plid = ch2_root_mlid
foreach item in ch2_items
  //Move the item to chapter 1.
  item.plid = ch1_root_mlid
  //Put the item at the end of chapter 1.
  high_weight++
  item.weight = high_weight
  update item record
end for

Summary

The Books module uses Drupal's core menu system. The table menu_links stores the hierarchical relationships that make up both menus and books.

The Books module adds one table, book, to keep track of which book menu items belong to.