Integrate Blogger content with PHP applications using the Blogger Data API

Use PHP to retrieve and add blog posts to Blogger through the sophisticated Blogger Data API

Blogger is a free, popular blogging platform that lets users create their own weblogs, and post messages and news, through a WYSIWYG editing interface. With the Blogger Data API, which is REST-based, you can access Blogger content through any XML-compliant programming toolkit. In this article, learn how to use the Blogger Data API to list blog content, add or edit blog posts, and manipulate blog comments with the Zend Framework's Zend_Gdata component. You'll build a simple application that demonstrates the power of the Blogger API.

Vikram Vaswani, Founder, Melonfire

Photo of Vikram VaswaniVikram Vaswani is the founder and CEO of Melonfire, a consulting services firm with special expertise in open-source tools and technologies. He is also the author of the books Zend Framework: A Beginners Guide and PHP: A Beginners Guide.



04 October 2011

Also available in Russian Japanese Spanish

Introduction

You might have heard of Blogger, a free blogging platform that lets you easily create personal weblogs and publish posts on any topic. Blogger, which has been around since 1999, was one of the first blogging platforms on the web. It was purchased by Google and integrated into Google's suite of free services.

Popular blogger applications

While Blogger is a popular option for blog platforms, many other options exist. For one, IBM Connections software includes integrated blog capabilities. Other popular blog software options include Wordpress, Drupal, and Typepad, among many others.

With Blogger, it's easy for people without specific technical knowledge to create and maintain their own weblog. Blogger:

  • Comes with several predefined templates and layouts
  • Supports comments, access controls, and mobile devices
  • Is tightly integrated with other services (such as Twitter, Google AdSense, and others) for content sharing and monetization.

In a nutshell, Blogger provides everything you need to publish your blog without having to worry about the technical details.

Frequently used acronyms

  • API: Application programming interface
  • HTML: Hypertext Markup Language
  • HTTP: Hypertext Transfer Protocol
  • REST: Representational State Transfer
  • RSS: Really Simple Syndication
  • URL: User interface
  • WYSISYG: What You See Is What You Get
  • XML: Extensible Markup Language

But Blogger isn't just interesting to users. Like many other Google services, Blogger has a developer API that lets you get your hands on the innards of the service and extract content to create your own custom applications and mashups. You can access the Blogger Data API, which follows the REST model, through any XML-capable development toolkit. It already has client libraries for many common programming languages, including my favorite: PHP.

In this article, learn about the Blogger Data API, and how to integrate and use Blogger content with PHP applications. Many examples will guide you through:

  • Connecting and authenticating to the API
  • Retrieving blog lists and blog feeds
  • Adding, updating and deleting content
  • Working with blog comments

Key concepts

Before you get into the PHP code, this section gives an overview of the Blogger Data API. As with all REST-based services, the API accepts HTTP requests containing one or more XML-encoded input arguments and returns XML-encoded responses that can be parsed in any XML-aware client. With the Blogger API, the response always consists of an Atom or RSS feed containing the requested information.

Blogger has both public and private feeds. You can access public feeds, such as lists of posts within a blog, without authentication. Private feeds require authentication. For example, a private feed requires authentication to perform add or update operations on a blog post or comment. The examples in this article demonstrate both types of feeds.

To get the most out of this article, you should have a Blogger account with at least one blog and one post. If you don't have one, you can log into Blogger (see Resources), create a new blog, and add a new post to it for testing purposes. While you're logged in, try to access the URL http://www.blogger.com/feeds/default/blogs in a web browser. You should see something similar to Listing 1.

Listing 1. Blogger metafeed
<?xml version='1.0' encoding='UTF-8'?>
<?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?>
<feed xmlns='http://www.w3.org/2005/Atom' 
 xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/'>
  <id>tag:blogger.com,1999:user-12345USERIDHERE12345.blogs</id>
  <updated>2011-09-02T03:35:34.547Z</updated>
  <title type='text'>V Vaswani's Blogs</title>
  <link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' 
   href='http://www.blogger.com/feeds/12345USERIDHERE12345/blogs'/>
  <link rel='self' type='application/atom+xml' 
    href='http://www.blogger.com/feeds/12345USERIDHERE12345/blogs'/>
  <link rel='alternate' type='text/html' 
    href='http://www.blogger.com/profile/12345USERIDHERE12345'/>
  <author>
    <name>V Vaswani</name>
    <email>noreply@blogger.com</email>
  </author>
  <generator version='7.00' uri='http://www.blogger.com'>Blogger</generator>
  <openSearch:totalResults>1</openSearch:totalResults>
  <openSearch:startIndex>1</openSearch:startIndex>
  <openSearch:itemsPerPage>25</openSearch:itemsPerPage>
  <entry>
    <id>tag:blogger.com,1999:user-12345USERIDHERE12345.blog
     -12345BLOGIDHERE12345</id>
    <published>2008-02-19T23:30:41.755-08:00</published>
    <updated>2011-09-01T04:01:19.669-07:00</updated>
    <title type='text'>V Vaswani's Blog</title>
    <summary type='html'></summary>
    <link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/
     12345USERIDHERE12345/blogs/12345BLOGIDHERE12345'/>
    <link rel='alternate' type='text/html' href='http://vvaswani.blogspot.com/'/>
    <link rel='http://schemas.google.com/g/2005#feed' 
     type='application/atom+xml' 
     href='http://vvaswani.blogspot.com/feeds/posts/default'/>
    <link rel='http://schemas.google.com/g/2005#post' type='application/atom+xml'
     href='http://www.blogger.com/feeds/12345BLOGIDHERE12345/posts/default'/>
    <link rel='http://schemas.google.com/blogger/2008#template' 
     type='application/atom+xml' 
     href='http://www.blogger.com/feeds/12345BLOGIDHERE12345/template'/>
    <link rel='http://schemas.google.com/blogger/2008#settings' 
     type='application/atom+xml' 
     href='http://www.blogger.com/feeds/12345BLOGIDHERE12345/settings'/>
    <author>
      <name>V Vaswani</name>
      <email>noreply@blogger.com</email>
    </author>
    <gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' 
     name='IS_PUBLIC_BLOG' value='true'/>
    <gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' 
     name='PICASAWEB_ALBUM_ID' value='0'/>
  </entry>
