Storing books in Drupal 7 databases
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.
Here's a main menu from a test Web site:
Here's how it might look to users if we use the subtree menu module:
How is the menu represented in the database? To start with, we need some nodes. Here they are in the node table:
The menu data is in the 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:
How does Drupal know that Welcome comes before Unicorns comes before Rainbows?
Here's the menu_links table again:
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:
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
You can see the node ids on the left.
Here is the menu_links table for the book items:
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:
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:
The first row stores the first node I created, hence it has an
nid of 1. That
nid is stored as
book, to show which book menu items belong to.
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.
item.weight = high_weight
update item record
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.