Building a Barebones Content Management System: The Yaapi API

This article will show you how to use the different API methods of yaapi, which is an API tool useful for managing content. It will explain how to retrieve a list of articles, display an article on a web page, retrieve a list of categories, and more.

Time To Catch Up

Welcome to the second part of my series on “Building a Barebones Content Management System.” For those who missed out earlier, here is a quick refresher of the topics covered in the first part. It started by listing the reasons why I initiated the exercise of developing a custom CMS: the basic need for dynamic updating of content, quirks and limitations of popular CMS software packages and so on. So, I decided to put together a simple and flexible CMS using a bunch of readily available APIs: yaapi, a useful API to manage content; patTemplate, a Templating engine; and patUser, a tool to manage users and permissions. It does not pay to re-invent the wheel, does it?

Today, I’ll continue where I left off in the previous article. I shall show how to use the different API methods of yaapi to retrieve a list of “articles”, display an “article” on a web page, retrieve a list of “categories” defined from the administration module and much more.

If the terms “articles” and “categories” sound like Greek and Latin, there’s no reason to despair. I’ll explain these concepts in the next section, which outlines how yaapi organizes content in the database. A clear understanding of these terms is essential to leverage fully on the capabilities of the API.

Let’s get started, shall we?

{mospagebreak title=Under The Hood}

As I may or may not have already mentioned, yaapi is an object-oriented API. It consists of a utility article() class that is equipped with a bunch of useful properties and utility methods — you’ve already seen its constructor in action in the first part — that strikes the right balance. While it is not so complicated that it intimidates a novice PHP programmer, it does provide enough functionality that it will not disappoint the experts.

Consider this: the article() class has a get_recent() method, which returns the last five items added to the database. I can conveniently invoke this method to render a “What’s New” section on my website without the need to resort to any custom programming.

The bottom line: the object-oriented approach of yaapi allows me to update the business logic encapsulated within its API methods. For example, if I want to update the aforementioned get_recent() method to return ten items instead of five, the default value, all I need to do is tweak the core PHP script containing the definition of the articles() class, and the front-end display is updated seamlessly.

Coming back to the introductory section (of this article): I spoke about “articles” and “categories”, the two entities at the core of the yaapi application. Let me provide some insight about them and why they play such a critical role as far as my barebones CMS is concerned.

An “article”, as the name suggests, is the most basic unit in yaapi (and most CMS software packages). Each article in yaapi is associated with a bunch of attributes; these include among other things the “title”, “author” (or “authoress”) and his/her “email address”, the “category” (more about this in a minute) and finally, we have the “content” that represents an article. I can also turn off the display of an article without having to delete it from the database, courtesy of an “approved” attribute.

Next, I demystify the concept of a “category.” In a yaapi context, this concept allows me to group “articles” logically. Let’s consider the example of the website on which you are reading this article — DevShed. You’ll notice that the content has been organized on the basis of different technologies. So, you have categories such as “PHP” for PHP-related articles, “Perl” for Perl-related articles, “Apache” for … you get the picture!

Yawning already, are you? Well, things begin to get interesting from here on as I demonstrate how different methods of the yaapi article() class can be used in PHP scripts. I promise.

{mospagebreak title=yaapi — Getting Started}

Last week, I created a category titled “Test.” Let’s rename it to something more meaningful, say “PHP” in keeping with my “DevShed” example, outlined in the previous section. Currently, I have one article (created in the previous article) in the database. Let me be practical; it is nonsensical to demonstrate how to display a list consisting of one article. It’s time to rectify this discrepancy; I will proceed to add some more test articles using the yaapi administration module.

One final bit before I dive into some PHP code. In the last article, I stated that “significant components of a web page are constant.” For example, you may have a header with a logo and a banner, a left hand menu of hyperlinks, footer with legal copyright notices and so on. For the reader’s benefit, I have put together a simple HTML outline that may help you visualize such a layout and at the same time, it shoud serve as a framework for my examples.