</feed>

The metafeed in Listing 1 provides a list of all the blogs associated with the currently authenticated user. It opens with a <feed> element as the root element. The <feed> element contains:

  • <link> elements, which contain URLs for different versions of the feed
  • An <author> element, which specifies the account owner
  • <openSearch> elements, which contain summary statistics

The outermost <feed> element encloses one or more <entry> elements, each representing a blog. Each <entry> contains further detail, including the title, description, creation date, last update date, and author of each blog. Each entry also includes a set of feed links, which are feeds that contain the blog posts, blog template, and blog settings.

In Listing 1, each blog is associated with a unique identifier, or blog ID. The blog ID is used to construct the URLs to the blog-specific feeds. For example, to find the post feed for a specific blog, use the URL specified in the <link rel='http://schemas.google.com/g/2005#post' ... /> element. Listing 2 shows what the post feed might look like.

Listing 2. Blogger post feed
<?xml version='1.0' encoding='UTF-8'?>
<?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?>
<feed xmlns='http://www.w3.org/2005/Atom' 
 xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' 
 xmlns:georss='http://www.georss.org/georss' 
 xmlns:thr='http://purl.org/syndication/thread/1.0'>
  <id>tag:blogger.com,1999:blog-12345BLOGIDHERE12345</id>
  <updated>2011-09-01T21:06:44.240-07:00</updated>
  <title type='text'>V Vaswani's Blog</title>
  <subtitle type='html'></subtitle>
  <link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' 
   href='http://vvaswani.blogspot.com/feeds/posts/default'/>
  <link rel='self' type='application/atom+xml' 
   href='http://www.blogger.com/feeds/12345BLOGIDHERE12345/posts/default'/>
  <link rel='http://schemas.google.com/g/2005#post' type='application/atom+xml' 
   href='http://www.blogger.com/feeds/12345BLOGIDHERE12345/posts/default'/>
  <link rel='alternate' type='text/html' href='http://vvaswani.blogspot.com/'/>
  <author>
    <name>V Vaswani</name>
    <email>noreply@blogger.com</email>
  </author>
  <generator version='7.00' uri='http://www.blogger.com'>Blogger</generator>
  <openSearch:totalResults>3</openSearch:totalResults>
  <openSearch:startIndex>1</openSearch:startIndex>
  <openSearch:itemsPerPage>25</openSearch:itemsPerPage>
  <entry>
    <id>tag:blogger.com,1999:blog-12345BLOGIDHERE12345.post-
     12345POSTIDHERE12345</id>
    <published>2011-09-01T03:34:00.001-07:00</published>
    <updated>2011-09-01T21:06:44.258-07:00</updated>
    <title type='text'>My Third Post</title>
    <content type='html'>This is my third post
    <br /></content>
    <link rel='replies' type='application/atom+xml' 
     href='http://vvaswani.blogspot.com/feeds/12345POSTIDHERE12345/comments/default'
     title='Post Comments'/>
    <link rel='replies' type='text/html' 
     href='https://www.blogger.com/comment.g?
     blogID=12345BLOGIDHERE12345&postID=12345POSTIDHERE12345' 
     title='0 Comments'/>
    <link rel='edit' type='application/atom+xml' 
     href='http://www.blogger.com/feeds/12345BLOGIDHERE12345/posts/
     default/12345POSTIDHERE12345'/>
    <link rel='self' type='application/atom+xml' 
     href='http://www.blogger.com/feeds/12345BLOGIDHERE12345/posts/
     default/12345POSTIDHERE12345'/>
    <link rel='alternate' type='text/html' 
     href='http://vvaswani.blogspot.com/2011/09/my-third-d.html' 
     title='My Third Post'/>
    <author>
    <name>V Vaswani</name>
    <email>noreply@blogger.com</email>
    </author>
    <thr:total>0</thr:total>
  </entry>
  ...
