The Upcoming API as a CakePHP Model

As part of a calendar-of-events application, I needed a way to easily publish and update our events to external services, such as Upcoming or Eventful. Here are a few tools to help you integrate your CakePHP application with the community events service formerly known as upcoming.org (now known as just simply, Upcoming).

Felix Geisendörfer has an excellent series of REST API tutorials for CakePHP. These were mainly applied to Google services, but the examples he gives were more than enough to get me started. While Felix’s examples give a framework for integrating this into CakePHP, most of the “meat” (ganache?) of the functions was gleaned from the example file that came packaged with one of the Upcoming API wrappers.

First things first

In addition to needing some version of an Upcoming API wrapper in the vendors directory, you’ll of course need an API key. Don’t forget to read through the community guidelines and the other API documentation – especially the information regarding what to do with your test data. Got all that? Ok, then, let’s get started.

Some structure

If I was to put all this into an application, it would probably look something like this:

app/models/upcoming.php
app/tmp/cache/services/upcoming/

Actually, that might be all you need, aside from a the code to interact with the model. But if you wanted to be able to generate and store your keys, tokens, and default parameters, you might want these files as well.

app/config/sql/settings.sql
app/views/settings/admin_index.thtml
app/models/settings.php
app/controllers/settings_controller.php

The basics

Again, if you didn’t want the extra overhead, you could always just skip the settings interface and set the defaults in the model. The


	// app/models/upcoming.php
	
	vendor('Upcoming');

	class Upcoming extends AppModel {

		var $name = 'Upcoming';
		var $useTable = false;  

		// You should set these manually
		// if not storing them in a db
		var $venue_id = null;
		var $category_id = null;
		var $token = null;
		var $API_KEY = '';
		var $UPCOMING_DEBUG = false;

		function __construct() {
			parent::__construct();
			$this->options = array(
			        'DEBUG' => $this->UPCOMING_DEBUG, 
			        'api_key' => $this->API_KEY, 
			        'endpoint' => 'http://upcoming.yahoo.com/services/rest/',
			        'auth_endpoint' => 'http://upcoming.yahoo.com/services/auth/',
					'cache_dir' => ROOT . DS . APP_DIR . DS . 'tmp/cache/services/upcoming/'
			        );
			$this->errorArray = array();

		}

		function edit ( $params )
		{

			$params['token'] = $this->token;
			$params['venue_id'] = $this->venue_id;
			$params['category_id'] = $this->category_id;

			$query = array(  'method' => 'event.edit',
			                 'http_method' => 'POST',
			                 'params' => $params,
			                 'nodeType' => 'event',
							 //data you want back
			                 'attributes' => array(
			                     'id',
			                     'name'
			                     )
						);
						
			return $this->__upcCallMethod ( $query );
		}
		
		function __upcCallMethod ( $query )
		{
			$this->upc =& new Services_Upcoming($this->options, $this->errorArray);
			$xml = $this->upc->callMethod($query['method'], $query['params'], $query['http_method']);
			return $xml;
		}
	}

I’ve added a few helper functions to my controller, which look something like the below. The model itself is pretty straightforward. For this example, I’ve only included the edit function. I don’t yet have a read or find function, but these would be very similar to what you see below. The add function is exactly the same, but sets the method as event.add.


function __build_external_event()
{
	// TODO: there's got to be a more elegant way of doing this...
	$params = array();
	
	// here you should have some logic to prep your data
	// in other words, make sure that each item going into  
	// the external service is named appropriately, and that 
	// defaults exist (i.e. you are sending all the required params)
	
	// optional, depending on what you are doing
	$params["selfpromotion"] = 1;
	
	return $params;
	
}

//sets vars for external model
function __init_external()
{
	// you could set these values manually here if you 
	// weren't using a db or if you wanted to override
	// those settings at "runtime." 
	$api_key = $this->Setting->field("value", "Setting.key='upcoming_api_key'");
	$this->Upcoming->token = $this->Setting->field("value", "Setting.key='upcoming_auth_token'");
	$this->Upcoming->venue_id = $this->Setting->field("value", "Setting.key='venue_id'");
	$this->Upcoming->category_id = $this->Setting->field("value", "Setting.key='category_id'");
	
	//send stuff to the view
	$this->set('venue_id',$this->Upcoming->venue_id);
	$this->set('external_enabled',$this->external_enabled);
	
	if($api_key && $this->Upcoming->token) 
		$this->external_enabled = true;
		$this->Upcoming->options["api_key"] = $api_key;
}  

In the controller’s edit function, I should be able to add something to make use of the above.


$params = $this->__build_external_event();
$publish_results = $this->Upcoming->edit($params);
if($publish_results["stat"] == "ok") {
	$this->data["Event"]["externalid"] = $publish_results['event']["id"];
	$errors .= 'Successfully published event externally.';
} else $errors .= 'Error publishing to external service';

And that’s about it. Oh, right, you want to be able to store your settings.

Set it and forget it

All of this (above and below) is really meant as an example. So, it might look like it just rolled out of bed in the morning and showed up here. You’ve been warned.


//app/config/sql/settings.sql

