CodeIgniter + Facebook Connect

In this post I will talk about how to create a Facebook Connect site using the CodeIgniter framework.

So first of all, before you being, download the Facebook Platform client library from here. Extract the files and copy everything inside /php folder (including the ‘jsonwrapper’ folder) to the /system/plugins folder of your CI application. Once you do that, rename the facebook.php file to facebook_pi.php. Doing so will make CI recognize the library as a plugin. Now you will be able to reference the library just how you would reference any other plugins as you will see below.

Once you’ve done that. We are all set to write some code. The first key step to this is to extend the base controller and create a Facebook Controller that can handle user authentication and pass other needed variables. This extended controller would have to reside in /system/application/libraries/MY_Controller.php:

class Facebook_Controller extends Controller {

    var $facebook;
    var $fb_uid;
    var $fb_user_details;
    var $user;
    var $permissions_to_request;
    var $logged_in;

    public $data;

	function Facebook_Controller() {

	parent::Controller();

        $this->output->set_header("Cache-Control: private, no-store, no-cache, must-revalidate, post-check=0, pre-check=0");
        $this->output->set_header("Pragma: no-cache");

        // Created some Facebook helper functions to make things easier and pull API Key, Secret Key, Base URLs, etc from the Facebook config file.
        $this->__fbApiKey = get_facebook_api_key();
        $this->__fbSecret = get_facebook_secret_key();

        // Prevent the 'Undefined index: facebook_config' notice from being thrown.
        $GLOBALS['facebook_config']['debug'] = NULL;

        // Create a Facebook client API object.
        $this->facebook = new Facebook($this->__fbApiKey, $this->__fbSecret);
        $this->fb_uid = $this->facebook->get_loggedin_user();

        //Check to see if the user is logged in with facebook
        if($this->fb_uid)
        {
            $fb_uid = $this->fb_uid;

            //If this is the first login with the site
            if(!$this->_get_user_session_object($fb_uid))
            {
                //Check to see if user is in the db
                $this->load->model('User_model');
                $this->user = $this->User_model->get_user_by_fb_uid($fb_uid);
                $this->fb_user_details = $this->Facebook_model->get_user_details($fb_uid);

                //If its not in the db, do an insert
                if(!$this->user)
                {
                   $user_data = array('fb_uid' => $fb_uid, 'created_at' => date('Y-m-d H:i:s'));
                   $user_id = $this->User_model->insert_user($user_data);

				   //Give them a default username
                   $user_data = array('username' => 'user'.$user_id);
				   $this->User_model->update_user($user_id, $user_data);

                    $this->user = $this->User_model->get_user_by_user_id($user_id);
                }

                $this->_set_user_session_object($fb_uid, $this->user);
                $this->_set_user_details_session_object($fb_uid, $this->fb_user_details);

                //Check to see if user has all the required permissions
                $has_publish_stream_permission = $this->facebook->api_client->call_method('Users.hasAppPermission',array('ext_perm'=>'publish_stream', 'uid'=>$fb_uid));
                $has_email_permission = $this->facebook->api_client->call_method('Users.hasAppPermission',array('ext_perm'=>'email', 'uid'=>$fb_uid));

                //Request the missing permissions
                $permissions_to_request = $has_publish_stream_permission == 0 ? "publish_stream" : null;
                if($permissions_to_request)
                    $permissions_to_request .= ",";
                $permissions_to_request .= $has_email_permission == 0 ? "email" : null;

                // I wanted to use this approach but it left a trailing comma in the end so it didn't work out. I'm sure there is a way around it.
                //$arry_permissions_to_request = array($has_publish_stream_permission == 0 ? "publish_stream" : null, $has_email_permission == 0 ? ",email" : null);
                //$permissions_to_request = implode(",", $arry_permissions_to_request);

                $this->permissions_to_request = $permissions_to_request;
            }
            else
            {
                //Load the user object back from the session
                $this->user = $this->_get_user_session_object($this->fb_uid);
                $this->fb_user_details = $this->_get_user_details_session_object($this->fb_uid);
            }
        }

        //Set master page data variables
        $this->data['user'] = $this->user;
        $this->data['fb_user_details'] = $this->fb_user_details;
        $this->logged_in = ($this->user && $this->fb_uid)? true : false;
        $this->data['logged_in'] = $this->logged_in;
        $this->data['request_permissions'] = $this->permissions_to_request ? true : false;
        $this->data['permissions_to_request'] = $this->permissions_to_request;
        $this->data['fb_login_prompt'] = false;
        $this->data['current_controller'] = get_class($this);
	}

    function _get_user_session_object($fb_uid)
    {
        return $this->session->userdata($fb_uid.'_user_object');
    }

    function _get_user_details_session_object($fb_uid)
    {
        return $this->session->userdata($fb_uid.'_user_details_object');
    }

    function _set_user_session_object($fb_uid, $user)
    {
        $this->session->set_userdata($fb_uid.'_user_object', $user);
    }