</feed>

Listing 2 reveals that the post feed contains a set of <entry> elements, and that each one represents a single post of the blog. Each <entry> includes:

  • The title, content, and publication date of the post.
  • Links to the post's public URL, post edit URL, comments feed URL, and comments feed edit URL. These feeds are used to view and edit the post, or its comments, through the Blogger Data API.

Each post also has a unique identifier, called the post ID, which is in the <id> element of the entry. It is a composite string that includes both the blog ID and post ID, in the format tag:blogger.com,1999:blog-BLOGID.post-POSTID. For example, in the identifying string tag:blogger.com,1999:blog-1234.post-6789, the blog identifier is 1234 and the post identifier is 6789.


Retrieving blogs and posts

Now that you know how to access blog and post feeds through the API, try the same thing from within a PHP application. To generate the feeds in Listings 1 and 2, you first manually log in to Blogger with a Google account. To retrieve and process the feeds from within a PHP application, the same authentication task needs to be performed programmatically.

Doing the authentication manually is a fairly messy task and requires a lot of code to account for the various scenarios that can arise during a typical authentication transaction. Fortunately, the Zend Framework includes Zend_Gdata, a PHP client library designed specifically for developers trying to integrate PHP applications with the Google Data API. Zend_Gdata provides a convenient, object-oriented interface to the Google Data API (see the link in Resources to download it). It encapsulates most of the common tasks, including authentication, and leaves you free to focus on the core functions of your application.

After you install Zend's Gdata library, look through Listing 3. It shows how to process the feed from Listing 1 and convert it into a web page listing all of the authenticated user's weblogs on Blogger.

Listing 3. Listing blogs
<?php
// load Zend Gdata libraries
require_once 'Zend/Loader.php';
Zend_Loader::loadClass('Zend_Gdata');
Zend_Loader::loadClass('Zend_Gdata_Query');
Zend_Loader::loadClass('Zend_Gdata_ClientLogin');
Zend_Loader::loadClass('Zend_Gdata_Feed');

// set credentials for ClientLogin authentication
$user = "your-username@gmail.com";
$pass = "your-password";

try {
  // perform login 
  // initialize service object
  $client = Zend_Gdata_ClientLogin::getHttpClient(
    $user, $pass, 'blogger');
  $service = new Zend_Gdata($client);

  // get list of all blogs
  $query = new Zend_Gdata_Query('http://www.blogger.com/feeds/default/blogs');
  $feed = $service->getFeed($query);
} catch (Exception $e) {
  die('ERROR:' . $e->getMessage());  
}
?>
<html>
  <head></head>
  <body>
    <h2>Blogs</h2>
    <ul>
      <?php foreach ($feed as $entry): ?>      
      <li>
        <a href="<?php 
         echo $entry->getLink('alternate')->getHref(); ?>">
         <?php echo $entry->getTitle(); ?> 
        </a>          
      </li>
      <?php endforeach; ?>
    </ul>  
  </body>
</html>

Listing 3 loads the Zend class libraries and initializes an instance of the Zend_Gdata service class. Zend_Gdata uses a Zend_Http_Client object, which is provided with the necessary user authentication information and used to open an authenticated connection to the Blogger service. This class also serves as the control point for all subsequent interaction with the Blogger Data API.

Once an authenticated connection is open, the getFeed() method is used to retrieve the list of blogs. getFeed() accepts a Zend_Gdata_Query object, which is passed the URL for the blog listing feed. The response to the getFeed() API call is an XML feed similar to that in Listing 1, which is then parsed and converted into a PHP object. Entries within the feed are represented as array elements, making it possible to:

  • Iterate over the feed
  • Retrieve the individual blog names and URLs
  • Format them for display in a web browser

Figure 1 shows the output you might see.

Figure 1. Example web page displaying a user's blogs
Screen capture of a web page that lists a user's blogs

As noted earlier, each blog and post within the Blogger service has a unique identifier. If you have the unique identifier for a blog, you can generate the URL for the corresponding blog feed, which contains a list of all the posts within that blog. Listing 4 shows the process for retrieving and processing such a blog feed with the Zend_Gdata client library.

Listing 4. Listing blog posts
<?php
// load Zend Gdata libraries
require_once 'Zend/Loader.php';
Zend_Loader::loadClass('Zend_Gdata');
Zend_Loader::loadClass('Zend_Gdata_Query');
Zend_Loader::loadClass('Zend_Gdata_ClientLogin');
Zend_Loader::loadClass('Zend_Gdata_Feed');

// set credentials for ClientLogin authentication
$user = "your-username@gmail.com";
$pass = "your-password";

