Skip to content

Instantly share code, notes, and snippets.

@bergie
Created January 15, 2011 11:08
Show Gist options
  • Save bergie/780841 to your computer and use it in GitHub Desktop.
Save bergie/780841 to your computer and use it in GitHub Desktop.
Blog post in Midgard MVC

A blog in Midgard Create

This is an example of how to create a blog system powered by the Midgard Create web editing tool. This example follows the principles of literate programming, meaning that it is not only a human-readable document but can also be used to construct the full running blog system using the code examples inside this file.

As this blog system is an example application, we will call it net_example_blog. This will be used as a namespace in PHP classes and global variables.

Our blog post

To be able to store and display blog posts we have to first define a persistent storage for them. Midgard MVC itself is not in way tied to any storage solution, but in this case we will use the Midgard2 content repository for our data persistence needs. Midgard2 is an object-oriented content store that can be accessed from various programming languages including PHP, Python and Vala.

With Midgard2, the content we want to store must be defined in MgdSchema XML files. In the MgdSchema we will describe a class of persistent object, and its properties.

Let us start with the title of a post. In this case we connect the title also to a RDF definition so that it can be understood by search engines and other semantic tools. The RDF definition we use for titles comes from Dublin Core:

<property name="title" type="string">
    <description>Title of a blog post</description>
    <property>dcterms:title</property>
</property>

Then we need to define a holder for the actual post contents. In addition to the regular field definition, we'll also specify that this property holds HTML-formatted contents. We will connect it to the definition of Content from the SIOC specification:

<property name="content" type="text">
    <description>Content of the post</description>
    <contenttype>html</contenttype>
    <property>sioc:content</property>
</property>

Now that we have the actual properties of a post defined, we can define the actual blog post type. We will give it a class name, and also the database table where the posts should be stored:

<?xml version="1.0" encoding="UTF-8"?>
<Schema xmlns="http://www.midgard-project.org/repligard/1.4">
    <type name="net_example_blog_post" table="net_example_blog_post">

As the properties contain RDF mappings, we will have to provide a list of namespaces defining them here. We also specify a RDF typeOf mapping explaining that our class follows the SIOC description of a Post:

        <user_values>
            <typeof>http://rdfs.org/sioc/ns#Post</typeof>
            <namespaces>sioc:http://rdfs.org/sioc/ns#,dcterms:http://purl.org/dc/terms/</namespaces>
        </user_values>

Then we include our properties and a mandatory database ID, and we're done:

        <property name="id" type="unsigned integer" primaryfield="id">
            <description>Local non-replication-safe database identifier</description>
        </property>
        <<blog post properties>>
    </type>
</Schema>

Displaying a list of blog entries

The most important thing in a blog is the list of new posts. The posts are displayed in a time-based order with latest entries shown first.

In order to show our listing of posts on the front page we need to register a Route. Routes are a way to connect a URL request sent by a browser to some PHP code on the server that performs the desired operation and displays the results. Route definitions are specified in the YAML format.

First we give a name to the route, and define the path this route handles:

index:
    path: '/'

Then we need to provide the name of the controller class, and the action method in that class used to fetch the latest posts:

    controller: net_example_blog_controllers_latest
    action: items

The controller action code is reasonably simple PHP. First we generate a query for fetching latest items:

$qb = new midgard_query_builder('net_example_blog_post');
$qb->add_order('metadata_created', 'DESC');

We want to be able to control the amount of items shown on the front page, and to make this easy to change by configuration later. For this we introduce a new configuration key:

index_items: 6

Now we can use this configuration key to limit the amount of items we query:

$qb->set_limit(midgardmvc_core::get_instance()->configuration->index_items);

Then we need to execute our query, and set the blog items to a list accessible in the display phase:

$items = $qb->execute();
$this->data['items'] = new midgardmvc_ui_create_container();
foreach ($items as $item)
{
    $this->data['items']->attach($item);
}

The query method defined above needs to be written into a controller class in order to run:

<?php
class net_example_blog_controllers_latest
{
    public function __construct(midgardmvc_core_request $request)
    {
        $this->request = $request;
    }

    public function get_items(array $args)
    {
        <<net_example_blog_controllers_latest::get_items>>
    }
}