CREATE TABLE `settings` (
  `id` int(11) NOT NULL auto_increment,
  `key` varchar(255) default NULL,
  `value` varchar(255) default NULL,
  `label` varchar(255) default NULL,
  PRIMARY KEY  (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

INSERT INTO `settings` (`id`,`key`,`value`,`label`) VALUES ('1','upcoming_api_key','','Upcoming.org API Key');
INSERT INTO `settings` (`id`,`key`,`value`,`label`) VALUES ('2','upcoming_auth_token','','Upcoming.org Authentication Token');
INSERT INTO `settings` (`id`,`key`,`value`,`label`) VALUES ('4','venue_id','','Venue ID');
INSERT INTO `settings` (`id`,`key`,`value`,`label`) VALUES ('5','category_id','','Category ID');

//app/models/settings.php

class Setting extends AppModel
{
    var $name = 'Setting';

	/* 
	// Uncomment this block to provide 
	// yet another way to do settings without a database table(?).
	
	var $useTable = false;
	var $settings = array('upcoming_api_key' => 'NULL', 'upcoming_auth_token' => 'NULL');
	
	*/
	
}

// app/controllers/settings_controller.php

class SettingsController extends AppController
{
	var $name = 'Settings';
	var $helpers = array('Html', 'Form', 'Javascript', 'Time');
	var $uses = array('Setting','Upcoming');
	//var $scaffold;
	function admin_index()
	{
		
		$api_key = $this->Setting->field("value", "Setting.key='upcoming_api_key'");
		if ($api_key) {
			$this->Upcoming->options["api_key"] = $api_key;
			$this->Upcoming->upc =& new Services_Upcoming($this->Upcoming->options, $this->Upcoming->errorArray);
			$auth_url = $this->Upcoming->upc->getAuthUrl();

			$this->set('auth_url',$auth_url);
		}
		
		
		$this->set('data',$this->Setting->findAll());
					
	} 
	
	function admin_authtoken($frob)
	{
		
		$api_key = $this->Setting->field("value", "Setting.key='upcoming_api_key'");
		if ($api_key && $frob) {
			$this->Upcoming->options["api_key"] = $api_key;
			$this->Upcoming->upc =& new Services_Upcoming($this->Upcoming->options, $this->Upcoming->errorArray);
			$xml = $this->Upcoming->upc->callMethod('auth.getToken', array('frob' => $frob));
	        $token = (string)$xml['token']['token'];
			
			$this->data["Setting"]["value"] = $token;
			
			if ($token && $this->Setting->save($this->data))
			{
				$this->Session->setFlash('Auth token updated.');
				$this->redirect('/admin/settings/index');
				exit();
			}
			else {
				$this->Session->setFlash('Error updating auth token.<pre>' . print_r($this->Upcoming->errorArray,true) . '</pre>');
				$this->redirect('/admin/settings/index');
				exit();
			}
			
		}
		
	}
	
	function admin_add()
	{
		$this->layout = 'admin';
		if (empty($this->params['data']))
		{
			$this->render();
		}
		else
		{
			if ($this->Setting->save($this->params['data']))
			{
				$this->Session->setFlash('Your Setting has been saved');
				$this->redirect('/admin/settings/index');
			}
			else
			{
				$this->set('data', $this->params['data']);
				$this->validateErrors($this->Setting);
				$this->render();
			}
		}
	} 

	function admin_edit($id=null) 	{
		//$this->layout = 'admin';
		if (empty($this->params['data']))
		{
			$this->Setting->setId($id);
			$this->params['data'] = $this->Setting->read();
			$this->set('data', $this->params['data']);	
			$this->render();
		}
		else
		{
			$this->Setting->set($this->params['data']);
			if ( $this->Setting->save())
			{
				
				$this->Session->setFlash('Your Setting has been updated.');
				$this->redirect('/admin/settings/index');
			}
			else
			{
				$this->set('data', $this->params['data']);
				$this->validateErrors($this->Setting);
				$this->render();
			}
		}
	} 
	}
}


	
// app/views/settings/admin_index.thtml

Configure calendar

External services

flash(); ?>

Upcoming.org

formTag('/admin/settings/edit'); ?> hidden('Setting/id', array( 'value'=>$setting["Setting"]["id"])); ?>

input('Setting/value', array('id'=>$setting["Setting"]["key"], 'value'=>$setting["Setting"]["value"])) ?> submit(' Save/Update ',array('class'=>'submit-form')) ?>

formTag('/admin/settings/edit'); ?> hidden('Setting/id'); ?>

input('Setting/value', array('id'=>$setting["Setting"]["key"], 'value'=>$setting["Setting"]["value"])) ?> submit(' Save/Update ',array('class'=>'submit-form')) ?> Or,

You can also create a new token. To do so,

  1. Start by authenticating your api key. You should be brought back to this page. If not, be sure that the callback url is set to the address of this page.
  2. formTag('/admin/settings/authtoken/' . $_GET['frob']); ?> hidden('Setting/id', array( 'value'=>$setting["Setting"]["id"])); ?>
  3. It worked! Now go ahead and submit(' Generate Your New Token ', array('class'=>'submit-form')); ?>

formTag('/admin/settings/edit'); ?> hidden('Setting/id', array( 'value'=>$setting["Setting"]["id"])); ?>

input('Setting/value', array('id'=>$setting["Setting"]["key"], 'value'=>$setting["Setting"]["value"])) ?> submit(' Save/Update ',array('class'=>'submit-form')) ?>

Post a Comment

Your email is never published nor shared. Required fields are marked *
*
*

You can follow any discussion on this article with the RSS feed for this post.

About this article

Green Galoshes is a weblog written by Justin D. Henry. This entry was published on or around April 27, 2007.

Categories & Tags

This article is filed under code, tools. It is further described as , , , , , , , , , , , .