<HTML>
<HEAD>
<BASEFONT FACE=”Arial”>
</HEAD>
<BODY>
<TABLE WIDTH=”100%” CELLSPACING=”0″ CELLPADDING=”5″ ALIGN=”CENTER”
 VALIGN=”TOP” HEIGHT=”450″ BORDER=”1″>
<TR>
 <TD COLSPAN=”2″ WIDTH=”100%” ALIGN=”CENTER”>
  <P>&nbsp;</P>
  <H1>BB_CMS – A Barebones Content Management System</H1>
  <P>&nbsp;</P>
 </TD>
</TR>
<TR HEIGHT=”350″>
 <TD WIDTH=”25%” ALIGN=”MIDDLE” VALIGN=”TOP”>
  <P><A HREF=”#”>LINK 1</A></P>
  <P><A HREF=”#”>LINK 2</A></P>
  <P><A HREF=”#”>LINK 3</A></P>
<P><A HREF=”#”>.. and so on.</A></P>
</TD>
 <TD WIDTH=”50%” ALIGN=”LEFT” VALIGN=”TOP”> Content from database will
come here.</TD>
</TR>
<TR>
 <TD COLSPAN=”2″ WIDTH=”100%” ALIGN=”CENTER”>
  <H5><A HREF=”#”>Copyright</A> | <A HREF=”#”>Privacy Policy</A></H5>
  <BR CLEAR=”all” />
  <H6>&copy; 2005 BB_CMS Inc.</H6>
</TD>
</TR>
</TABLE>
</BODY>
</HTML>

Load this HTML template in your browser to view the following output:

I have content. I have an HTML layout. All that remains to be done is to pump in some dynamic content — using yaapi, obviously.

{mospagebreak title=yaapi — Listing Articles}

Review the following code listing (say “category.php”) where I’ve integrated yaapi with my HTML template to display a list of articles in a particular category.

<?php

// the include file
include_once(“./article.class.php”);

// initialize objects
$article = new article;

// The $id variable should always have a value
if(!isset($_GET["id"]) || $_GET["id"] == “”) {
 $category_id = $GLOBALS['DEFAULT_CATEGORY_ID']; // Set this to a default
category ID
} else {
 $category_id = $_GET["id"];
}

?>

<HTML>
<HEAD>
<BASEFONT FACE=”Arial”>
</HEAD>
<BODY>
<TABLE WIDTH=”100%” CELLSPACING=”0″ CELLPADDING=”5″ ALIGN=”CENTER”
VALIGN=”TOP” HEIGHT=”450″ BORDER=”1″>
<TR>
 <TD COLSPAN=”2″ WIDTH=”100%” ALIGN=”CENTER”>
  <P>&nbsp;</P>
  <H1>BB_CMS – A Barebones Content Management System</H1>
  <P>&nbsp;</P>
 </TD>
</TR>
<TR HEIGHT=”350″>
 <TD WIDTH=”25%” ALIGN=”MIDDLE” VALIGN=”TOP”>
  <P><A HREF=”#”>LINK 1</A></P>
  <P><A HREF=”#”>LINK 2</A></P>
  <P><A HREF=”#”>LINK 3</A></P>
  <P><A HREF=”#”>.. and so on.</A></P>
 </TD>
 <TD WIDTH=”50%” ALIGN=”LEFT” VALIGN=”TOP”>
  <P>&nbsp;</P>
  <UL>
  <?php

   // Get the articles for the selected category
   $articles = $article->get_titles($category_id);

   if(number_of_elements($articles) == 0) {
    $articles = $article->get_titles($GLOBALS['DEFAULT_CATEGORY_ID']);
   }


   // now, we are certain that the category will always have at least one
article
   for ($count = 0; $count < number_of_elements ($articles); $count++) {

     echo “<LI><P><A HREF=”article.php?id=”.$articles[$count]
['id'].””>”.$articles[$count]['title'].”</A></P></LI>”;

   }

  ?>
  </UL>
 </TD>
</TR>
<TR>
 <TD COLSPAN=”2″ WIDTH=”100%” ALIGN=”CENTER”>
  <H5><A HREF=”#”>Copyright</A> | <A HREF=”#”>Privacy Policy</A></H5>
  <BR CLEAR=”all” />
  <H6>&copy; 2005 BB_CMS Inc.</H6>