The next question then is how to display the news items. For this we define a display template. The display template ought to be registered into the route that we have, through template aliasing. With this alias we tell that template alias called content should contain the contents of our template called neb-show-latest:

    template_aliases:
        content: neb-show-latest

To make the template work we need to write it to file matching the template alias set earlier:

<<list of blog items>>

The actual template is defined in HTML using the TAL templating system. TAL enables us to include content set to the data array of our controller into the generated HTML. To traverse the news items we do the following TAL. First we have a list container, with some RDFa mark-up to explain to the browser that this is a list of items:

<ol mgd:type="container" mgd:order="desc">

Then we can define how an individual item ought to be displayed, and tell TAL to repeat this definition for each item our controller returned. For templates, the data array of the controller is accessible as a variable matching the name of our component, net_example_blog:

    <li tal:repeat="item net_example_blog/items">

Now we're ready to display an individual blog item. In this case we will wrap each item within a HTML5 article element for clarity. The item will include RDFa attributes identifying and explaining the data within. These attributes can be hard-coded into the template, or we can use the rdfmapper tool provided by the framework, as we do in this example:

        <article tal:attributes="about item/rdfmapper/about; typeof item/rdfmapper/typeof">
            <h1 tal:content="item/title" tal:attributes="property item/rdfmapper/title">Title of a news item</h1>
            <div tal:content="item/content" tal:attributes="property item/rdfmapper/content">
                <p>
                    Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. Nam liber tempor cum soluta nobis eleifend option congue nihil imperdiet doming id quod mazim placerat facer possim assum.
                </p>
            </div>
        </article>

Then we just wrap up the template by closing the list:

    </li>
</ol>

Component manifest

To make our blog installable we need to provide a manifest file explaining the dependencies and routes of the component.

routes:
    <<blog routes>>
  • [RDF]: Resource Description Framework, a way to specify semantic content as subject-predicate-object triples
  • [SIOC]: Semantically-Interlinked Online Communities
