Showing posts with label php. Show all posts
Showing posts with label php. Show all posts
Tuesday, 5 August 2008

Installing the Facebook Open Platform

Facebook is cool. It's a little past its best now (it may be seen as 2007's YoYo) but the technology underneath it is proven, scalable and solves many of the problems any site wanting to introduce some 'magic social dust' contains.

Now, Facebook have released their Facebook Open Platform. This looks interesting. Not sure what I can do with it yet, but its a nice thing to start messing around with - particularly the FBML and FBJS implementations - FBML especially as it's a lovely thing which we can use for all kinds of simple site layout things.

It wasn't totally easy getting it up and running, but here's my method which you're welcome to follow if you'd like.

Background

I'm running Apache on CentOS with PHP 5.1.6 already installed and working with Apache. In this tutorial, I'll assume you have:
  • Apache 2+
  • PHP 5+ working with Apache
  • CentOS
  • FTP/sFTP access to your server
  • command line access with sudo permissions where needed (for restarting Apache and editing httpd.conf for example)
  • MySQL 5+ working with PHP

Getting Started

  1. Grab the code from Facebook: http://developer.facebook.com/fbopen/
  2. Unzip
  3. FTP to your server and create a folder in your webroot like 'fbop'. This will be where your instace of the platform lives i.e http://www.example.com/fbop/
  4. Upload the contents of the 'html' folder in the unzipped fb-open-platform folder into your new 'fbop' folder on your web server. If you now go to http://www.example.com/fbop/fbopentest/fbml.php you'll see a ton of errors. That's okay, we'll fix them in a bit.
  5. Now, choose where you want your facebook open platform libraries to live. Sensibly, this will be away from your webroot, but with all your other php libraries. I've chosen /var/www/fblib/lib. Go ahead and create this folder and upload the contents 'lib' folder from your unzipped fb-open-platform folder.
You should now have the following files uploaded to your webserver in roughly the following ways.

  • /var/www/fblib/lib/
    • api/
    • common.php
    • core/
    • display/
    • .....
  • /var/www/html/fbop/
    • api/
    • canvas.php
    • common/
    • fbml/
    • fbopentest/
    • js/
So far so good. But we still have all those errors at http://www.example.com/fbop/fbopentest/fbml.php

Configuring Apache

Facebook have been a bit wierd and rather than telling PHP where to look for includes like most apps, they've set a custom Apache variable which PHP then uses to find some files. This isn't easy info to find out by Googling, but its very simple to implement.
  1. Open your httpd.conf file.
  2. At the bottom, or near your other non-standard config changes, add the line...
    SetEnv PHP_ROOT "/var/www/fblib"
  3. This sets a new variable constant in Apache called 'PHP_ROOT'. From within PHP this is now accessible using the $_SERVER["PHP_ROOT"] variable. The lack of this variable being present was that cause of all those errors we've been seeing.
  4. Restart Apache. For me on CentOS, this means running $sudo /sbin/service httpd restart
  5. You should now be able to test if this has worked by creating a new PHP file, and adding (inside you php braces, this line... print_r($_SERVER); . Opening that in a browser will list all the server variables available to PHP. Magically, one of them should be PHP_ROOT with the value you specified earlier. This is good.
Now, going to http://www.example.com/fbop/fbopentest/fbml.php will give us no errors! Sadly it won't do much else.

Configuring the database

Just like Facebook itself, running apps on the Platform requires the Platform to keep a list of all the applications running on it. In the Open Platform, this is held in a MySQL database. Helpfully, they provide you with enough data to get you started, you just have to load it into MySQL.
  1. Connect to MySQL using your favourite client - I'll use phpMyAdmin
  2. Create a user which which we'll give to the Platform so it can access the database
  3. In the SQL Tab, copy and paste the contents of the file /fbopen_data_dump where fb-open-platform is the archive you downloaded and unzipped from Facebook
  4. Run the Query
You should see a database called 'fbopentest' has been created with 12 tables. If you can't see this, the query may have errored. Have a look at your debug and see why. In my case, phpMyAdmin threw an error at the comments which had three dashes preceeding them '---' instead of the usual two '--'. Odd, but thats textual SQL for you. It never works first time. Changing those in the three lines in which it appeared got me up and running.

Now we have to configure the Platform to talk to the database.
  • On your web server, edit a file in the fb open platform lib directory - in our example, I mean this file: /var/www/fblib/lib/core/init.php
  • Change $DB_USERNAME to match the user you created. In my case it was 'fbopenplatform'
  • Change $DB_IP to match IP address of your database server. In most cases, this will just be 'localhost'
  • Change $DB_PASSWORD to match the user's password you created earlier. I'm not going to tell you what mine was ;-)
  • Save the file.
To test if this has been successful,

Hold up. You with me?

Before we go any further, lets test that the basics of the facebook open platform are up and running. In a browser, go to your http://www.example.com/fbop/fbopentest/ folder. You should see a nice list of test file we can use. At present, the most interesting one is http://www.example.com/fbop/fbopentest/fbml.php. You'll see that it loads, and you can see all the code if you view source, but its not parsing it into HTML from FBML. That's because we haven't installed the FBML parsing libraries yet!

This is a little more tricky for the novice as we're not messing about with PHP anymore - but proper unix and C++ stuff, but lets have a go. I would however, ensure you have a backup of your server or virtual machine. If this screws up, it has the capacity to ruin your install of a number of other things as Sean B discovered.

Installing the FBML Parser


Back soon to finish this off.

Friday, 18 April 2008

CakePHP Tree Behaviour

Its often really useful to be able to represent data in a tree, or self-referential way - like product categories, music genres, organisational structures etc. Often, that takes quite a bit of code. However thanks to CakePHP, we can do it in far less lines of code, thanks to a behavior var $actsAs = array('Tree');.

First create a db table as normal with an id and title. Then add three special fields: 'parent_id', 'lft' and 'rght' - all Integers. Cake uses these to manage the hierarchical relationships between each row, and it does this all behind the scenes. Next, add the behaviour tag to your model. My example is for Genres. and its as easy as this... in genre.php...


var $actsAs = array('Tree');


Some of the clever stuff happens in the Controller to make nice URL's so lets look at that now.

What I want is a URL structure like this www.example.com/genres/genre1/genre2/genre3 etc where genre 3 is the child of genre 2 which is the child of genre 1 etc. To get this behaviour, we first add a line to our app/config/routes.php file.


Router::connect('/genres/*', array('controller' => 'genres', 'action' => 'index'));


Its a very simple line which routes any sub URL to the index action of the genre controller. Then we can do all the goodness there. So, in our genres controller, we need to get to work building a clever index action.

First, we get the current URL, split it into an array on every slash using explode. If there are no other URL paths, we assume we're at the route and list all top level genres accordingly. If there are other paths, then we can lookup current genre (the last element in the array) from the database.


function index(){
$url = $this->params['url']['url'];
$genreUrl = explode('/',$url);
if (count($genreUrl)>1){
$genreCurrent = $genreUrl[count($genreUrl)-1];
if($genre = $this->Genre->findByTitle($genreCurrent)){
pr($genre);
}else{
$this->Session->setFlash('There such genre as ' . $genreCurrent);
$this->redirect(array('action' => 'index'));
}
}else{
$genres = $this->Genre->findAll("parent_id IS NULL");
pr($genres);
}
}


There are some problems with this so far. It only works if all your genre names are unique, and worse, allows you to specify any path (like www.example.com/genres/blah/this/is/wrong/validGenreName) where validGenreName is valid at any level, but the path to it does not reflect it's true higherachy. To fix this, we're now going to parse every genre given in the URL, and check to see if the one before it, and the one after it in the DB match those in the URL. This way we can ensure the URL is a true reflection of the database structure.

I've got to go now, but I'll come back and finish this post when I get another chance.

There's more info on Tree Behaviour in the CakePHP 1.2 manual.

Text to Speech

Now this is incredible.

http://www.cereproc.com/demo.html

And this... https://www.cepstral.com/demos/

Text to speech has come on a long way from the Stephen Hawking-esq stuff of the 80's.

Wanna have a real play? Make George W Bush say some dodgy stuff in this demo.

Thursday, 10 April 2008

HABTM Relationship building in CakePHP

Yeah, I know its not as good as rails, but i'm trying to get to grips with it anyway: CakePHP. I'm gonna start using this blog as a way of keeping a note of things I learn as I build my first apps - mainly for my own benefit, but also to help the active and very friendly CakePHP community. Documentation for 1.2 is currently shocking, so it takes a lot of trawling to get what you need - so I need my own reference.

That said, heres my first nugget of gold.

Has And Belongs To Many (HABTM) relationships are great, but manipulating those relationships is hard. If you want to create a new object, and save relationships to it a the moment of creation, I found it hard to work out how to do both at the same time. Turns out its all about how you push data into $this->data and then pass this to be saved.

I needed to create a new Bulletin object, tie the current logged in user to it, and fill it with 5 (currently random) news stories which already exist. First I found the user_id of the current user and added that relationship to the Bulletin object using...

$this->Bulletin->create();
$user = $this->Auth->user('id');
$this->data->Bulletin->user_id = $user;

Then I got an array of the stories I wanted to attach (currently 5 random ones). Then, heres the tricky part, I had to extract their arrays, as the data findAll() returns can't be shoved into $this->data. Instead, $this->data wants an array of IDs which it can save. To do this, I did it messily....

$stories=array();
$this->Bulletin->Story->recursive = 0;
$stories = $this->Bulletin->Story->findAll(null, null, 'RAND()', 5);
$story_ids = array();
foreach($stories as $story){
$story_ids[] = $story["Story"]['id'];
}
$this->data->Story->Story = $story_ids;

Now that $this->data contains everything we want, we can just save it as normal by passing $this->data to the $this->save() method.

if($this->Bulletin->save($this->data)){
$this->Session->setFlash('A new bulletin has been generated for you');
$this->redirect('/bulletins/view/'.$this->Bulletin->id);
}else{
$this->Session->setFlash("There was an error building your bulletin");
$this->redirect('/bulletins');
}

Voila. This works nice. I hit a url like /bulletin/generate, and the system generates a random Bulletin object for me, and redirects me to its view page. Its a bit hacky tho, so any suggestions would be greatfully received.