dot CMS

Rendering dotCMS Pages with Pure PHP

Rendering dotCMS Pages with Pure PHP
Author image

Freddy Montes

Product Manager

Share this article on:

This guide provides a step-by-step approach to rendering dotCMS pages using pure PHP. We'll focus on retrieving data from the Page API, understanding the layout structure, and correctly implementing CSS grids to display content in its intended format.

Prerequisites

  1. A PHP environment (PHP 8 or higher).

  2. Access to a dotCMS server with an API token.

  3. Basic knowledge of HTML, CSS, and PHP.

Project Folder Structure

Organize your project with the following structure:

.

├── .env               # Environment variables (API token)

├── index.php          # Main PHP file to render the page

├── style.css          # CSS for grid and layout

└── README.md          # Documentation (optional)

Get Your API Token

Before starting, you need to generate an API token from dotCMS. Follow the instructions provided in the dotCMS API Authentication Documentation.

Note: If you don’t have a dotCMS instance, you can use our https://demo.dotcms.com/c and login with admin@dotcms.com and admin. Take into account that this instance reboot every 24h and that is open to anyone.

Store the token securely in your .env file:

DOTCMS_API_TOKEN=your_api_token_here

Step 1: Rendering the Page Title

The first step is to fetch the page data from the dotCMS API and display the page title.

Fetching Page Data

Use the Page API to retrieve the JSON representation of the page:

<?php

// Load environment variables

$envFile = DIR . '/.env';

$_ENV = file_exists($envFile) ? parse_ini_file($envFile) : [];




// Fetch page data

try {

    $token = $_ENV['DOTCMS_API_TOKEN'] ?? throw new RuntimeException('API token missing');

    $url = 'https://demo.dotcms.com/api/v1/page/json/index';




    $context = stream_context_create([

        'http' => [

            'header' => "Authorization: Bearer $token",

            'method' => 'GET',

            'ignore_errors' => true,

        ]

    ]);




    $response = file_get_contents($url, false, $context);

    $data = json_decode($response, true, JSON_THROW_ON_ERROR)['entity'];




    $pageTitle = $data['page']['friendlyName'];

} catch (Throwable $e) {

    die("Error loading page: " . $e->getMessage());

}

?>

<!DOCTYPE html>

<html lang="en">

<head>

    <meta charset="UTF-8">

    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    <link rel="stylesheet" href="style.css">

    <title><?= htmlspecialchars($pageTitle, ENT_QUOTES) ?></title>

</head>

<body>

    <header>

        <h1><?= htmlspecialchars($pageTitle, ENT_QUOTES) ?></h1>

    </header>

    <main>

        <p>Your dotCMS page is being set up!</p>

    </main>

</body>

</html>

The pageTitle variable is extracted from the page object returned by the API. It contains the title of the page, which we display in both the <title> tag and the header.

Running the Project Locally

You can run your PHP project locally using the PHP development server included with PHP 8. This is a lightweight and easy way to serve your application during development.

To start the server, run the following command from the root of your project:

php -S localhost:8000

This will start a development server accessible at http://localhost:8000. Open this URL in your browser to view your rendered dotCMS page.

Note: The PHP development server is intended for local testing and development only. Do not use it in a production environment.

Understanding the dotCMS Layout System

The dotCMS layout system is based on a hierarchy that defines how content is organized and displayed on a page. This system ensures flexibility while allowing developers to control the appearance and functionality of pages.

Overview of the Page API Response

The Page API response entity contains three key parts:

{

  "page": {},

  "containers": {},

  "layout": {

    "body": {},

    ...

  },

  ...

}

There is more page information in the API response, but for rendering a basic page we can focus on these three.

  1. Page Properties (page): Contains metadata about the page, including its title, description, and SEO-related information. Example:

{

  "page": {

    "friendlyName": "Home Page",

    "description": "Welcome to the home page",

    "title": "Home - dotCMS",

    "seo": {

      "keywords": "dotCMS, CMS, home"

    }

  }

}

This information is used for setting up the <title> tag and other metadata on the page.

  1. Layout Information (layout.body): Defines the structural layout of the page, including rows and columns. Each row can have multiple columns, and columns define their grid placement and size. Also include a reference to the container located in a specific column. Example:

{

  "layout": {

    "body": {

      "rows": [

        {

          "columns": [

            {

              "leftOffset": 1,

              "width": 6,

              "containers": [

                { "identifier": "container-id", "uuid": "container-uuid" }

              ]

            }

          ]

        }

      ]

    }

  }

}

The leftOffset and width determine the position and span of each column in the grid.

  1. Containers and Contentlets (containers): Holds the actual content blocks associated with each container. Each container is linked to “contentlets,”  our name for the units of content displayed on the page. Example:

{

  "containers": {

    "container-id": {

      "contentlets": {

        "uuid-container-uuid": [

          {

            "contentType": "Banner",

            "title": "Welcome",

            "description": "Welcome to our site!",

            "image": "/path/to/image.jpg"

          }

        ]

      }

    }

  }

}

This section contains both metadata about the containers and the actual contentlets, which are rendered as content on the page.

This section is where you will look up the containers using the reference in the layout property for each column.

Key Components of the Layout System

  1. Rows: Represent horizontal sections of a page. Rows are the primary structures for organizing content.

  2. Columns: Define vertical slices within a row. Columns determine the width of content using a 12-column grid system.

  3. Containers: Sit inside columns and hold content blocks, also known as contentlets.

  4. Contentlets: The smallest units of content, created based on the schema defined in the content types, example: Blog, Product, Feature.

Grid System

The layout system uses a 12-column grid. Each column spans a portion of the grid based on its width. For example, a column with a width of 6 spans half the grid.