A blog in Midgard Create
========================
This is an example of how to create a blog system powered by the Midgard Create web editing tool. This example follows the principles of [literate programming](http://en.wikipedia.org/wiki/Literate_programming), meaning that it is not only a human-readable document but can also be used to construct the full running blog system using the code examples inside this file.
As this blog system is an example application, we will call it `net_example_blog`. This will be used as a namespace in PHP classes and global variables.
## Our blog post
To be able to store and display blog posts we have to first define a persistent storage for them. Midgard MVC itself is not in way tied to any storage solution, but in this case we will use the Midgard2 content repository for our data persistence needs. Midgard2 is an object-oriented content store that can be accessed from various programming languages including PHP, Python and Vala.
With Midgard2, the content we want to store must be defined in MgdSchema XML files. In the MgdSchema we will describe a class of persistent object, and its properties.
Let us start with the title of a post. In this case we connect the title also to a RDF definition so that it can be understood by search engines and other semantic tools. The RDF definition we use for titles comes from Dublin Core:
<<blog post properties>>=
<property name="title" type="string">
<description>Title of a blog post</description>
<property>dcterms:title</property>
</property>
@
Then we need to define a holder for the actual post contents. In addition to the regular field definition, we'll also specify that this property holds HTML-formatted contents. We will connect it to the definition of Content from the SIOC specification:
<<blog post properties>>=
<property name="content" type="text">
<description>Content of the post</description>
<contenttype>html</contenttype>
<property>sioc:content</property>
</property>
@
Now that we have the actual properties of a post defined, we can define the actual blog post type. We will give it a class name, and also the database table where the posts should be stored:
<<models/post.xml>>=
<?xml version="1.0" encoding="UTF-8"?>
<Schema xmlns="http://www.midgard-project.org/repligard/1.4">
<type name="net_example_blog_post" table="net_example_blog_post">
@
As the properties contain RDF mappings, we will have to provide a list of namespaces defining them here. We also specify a RDF typeOf mapping explaining that our class follows the SIOC description of a Post:
<<models/post.xml>>=
<user_values>
<typeof>http://rdfs.org/sioc/ns#Post</typeof>
<namespaces>sioc:http://rdfs.org/sioc/ns#,dcterms:http://purl.org/dc/terms/</namespaces>
</user_values>
@
Then we include our properties and a mandatory database ID, and we're done:
<<models/post.xml>>=
<property name="id" type="unsigned integer" primaryfield="id">
<description>Local non-replication-safe database identifier</description>
</property>
<<blog post properties>>
</type>
</Schema>
@
## Displaying a list of blog entries
The most important thing in a blog is the list of new posts. The posts are displayed in a time-based order with latest entries shown first.
In order to show our listing of posts on the front page we need to register a Route. Routes are a way to connect a URL request sent by a browser to some PHP code on the server that performs the desired operation and displays the results. Route definitions are specified in the YAML format.
First we give a name to the route, and define the path this route handles:
<<blog routes>>=
index:
path: '/'
@
Then we need to provide the name of the controller class, and the action method in that class used to fetch the latest posts:
<<blog routes>>=
controller: net_example_blog_controllers_latest
action: items
@
The controller action code is reasonably simple PHP. First we generate a query for fetching latest items:
<<net_example_blog_controllers_latest::get_items>>=
$qb = new midgard_query_builder('net_example_blog_post');
$qb->add_order('metadata_created', 'DESC');
@
We want to be able to control the amount of items shown on the front page, and to make this easy to change by configuration later. For this we introduce a new configuration key:
<<configuration/defaults.yml>>=
index_items: 6
@
Now we can use this configuration key to limit the amount of items we query:
<<net_example_blog_controllers_latest::get_items>>=
$qb->set_limit(midgardmvc_core::get_instance()->configuration->index_items);
@
Then we need to execute our query, and set the blog items to a list accessible in the display phase:
<<net_example_blog_controllers_latest::get_items>>=
$items = $qb->execute();
$this->data['items'] = new midgardmvc_ui_create_container();
foreach ($items as $item)
{
$this->data['items']->attach($item);
}
@
The query method defined above needs to be written into a controller class in order to run:
<<controllers/latest.php>>=
<?php
class net_example_blog_controllers_latest
{
public function __construct(midgardmvc_core_request $request)
{
$this->request = $request;
}
public function get_items(array $args)
{
<<net_example_blog_controllers_latest::get_items>>
}
}
@
The next question then is how to display the news items. For this we define a display template. The display template ought to be registered into the route that we have, through template aliasing. With this alias we tell that template alias called `content` should contain the contents of our template called `neb-show-latest`:
<<blog routes>>=
template_aliases:
content: neb-show-latest
@
To make the template work we need to write it to file matching the template alias set earlier:
<<templates/neb-show-latest.xhtml>>=
<<list of blog items>>
@
The actual template is defined in HTML using the TAL templating system. TAL enables us to include content set to the `data` array of our controller into the generated HTML. To traverse the news items we do the following TAL. First we have a list container, with some RDFa mark-up to explain to the browser that this is a list of items:
<<list of blog items>>=
<ol mgd:type="container" mgd:order="desc">
@
Then we can define how an individual item ought to be displayed, and tell TAL to repeat this definition for each item our controller returned. For templates, the `data` array of the controller is accessible as a variable matching the name of our component, `net_example_blog`:
<<list of blog items>>=
<li tal:repeat="item net_example_blog/items">
@
Now we're ready to display an individual blog item. In this case we will wrap each item within a HTML5 `article` element for clarity. The item will include RDFa attributes identifying and explaining the data within. These attributes can be hard-coded into the template, or we can use the `rdfmapper` tool provided by the framework, as we do in this example:
<<list of blog items>>=
<article tal:attributes="about item/rdfmapper/about; typeof item/rdfmapper/typeof">
<h1 tal:content="item/title" tal:attributes="property item/rdfmapper/title">Title of a news item</h1>
<div tal:content="item/content" tal:attributes="property item/rdfmapper/content">
<p>
Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. Nam liber tempor cum soluta nobis eleifend option congue nihil imperdiet doming id quod mazim placerat facer possim assum.
</p>
</div>
</article>
@
Then we just wrap up the template by closing the list:
<<list of blog items>>=
</li>
</ol>
@
## Component manifest
To make our blog installable we need to provide a manifest file explaining the dependencies and routes of the component.
<<manifest.yml>>=
routes:
<<blog routes>>
@
* [RDF]: Resource Description Framework, a way to specify semantic content as subject-predicate-object triples
* [SIOC]: Semantically-Interlinked Online Communities
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment