« Querying a MySQL database with PHP - Random Quotes | Main | Custom Error Messages »

Random Entries Using PHP

There are two methods to create a random entry, pulled from your weblog database (if you aren't using dynamic publishing). The easiest is David Raynes' MTRandomEntries plugin. However, MTRandomEntries generates a random entry only when you rebuild the page on which the MTRandomEntry code is located. If you want a random entry to be generated each time the page is refreshed in a browser, you can do that with a PHP script that pulls the data from your MySQL database. For this method to work your blog needs to be PHP enabled and you need to be using a MySQL database.

This tutorial will outline variations of a PHP script you can use to generate random entries, similar to what can be seen in my Quotes blog. In the Quotes blog if you refresh the page in your browser a new quote will be displayed as the main entry. This tutorial builds on the material already covered in the LMT tutorial, Querying a MySQL database with PHP - Random Quotes. Please make sure you are comfortable with that tutorial before proceeding with this one.

In the previous tutorial, we created a random quotes script, and in so doing, covered how to connect to a MySQL database, how to create a basic MySQL query statement, how to query the database and display the results, and how to use a PHP include to include the script on to a web page. There are several differences between the simple script to generate random quotes and a random entry script. You may want your random entry to have a permalink to its individual archive page. If the entry has an extended entry section, this needs to be indicated. You might also want your random entry to include author and date posted information. The random entry may need to be formatted using style tags that are consistent with how you style your entries.

The trickiest part of the random entry script is generating the permalink URL. People use different ways to format their file names. (See File Names and URLs). The default URL file name structure in MT 3.15 is

http://www.yourblog.com/archives/yyyy/mm/short_entry_title.php

assuming that you are using .php file extensions. If you have selected the "Old-Style Archive Links" in your weblog config preferences, your file names look something like this:

http://www.yourblog.com/archives/000123.php
If you are like me, you customized your URL file names so that they are the actual entry titles, dirified - title words connected with an underscore like this:
http://www.yourblog.com/archives/longentrytitle_name.php

In this tutorial I will first describe a random entries script that uses the MT 3.15 default Archive URL type ( years, months, and a shortened, dirified entry title) for its permalinks, a "Posted by author", a date posted, and a link for an extended entry. I will then describe how you would change the permalink section of the script if you were using the Old style archive URL type or if you were using a dirified entry title. Finally I will explain how to pull entry excerpts from the database.

The Random Entries PHP Script, default archive type

The following is the script that we will be using throughout most of this tutorial.

Note that I have placed comments in various places in the script to help you better understand the script. In PHP, there are several methods you can use to comment your code. Placing a pound sign - # before your comment will allow you to make a comment on a new line, separate from the code. Placing a double slash - // - in front of your comment will allow you to place a comment on the same line as the code or on a separate line. Nesting your comment between a / and a / will let you extend your comments over several lines.

<?php

# Connect to database
require_once ('../mysql_connect.php');

# Enter the Archive URL from your Weblog Config and the blog ID number
$archive_url="http://www.your-blog.com/path/to/archives/";
$entry_blog_id="1";


$query = "SELECT entry_basename ,
entry_title,
entry_text,
entry_text_more,
date_format(entry_created_on, '%Y') AS year,
date_format(entry_created_on, '%m') AS month,
date_format(entry_created_on, '%M %d, %Y') AS readable_date,
entry_author_id,
author_id,
author_name
FROM mt_entry , mt_author
WHERE (entry_blog_id=$entry_blog_id) and (entry_status=2) and (author_id=entry_author_id)
ORDER by rand()
LIMIT 1";

$result = mysql_query($query);

while ($row = mysql_fetch_array($result)) {

$title = $row['entry_title'];
$entry = nl2br($row['entry_text']); // nl2br inserts HTML line breaks
$author = $row['author_name'];
$permalink_url=$archive_url . ($row['year']) . "/" . ($row['month']) . "/" . ($row['entry_basename']) . ".php";


# Displays the entry title
echo "<h3>$title</h3>\n";

# Displays the entry body
echo "<p>$entry</p>\n";

/* The following if statement checks to see if there is an extended entry.
If there is, it displays the Continued Reading link. */

if (strlen($row['entry_text_more']) >0)
echo "<p><a href=\"$permalink_url\">Continue Reading...</a>\n</p>";


# The following statement displays the "posted by" line

echo "<p class=\"posted\">", "Posted by " , $author , " on " , ($row['readable_date']) , " | " , "<a href=\"$permalink_url\">Permalink</a>" , "</p>";

}

mysql_free_result($result);

?>

1. Connect to database We covered the Connect to database section in the Querying a MySQL database with PHP - Random Quotes tutorial.

2. Archive URL

# Enter the Archive URL from your Weblog Config and the blog ID number
$archive_url="http://www.your-blog.com/path/to/archives/";
$entry_blog_id="1";

In this script, I've pulled to the top the variables that you need to customize for the script to work on your blog. First, enter your Archive URL in the place between the quotation marks. You can find your Archive URL from your blog's Weblog Config. Enter your blog ID. You can usually find your blog ID by looking at the browser URL when you are in the blog editing window. For example, this section might look like this:

$archive_url="http://www.elise.com/mt/archives/";
$entry_blog_id="5";

3. Create a MySQL Query Statement

$query = "SELECT entry_basename ,
entry_title,
entry_text,
entry_text_more,
date_format(entry_created_on, '%Y') AS year,
date_format(entry_created_on, '%m') AS month,
date_format(entry_created_on, '%M %d, %Y') AS readable_date,
entry_author_id,
author_id,
author_name
FROM mt_entry , mt_author
WHERE (entry_blog_id=$entry_blog_id) and (entry_status=2) and (author_id=entry_author_id)
ORDER by rand()
LIMIT 1";

$result = mysql_query($query);

This query statement is more complex than the one in the previous tutorial. In addition to entrytitle and entrytext, we are also pulling many other fields from the database. entrybasename is a shortened, dirified version of the entry title which Movable Type uses to generate a default individual archive file name. entrytextmore is the field that holds the extended entry text, if there is any. In

dateformat(entrycreatedon, '%Y') AS year, 
the function "dateformat" is applied to the "entrycreatedon" field with the instruction to return the year, formatted with 4 digits. AS year is the instruction to call this data by an alias name, "year". (See MySQL Manual.) The next two dateformat lines are similar. The year and month aliases will be used for the permalink URL and the readable_date alias will be used in the "Posted" line.

entryauthorid is from the mtentry table and authorid and authorname are fields from a separate table - the mtauthor table. FROM mtentry , mtauthor tells the query to pull the preceding fields from both the mtentry and mtauthor tables. WHERE is the conditional. The entryblogid is set to the entryblogid variable we declared near the top of the script. entrystatus=2 means the entry is published, not in draft mode. authorid=entryauthorid means that the query will only return the author ID from the long list of authors (including comment authors) in the mtauthor table that matches the entry's author ID. Thus the authorname pulled from the mt_author table will be the author name that matches the entry author ID.

As previously described, ORDER by rand() pulls the results in a random order and LIMIT 1 limits the results pulled to one entry at a time. $result = mysql_query($query); will define the variable $result as the initiation of the query that uses the parameters defined previously in the variable $query.

4. The While Loop - Displaying Results

The while loop in this script is much more involved than the script in the previous tutorial.

while ($row = mysql_fetch_array($result)) {

$title = $row['entry_title'];
$entry = nl2br($row['entry_text']); // nl2br inserts HTML line breaks
$author = $row['author_name'];
$permalink_url=$archive_url . ($row['year']) . "/" . ($row['month']) . "/" . ($row['entry_basename']) . ".php";

To make the echo statements easier to write, and easier to understand, I declare several variables up front. $row['entrytitle'] returns the entry title, so that the variable name for this one is simply $title. $row['entrytext'] returns the entry text. As previously noted, if you've set your Text Formatting to "Convert Line Breaks", MySQL doesn't store the break tags in the entry fields. By invoking the nl2br function, line breaks will be displayed appropriately. Thus $entry = nl2br($row['entry_text']). $author is obvious.

Default permalink URLs are in the format http://www.your-blog.com/archives/yyyy/mm/shortentrytitle.php (or .html).
We can achieve the same format by joining several strings of text together. This is called "concatenation" and is performed using the period ( . ). The $archiveurl declared at the beginning of the script is the beginning of the Permalink URL and should end with a slash. $row['year'] returns the year the entry was made in the correct 4 digit format; $row['month'] returns the month; $row['entrybasename'] returns the shortened, dirified entry title.

# Displays the entry title
echo "<h3>$title</h3>\n";

# Displays the entry body
echo "<p>$entry</p>\n";

/* The following if statement checks to see if there is an extended entry.
If there is, it displays the Continued Reading link. */

if (strlen($row['entry_text_more']) >0)
echo "<p><a href=\"$permalink_url\">Continue Reading...</a>\n</p>";

The first two echo statements are self explanatory. Note that if you wrap your entry titles in something other than h3 tags on the page on which you want the random entry displayed, you will want to make the change to the script code.

if (strlen($row['entrytextmore']) >0) uses the PHP function strlen to see if the number of characters in the extended entry field are greater than zero. (PHP manual). If the conditional is true, then an extended entry does exist and echo "<p><a href=\"$permalink_url\">Continue Reading...</a>\n</p>"; displays a hyperlink to the individual entry page. If the conditional is false, an extended entry does not exist and nothing happens.

# The following statement displays the "posted by" line

echo "<p class=\"posted\">", "Posted by " , $author , " on " , ($row['readable_date']) , " | " , "<a href=\"$permalink_url\">Permalink</a>" , "</p>";

}

The last lines of the while loop display the "Posted by" line in the following format:

Posted by author on July 4, 2005 | Permalink

In the default MT 3 templates, the paragraph tag before the "Posted by" section is <p class="posted">. To have a PHP script display this tag, you need to wrap the whole tag in double quotation marks and then "escape" the tag's internal double quotation marks with back slashes as shown. The same is done for the <a href tag.

Modifications to the script, "Old-style Archive Links"

The Old-style archive links use the entry ID number, instead of the date and entry basename, in the link. The format looks like this:

http://www.your-blog.com/archives/000123.php

The number in the URL is always six digits long, even though the actual ID number may only be a few digits long. The modifications you need to make to use this URL type are the following:

  1. In the $query section, after SELECT, remove entrybasename and replace it with entryid. Also remove dateformat(entrycreatedon, '%Y') AS year, and dateformat(entrycreatedon, '%m') AS month,.

  2. In a new line after while ($row = mysqlfetcharray($result)) { place the following variable statement:

    $padded=str_pad(($row['entry_id']), 6, "0", STR_PAD_LEFT);
    

    The PHP strpad function used this way will add zeros to the left of the entry ID number until there are six digits. (See the PHP manual for details about the strpad function.)

  3. Change the $permalink_url statement to:

    $permalink_url=$archive_url . $padded . ".php";
    

Modifications to the script, "Dirified Entry Title Links"

If you have customized your individual entry archive URL and are using the dirify function to write out your entry title in the URL, you will need to add a special dirify function to the PHP script in order to reproduce Movable Type's dirify function. Fortunately, Adam Kalsey has done the heavy lifting for us and has produced a PHP dirify function which you can get here.

  1. Add all 69 lines of Adam's dirify function (everything between the <?php and ?>) to right underneath the requireonce ('../mysqlconnect.php'); statement at the top of the script.

  2. In the $query section, after SELECT, remove entrybasename. Also remove dateformat(entrycreatedon, '%Y') AS year, and dateformat(entrycreated_on, '%m') AS month,.

  3. Change the $permalink_url statement to:

    $permalink_url=$archive_url . dirify($title) . ".php";
    

Creating Excerpts

If you would like your random entry script to show an excerpt of the entrytext, and not the whole entry body, you can do so by using the MySQL function substringindex. From the MySQL Manual:

SUBSTRING_INDEX(str,delim,count) Returns the substring from string str before count occurrences of the delimiter delim. If count is positive, everything to the left of the final delimiter (counting from the left) is returned. If count is negative, everything to the right of the final delimiter (counting from the right) is returned. mysql> SELECT SUBSTRING_INDEX('www.mysql.com', '.', 2); -> 'www.mysql' mysql> SELECT SUBSTRING_INDEX('www.mysql.com', '.', -2); -> 'mysql.com'
  1. In the $query section, after SELECT, add

    substring_index(entry_text, ' ', 40) AS excerpt,
    

    excerpt is now an alias for the first 40 words in your entry text. Change the word count if you want.

  2. To the variables listed after the beginning of the while loop, add the following:

    $excerpt=($row['excerpt']);
    
  3. Display the excerpt by changing echo "<p>$entry</p>\n"; to echo "<p>$excerpt</p>\n";

  4. If the entry is larger than your excerpt word length and you want to have a "Continue" link replace the if (strlen($row['entrytextmore']) >0) conditional with a conditional using the strwordcount function like so:

    if (str_word_count($row['entry_text']) >40)
    echo "<p><a href=\"$permalink_url\">Continue Reading...</a>\n</p>";
    

Final Notes

That's it. Please refer to the Querying a MySQL Database with PHP tutorial for instructions on how to apply the finishing touches to your script and for directions on how to include the script on to your web page.

I'm going to repeat what I said in the earlier tutorial. I am not an expert in PHP at all; I'm barely a beginner. Because my brain doesn't hold on to details that well - in one ear and out the other - I write down notes like these to remind myself the next time I want to tackle a PHP script. I welcome comments from those of you who understand PHP and MySQL who have a remark that would be beneficial to the understanding of those of us who are beginners. To those of you with specific questions about your script implementation, unfortunately, I can't help you here. I have found the MT Support Forums to be helpful regarding PHP scripts and their interaction with Movable Type, so I refer you to the forums.

Links: PHP Manual MySQL Manual PHP and MySQL for Dynamic Web Sites - very useful book for learning about PHP and MySQL LMT tutorial Converting to PHP Using PHP and MT Includes - LMT tutorial Querying a MySQL Database with PHP - LMT tutorial

Comments (5)

Thanks for writing this article. I was able to adapt it to get the 'long_entry_title_name.php' version to work by following the procedures for padding and dirify and using them together. I'm learning some php in the process. Thanks.

KO:

Will the above script work with MT 3.2 using basenames to generate the url structure?

Chip:

Thanks for the tutorial. I wonder if it's possible to customize the script to pull entries from one specific category only?

Chip:

Elise and Michael G. Schneider kindly and patiently helped me alter this script to pull a random entry from a specific category.

To do this, swap:
FROM mt_entry , mt_author
WHERE (entry_blog_id=$entry_blog_id) and (entry_status=2) and (author_id=entry_author_id)

with this:

FROM mt_entry , mt_author , mt_category, mt_placement
WHERE (entry_blog_id=$entry_blog_id) and (entry_status=2) and (author_id=entry_author_id) and
(placement_entry_id=entry_id) and (placement_category_id=category_id) and (category_label='Personal things')

Be sure to replace 'Personal things' with your own category, and make sure you use caps when required.

I'm a lot late in finding this. Excellent script - works well. Thanks.

One query - I since changed my individual Archive File Path (to primary_category/entry_basename/index.php) and can't figure out how to alter the script so the link still works.

Advice appreciated, please and thanks.

Post a comment

(If you haven't left a comment here before, you may need to be approved by the site owner before your comment will appear. Until then, it won't appear on the entry. Thanks for waiting.)