    function _set_user_details_session_object($fb_uid, $fb_user_details)
    {
        $this->session->set_userdata($fb_uid.'_user_details_object', $fb_user_details);
    }
}

Once we have that setup, we can go ahead and create a master page which will load the header, footer, navigation and all the other site-wide global elements. Part of which, the header, could look something like this, it would show the user’s profile pic & name when logged in and show a Facebook Connect button when logged out. You can put this master page inside /system/application/views/layouts/master.php:

<div id="header">
<?php if (!$logged_in) { ?>
<div style="margin-top:18px">
	<a href="#" id="fbconnect_login_button" class="fbconnect_login_button" onclick="FB.Connect.requireSession(); return false;" >
		<img id="fb_login_image" src="http://static.ak.fbcdn.net/images/fbconnect/login-buttons/connect_light_medium_long.gif" style="border:0;" alt="Connect"/>
	</a>
</div>
<?php } else { ?>
	<div class="floatR">
		<fb:profile-pic uid="<?=$user['fb_uid']?>" size="square" facebook-logo="true"></fb:profile-pic>
	</div>
	<div class="floatR" style="margin-top:13px">
	Welcome, <?=$fb_user_details[0]['name']?>
	<br />
	<a href="#" onclick="FB.Connect.logout(function() { facebook_onlogout(); }); facebook_onlogout()">Logout of Facebook</a>
	</div>
	<br class="clearFloat" />
<?php } ?>
</div>

Content area would just be one variable which will load the content from the page controller as we will see further on:

<div id="mainContent">
	<?=$content?>
</div>

And finally, the most important part, the footer which would load & initialize the Facebook library, take care of prompting for the right permissions, and require the user to login on certain pages based on the controller:

<div id="footer">
	<script type="text/javascript">
		function setFocus() { window.document.<?=$current_controller?>.focus(); }
	</script>
	<script src="<?=get_static_facebook_root();?>/js/api_lib/v0.4/FeatureLoader.js.php/en_US" type="text/javascript"></script>
	<script type="text/javascript">
		FB.init("<?=get_facebook_api_key();?>", "<?=base_url();?>xd_receiver.htm", {"reloadIfSessionStateChanged":true});
	</script>
	<script src="<?=base_url();?>shared/js/fbconnect.js" type="text/javascript"></script>
	<script type="text/javascript">
		window.onload = function() {
			facebook_onload(<?=$logged_in?>);
			<?php if ($request_permissions) { ?>facebook_prompt_permission("<?=$permissions_to_request?>");<?php } ?>
		};
	</script>
	<?php if ($fb_login_prompt && !$logged_in) { ?>
	<script type="text/javascript">
		FB.ensureInit(function() {
			FB.Connect.requireSession(null, function(){ location.href = "<?=base_url();?>home"; });
		});
	</script>
	<?php } ?>
</div>

As you can see, I’ve used some JavaScript in fbconnect.js to help us invoke the permissions and/or dialog box as needed:

/*
 * The facebook_onload statement is printed out in the PHP. If the user's logged in
 * status has changed since the last page load, then refresh the page to pick up
 * the change.
 *
 * This helps enforce the concept of "single sign on", so that if a user is signed into
 * Facebook when they visit your site, they will be automatically logged in -
 * without any need to click the login button.
 *
 * @param already_logged_into_facebook  reports whether the server thinks the user
 *                                      is logged in, based on their cookies
 *
 */
function facebook_onload(already_logged_into_facebook) {
  // user state is either: has a session, or does not.

  // if the state has changed, detect that and reload.

  FB.ensureInit(function() {
      FB.Facebook.get_sessionState().waitUntilReady(function(session) {
          var is_now_logged_into_facebook = session ? true : false;

          // if the new state is the same as the old (i.e., nothing changed)

          // then do nothing

          if (is_now_logged_into_facebook == already_logged_into_facebook) {
            return;
          }

          // otherwise, refresh to pick up the state change

          refresh_page();
        });
    });
}

function facebook_onlogin_ready() {
  refresh_page();
}

function facebook_onlogout() {
    $.post("./Logout", null, null);
}

function refresh_page() {
    location.reload(true);
}

function facebook_prompt_permission(permission) {
  FB.ensureInit(function() {
    FB.Connect.showPermissionDialog(permission);
  });
}

Now that we have the master page setup, we can inject our content into the master page from any page controller:

class Test extends Facebook_Controller {

   function Test()
   {
        parent::Facebook_Controller();
   }

   function Index()
   {
        $this->data['fb_login_prompt'] = true;
        $this->data['content'] = $this->load->view('test', $this->data, true);
        $this->load->view('layouts/master' ,$this->data);
   }

So, thats it! Now you should have a fully functional Facebook connect site in CodeIgniter!

Inspired by:
http://junal.wordpress.com/2008/01/20/a-sample-facebook-application-with-codeigniter/
http://www.simpleprojectz.com/2008/10/facebook-codeigniter/
http://codeigniter.com/forums/viewthread/120393/

If you have any questions or comments, please post them below. If you liked this post, you can share it with your followers or follow me on Twitter!