Pragmatism in the real world

TweetGT: an example of Zend_Service_Twitter via OAuth

TweetGT is a simple application that talks to Twitter. I wrote it as I couldn’t find another way to send a geotagged tweet sent from an arbitrary location.

Screenshot of tweetgt.funkymongoose.com

Also, my friend Cal Evans says that writing a Twitter app is the new Hello World, so I thought I’d better find out how to do it! Obviously, I used Zend Framework :)

The source is up on github so you can have a look at the entire project. The section I want to concentrate on in this post is the Twitter OAuth integration, which was added to Zend_Service_Twitter in version 1.10.6.

To implement my Twitter integration, I used a model, Application_Model_Twitter, which has a protected member variable to an instance of Zend_Service_Twitter. This means that the rest of the application has to go through the model to get to the service and so in principle at least, I get to control access.

OAuth integration requires that we get an access token from twitter. The basic process is that we hand off from our website to Twitter, who then call back to a URL on our site once the user has logged in.

Login: Redirect to Twitter

The loginAction looks like this:


public function loginAction()
{
$twitter = $this->_helper->twitter(); /* @var $twitter Application_Model_Twitter */

// We need the request token for use in the callback when the user is
// redirected back here from Twitter after authenticating
$session = new Zend_Session_Namespace();
$session->requestToken = $twitter->getRequestToken();

// redirect to the Twitter website
$twitter->loginViaTwitterSite();
}

Three things going on here. Firstly, I have set up an action helper to retrieve an instance of my model for me. This is mainly for ease of use as there’s a bit of configuration required, so having it centralised saves having to duplicate code. There’s other ways of doing this of course, but a action helper suited me this time :)

When we hand over to Twitter, we send a request token over too. We will need the request token in the call back so we store to the session ready for use after the user has authenticated on Twitter’s site.

Finally we do the redirect to Twitter’s site via a model method, loginViaTwitterSite, which simply proxies to the redirect method within Zend_Service_Twitter‘s OAuth consumer.

Instantiating the model

The model is instantiated within a controller action helper. To log in to twitter using OAuth we need a consumer key and a consumer secret that are available from Twitter on a per-application basis. I’ve chosen to store these in the application.ini file. We also need to configure Zend_Service_Twitter with the callback URL to use and, if we are logged in, the username and access token from Twitter. This action helper does all that for us and looks like this:


class Application_Controller_Helper_Twitter extends Zend_Controller_Action_Helper_Abstract
{
/**
* @var Application_Model_Twitter
*/
protected $_twitter;

public function direct()
{
if (!$this->_twitter) {
$controller = $this->getActionController();

$config = array();

$session = new Zend_Session_Namespace();
if ($session->accessToken) {
$token = $session->accessToken;
$config['username'] = $token->screen_name;
$config['accessToken'] = $token;
}

$options = $controller->getInvokeArg('bootstrap')->getOptions();
$config['consumerKey'] = $options['twitter']['consumerKey'];
$config['consumerSecret'] = $options['twitter']['consumerSecret'];

$request = $controller->getRequest();
$url = $request->getScheme() . '://' . $request->getHttpHost() . $request->getBaseUrl();
$config['callbackUrl'] = $url . '/callback';

$this->_twitter = new Application_Model_Twitter($config);
}

return $this->_twitter;
}

}

I didn’t want my model interacting with sessions or the bootstrap options, so I used an action controller. I could equally have used a service layer object, or instantiated the model in the bootstrap or in a Front Controller plugin. The most important thing is that I only deal with the config of sorting out the config array that Zend_Service_Twitter needs once.

Callback

I have chosen /callback as the URL for Twitter to use. The easiest way to set this up is to have in indexAction() within a CallbackController class. This code will use the model’s twitter service to retrieve the access token and store it to the session.

It looks like this:

class CallbackController extends Zend_Controller_Action
{
public function indexAction()
{
$session = new Zend_Session_Namespace();
if (!empty($this->getRequest()->getQuery()) && isset($session->requestToken)) {

// Get the model instance from the action helper
$twitter = $this->_helper->twitter(); /* @var $twitter Application_Model_Twitter */

// turn the request token into an access token
$accessToken = $twitter->getAccessToken($this->getRequest()->getQuery(),
$session->requestToken);

// store the access token
$session->accessToken = $accessToken;

// we don't need the request token any more
unset($session->requestToken);

// redirect back to home page
$this->_helper->redirector('index', 'index');
} else {
throw new Zend_Exception('Invalid callback request. Oops. Sorry.');
}
}

}

The code should be fairly self-explanatory with the inline comments to help :)

That’s it. We are now logged into Twitter and back on our own website able to do whatever we want to do :) Have a look at the source to see the rest of the details of how it all fits together.

3 thoughts on “TweetGT: an example of Zend_Service_Twitter via OAuth

  1. I tried applying this on an existing project, but I keep on getting

    Message: Invalid method "getRequestToken"

    #0 /twitter-test/application/models/Twitter.php(29): Zend_Service_Twitter->__call('getRequestToken', Array)
    #1 /twitter-test/application/models/Twitter.php(29): App_Service_Twitter->getRequestToken()

    I'm pretty sure I have included all the necessary files and lines from the source. What could I be missing?

  2. I think I get it now. Going back to /index/login after the token has been returned gives out the error. I haven't dug enough to figure out why it's happening though.

Comments are closed.