try {
  // perform login 
  // initialize service object
  $client = Zend_Gdata_ClientLogin::getHttpClient(
    $user, $pass, 'blogger');
  $service = new Zend_Gdata($client);

  // get list of all posts in blog feed
  $query = new Zend_Gdata_Query(
   'http://www.blogger.com/feeds/YOUR-BLOG-ID-HERE/posts/default');
  $feed = $service->getFeed($query);
} catch (Exception $e) {
  die('ERROR:' . $e->getMessage());  
}
?>
<html>
  <head></head>
  <body>
    <h2>Posts</h2>
    <ul>
      <?php foreach ($feed as $entry): ?>      
      <li>
        <a href="<?php 
         echo $entry->getLink('alternate')->getHref(); ?>">
            <?php echo $entry->getTitle(); ?> 
        </a>          
      </li>
      <?php endforeach; ?>
    </ul>  
  </body>
</html>

As in Listing 3, Listing 4 first initializes the Zend libraries and creates an authenticated Zend_Gdata service object. The blog identifier is used to construct the URL to the blog feed, which is converted into a Zend_Gdata_Query object and passed to the getFeed() method. The resulting feed is then parsed and processed, in the usual manner, to produce a list of all the posts within the specified blog. Figure 2 shows an example of the output.

Figure 2. Web page displaying user's blog posts
Screen capture of a web page that list a user's blog posts

You can combine Listing 3 and Listing 4 to produce a list of all the authenticated user's blogs and posts within each blog. Listing 5 shows the combined code.

Listing 5. Listing blogs and blog posts
<?php
// load Zend Gdata libraries
require_once 'Zend/Loader.php';
Zend_Loader::loadClass('Zend_Gdata');
Zend_Loader::loadClass('Zend_Gdata_Query');
Zend_Loader::loadClass('Zend_Gdata_ClientLogin');
Zend_Loader::loadClass('Zend_Gdata_Feed');

// set credentials for ClientLogin authentication
$user = "your-username@gmail.com";
$pass = "your-password";

try {
  // perform login 
  // initialize service object
  $client = Zend_Gdata_ClientLogin::getHttpClient(
    $user, $pass, 'blogger');
  $service = new Zend_Gdata($client);

  // create array
  $data = array();
  
  // get list of all posts in all blog feeds
  $query1 = new Zend_Gdata_Query('http://www.blogger.com/feeds/default/blogs');
  $feed1 = $service->getFeed($query1);
  foreach ($feed1 as $entry1) {
    $arr = explode('-', (string)$entry1->getId());
    $id = $arr[2];
    $query2 = new Zend_Gdata_Query(
     'http://www.blogger.com/feeds/'.$id.'/posts/default');
    $feed2 = $service->getFeed($query2);
    $blogArray = array(
      'title' => (string)$entry1->getTitle(), 
      'uri' => (string)$entry1->getLink('alternate')->getHref(),
      'posts' => array()
    );
    foreach ($feed2 as $entry2) {
      $postArray = array(
        'title' => (string)$entry2->getTitle(), 
        'uri' => (string)$entry2->getLink('alternate')->getHref()
      );
      $blogArray['posts'][] = $postArray;
    }
    $data[] = $blogArray;
  }
} catch (Exception $e) {
  die('ERROR:' . $e->getMessage());  
}
?>
<html>
  <head></head>
  <body>
    <h2>Blogs and Posts</h2>
    <div id="results">
      <?php foreach ($data as $d): ?>      
      <ul>      
        <li><a href="<?php echo $d['uri']; ?>">
        <?php echo $d['title']; ?></a></li>
          <ul>
          <?php foreach ($d['posts'] as $p): ?>      
            <li><a href="<?php echo $p['uri']; ?>">
            <?php echo $p['title']; ?></a></li>
          <?php endforeach; ?>
          </ul>
      </ul>
      <?php endforeach; ?>
    </div>  
  </body>
</html>

Listing 5 first requests the blog metafeed and iterates over the entries within it to retrieve each blog's unique identifier. This identifier is then used to construct the URL to the blog's post feed, which is requested and processed to extract each post's title and URL. All of this information is stored in a nested array, as in Listing 6.

Listing 6. Nested array
Array
(
  [0] => Array
  (
    [title] => V Vaswani's Blog
    [uri] => http://***.blogspot.com/
    [posts] => Array
    (
      [0] => Array
      (
        [title] => My Fourth Post
        [uri] => http://***.blogspot.com/2011/09/my-fourth-post.html
      )

      [1] => Array
      (
        [title] => My Third Post
        [uri] => http://***.blogspot.com/2011/09/my-third-d.html
      )

      [2] => Array
      (
        [title] => My Second Post
        [uri] => http://***.blogspot.com/2011/08/my-second-post.html
      )

      [3] => Array
      (
        [title] => My First Post
        [uri] => http://***.blogspot.com/2008/02/testing.html
      )
    )
  )

  [1] => Array
  (
    [title] => V Vaswani's Second Blog
    [uri] => http://***.blogspot.com/
    [posts] => Array
    (
      [0] => Array
      (
        [title] => My First Post in my Second Blog
        [uri] => http://***.blogspot.com/2011/09/my-first-post-in-my-second-blog.html
      )
    )
  )
)

Once the array is completely generated, you can simply iterate over it and prepare a nested list of blogs and their posts. Figure 3 shows an example of the output.

Figure 3. Web page displaying user's blogs and blog posts
Screen capture of a web page that lists a user's blogs and blog posts for each blog

Adding blog posts

In addition to allowing post retrieval, the Blogger Data API also allows authenticated users to add new posts to a blog programmatically. Create a new Zend_Gdata_App_Entry object, set its title and content, and then POST it to the post feed. Listing 7 shows the example code.

Listing 7. Adding a blog post
<?php
if (isset($_POST['submit'])) {

  // load Zend Gdata libraries
  require_once 'Zend/Loader.php';
  Zend_Loader::loadClass('Zend_Gdata');
  Zend_Loader::loadClass('Zend_Gdata_Query');
  Zend_Loader::loadClass('Zend_Gdata_ClientLogin');
  Zend_Loader::loadClass('Zend_Gdata_Feed');

  // set credentials for ClientLogin authentication
  $user = "your-username@gmail.com";
  $pass = "your-password";

  // set blog id
  $id = 'YOUR-BLOG-ID-HERE';

  try {
    // perform login 
    // initialize service object
    $client = Zend_Gdata_ClientLogin::getHttpClient(
      $user, $pass, 'blogger');
    $service = new Zend_Gdata($client);

    // create a new entry object
    // populate it with user input    
    $uri = 'http://www.blogger.com/feeds/' . $id . '/posts/default';
    $entry = $service->newEntry();
    $entry->title = $service->newTitle($_POST['title']);
    $entry->content = $service->newContent($_POST['body']);
    $entry->content->setType('text');

    // save entry to server
    // return unique id for new post
    $response = $service->insertEntry($entry, $uri);
    $arr = explode('-', $response->getId());
    $id = $arr[2];
    echo 'Successfully added post with ID: ' . $id;

  } catch (Exception $e) {
    die('ERROR:' . $e->getMessage());  
  }
}
?>
<html>
  <head></head>
  <body>
    <h2>Add New Post</h2>
    <form method="post">
      Title: <br/> <input type="text" name="title" 
       size="50" /> <br/>
      Body: <br/> <textarea name="body" cols="40" 
       rows="10"> </textarea> <br/>
      <input type="submit" name="submit" value="Post" />
    </form>
  </body>
</html>

In the code in Listing 7, a simple web form allows the user to input a title and body for a blog post. After the form is submitted, the script loads the Zend class libraries and creates an authenticated service object. The script then uses the service object's newEntry() method to create a new entry object and uses the newTitle() and newContent() methods to set the title and content of the blog post from the user's input. After these attributes are set, the insertEntry() method is used to save the new post to Google's servers.

Figure 4 shows an example of the form.

Figure 4. Web form to add a new blog post
Screen capture of the web form to add a new blog post with Title and Body fields

The return value of the insertEntry() method is an entry representing the newly created post. This entry now contains the post's unique identifier, which can be extracted and displayed on the result page. After it is saved, the new post becomes visible in the public blog and also appears in all API feeds for that blog.


Editing and deleting blog posts

You can also delete or edit existing posts through the Blogger Data API. In both cases, the corresponding DELETE or PUT request must be transmitted to the post's edit URL (see Listing 2 for an example). Only authenticated users can perform both of these operations.

The code in Listing 8 shows how to delete an existing blog post through the API, assuming you already have the blog and post IDs.

Listing 8. Deleting a blog post
<?php
// load Zend Gdata libraries
require_once 'Zend/Loader.php';
Zend_Loader::loadClass('Zend_Gdata');
Zend_Loader::loadClass('Zend_Gdata_Query');
Zend_Loader::loadClass('Zend_Gdata_ClientLogin');
Zend_Loader::loadClass('Zend_Gdata_Feed');

// set credentials for ClientLogin authentication
$user = "your-username@gmail.com";
$pass = "your-password";

// set blog and post ids
$bid = 'YOUR-BLOG-ID-HERE';
$pid = 'YOUR-POST-ID-HERE';

try {
  // perform login 
  // initialize service object
  $client = Zend_Gdata_ClientLogin::getHttpClient(
    $user, $pass, 'blogger');
  $service = new Zend_Gdata($client);

  // set URI for entry to delete
  // obtain from <link rel=edit ... />
  $uri = 'http://www.blogger.com/feeds/' . $bid . '/posts/default/' . $pid;
  $service->delete($uri);
  echo 'Successfully deleted post with ID: ' . $pid;

} catch (Exception $e) {
  die('ERROR:' . $e->getMessage());  
}
?>

Listing 8 uses the service object's delete() method to delete the specified blog post. The delete() method accepts the post's edit URL and transmits a DELETE request to that URL. After it is deleted, the blog post will disappear from both the public blog and any API feeds that reference it.

To display the code to update an existing blog post through the API, again assuming you already have the blog and post IDs, use the code in Listing 9.

Listing 9. Updating a blog post
<?php
// load Zend Gdata libraries
require_once 'Zend/Loader.php';
Zend_Loader::loadClass('Zend_Gdata');
Zend_Loader::loadClass('Zend_Gdata_Query');
Zend_Loader::loadClass('Zend_Gdata_ClientLogin');
Zend_Loader::loadClass('Zend_Gdata_Feed');