</TD>
</TR>
</TABLE>
</BODY>
</HTML>

I launch the above PHP script in the browser. Note that I pass an “id” parameter, which represents the unique ID created for each category assigned by yaapi, in the query string: http://localhost/yaapi/category.php?id=1.

If all goes well, I should be able to view a list of articles (that I added earlier) as seen in the output, below.

Now, let me quickly take you through the PHP script that has brought about this transformation.

<%

..// snip

// the include file
include_once(“./article.class.php”);

// snip

First, I’ve included the “article.class.php” file. As you have already seen, this PHP file encloses the definition of the all-important article() class.

<%

// snip

// initialize objects
$article = new article;

// The $id variable should always have a value
if(!isset($_GET["id"]) || $_GET["id"] == “”) {
 $category_id = $GLOBALS['DEFAULT_CATEGORY_ID']; // Set this to a default
category ID
} else {
 $category_id = $_GET["id"];
}

// snip

%>

Here, I create an instance of the yaapi article() object and store the category “id” from the query string in a local variable titled “$category_id”. I’ll let you in on a little secret, now: in my rush to get started, I created and deleted several categories. As a result, I did not have any category with an “id” value of 1.

Here, I could update my PHP scripts to display a polite error message that the category does not exist. While this works fine in a development environment, I do not expect visitors to return if they come across such messages once my website is up and running.

This can be easily avoided by the introduction of a default, fall-back category “id” in the configuration file — a category that I am certain will always contain an article. Take a peek at the updated configuration file:

<?php

/**
* Configuration file
*
*/

// snip

/**
 * yaapi constants
 */
$DEFAULT_CATEGORY_ID = 3;
$DEFAULT_ARTICLE_ID = 1;

// snip

?>

You’ll notice that I introduced a block titled “yaapi constants.” Here, I have defined two new global variables: “DEFAULT_CATEGORY_ID” and “DEFAULT_ARTICLE_ID”. The former represent a default category as described above and the latter represents an article that will always exist in the database. Now, let me show you how I’ve used this parameter in my PHP script.

There are two scenarios where I would need to use this “default” category. The first one, handled above, is when I do not pass a category id in the query string. The second scenario results when I pass a valid category “id” in the query string; however, there are no articles associated with this category in the database. This can be handled by the PHP code snippet listed below.

<%

 // snip

 <TD WIDTH=”50%” ALIGN=”LEFT” VALIGN=”TOP”>
  <?php

   // Get the articles for the selected category
   $articles = $article->get_titles($category_id);

   if(number_of_elements($articles) == 0) {
    $articles = $article->get_titles($GLOBALS['DEFAULT_CATEGORY_ID']);
   }


   // now, we are certain that the category will always have at least one
article
   for ($count = 0; $count < number_of_elements ($articles); $count++) {

     echo “<LI><P><A HREF=”article.php?id=”.$articles[$count]
['id'].””>”.$articles[$count]['title'].”</A></P></LI>”;

   }

  ?>
 </TD>

 // snip
   
%>

As seen above, I invoke the get_titles() method of the article() object. This returns an associative array that contains the “article_id” and “title” of the articles belonging to the category (whose id is stored in the “$category_id” variable).

I check the number of elements in my $article array with a quick call to the number_of_elements() function. The definition of this custom function can be found in the “inc.config.php” file.

<?php

//snip

/**
 * Utility function definitions
 */

// Accurately return the number of elements in an array
function number_of_elements($array) {

 // Initialize count variable to 0
 $count = 0;

 // Check if the element passed is a valid array
 if(is_array($array)) {
  while (list($key, $value) = each($array)) {
   if ($value) {
    $count++;
   }
  }
 }

 return $count;
}

// snip

?>

Coming back to my “category.php” script: I verify that there is at least one element in the associative array and iterate over the latter to display the title of each article (along with an appropriate hyperlink) on the web page, as seen in the output above.

Some of you might point out that the above code snippet can handle both of he scenarios (about an invalid category id in the query string) that I listed earlier. So, why double the effort?  The reason is simple: this two-step approach allows me to debug my PHP scripts better. In the future, I may wish to integrate an audit log mechanism and then, I can store appropriate messages in the log file.

{mospagebreak title=yaapi — Display An Article}

After having shown you how to list the articles in a category, I will now demonstrate how to display the contents of an article in the list. You may have noticed that each article title in the previous example was linked to a script titled “article.php” (with the “id” of the article passed in the query string). It’s time to review the PHP script.

<?php

// the include file
include_once(“./article.class.php”);

// initialize objects
$article = new article;

// The $id variable should always have a value
if(!isset($_GET["id"]) || $_GET["id"] == “”) {
 $article_id = $GLOBALS['DEFAULT_ARTICLE_ID']; // Set this to a default article ID
} else {
 $article_id = $_GET["id"];
}

// The $id variable should always have a value
if(!isset($_GET["current_page"]) || $_GET["current_page"] == “”) {
 $current_page = 1; // Set current page value to 1, if not set
}

?>

<HTML>
<HEAD>
<BASEFONT FACE=”Arial”>
</HEAD>
<BODY>
<TABLE WIDTH=”100%” CELLSPACING=”0″ CELLPADDING=”5″ ALIGN=”CENTER” VALIGN=”TOP” HEIGHT=”450″ BORDER=”1″>
<TR>
 <TD COLSPAN=”2″ WIDTH=”100%” ALIGN=”CENTER”>
  <P>&nbsp;</P>
  <H1>BB_CMS – A Barebones Content Management System</H1>
  <P>&nbsp;</P>
 </TD>
</TR>
<TR HEIGHT=”350″>
 <TD WIDTH=”25%” ALIGN=”MIDDLE” VALIGN=”TOP”>
  <P><A HREF=”#”>LINK 1</A></P>
  <P><A HREF=”#”>LINK 2</A></P>
  <P><A HREF=”#”>LINK 3</A></P>
  <P><A HREF=”#”>.. and so on.</A></P>
 </TD>
 <TD WIDTH=”50%” ALIGN=”LEFT” VALIGN=”TOP”>
  <P>&nbsp;</P>
  <UL>
  <?php

   // Get the intro for the article
   $current_article = $article->get_article($article_id);

   if(!isset($current_article) || !$current_article) {
    $current_article = $article->get_article($GLOBALS['DEFAULT_ARTICLE_ID']);
   }

   echo “<H2>$current_article->title</H2>”;
   echo “<H5>Author: <A HREF=”
mailto:$current_article->email”>$current_article->author</A>”;
   echo “<BR CLEAR=”all”>Date Published: $current_article->date</H5>”;
   echo “<P>$current_article->content</P>”;

  ?>
  </UL>
 </TD>
</TR>
<TR>
 <TD COLSPAN=”2″ WIDTH=”100%” ALIGN=”CENTER”>
  <H5><A HREF=”#”>Copyright</A> | <A HREF=”#”>Privacy Policy</A></H5>
  <BR CLEAR=”all” />
  <H6>&copy; 2005 BB_CMS Inc.</H6>
</TD>
</TR>
</TABLE>
</BODY>
</HTML>

Click on the title of any article listed by “category.php” script to view the following output:

Now, let me guide you through the contents of my second PHP script. 

<?php

// the include file
include_once(“./article.class.php”);

// initialize objects
$article = new article;

// The $id variable should always have a value
if(!isset($_GET["id"]) || $_GET["id"] == “”) {
 $article_id = $GLOBALS['DEFAULT_ARTICLE_ID']; // Set this to a default article ID
} else {
 $article_id = $_GET["id"];
}

// snip
?>

You’ll notice that the first few lines are similar to those in “category.php”: after including “article.class.php”, I proceed to create an instance of the article() class for use in this script. Next, I assign value to the “$article_id” local variables. If I do not find a value in the query string, I assign the default value stored in the configuration file, as explained in the previous section.

<%

// snip

<?php

