Friday, 12 November 2010

Facebook Integration on a symfony project - part I

Facebook is the world's leading social network, with over 500 million users. As so, it is most useful to integrate your symfony project with it, bringing more users to your website, and providing them some commodities.
There is a symfony plugin, sfFacebookConnectPlugin, that does most of the things you would want. Unfortunately, and at the time I write this, it uses the old REST API, wich is no longer recommended, since facebook has adopted the Graph API. As so, this tutorial will cover how to make the integration using the official PHP SDK.

What Will I Cover?
- Facebook Connect
- Authentication
- Permissions
- Wall Publish
- App Tab (now on facebook pages only :/ )

You will need:
- To be using sfGuardPlugin
- Propel (some simple queries, used old Criteria for compatibility)
- Have a field in your user profile table to store the facebook uid (optional, but recommended; preferably an index to speed up search)
- Javascript SDK
- PHP cUrl enabled
- A Facebook App:

You can download the Javascript SDK from

Facebook Connect
You must have already seen the facebook popup in many sites, either asking for your login details (if not logged on facebook), or for permissions, or for sharing content. To get the same experience on your symfony project, you must use Javascript SDK.
After downloading the .js and placing it in your web/js directory (and perhaps renaming it facebook.js), you will need to include it in all the pages you want the user to be facebook connected. This is so because the user may logout of facebook at any time, so you need to keep checking the user login status everytime a page loads. So go ahead and add facebook.js to the application config/view.yml, on the javascripts section.
Now you need to call the initializer on your pages. You may either hardcode it on your layout file, create a partial for it in case you use many different layouts, or even create a slot, if you will only need it on certain pages. It's up to you and whatever your needs are.
So this is how you can call the initializer:

<div id="fb-root"></div>
    appId  : 'YOUR APP ID',
    status : true, // check login status
    cookie : true, // enable cookies to allow the server to access the session
    xfbml  : true  // parse XFBML

You will need to have the div "fb-root" somewhere on the page.
There is also an asynchronous way to do it, check this link

The single click signin is, in my opinion, the best feature in facebook integration.

The Button:
The login button comes with the Javascript SDK, and is one of the facebook "social plugins".
You can put it anywhere by simply typing <fb:login-button></fb:login-button>. You can also configure it by passing arguments (check the social plugin page) and write any text inside the tag to personalize the button text.
Pressing the button will either popup a window asking for facebook user and password (if user is not logged on and has no cookie stored), or for permissions if it's the first visit to your site, or nothing at all for the rest. But before the popup windows can appear, it is needed to overcome javascript security concern limitation, cross-domain requests. For that, you will need the file xd_reciever.html inside you web/ directory. You can create it and paste this code inside it.

The login:
You will now want to login the user into your site. You will need to check the user status on the facebook platform and redirect him accordingly. You can do that with the following code, which has to be used after FB.init is executed (can be right after it, still inside the script tag):

    FB.Event.subscribe('auth.sessionChange', function(response) {
        if (response.session) {
          // A user has logged in, and a new cookie has been saved
            window.location="<?php echo url_for('@facebook_connect',true); ?>";
        } else {
          // The user has logged out, and the cookie has been cleared
                window.location="<?php echo url_for('@homepage',true); ?>";

This code will check any change in the user facebook session. It redirects the user to the route facebook_connect  (you can of course just link it to the module/action route) if he has/is logged in, and to the homepage if he has logged out. You now need to create the action to manage the user session and authenticate the user into sfGuard; I will use facebook/connect on this tutorial.
On the connect action you will switch to the PHP SDK. This sdk will allow you to connect to facebook and retrieve the user account details, allowing you to authenticate him on your application. You will only need to have the facebook.php file somewhere inside your lib folder, so that it is autoloaded.
I will cover three user cases:

  1. The user has logged on to your website before, using facebook connect.
  2. The user is registered to your website, but its the first time he logs using facebook connect.
  3. The user is new to your website, therefore, he is not in your database.

        $facebook = new Facebook(array(
                'appId'  => 'YOUR APP ID GOES HERE',
                'secret' => 'YOUR APP SECRET GOES HERE',
                'cookie' => true,

        $session = $facebook->getSession();
        $me = null;
        // Session based API call.
        if ($session) {
                $uid = $facebook->getUser();
                $me = $facebook->api('/me');
                $c=new Criteria();
                if($user) //User case 1 - you have the fb uid in your database
                        $c=new Criteria();
                        if($user) //User case 2 - you don't have fb uid, but you do have his e-mail
                                //Store his uid
                          //User case 3 - you might register an account for him, or redirect to register form with some pre-filled fields.

                       //Your user might be coming from an external link, so redirect to the intended link
          catch (FacebookApiException $e) {

You may ask: why check for the user facebook uid, if all we need is the e-mail??? For starters, you can change the e-mail associated with your facebook account at any time. Secondly, it happens that a facebook user can have multiple e-mails associated with his account. Being so, you may prevent a facebook connected user from your database from having to register again on your website, just because his facebook e-mail has changed.