Key properties in the API response, as referenced in the layout info above:

  • leftOffset: Determines where the column starts in the grid.

  • width: Specifies how many grid columns the column spans.

  • containers: References to containers within the column.

Example

Consider a page with one row, containing a single column that spans half the width of the page:

{

  "leftOffset": 1,

  "width": 6,

  "containers": [

    { "identifier": "container-id", "uuid": "container-uuid" }

  ]

}

This translates to the following CSS:

.col-start-1 {

    grid-column-start: 1;

}

.col-end-7 {

    grid-column-end: 7; /* Starts at 1 and spans 6 columns */

}

By combining rows, columns, and containers, you can build highly customizable layouts with easily generable styles.

Step 2: Rendering Rows

Rows are the top-level structures for organizing content on the page. Each row can have multiple columns.

Understanding the Layout

The layout.body.rows property from the API response contains an array of rows. Each row has an array of columns. Here’s how to iterate through rows:

<main>

<?php foreach ($data['layout']['body']['rows'] as $row): ?>

    <div class="row">

        <!-- Columns will go here -->

    </div>

<?php endforeach; ?>

</main>

Adding CSS for Rows

.row {

    display: grid;

    grid-template-columns: repeat(12, 1fr);

    gap: 1rem;

}

The .row class sets up a CSS grid with 12 equal-width columns.

Step 3: Rendering Columns

Each row contains one or more columns. Columns specify their width, starting position, and any containers they house.

Adding Columns to Rows

<?php foreach ($row['columns'] as $column): ?>

    <?php

    $start = $column['leftOffset'];

    $end = $start + $column['width'];

    ?>

    <div class="col-start-<?= $start ?> col-end-<?= $end ?>">

        <!-- Containers will go here -->

    </div>

<?php endforeach; ?>

CSS for Columns

.col-start-1 { grid-column-start: 1; }

.col-end-12 { grid-column-end: 12; }

Columns use classes like col-start-{N} and col-end-{N} to define their grid placement. For example, col-start-1 starts at the first column, and col-end-6 spans up to the sixth column.

Step 4: Rendering Containers

Containers hold the actual content blocks (contentlets) of the page. Each column can contain multiple containers.

Adding Containers to Columns

<?php foreach ($column['containers'] as $container): ?>

    <div data-dot-object="container" data-dot-uuid="<?= $container['uuid'] ?>">

        <!-- Contentlets will go here -->

    </div>

<?php endforeach; ?>

Containers are identified by their uuid and identifier, which are used to locate the contentlets within.

Step 5: Rendering Contentlets

Contentlets are the smallest units of content, such as text blocks, images, or widgets. These are stored in the containers property of the API response.

Adding Contentlets to Containers

<?php

$contentlets = $data['containers'][$container['identifier']]['contentlets']['uuid-' . $container['uuid']] ?? [];

foreach ($contentlets as $contentlet):

?>

    <?= renderContentlet($contentlet); ?>

<?php endforeach; ?>

Mapping Contentlets by Type

Each contentlet is rendered based on its contentType. Define a function to handle different content types:

function renderContentlet(array $contentlet): string {

    $contentType = $contentlet['contentType'] ?? '';



    switch ($contentType) {

        case 'Banner':

            return sprintf(

                '<article><img src="%s" alt="%s"><h2>%s</h2><p>%s</p></article>',

                htmlspecialchars($contentlet['image'] ?? '', ENT_QUOTES),

                htmlspecialchars($contentlet['title'] ?? '', ENT_QUOTES),

                htmlspecialchars($contentlet['title'] ?? '', ENT_QUOTES),

                htmlspecialchars($contentlet['description'] ?? '', ENT_QUOTES)

            );

        case 'Product':

            return sprintf(

                '<article><h3>%s</h3><p>Price: %s</p><a href="%s">Buy Now</a></article>',

                htmlspecialchars($contentlet['title'] ?? '', ENT_QUOTES),

                htmlspecialchars($contentlet['price'] ?? '', ENT_QUOTES),

                htmlspecialchars($contentlet['url'] ?? '', ENT_QUOTES)

            );

        default:

            return sprintf(

                '<article><h2>%s</h2><p>%s</p></article>',

                htmlspecialchars($contentlet['title'] ?? '', ENT_QUOTES),

                htmlspecialchars($contentlet['description'] ?? '', ENT_QUOTES)

            );

    }

}
  • Banner: Includes an image, title, and description.

  • Product: Displays a title, price, and link.

  • Default: Renders generic content with a title and description.

This approach ensures that each contentlet is styled and displayed according to its type.

Step 6: Grid System Explanation

The CSS grid system divides the page into 12 equal-width columns. This allows for flexible layouts:

  • Rows: Define horizontal sections of the page.

  • Columns: Specify the width and position within a row using grid-column-start and grid-column-end.

  • Containers: Sit inside columns and hold contentlets. Containers do not have inherent spatial properties.

Example:

.row {

    display: grid;

    grid-template-columns: repeat(12, 1fr);

    gap: 1rem;

}

.col-start-1 { grid-column-start: 1; }

.col-end-4 { grid-column-end: 4; }

This setup ensures that content aligns consistently across the page.

Conclusion

By following this step-by-step guide, you can render dotCMS pages dynamically using PHP. The grid system ensures a responsive layout, while the API provides all necessary data to populate your page. 

While a production-ready web application would require additional considerations — such as error management, routing, and integration with frameworks like Laravel — the purpose of this guide is to explain the concepts of rendering dotCMS pages and demonstrate how easy it is to achieve with any technology, because the Universal Visual Editor is tech agnostic.