// set credentials for ClientLogin authentication
$user = "your-username@gmail.com";
$pass = "your-password";

// set blog and post ids
$bid = 'YOUR-BLOG-ID-HERE';
$pid = 'YOUR-POST-ID-HERE';

try {
  // perform login 
  // initialize service object
  $client = Zend_Gdata_ClientLogin::getHttpClient(
    $user, $pass, 'blogger');
  $service = new Zend_Gdata($client);

  // set URI for entry to update
  // obtain from <link rel=self ... />
  $uri = 'http://www.blogger.com/feeds/' . $bid . '/posts/default/' . $pid;

  // get the existing entry
  // set new values
  // save it back
  $response = $service->get($uri);
  $entry = new Zend_Gdata_App_Entry($response->getBody());
  $entry->title = $service->newTitle("New title");
  $entry->content = $service->newContent("New content");
  $entry->content->setType('text');
  $service->updateEntry($entry);
  echo 'Successfully updated post with ID: ' . $pid;

} catch (Exception $e) {
  die('ERROR:' . $e->getMessage());  
}
?>

Updating a blog post through the API is a two-step process. First, you need to retrieve the existing blog post and convert it to a Zend_Gdata_App_Entry object. Then, you can use service object methods, such as newTitle() and newContent(), to update the entry and save it back to the server using the updateEntry() method.

Similar to the insertEntry() method, the return value of the updateEntry() method is an entry representing the post, including its unique identifier and content. After it is saved, the updated post becomes visible in the public blog and in all API feeds referencing that blog.


Working with comments

Just as you can add, edit, and delete posts, you can also add, edit, and delete post comments. In Listing 2, notice that each post entry includes links to the post's comments feed. Adding a new comment is as simple as inserting an entry into this comments feed.

Listing 10 shows the process for adding a new comment to a post.

Listing 10. Adding a new comment
<?php
// load Zend Gdata libraries
require_once 'Zend/Loader.php';
Zend_Loader::loadClass('Zend_Gdata');
Zend_Loader::loadClass('Zend_Gdata_Query');
Zend_Loader::loadClass('Zend_Gdata_ClientLogin');
Zend_Loader::loadClass('Zend_Gdata_Feed');

// set credentials for ClientLogin authentication
$user = "your-username@gmail.com";
$pass = "your-password";

// set blog and post ids
$bid = 'YOUR-BLOG-ID-HERE';
$pid = 'YOUR-POST-ID-HERE';

try {
  // perform login 
  // initialize service object
  $client = Zend_Gdata_ClientLogin::getHttpClient(
    $user, $pass, 'blogger');
  $service = new Zend_Gdata($client);

  // create a new entry object
  // populate it with user input    
  $uri = "http://www.blogger.com/feeds/$bid/$pid/comments/default";
  $entry = $service->newEntry();
  $entry->content = $service->newContent('This is a very interesting post.');
  $entry->content->setType('text');

  // save entry to server
  // return unique id for new comment
  $response = $service->insertEntry($entry, $uri);
  $arr = explode('/', $response->getEditLink()->href);
  $id = $arr[8];
  echo 'Successfully added comment with ID: ' . $id;

} catch (Exception $e) {
  die('ERROR:' . $e->getMessage());  
}
?>

Adding a new comment is almost the same as adding a new post; the only difference lies in the URL to which the request is posted (in this case, the comments feed URL). The response to the insertEntry() method is an object representing the newly added comment. The object can then be parsed to retrieve the unique comment identifier, which is needed if you want to delete the comment later.

Assuming you have the comment identifier, you can use the code in Listing 11 to programmatically remove the comment with the delete() method.

Listing 11. Deleting a comment
<?php
// load Zend Gdata libraries
require_once 'Zend/Loader.php';
Zend_Loader::loadClass('Zend_Gdata');
Zend_Loader::loadClass('Zend_Gdata_Query');
Zend_Loader::loadClass('Zend_Gdata_ClientLogin');
Zend_Loader::loadClass('Zend_Gdata_Feed');

// set credentials for ClientLogin authentication
$user = "your-username@gmail.com";
$pass = "your-password";

// set blog, post and comment ids
$bid = 'YOUR-BLOG-ID-HERE';
$pid = 'YOUR-POST-ID-HERE';
$cid = 'YOUR-COMMENT-ID-HERE';

try {
  // perform login 
  // initialize service object
  $client = Zend_Gdata_ClientLogin::getHttpClient(
    $user, $pass, 'blogger');
  $service = new Zend_Gdata($client);

  // set URI for entry to delete
  // obtain from <link rel=edit ... />
  $uri = "http://www.blogger.com/feeds/$bid/$pid/comments/default/$cid";
  $service->delete($uri);
  echo 'Successfully deleted comment with ID: ' . $cid;

} catch (Exception $e) {
  die('ERROR:' . $e->getMessage());  
}
?>

You can also use the same comments feed URL to retrieve a list of all the comments for a post, as in Listing 12.

