Get Twitter RSS Feeds with CakePHP
Using CakePHP model get your RSS twitter feeds with caching and limits.
Installation
- Save this script below into your models directory
- Change your Twitter ID and Twitter Name
- Add to your $uses controller variable, public $uses = array('Twitter')
- Add to your controller method, $this->set('twits',$this->Twitter->find());
- Then add to your view
Model Class:
<?php
/**
* Get Twitter Updates
*
* $twits = $this->Twitter->find(array('cache'=>true,'limit'=>8));
*
* @author Darren Moore, zeeneo@gmail.com
* @link http://www.zeen.co.uk
*/
class Twitter extends AppModel
{
/**
* Your Twitter ID
*
* @var integer
* @access public
*/
public $twitterId = 14210082;
/**
* Remove your name from posts
* Set to false to not remove your name, otherwise set to your name
*
* @var mixed
* @access public
*/
public $twitterName = 'zeeneo';
/**
* Show replies to people
*
* @var boolean
* @access public
*/
public $showReplies = false;
/**
* Twitter RSS URL
*
* @var string
* @access public
*/
public $rssUrl = 'http://twitter.com/statuses/user_timeline/:twitterId.rss';
/**
* Turn off table usage
*
* @var string
* @access public
*/
public $useTable = false;
/**
* Duration of cache
*
* @var string
* @access public
*/
public $cacheDuration = '+30 mins';
/**
* Find Twitters
*
* @param array $options Options when getting twits, as followed:
* - cache: Force caching on or off
* - limit: Limit number of records returned
* @access public
* @return array
*/
public function find($options = array())
{
//Get twits
if((isset($options['cache']) && $options['cache'] == false) || ($twits = Cache::read('Twitter.lines')) == false)
{
$twits = $this->_getTwits();
Cache::set(array('duration' => $this->cacheDuration));
Cache::write('Twitter.lines',$twits);
}
//Set to limit
if(isset($options['limit']) && count($twits) > $options['limit'])
{
$twits = array_slice($twits, 0, $options['limit']);
}
return $twits;
}
/**
* Get Twitter Lines
*
* @access private
* @return array
*/
private function _getTwits()
{
//Get feed
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL,String::insert($this->rssUrl,array('twitterId'=>$this->twitterId)));
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 2);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$feed = curl_exec($ch);
curl_close($ch);
if(!$feed) { return false; }
$xml = new SimpleXmlElement($feed);
foreach($xml->channel->item as $item)
{
//
$title = (string)$item->title;
//Skip if it's a reply
if(!$this->showReplies && preg_match('/^'.$this->twitterName.': @/',$title))
continue;
//Remove name
if($this->twitterName)
$title = trim(preg_replace('/^'.$this->twitterName.':/','',$title));
$out[] = array(
'title' => $title,
'description' => (string)$item->description,
'pubDate' => strtotime($item->pubDate),
'link' => (string)$item->link
);
}
return $out;
}
}
?>








// turn URLs into hyperlinks
$tweet = preg_replace("/(http:\/\/)(.*?)\/([\w\.\/\&\=\?\-\,\:\;\#\_\~\%\+]*)/", "<a href=\"\\0\">\\0</a>", $tweet);
// link to users in replies
$tweet = preg_replace("(@([a-zA-Z0-9\_]+))", "<a href=\"http://www.twitter.com/\\1\">\\0</a>", $tweet);
The frustrating thing to me is that from time to time it will work, yet when I visit half an hour later, it won't.
Does anybody have an explanation for me? I a using the optimized model by Mark.
Controller:
<?php
class TweetsController extends AppController {
public $uses = array('Twitter');
var $name = 'Tweets';
function index(){
$export = $this->Twitter->find('stefanvanaken',array('limit'=>6));
return $export;
}
}
?>
Element:
<div id="twitter_box">
<?=$html->link($html->image('layout/twitter.png',array('border'=>0,'alt'=>'@stefanvanaken','class'=>'event_logo')),'http://twitter.com/stefanvanaken',array('target'=>'_blank'),false,false);?>
<? $tweets = $this->requestAction('tweets/index'); ?>
<? foreach ($tweets as $tweet) { ?>
<p><span style="font-size:10px;margin-bottom:10px;"><?=date('d-m-Y \o\m G:i',$tweet['pubDate']);?></span><br>
<?=$tweet['title'];?>
</p>
<? } ?>
</div>
Thanks a million!
Controller Class:
<?php$lastTweet = $this->Twitter->find('myTwitterUsername', array('cache'=>true,'limit'=>10));
debug($lastTweet);
?>
- supports channelInfos, twitterName (cached as well)
- now multiple find($accountId) possible (auto-reset feature)
- you can retrieve tweets by either twitterId or twitterName
- error logging for severe errors
<?php
/**
* Get Twitter Updates
* $twits = $this->Twitter->find($id, array('cache'=>true,'limit'=>8));
* NOTE: don't forget to sanitize or escape output: h() should work for most cases
*/
class Twitter extends AppModel {
/**
* Twitter ID
* @var integer/string
*/
private $twitterId = null;
/**
* Twitter Name
* @var string
*/
private $twitterName = '';
/**
* Remove twitterName from posts
* Set to false to not remove your name, otherwise set to your name
* @var boolean
*/
private $removeTwitterName = false;
/**
* Channel info
* @var array
*/
private $twitterChannel = array();
/**
* Show replies to people
* @var boolean
*/
private $showReplies = false;
/**
* Twitter RSS URL
* @var string
*/
private $rssUrl = 'http://twitter.com/statuses/user_timeline/:twitterId.rss';
/**
* Turn off table usage
* @var string
* @access public
*/
public $useTable = false;
/**
* Duration of cache
* @var string
*/
private $defaultDuration = '+30 mins';
private $cacheDuration = '';
/**
* Duration of cache
* @var string
*/
private $logErrors = true;
/**
* Error
* @var string
*/
private $error = '';
public function __construct() {
parent::__construct();
$this->reset(true);
# try to get default id from configs
$id = Configure::read('Twitter.id');
if (!empty($id)) {
$this->setAccount($id);
}
# set the cacheDuration to default one (can be overridden by setDuration() later on)
$duration = Configure::read('Twitter.cache_duration');
if (!empty($duration)) {
$this->cacheDuration = $duration;
} else {
$this->cacheDuration = $this->defaultDuration;
}
}
/**
* reset just error message or all values
* @param boolean $resetWholeAccount (default: false)
*/
public function reset($resetAccount = false) {
$this->error = '';
if ($resetAccount) {
$this->twitterId = null;
$this->twitterName = '';
$this->twitterChannel = array(
'title' => '',
'link' => '',
'description' => '',
'language' => '',
'ttl' => '',
);
}
}
/**
* @param int/string $account: twitter id or twitter name
* @param string $name: twitter name (optional)
*/
public function setAccount($account, $name = null) {
if (empty($account)) {
return false;
}
$this->reset(true);
$this->twitterId = $account;
if (!empty($name)) {
$this->twitterName = $name;
} elseif (!is_numeric($account) || strlen($account) != 8) {
$this->twitterName = $account;
}
return true;
}
/**
* retrieve current twitter accountName
*/
public function name() {
return $this->twitterName;
}
/**
* retrieve twitter channelInfos for current account
*/
public function channel() {
return $this->twitterChannel;
}
/**
* enable/disable logging of (severe) errors
*/
public function logErrors($value = true) {
$this->logErrors = (bool)$value;
}
/**
* enable/disable removal of twitterName from 'twitterName: xyz' (defaults to FALSE)
*/
public function removeName($value = true) {
$this->removeTwitterName = (bool)$value;
}
/**
* setDuration with string '+x Minutes' etc or with int 'x' seconds
* tip: null as param resets duration
*/
public function setDuration($cacheDuration = null) {
if ($cacheDuration === null) {
$cacheDuration = $this->defaultDuration;
}
$this->cacheDuration = $cacheDuration;
return true;
}
/**
* Find Twitters
* @param int/string $account: twitter id or twitter name (optional)
* - leave empty if defined before (or if set via Configure::write('Twitter.id'))
* @param array $options: Options when getting twits, as followed:
* - cache: Force caching on or off
* - limit: Limit number of records returned
* - duration: Cache Duration (if caching is on)
* @access public
* @return array
*/
public function find($account = null, $options = array()) {
$this->setAccount($account);
if (empty($this->twitterId)) {
$this->_setError('no twitterAccount found', true);
return array();
}
# setDuration
if(!empty($options['duration'])) {
$this->setDuration($options['duration']);
}
# get twits
if((isset($options['cache']) && $options['cache'] == false) || (($twits = $this->_getCachedTwits()) === false)) {
$twits = $this->_getTwits();
$this->_setCachedTwits($twits);
}
# set to limit
if(isset($options['limit']) && count($twits) > $options['limit'])
{
$twits = array_slice($twits, 0, $options['limit']);
}
return $twits;
}
/**
* get cached twitterItems, twitterCannel, twitterName
*/
private function _getCachedTwits() {
Cache::set(array('duration' => $this->cacheDuration));
$twits = Cache::read('Twitter.'.$this->twitterId.'.lines');
if ($twits != false && isset($twits['items'])) {
$this->twitterChannel = $twits['channel'];
$this->twitterName = $twits['name'];
$this->error = $twits['error'];
return $twits['items'];
}
return false;
}
/**
* cache twitterItems, twitterCannel, twitterName
* @todo can some specialchared twitterNames cause problems in filename???
* @todo save in subfolder of "cache" (not in root level!)
*/
private function _setCachedTwits($twits) {
$twits = array(
'items' => $twits,
'channel' => $this->twitterChannel,
'name' => $this->twitterName,
'error' => $this->error,
);
Cache::write('Twitter.'.$this->twitterId.'.lines',$twits);
return true;
}
/**
* Get Twitter Lines
* @access private
* @return array
*/
private function _getTwits() {
$out = array();
# Get feed
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL,String::insert($this->rssUrl,array('twitterId'=>$this->twitterId)));
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 2);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$feed = curl_exec($ch);
curl_close($ch);
if(!$feed) {
$this->_setError('Invalid request', true);
return array();
}
$xml = new SimpleXmlElement($feed);
if (!empty($xml->error)) {
$this->_setError((string)$xml->error); # usually no log entry neccessary
return array();
}
$this->twitterChannel = array(
'title' => (string)$xml->channel->title,
'link' => (string)$xml->channel->link,
'description' => (string)$xml->channel->description,
'language' => (string)$xml->channel->language,
'ttl' => (int)$xml->channel->ttl,
);
$twitterName = trim(str_replace('http://twitter.com/','',$xml->channel->link));
if (!empty($twitterName)) {
$this->twitterName = $twitterName;
}
foreach($xml->channel->item as $item) {
$title = (string)$item->title;
//Skip if it's a reply
if(!$this->showReplies && preg_match('/^'.$this->twitterName.': @/',$title))
continue;
//Remove name
if($this->removeTwitterName && !empty($this->twitterName)) {
$title = trim(preg_replace('/^'.$this->twitterName.':/','',$title));
}
$out[] = array(
'title' => $title,
'description' => (string)$item->description,
'pubDate' => strtotime($item->pubDate),
'link' => (string)$item->link
);
}
return $out;
}
/**
* return latest error (if any)
* @return string
*/
public function error() {
return $this->error;
}
private function _setError($error, $log = false) {
$this->error = $error;
if ($log && $this->logErrors) {
$this->log('ID: '.(string)$this->twitterId.' - '.$error,'twitter');
}
}
}
?>
Usage:
<?php
$this->Twitter->setAccount($account);
$this->set('twits',$this->Twitter->find());
or
Configure::write('Twitter.id', $account); // in configs e.g.
$this->set('twits',$this->Twitter->find());
or just
$this->set('twits',$this->Twitter->find($account, $options));
?>
Test-Cases are always a great way to ensure the model is working correctly:
<?php
App::import('Model', 'Twitter');
class TwitterTestCase extends CakeTestCase {
var $Twitter = null;
function startTest() {
$this->Twitter =& ClassRegistry::init('Twitter');
}
function testTwitterInstance() {
$this->assertTrue(is_a($this->Twitter, 'Twitter'));
}
function testTwitterFind() {
# valid ID
$id = 14210082;
$this->Twitter->setAccount($id);
$results = $this->Twitter->find();
$this->assertTrue(!empty($results));
pr ($results);
# invalid ID
$id = 14210081;
$this->Twitter->setAccount($id);
$results = $this->Twitter->find();
$this->assertTrue(empty($results));
$error = $this->Twitter->error();
$this->assertTrue(!empty($error));
pr ($error);
}
}
?>
- reset + indirect access functionality
- several twitterAccounts possible
- error handling (+ retrieval)
<?php
class Twitter extends AppModel {
/**
* Twitter ID
*
* @var integer
*/
private $twitterId = null;
/**
* Remove your name from posts
* Set to false to not remove your name, otherwise set to your name
*
* @var mixed
*/
private $twitterName = '';
/**
* Show replies to people
*
* @var boolean
*/
private $showReplies = false;
/**
* Twitter RSS URL
*
* @var string
*/
private $rssUrl = 'http://twitter.com/statuses/user_timeline/:twitterId.rss';
/**
* Turn off table usage
*
* @var string
* @access public
*/
public $useTable = false;
/**
* Duration of cache
*
* @var string
*/
private $cacheDuration = '+30 mins';
/**
* Duration of cache
*
* @var string
*/
private $logErrors = true;
/**
* Error
*
* @var string
*/
private $error = '';
public function __construct() {
parent::__construct();
# try to get default id from configs
$id = Configure::read('Twitter.id');
if (!empty($id)) {
$this->setId();
}
}
public function reset($resetAccount = false) {
$this->error = '';
if ($resetAccount) {
$this->twitterId = null;
}
}
public function setAccount($id, $name = null) {
$this->reset();
$this->twitterId = $id;
if (!empty($name)) {
$this->twitterName = $name;
}
}
public function logErrors($log = true) {
$this->logErrors = $log;
}
public function setDuration($cacheDuration) {
$this->cacheDuration = $cacheDuration;
}
/**
* Find Twitters
*
* @param array $options Options when getting twits, as followed:
* - cache: Force caching on or off
* - limit: Limit number of records returned
* - duration: Cache Duration (if caching is on)
* @access public
* @return array
*/
public function find($options = array()) {
if (empty($this->twitterId)) {
$this->_setError('no twitterAccount found', true);
return array();
}
# setDuration
if(!empty($options['duration'])) {
$this->setDuration($options['duration']);
}
# Get twits
if((isset($options['cache']) && $options['cache'] == false) || ($twits = Cache::read('Twitter.'.$this->twitterId.'.lines')) == false)
{
$twits = $this->_getTwits();
Cache::set(array('duration' => $this->cacheDuration));
Cache::write('Twitter.'.$this->twitterId.'.lines',$twits);
}
# Set to limit
if(isset($options['limit']) && count($twits) > $options['limit'])
{
$twits = array_slice($twits, 0, $options['limit']);
}
return $twits;
}
/**
* Get Twitter Lines
*
* @access private
* @return array
*/
private function _getTwits() {
$out = array();
# Get feed
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL,String::insert($this->rssUrl,array('twitterId'=>$this->twitterId)));
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 2);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$feed = curl_exec($ch);
curl_close($ch);
if(!$feed) {
$this->_setError('invalid request', true);
return array();
}
$xml = new SimpleXmlElement($feed);
if (!empty($xml->error)) {
$this->_setError((string)$xml->error); # usually no log entry neccessary
return array();
}
foreach($xml->channel->item as $item) {
//
$title = (string)$item->title;
//Skip if it's a reply
if(!$this->showReplies && preg_match('/^'.$this->twitterName.': @/',$title))
continue;
//Remove name
if($this->twitterName) {
$title = trim(preg_replace('/^'.$this->twitterName.':/','',$title));
}
$out[] = array(
'title' => $title,
'description' => (string)$item->description,
'pubDate' => strtotime($item->pubDate),
'link' => (string)$item->link
);
}
return $out;
}
/**
* return error (if any)
* @return string
*/
public function error() {
return $this->error;
}
private function _setError($error, $log = false) {
$this->error = $error;
if ($log && $this->logErrors) {
$this->log('ID: '.(string)$this->twitterId.' - '.$error,'twitter');
}
}
}
?>
When I get a spare 10 minutes I'll update this code to use HttpSocket, optional table caching and a few other clean ups, like using the word 'tweet' instead of 'twit' :)
That said.
1. I created the model as you've instructed called twitter.php under models folder
2. I changed the twitter id and the twitter username
3. I created a controller called TwittersController under controller folder
4. Added the code you mentioned, whch is here
class TwittersController extends Controller{
var $uses= array("Twitter"); // this is the model to use
//var $autoRender=false; // this specifies the view to use
function tweet(){
$this->set('twits',$this->Twitter->find());
}
}
?> 5. I am not using any view for now.
When I run this as
http://localhost/cake/twitters/tweet
it gives me an error stating:
Fatal error: Call to undefined function curl_init() in C:\xampp\htdocs\cake\app\models\twitter.php on line 99
Thanks for help in advance
You need to have curl installed on your server. I will update this script soon to use HttpSocket
I think it would be quite important to keep an eye on the quality of tutorials that are posted here. People will adapt bad techniques published here and problems will fall back on the cakephp project as "not beeing safe" or something. Thanks!
I have not included a view example. It is up to you how you escape the output and you'll do this in your view file.
Comments are closed for articles over a year old