menu_book Navigation menu

Tutorial: Building Your First Site

To understand the core philosophy of the system, we will build a simple "Project" section.

In Pragma CMS, data, routing, and presentation are strictly decoupled. Building a page always follows this logical workflow:

  1. Model: Define the structure (Content Types).
  2. Data: Write the content and upload files (Entries & Media).
  3. Route: Assign a URL (Pages).
  4. View: Display the data (Controllers & Templates).

Step 1: Defining the Model (Content Types)

Before writing articles, we need to define their structure.

Go to Administration > Content Types and create a new type called "Project".

Content Types act as the blueprint for your data.

Since every project is different, we want structured data. Add these fields:

  • Text: Client Name
  • Media: Featured Image
  • Builder: Case Study Content

By defining explicit fields, you ensure your content editors always output perfectly structured data.

Step 2: Adding Data (Entries & Media)

Now that your blueprint exists, go to Content > Articles and create your first Entry.

Entries are the actual pieces of content. Fill in your title, text, and upload an image. Pragma CMS handles revisions natively, allowing you to draft, preview, and restore previous versions of your content effortlessly.

Managing Files (Media)

When a file is uploaded, its original version is stored in the media/source folder to preserve an unaltered copy.

For images, the CMS then automatically generates optimized variants (compressed jpgwebp, and avif) in various dimensions (thumbnailmediumlarge, xlarge and social).

Public versions of media files are then made available in their respective folders (media/images, media/documents, or media/videos). For images, these are generated variants, while for documents and videos, this corresponds to a direct copy of the original file.

lightbulb
Best Practice
To ensure optimal quality and web performance, we recommend uploading images with a width between 1920px and 2560px maximum. Larger files can slow down server processing and produce visual artifacts during compression or resizing.

Step 3: Setting up the Route (Pages)

Now, we need to make these projects visible at /portfolio.

  1. Go to Pages > Create New Page.
  2. Type: Choose Dynamic Page. (Refer to the Page Creation documentation for details on Static vs Dynamic vs Builder).
  3. Route: Set it to /portfolio.
  4. Content Type: Select Project.

Step 4: Rendering the View (Controllers & Templates)

The final step is to display the data on the front-end. To do this, we use a Page Controller and a Template.

info
Pragma CMS natively integrates Twig for secure and elegant templating. You can also use raw PHP files, but you must be vigilant with security by escaping variables manually (e.g., using htmlspecialchars()).

Preparing Data (Controller Layer)

The global $page object is automatically populated by the router (router.php). You just need to prepare the variables for your view.PHP Method Example (Blog List Route):

PHP
// Retrieve SEO and Page data
$page->viewData["title"] = $pageData["title"];
$page->metaTitle = $pageData["meta_title"];
$page->metaDescription = $pageData["meta_description"];

// Inject the Entry data (Fields)
$page->entry = $entry; 

// Build layout shell
$page->header = renderHeader($page);
$page->footer = renderFooter($page);

// Render the specific view into the main layout
$page->main = render_template("blog/list.php", $page);

// Output the final base layout
echo render_template("base.php", $page);

The Template Layer (HTML Output)

Templates are strictly used for display. Use the render_template() function to locate and load them.

PHP
<section id="intro">   
    <div class="container">          
        <h1><?= htmlspecialchars($page->entry["fields"]["hero-title"]); ?></h1>
        <p><?= htmlspecialchars($page->entry["fields"]["hero-subtitle"]); ?></p>
    </div>
    
    <div id="hero-cover">
        <!-- The render_image() helper generates a full <picture> tag with WebP/AVIF support -->
        <?php render_image($page->entry["fields"]["hero-image"]["id"], 'large'); ?>
    </div>
</section>

Using Twig (blog/list.twig):

If using Twig, the controller logic remains the same (render_template('blog/list.twig', ['page' => $page])), but the view is rendered like this:

TEXT
{{ BreadcrumbManager.renderBreadcrumbs() }}

<h1>{{ page.viewData.title|e }}</h1>

<div class="hero-cover">
    {{ renderImage(page.entry.fields['hero-image'].id, 'large') }}
</div>

<ul>
{% for entry in page.entries %}
  <li><a href="{{ entry.url|e }}">{{ entry.title|e }}</a></li>
{% endfor %}
</ul>

(Note: In Twig, the |e or |escape filter is highly recommended to secure HTML output).

Templates Location

To render a template, use render_template().

If you need to render a template from an extension, prefix it with the extension name (e.g., my-extension/blog/list.php).

The find_template_file() core function will automatically locate the file. The global HTML structure is defined in base.php and should wrap all page renders.

Images Rendering

To render a fully optimized responsive image, use:

render_image(int $mediaId, string $sizeSuffix = 'large', array $attributes = [])

This core function outputs an HTML <picture> tag, automatically serving modern formats (AVIF, WebP) with the correct srcset when available.