Listing 12. Listing comments
<?php
// load Zend Gdata libraries
require_once 'Zend/Loader.php';
Zend_Loader::loadClass('Zend_Gdata');
Zend_Loader::loadClass('Zend_Gdata_Query');
Zend_Loader::loadClass('Zend_Gdata_ClientLogin');
Zend_Loader::loadClass('Zend_Gdata_Feed');

// set credentials for ClientLogin authentication
$user = "your-username@gmail.com";
$pass = "your-password";

// set blog and post ids
$bid = 'YOUR-BLOG-ID-HERE';
$pid = 'YOUR-POST-ID-HERE';

try {
  // perform login 
  // initialize service object
  $client = Zend_Gdata_ClientLogin::getHttpClient(
    $user, $pass, 'blogger');
  $service = new Zend_Gdata($client);

  // get list of all comments for post
  $uri = "http://www.blogger.com/feeds/$bid/$pid/comments/default/";
  $query = new Zend_Gdata_Query($uri);
  $feed = $service->getFeed($query);
} catch (Exception $e) {
  die('ERROR:' . $e->getMessage());  
}
?>
<html>
  <head></head>
  <body>
    <h2>Comments</h2>
    <div id="results">
      <ul>
      <?php foreach ($feed as $entry): ?>      
        <?php $author = $entry->getAuthor(); ?>
        <li><?php echo $entry->getTitle(); ?> 
         - <?php echo $author[0]->getName(); ?> <br/>
        <?php echo date('d M Y H:i', strtotime($entry->getUpdated())); ?>
        </li>          
      <?php endforeach; ?>
    </ul>  
  </body>
</html>

Example application: Content Management System

Let's put everything into practice by building a simple real-world application that demonstrates the power of the Blogger Data API. The example application is a web-based content manager that lets users view their existing blog posts, add new posts, and edit or delete existing posts. Edits are performed through a browser-based WYSIWYG editor and transmitted to Blogger through the Blogger Data API.

To begin, download and install the TinyMCE JavaScript editor on your development server (see Resources for a link). Create a script containing the code shown in Listing 13.

Listing 13. Managing blog content
<?php
// load Zend Gdata libraries
require_once 'Zend/Loader.php';
Zend_Loader::loadClass('Zend_Gdata');
Zend_Loader::loadClass('Zend_Gdata_Query');
Zend_Loader::loadClass('Zend_Gdata_ClientLogin');
Zend_Loader::loadClass('Zend_Gdata_Feed');

// set credentials for ClientLogin authentication
$user = "your-username@gmail.com";
$pass = "your-password";

// set blog id
$bid = 'YOUR-BLOG-ID-HERE';

try {
  // perform login 
  // initialize service object
  $client = Zend_Gdata_ClientLogin::getHttpClient(
    $user, $pass, 'blogger');
  $service = new Zend_Gdata($client);

  // for add and edit operations
  if (isset($_POST['submit'])) {
    // if post id present
    // update post with new content
    if (isset($_POST['pid'])) {      
      $arr = explode('-', $_GET['pid']);
      $pid = $arr[2];
      $uri = 'http://www.blogger.com/feeds/' . $bid . '/posts/default/' . $pid;
      $response = $service->get($uri);
      $entry = new Zend_Gdata_App_Entry($response->getBody());
      $entry->title = $service->newTitle($_POST['title']);
      $entry->content = $service->newContent($_POST['body']);
      $entry->content->setType('text');
      $service->updateEntry($entry);
      echo 'Successfully updated post with ID: ' . $pid;    
    } else {
      // if post id not present
      // create a new post
      // populate it with user input    
      $uri = 'http://www.blogger.com/feeds/' . $bid . '/posts/default';
      $entry = $service->newEntry();
      $entry->title = $service->newTitle($_POST['title']);
      $entry->content = $service->newContent($_POST['body']);
      $entry->content->setType('text');
      $response = $service->insertEntry($entry, $uri);
      $arr = explode('-', $response->getId());
      $id = $arr[2];
      echo 'Successfully added post with ID: ' . $id;  
    }
  }

  // for edit and delete operations
  if (isset($_GET['op'])) {
    // generate post URL
    $arr = explode('-', $_GET['pid']);
    $pid = $arr[2];
    $uri = 'http://www.blogger.com/feeds/' . $bid . '/posts/default/' . $pid;
    switch($_GET['op']) {
      case 'delete':
        // delete post
        $service->delete($uri);
        echo 'Successfully deleted post with ID: ' . $pid;      
        break;
      case 'edit':
        // retrieve post for display in edit form
        $response = $service->get($uri);
        $entry = new Zend_Gdata_App_Entry($response->getBody());
    }
  }
  
  // get list of all posts in blog feed
  $query = new Zend_Gdata_Query(
   'http://www.blogger.com/feeds/'.$bid.'/posts/default');
  $feed = $service->getFeed($query);
} catch (Exception $e) {
  die('ERROR:' . $e->getMessage());  
}
?>
<html>
  <head>
    <script type="text/javascript" 
     src="tinymce/jscripts/tiny_mce/tiny_mce.js"></script>
    <script type="text/javascript">
    tinyMCE.init({
            mode : "textareas",
            theme : "advanced",
            theme_advanced_buttons1 : "bold,italic,underline,|,justifyleft,
             justifycenter,justifyright,|,code,preview,|,forecolor,backcolor,|,sub,sup,
             |,fontsizeselect",
            theme_advanced_buttons2 : "cut,copy,paste,|,bullist,numlist,|,outdent,indent,
             |,undo,redo,|,link,unlink,anchor,image,|,removeformat",
            theme_advanced_buttons3 : "",      
            theme_advanced_toolbar_location : "top",
            theme_advanced_toolbar_align : "left",
            theme_advanced_statusbar_location : "bottom",
            theme_advanced_resizing : true
    });

    </script>
  </head>
  <body>
    <h2>View Posts</h2>
    <ul>
    <?php foreach ($feed as $e): ?>  
      <li><?php echo $e->getTitle(); ?> <br/>
      <a href="<?php 
      echo $e->getLink('alternate')->getHref(); ?>">View</a> 
       | <a href="?op=edit&pid=<?php echo $e->getId(); ?>">Edit</a>
       | <a href="?op=delete&pid=<?php echo $e->getId(); ?>">Delete
       </a>
      </li>
    <?php endforeach; ?>
    </ul>
    <a href="<?php echo $_SERVER['PHP_SELF']; ?>">
     Add a new post</a>
    <?php if (isset($_GET['op']) && $_GET['op'] == 'edit'): ?>
    <h2>Edit Post</h2>
    <form method="post">
      Title: <br/> <input type="text" name="title" size="50" 
       value="<?php echo $entry->title; ?>" /> <br/>
      Body: <br/> <textarea name="body" cols="40" 
       rows="10"><?php echo $entry->content; ?></textarea> <br/>
      <input type="hidden" name="pid" 
       value="<?php echo $entry->id; ?>" />
      <input type="submit" name="submit" value="Post" />
    </form>
    <?php else: ?>
    <h2>Add New Post</h2>
    <form method="post">
      Title: <br/> <input type="text" name="title" 
       size="50" /> <br/>
      Body: <br/> <textarea name="body" cols="40" 
       rows="10"> </textarea> <br/>
      <input type="submit" name="submit" value="Post" />
    </form>
    <?php endif; ?>
  </body>
</html>

The code above might look complex, but it's just a couple of conditional statements wrapped around code you've already seen. It might help to break it down, as follows.

  • The script begins by loading the Zend class libraries and setting credentials for authentication. It also defines the blog ID. All subsequent operations will take place only on the specified blog.
  • Assuming no request parameters exist, the script connects to the Blogger API and requests a list of all posts in the specified blog. It then processes the resulting feed and displays the list of posts as an HTML list.

    Each post is accompanied by three links: to View, Edit, or Delete the post. The View link specifies the public URL of the post. The Edit and Delete links simply invoke the script again, passing it the selected post ID as a GET request parameter.

  • After generating the list of current posts, the script also generates a web form for the user to add a new post. The form contains a single-line text input field (for the post title) and a multi-line text area (for the post body).

    The TinyMCE editor, which is also initialized at page load, automatically takes care of converting the text area into a WYSIWYG text editing field with formatting and hyperlink controls.

  • What happens next depends on which operation the user chooses.
    If the user chooses...Then...
    To add a new postAfter content is entered into the web form and submitted, the script creates a new entry object using the newEntry() method, populates it with the submitted input, and saves it to the blog using the insertEntry() method.
    To delete an existing postThe ID of the selected post is retrieved from the request URL, and the post feed URL is constructed using the blog ID and post ID. The post is then deleted using the service object's delete() method.
    To edit an existing postThe ID of the selected post is retrieved from the request URL, and the post feed URL is constructed using the blog ID and post ID. The post is then retrieved using the service object's get() method. The content of the post is used to pre-populate the web form for editing, together with a hidden field containing the unique post ID.

    After the user makes changes to the content and resubmits the form, the hidden post ID is used to identify and update the existing post with the updateEntry() method.

For the sake of simplicity, this script does not include any input validation. In a production environment, you need to add validation to ensure the integrity and validity of the data being collected from the user.

Figure 5 shows the example content management system.

Figure 5. Blog content management interface
Screen capture of the blog content management interface

Conclusion

In this article, you learned how to integrate Blogger blog posts directly into a PHP application by using the Blogger Data API and Zend Gdata client library. The article provided an introduction to the Blogger feed format. You also learned how to:

  • Retrieve blogs, posts, and comments
  • Add, edit, and delete posts
  • View, add, and delete comments

The Blogger API provides a sophisticated, format-independent mechanism to integrate public blog content into any web application. It's great for mashups, or to build your own custom interface to Blogger. Try it out sometime, and see what you can do.

Resources

Learn

Get products and technologies

Discuss

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Select information in your profile (name, country/region, and company) is displayed to the public and will accompany any content you post. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into XML on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=XML, Open source
ArticleID=762363
ArticleTitle=Integrate Blogger content with PHP applications using the Blogger Data API
publish-date=10042011