Drupal 7 contains some incredibly powerful functionality in things like Render Arrays, Entity and Field APIs, etc. However, the actual details of these things are next to impossible to find. Especially difficult are specific examples and lists of available options. The more time I spend developing in Drupal 7, the more time I spend digging through code that I've already written trying to find a snippet that took me hours to find or figure out the first time I used it. I'm starting a short series of posts to document these items so I can find them more quickly. I am my own target audience for this. If anyone else can benefit, enjoy! There are many tutorials out there that explain how to do this, and I'll credit them as best I can, but this series is not a tutorial, it's just a collection of references.

Render Arrays for Item Lists

$variables: An associative array containing:

  • items: An array of items to be displayed in the list. If an item is a string, then it is used as is. If an item is an array, then the "data" element of the array is used as the contents of the list item. If an item is an array with a "children" element, those children are displayed in a nested list. All other elements are treated as attributes of the list item element.
  • title: The title of the list.
  • type: The type of list to return (e.g. "ul", "ol").
  • attributes: The attributes applied to the list element.
  //  Note that #type will be inserted directly into the resulting HTML,
  //  so use only 'ul' or 'ol' here.
  $output[] = [
    '#theme' => 'item_list',
    '#items' => [
      'value 1',
      'value 2',
      ['data' => 'value 3', 'id' => 'key3', 'class' => ['item-3-class']],
      [
        'data' => 'value 4',
        'class' => ['item-4-class'],
        'children' => [
          '4.1',
          '4.2',
          ['data' => '4.3', 'class' => ['list-4-3-class'], 'children'=>['4.3.1', '4.3.2']],
        ],
      ],
    ],
    '#type' => 'ul',
    '#attributes' => ['class' => 'list-class'],
  ];

Render Arrays for Tables

$variables: An associative array containing:

  • header: An array containing the table headers. Each element of the array can be either a localized string or an associative array with the following keys:

    • "data": The localized title of the table column.
    • "field": The database field represented in the table column (required if user is to be able to sort on this column).
    • "sort": A default sort order for this column ("asc" or "desc").
    • Any HTML attributes, such as "colspan", to apply to the column header cell.
  • rows: An array of table rows. Every row is an array of cells, or an associative array with the following keys:
    • "data": an array of cells
    • Any HTML attributes, such as "class", to apply to the table row.
    • "no_striping": a boolean indicating that the row should receive no 'even / odd' styling. Defaults to FALSE.

    Each cell can be either a string or an associative array with the following keys:

    • "data": The string to display in the table cell.
    • "header": Indicates this cell is a header.
    • Any HTML attributes, such as "colspan", to apply to the table cell.
  • attributes: An array of HTML attributes to apply to the table tag.
  • caption: A localized string to use for the <caption> tag.
  • colgroups: An array of column groups. Each element of the array can be either:

    • An array of columns, each of which is an associative array of HTML attributes applied to the COL element.
    • An array of attributes applied to the COLGROUP element, which must include a "data" attribute. To add attributes to COL elements, set the "data" attribute with an array of columns, each of which is an associative array of HTML attributes.
  • sticky: Use a "sticky" table header.
  • empty: The message to display in an extra row if table does not have any rows.
  $output[] = [
    '#theme' => 'table',
    '#header' => [t('Heading 1'), t('Heading 2'), t('Heading 3')],
    '#rows' => [
      ['value 1-1', 'value 1-2', 'value 1-3'],
      ['value 2-1', 'value 2-2', 'value 2-3'],
      [
        ['data' => 'value 3-1', 'colspan' => '2', 'class' => ['cell-class-3-1']],
        ['data' => 'value 3-3', 'class' => ['cell-class-3-3']],
      ],
    ],
    '#attributes' => ['class' => ['table-class']],
    '#empty' =>t('Your table is empty'),
  ];

Nested Arrays

Nested arrays were tricky to figure out, and I've had a hard time finding good examples of them. I finally found the key in this Stack Exchange post which explains that once an element is given #markup, the render process stops searching for nested elements. (Due to #children being overwritten by #markup in drupal_pre_render_markup().)

Once you know to stay away from #markup, for any element that has children, the rest is pretty easy to assemble. This example scans through an array of entity objects, renders each as an HTML <article>, then wraps the whole thing in a <div>.

  $rows = [];
  foreach ($entities as $entity) {
    $article = [
      '#prefix' => '<article>',
      '#suffix' => '</article>',
      'title' => [
        '#prefix' => '<h3 class="title">',
        '#markup' => l($entity->title, 'news/items/' . $entity->legacy_id),
        '#suffix' => '</h3>',
      ],
      'date' => [
        '#prefix' => '<div class="submitted">',
        '#markup' => format_date($entity->release_date['value'], 'custom', 'l, F j, Y'),
        '#suffix' => '</div>',
      ],
    ];
    // This emulates how text.module renders "trimmed or summary" fields.
    if (!empty($entity->body['summary'])) {
      $body = check_markup($entity->body['summary'], $entity->body['format']);
    }
    else {
      $body = check_markup($entity->body['value'], $entity->body['format']);
      $body = text_summary($body, $entity->body['format']);
    }
    $article['body'] = [
      '#markup' => $body,
    ];
    $article['links'] = [
      '#markup' => l(t('read more'), 'news/items/' . $entity->legacy_id),
    ];
    $rows[] = $article;
  }
  $output = [
    '#prefix' => '<div class="news-teasers">',
    'content' => $rows,
    '#suffix' => '</div>',
  ];

If we want better control over the attributes on each <article> element, we'll need to write a custom theme wrapper for it.

Processed Text

Introduced in Drupal 8.x:

$output = [
  '#type' => 'processed_text',
  '#text' => 'Hello world!',
  '#format' => 'filtered_text',
];

Plain Text

Introduced in Drupal 8.0.x.

$output = [
  '#plain_text' => 'Plain text example.',
];

Link

$output = [
  '#title' => $this->t('Examples'),
  '#type' => 'link',
  '#url' => \Drupal\Core\Url::fromRoute('examples.description'),
  '#options' => [
    'attributes' => [
      'class' => ['my-link-class'],
    ],
  ],
];

This is for Drupal 8+, see Link documentation. For Drupal 7, use #path instead.

Revision History:

  • Feb 12, 2013: Add section on nested arrays.
  • Sep 25, 2020: Convert examples to short array syntax.
  • Sep 20, 2024: Add text_format and plain_text examples.
  • Nov 1, 2024: Add link example.
Topics: