|
Posted on December 3rd, 2007 in Web Development by Greg
I just put together my first simple website for my new toy - my iPhone. It is a simple iPhone-specific interface for Yelp, my favorite source for restaurant and other local reviews. They have a ton of reviews for just about every local restaurant so I find myself using it all the time to find new restaurants to go to or to find more info about restaurants I already know. I could access the website just fine already on my phone, but it was a bit crowded and difficult to navigate on a small screen, so I decided it take this as a challenge to build my first iPhone web interface.
And yes, I’m probably about the 58th person to build a Yelp interface for the iPhone, but I was looking for something I’d actually use and learn from!
Yelp provides a REST search API that allows you to pass in a location and a search term and they return a JSON object with a list of matching businesses, their ratings, and the business info. I used the Zend Framework and the Prototype JavaScript library to put it together. I could have done a pure JavaScript implementation using script hacks, but figured it’d be better to have the PHP code serve as a basic proxy for the Yelp web service.
Try it out!
Posted on November 24th, 2007 in PHP by Greg
Looooong time since I've written anything here - about 9 months! This time around I've put together a simple PHP5 class for communicating with the Defensio web service. Defensio has an example PHP class, but unfortunately it is PHP4 only and it's more fun to rewrite it, right?
For those who don't know about Defensio, it is a web service for determining whether a comment on a blog or message in an application is spam. To use it, you must first register for an API key. Defensio is very similar to the venerable Akismet (see the article I wrote on Akismet) but they provide a couple other features such an indication of the 'spaminess' of an individual message. Akismet has worked out great for me (123,419 spam comments caught so far!), but I figured it would be good to try out something new as well.
The class is a PHP5 class and the public methods are reasonably well commented. The bottom of this post has a link to a test application and a download of the class/test application. So...the usage of this class is quite straightforward, as shown below:
PHP:
-
require('library/Gregphoto/Defensio.php');
-
require('library/Gregphoto/Defensio/Adapter/Streams.php);
-
$apiKey = 'mysecretapikey';
-
$siteUrl = 'http://my.secret.site.url.com';
-
-
$defensio = new Gregphoto_Defensio($apiKey,$siteUrl);
-
$defensio->setHttpClient(new Gregphoto_Defensio_Adapter_Streams());
-
-
-
'user-ip' => $_SERVER['REMOTE_ADDR'],
-
'article-date' => '2007/11/24',
-
'comment-author' => 'Big Bad Spammer',
-
'comment-type' => 'comment',
-
'comment-content' => 'please click links and buy viagra',
-
'comment-author-email' => 'bigbadspammer@annoying.com',
-
'comment-author-url' => 'http://www.annoying.com'
-
);
-
$result = $defensio->audit_comment($params);
-
if($result['spam'] == 'true') {
-
echo "Comment is spam with a spam score of " . $result['spaminess'];
-
} else {
-
echo "Comment is not spam";
-
}
A couple key points on usage:
- Each Gregphoto_Defensio object requires an Http adapter that it will use to make Http POST requests to the Defensio web service. I originally hardcoded it to use the Zend_Http_Client from the Zend Framework, but figured this could discourage people from using it. Then I wrote up a quick adapter to use PHP's Http Stream Wrappers, but realized that didn't work on my Dreamhost account, so I added in an extra one for the Curl extension. In theory I should have added a Gregphoto_Defensio_Adapter_Interface class, but I was lazy and didn't want to add in an extra file.
- Each Gregphoto_Defensio object can be used to make many requests
- All of the current Defensio API's are covered by the class. These are covered by the following methods in the Gregphoto_Defensio class:
- validate_key
- announce_article
- audit_comment
- report_false_negatives
- report_false_positives
- get_stats
- Each of these methods takes a single parameter, an associative array of options as defined by the Defensio API. Each API also returns an associative array of response parameters. Two static utility methods Gregphoto_Defensio::getActions (returns a list of defensio methods such as 'validate-key') and Gregphoto_Defensio::getActionDetails (returns a list of required parameters, optional parameters, and response parameters for a specific Defensio action) are provided in order to get the details of the input and output parameters.
I created a simple test application which can be used to test Gregphoto_Defensio and all of the APIs provided by Defensio.
Download the Gregphoto_Defensio class and the test application
Posted on June 10th, 2007 in Uncategorized by Greg
I just got back from 20 days of traveling to South Korea, India, and Singapore. I went to South Korea with Yenari to see her family and friends for about a week. After this I headed to Bangalore (India) for work while Yenari stayed in South Korea a bit longer. On the way back home I had a long layover (9 hours) in Singapore and got a chance to check out the city.
Check out my pictures here: http://picasaweb.google.com/gneustaetter/SouthKoreaIndiaAndSingapore
Posted on February 3rd, 2007 in PHP, Web Development by Greg
I put together a simple, lightweight PHP class for generating thumbnail images. The class is compatible with PHP5 and uses the GD2 extension (included by default with PHP5) to create JPEG, PNG, and GIF thumbnails. I've setup a Google Code project for it: Gregphoto_Image where you can checkout the source from SVN and file bugs.
The class has the following basic features:
- Ability to read JPEG, PNG, or GIF images
- Ability to output JPEG, PNG, or GIF images
- 4 modes of thumbnail creation
- MAX_HEIGHT - you specify a maximum height and the dimensions are calculated based off of the height
- MAX_WIDTH - you specify a maximum width and the dimensions are calculated based off of the width
- BEST_FIT - you specify a maximum height and width and the dimensions are calculated so that the thumbnail
is as large as possible without exceeding the maximum height or width
- EXACT - you specify a maximum height and width and these are directly used. Causes distortion if the
chosen aspect ratio is different from the aspect ratio of the image
- Renders/saves images in their input format by default, but allows changing the format. For example, input a GIF but output a PNG
- Fully documented object oriented code
- Fluent interface for creating thumbnails with a minimal amount of code
The class is licensed under the MIT license, which basically means it can be used and modified by anyone - for personal or commercial use.
You can Download it from the project page on Google Code. You can view examples of it running on my site - the examples are checked into SVN and can be viewed on the project site. You can also view the docs.
Example usage:
PHP:
-
require('path/to/Gregphoto_Image.php');
-
$image = new Gregphoto_Image('path/to/sample/image.jpg');
-
$image->setMaxHeight(200);
-
$image->setMaxWidth(200);
-
$image->setJpegQuality(90);
-
$image->resize(Gregphoto_Image::BEST_FIT);
-
$image->showThumbnail();
PHP:
-
$image = new Gregphoto_Image('../images/fan.jpg');
-
$image->setMaxHeight(200)->setJpegQuality(90)->resize()->showThumbnail();
PHP:
-
require('path/to/Gregphoto_Image.php');
-
$image = new Gregphoto_Image('path/to/image.jpg');
-
$image->setMaxHeight(200);
-
$image->setMaxWidth(200);
-
$image->setJpegQuality(90);
-
$image->setOutputType(IMAGETYPE_PNG);
-
$image->resize(Gregphoto_Image::BEST_FIT);
-
$image->saveThumbnail('path/to/thumbnail.png');
Enjoy!
Posted on January 16th, 2007 in Uncategorized by Greg
I haven't written anything in a long time so I figured it is time to write something up. Because a huge portion of my traffic seems to be related to my scriptaculous examples I've put up, I figured I'd add another one that covers the process of updating a database with the results of drag/drop with an Ajax call. Each time the user changes an item's position in the list it updates the database.
See the example (note: example uses Scriptaculous 1.7 Beta 2)
The methodology behind this is quite simple:
- Load the list values from the DB and show them on the screen
- Using JavaScript, setup the sortables
- Add a JavaScript callback to the drag/drop that points to a function that serializes the list and sends an Ajax request to the server
- On the server, loop through the list and update the database with the proper order
To do this I created three main files:
- index.php - the UI that shows the list and has the JavaScript
- ajax.php - the page that processes the Ajax request
- db.php - a simple database class included in both index.php and ajax.php that makes the connection to the DB and has methods to get the current list and to update the list
Reviewing Index.php
Index.php looks like this:
PHP:
-
<?
-
require('db.php');
-
$demo = new SortableExample();
-
$list = $demo->getList();
-
?>
-
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
-
<html>
-
<head>
-
<title>Scriptaculous 1.7 Sortables Ajax Example</title>
-
<link rel="stylesheet" type="text/css" href="style.css">
-
<script src="js/prototype.js"></script>
-
<script src="js/scriptaculous.js"></script>
-
<script>
-
Event.observe(window,'load',init,false);
-
function init() {
-
Sortable.create('listContainer',{tag:'div',onUpdate:updateList});
-
}
-
function updateList(container) {
-
var url = 'ajax.php';
-
var params = Sortable. serialize(container.id );
-
var ajax = new Ajax.Request(url,{
-
method: 'post',
-
parameters: params,
-
onLoading: function(){$('workingMsg').show()},
-
onLoaded: function(){$('workingMsg').hide()}
-
});
-
}
-
function handleResponse(req) {
-
// this function will fire after the ajax request is complete...but we have nothing to do here
-
}
-
</script>
-
</head>
-
<body>
-
-
<h2>Scriptaculous Sortables Demo with Ajax Callback</h2>
-
Built with Scriptaculous 1. 7 Beta 2. Drag items in the list below. Each time you update the list an Ajax call is made
-
that updated the database with the new order.<br><br>
-
-
<div id="listContainer">
-
<?
-
foreach($list as $item) {
-
?>
-
<div id="item_<?=$item['catid'];?>"><?=$item['category'];?></div>
-
<?
-
}
-
?>
-
</div>
-
<div id="workingMsg" style="display:none;">Updating database...</div>
-
-
</body>
-
</html>
Let's look at this in reverse order. At the bottom of the file there is a div with id 'containerDiv' (line 36). This is the container for our list. Within this is a small snippet of PHP that loops through an associative array and prints out each list item as a div with id 'item_12' where 12 is the ID for that record in the database (line 40). Lines 2-4 contain the PHP code that include the db.php file and use it's class to get the associative array that is used here.
Underneath the container div is another div that has the text 'Updating database...' - this div is hidden by default and will be shown while Ajax calls are in progress (line 45).
Now to the JavaScript...it starts off on line 14 with a call to Event.observe that tells the browser to call the init function after the page loads. The init function (line 15) creates a new sortable list on the listContainer element, setting all divs within it to be sortable and setting the updateList function as a callback whenever the list is updated. This means that when the order of items in the list change, this function will be called with the list element passed as an argument. This function will perform the Ajax request to update the database.
The updateList function (line 18) first sets a simple variable for the url used for the Ajax request. Next (line 20) it calls Sortable.serialize, passing the id of the container as an argument, which serializes the list to a format such as listContainer[]=5,listContainer[]=7,listContainer[]=2 - here, the first three items of the list would be the items 5, 7, and 2 and the divs representing these items would have the following id values: item_5, item_7, and item_2. This serialized list is set in the variable 'params'. Next, on line 21, an Ajax request is opened with Prototype's Ajax.Request class. It is called with two arguments, the url and an object with various options. The options include the type of request (POST in this example, parameters to pass (the params variable constructed on line 20), and two functions (onLoading/onLoaded) that handle the showing and hiding of the 'Updating database...' div. Normally in an Ajax request I'd also add a callback for onComplete to handle the response from the server, but in this case the Ajax response doesn't really matter to me because it is really only a one way communication in this demo - in a real application I'd have more error handling and would need to handle error conditions.
That's about it for the index page...not too bad, right?
Reviewing ajax.php
The file ajax.php is a simple file that is designed to receive the Ajax request and call the appropriate methods defined in db.php to update the list. Its contents are shown below:
PHP:
-
<?
-
-
require('db.php');
-
$demo = new SortableExample();
-
$demo->updateList($_POST['listContainer']);
-
?>
Yup, that's it. Basically it starts off by calling session_start() - I typically call session_start() at the top of pages like this because starting the session sends the right http headers to prevent the file from being cached and I'm too lazy to look up what the right headers are! Next it requires the db.php file, instantiates an object, and calls the updateList method of the object, passing the listContainer variable from the POST request (containing our serialized list). That's it for the ajax.php page.
Reviewing db.php
The file db.php is a simple database class that connects to the database and has methods to get the list and to update the order of the list. In a 'real' application I would definitely have a much cleaner implementation of the database access and would use other DB libraries to connect to the db - I used the simple mysql db functions to minimize on the number of lines needed here. Here are the contents of the file:
PHP:
-
<?
-
class SortableExample {
-
protected $conn;
-
protected $user = 'test';
-
protected $pass = 'test';
-
protected $dbname = 'scriptaculous';
-
protected $host = 'localhost';
-
-
public function __construct() {
-
$this-> conn = mysql_connect($this-> host, $this-> user, $this-> pass);
-
-
}
-
-
public function getList() {
-
$sql = "SELECT * FROM categories ORDER BY orderid";
-
-
-
-
$results[] = $row;
-
}
-
return $results;
-
}
-
-
public function updateList($orderArray) {
-
$orderid = 1;
-
foreach($orderArray as $catid) {
-
$catid = (int) $catid;
-
$sql = "UPDATE categories SET orderid={$orderid} WHERE catid={$catid}";
-
-
$orderid++;
-
}
-
}
-
}
-
?>
Important note - this file uses PHP 5 syntax in this object...you'd need some changes to get this to run in PHP 4. I'll skip talking about the constructor (which connects to the database) and the getList function (which...gets the list) in order to focus on the last method, 'updateList'. This function takes in an array of items, i.e. array(5,7,2), that come from the serialized list. It loops through the list and generates SQL UPDATE queries for each item. Each time it increments $orderid so that the first item has an orderid of 1, the 2nd an orderid of 2, etc. This means that whenever we get the list, as long as the SQL 'ORDER BY orderid' clause is used the list will show in the correct order.
Summary
That's it...hope you enjoyed it. The hopeful moral of the story is...Ajax is easy, and so is drag and drop....especially if you're using a good library. Personally I think that Prototype is incredibly useful for JavaScript development and Scriptaculous is a good sidekick for effects and drag/drop. I've also used the Yahoo YUI widgets at work where we needed to do some more dynamic widgets such as trees - it worked really well. Jack Slocum's YUI extension is incredibly well done too...though it's a heavy one in terms of file size! In the end, it comes down to finding the right tool for the job...
Download the sample application if you want to take a closer look....
|