// Get the intro for the article
$current_article = $article->get_article($article_id);

if(!isset($current_article) || !$current_article) {
  $current_article = $article->get_article($GLOBALS['DEFAULT_ARTICLE_ID']);
}

echo “<H2>$current_article->title</H2>”;
echo “<H5>Author: <A HREF=”
mailto:$current_article->email”>$current_article->author</A>”;
echo “<BR CLEAR=”all”>Date Published: $current_article->date</H5>”;
echo “<P>$current_article->content</P>”;

?>

// snip

%>

Now that I have an “id” for the article, I proceed to invoke get_article(). This aptly titled method returns the data associated with the article whose “id” is passed as the input to the method. I store the return value in another local variable, “$current_article” and use appropriate properties of the article() object to render the screen, as seen in the output.

For your reference, I have listed the properties of the article() object below:

  • title: the title of the article; you’ve already seen this property in action in the previous section.
  • author: the author (or authoress).
  • email: the email address entered in the database.
  • date: the data and time when the entry was inserted in the database.
  • content: the actual text for the article.

With this example behind me, I can safely conclude that I’ve now demonstrated two critical features that any practical CMS should provide: the ability to list the articles in the database, and then retrieve the actual content that constitutes an article.

{mospagebreak title=yaapi – Display List Of Categories}

One final bit before I drawn the curtains on this part of the series: I would like to update my application to display a list of categories in the left hand side menu, which was thus far populated with a bunch of static links. This is what I want it to look like:

Fortunately, yaapi comes to my rescue once again. This time it is the turn of the get_categories() method of the article() class that comes in handy. Since I need to display this menu on every page, it is only logical that I encapsulate the code that lists the categories in a separate function that can be invoked along the lines of my custom number_of_elements() function, listed earlier.

This is what my custom function, titled render_lhs_menu() and defined in the “inc.config.php” file, looks like:

<?php

// snip

// function to display the left hand side menu
function render_lhs_menu($article) {

 $return_value = “”;
 $categories = $article->get_categories();

 for ($count = 0; $count < count($categories); $count++) {

  $return_value .= “<P><A HREF=”category.php?id=”.$categories[$count]['id'].”” >”.$categories[$count]['name'].”</A></P>”;
 }

 return $return_value;
}

// snip

?>

There is no rocket science here. The function accepts an instance of the yaapi article() object as an input parameter and retrieves all the categories by calling the get_categories() method. As you may have guessed, this function returns an associative array containing the id and name of each category in the database. It concatenates the required HTML output as it iterates over the array and returns a string value to the calling script (say “article.php”), which in turn echoes it to the browser, as you can seen below.

<%

// snip

<TR HEIGHT=”350″>
 <TD WIDTH=”25%” ALIGN=”MIDDLE” VALIGN=”TOP”>
   <?php echo render_lhs_menu($article);?>
 </TD>
 <TD>
 
   // snip

 </TD>
</TR>

// snip

%>

Finally, I have managed to make all components of my web page dynamic — yes, I have excluded the footer section as it remains constant across most websites. You may argue that I could have defined another function to return the content for this footer section. Good point; I leave that as an exercise for you.

Conclusion

This brings me to the conclusion of the second part of the “Building a Barebones Content Management System” tutorial.

Let me quickly recap the topics that I covered today. Initially, I gave a detailed introduction to the different entities that drive yaapi. Next, I demonstrated how to retrieve a list of articles in a particular category as well as display the content of a selected article using the get_categories() and get_article() methods respectively. After that, I developed a custom function that returns the HTML code required to display the categories defined in yaapi. Finally, I have an application that fetches data from the database and renders it in a logical manner, as one expects from any program worthy of being called a CMS application.

In the next part, I shall introduce the patTemplate template engine. After demonstrating a few examples that show the versatility of this software API, I will integrate it with the PHP scripts listed today, thereby bringing me closer to fulfilling my goal of developing my barebones CMS.

Till then, enjoy!

[gp-comments width="770" linklove="off" ]

antalya escort bayan antalya escort bayan Antalya escort diyarbakir escort