Amitav Roy

Blog on web and travel

Table of content using Drupal Field collection module

Posted on July 2019

Drupal makes me happy

Having a dynamic table of content for long articles is a really nice feature. And field collection module is just the module you need to get this feature implemented. To know how, check this tutorial.

Drupal’s field collection module is one of my favorite modules from drupal. What it does is lets you add a collection of fields as a single field to a content multiple times. For example, in my website I want to write a trip log which will be quite long and so its a good idea to divide it in days. Now, obviously I can have anchor tags in the body content and have a list on the top which will take the reader to the correct location, but what if these things are done automatically. I will have a set of fields as body and title. As and when I keep adding a new title and body field data, the toc will get updated automatically. Neat isn’t it?

So, that’s what we will be doing in this tutorial. The module that we will be using in this tutorial is “Field collection” which you can find here. And this module is dependent on Entity module which you can find here.

Ok, so once you have the module installed successfully, you will see “Field collection” as one of the options when you try to create a new field as shown in the screen shot below:

Drupal makes me happy

When adding you will come across two options: hidden and embedded. Make sure you select Embedded or else you will not be able to see the fields in add content field. The basic idea is to add the content from the admin section and then use internal functions and theme to display them inside the node.

Ok, make sure that the field has number of values set to more than 1 or else why would you even use this module :). Till now we have just added a container field. Now we have to add the sub fields which will be inside the container field. To do that you need to go inside Structure->Field collections and you will see that your field is listed there.

Drupal makes me happy

Inside manage fields, now I will add two fields – one for the title and the second for the body text. Mind it, I have set the number for these fields to just one because inside the container, there will be only one title and one body. But if you have a different requirement, then feel free to change accordingly. Check my screen shot:

Drupal makes me happy

Now that the fields are added, its time to add some content. Here is a snap shot of the content that I have added and how it looks by default.

Drupal makes me happy

The next step is to get rid of the content which is getting displayed below the body text by default and show it the way we like. Go to Structure->Content types->[Name of the content type]->manage display and hide the field from both teaser and default view. Well, if these are hidden how will the content get displayed?

Drupal makes me happy

So now let’s start the coding.

The module that I have create is “TOC”.

Drupal makes me happy

As you can see there are two tpl files. Although I highly recommend you having a separate folder for TPLs, JS, CSS used for a module, but this is just for demo so ?

Ok, let’s start with the hook_theme().

/**
 * Implementing hook_theme()
 */
function toc_theme() {
    return array(
        'toccontent' => array(
            'template' => 'toccontent',
        ),
        'toclist' => array(
            'template' => 'toclist',
        ),
    );
}

Two tpls are handleded here. One for the toc content and the second will be a list of the titles which will be displayed at the top of the body content.

Once that is done, let’s implement hook_node_load. Here is the code:

/**
 * Implementing hook_node_load()
 */
function toc_node_load($nodes, $types) {
    foreach ($nodes as $node) {
        // Only for article node content
        if ($node->type == 'article') {
            $node->toccontent = _nodeelements_generate_tocContent($node);
            $node->toclist = _nodeelements_generate_tocList($node);
        }
    }
}

Here first we check if the node type is the one we want. In my case its the article content type. I assigned two new objects to the main node object while the node is getting loaded. $node->toccontent which will have all the body content from the field and $node->toclist which will have the main titles in the form of a list.

Toccontent uses an internal function call _nodeelements_generate_tocContent where I am passing the node object. Here is the function’s code:

/**
 * This function will generate the content of the TOC
 */
function _nodeelements_generate_tocContent($node) {
    if (isset($node->field_sections) && !empty($node->field_sections[$node->language])) {
  
        foreach ($node->field_sections[$node->language] as $key => $value) {
            $fieldData[] = field_collection_item_load($value);
            foreach ($fieldData as $key => $value) {
                $tocBody[$key]['body'] = $value->field_section_body[$node->language][0]['value'];
                $tocBody[$key]['title'] = $value->field_section_title[$node->language][0]['value'];
            }
        }
  
        $output = theme('toccontent', array(
            'tocBody' => $tocBody,
        ));
    }
  
    // Else send blank.
    else {
        $output = "";
    }
  
    return $output;
}

No rocket science here, at first checking if the field data is not empty and then looping through all the fields and building array $tocBody. One important function call is “field_collection_item_load”. This is the field collection module’s function which gives the collection of the field and the data. It requires the field id for it to load the data. With the data loaded through this function, I added it to the $tocBody array and passed it to the tpl where I have taken care of the formatting.

The second object which is passed to the node is the toclist which again uses a similar function _nodeelements_generate_tocList where again I am passing the node object. The rest functionality is the same.

/**
 * This function will generate the drop down list of the TOC
 */
function _nodeelements_generate_tocList($node) {
    if (isset($node->field_sections) && !empty($node->field_sections[$node->language])) {
  
        foreach ($node->field_sections[$node->language] as $key => $value) {
            $fieldData[] = field_collection_item_load($value);
            foreach ($fieldData as $key => $value) {
                $tocBody[$key]['title'] = $value->field_section_title[$node->language][0]['value'];
            }
        }
  
        $output = theme('toclist', array(
            'tocBody' => $tocBody,
        ));
    }
  
    // Else send blank.
    else {
        $output = "";
    }
  
    return $output;
}

Once these functions are executed, the final thing to do is print the output in the node tpl so that the data can be seen. I am using the default drupal theme and here is the modified tpl screenshot:

![Drupal makes me happy](https://d15dxe0kapai5v.cloudfront.net/articles/18_drupal_field_collection/field_collection_06 (1).jpg)

The tpls which I have used does not have a lot of logic inside it. Here is the code for both of them:

The toccontent tpl
<div id="toc-container" class="toc-data">
    <?php foreach ($tocBody as $key=>$value): ?>
        <h2 id="toc-item-<?php print $key ?>"><?php print $value['title']; ?></h2>
        <div class="toc-body-content"><?php print $value['body']; ?></div>
    <?php endforeach; ?>
</div>
 
The toclist tpl
<div id="toc-list-container">
    <h3>Table of content</h3>
    <ol>
        <li><a href="#start">Introduction</a></li>
        <?php foreach ($tocBody as $key=>$value): ?>
            <li><a href="#toc-item-<?php print($key) ?>"><?php print $value['title']; ?></a></li>
        <?php endforeach; ?>
    </ol>
</div>