Exploring FQL: Facebook Query Language Complete information on it

Exploring FQL: Facebook Query Language


In This Chapter Understanding how FQL differs from SQL Advantages of FQL over the Facebook API methods Writing a basic FQL query Creating subqueries with the IN operator Working with FQL functions Some Web developers love APIs, although others would much prefer to do everything via database queries. API folks like working in their normal language, although query-hounds prefer the efficiency of SQL, the lingua franca of the relational database world. If you read Chapter 3, you already know that you can use Facebook API calls to access Facebook data. However, the Facebook Platform also enables you to access the same databases using a query language as well. In this chapter, you explore Facebook Query Language (FQL) and walk through steps you can take to access Facebook social data in your applications. Along the way, I point out certain technical advantages that FQL has over the API. Discovering Why SQL + Facebook API = FQL FQL gives Facebook developers traditional SQL-like access to Facebook databases. SQL stands for Structured Query Language, which is the tried-and-true standard query language for relational database systems. Therefore, if you are used to SQL keywords like SELECT, WHERE, and IN, you will find yourself right at home querying Facebook data with FQL. 10_277959-ch05.qxp 5/5/08 11:26 AM Page 121 Comparing FQL and API access The data-gathering capabilities of FQL are identical to many of the API calls. (Psstt, here’s a secret — the API calls are actually wrappers around lowerlevel FQL calls!) However, before dismissing FQL as redundant, it is important to recognize that FQL does have some notable advantages over API access to Facebook, including the following: FQL is more efficient. When you retrieve data using the API, you bring back all of the field data for a given record. However, with effective use of the SELECT clause in an FQL statement, you can specify only those specific fields you want to have included in the result set. In fact, FQL does not even support selecting all records in a table with SELECT * statements. FQL can reduce the number of server requests for complex requests. When you are retrieving data, you often find yourself making an initial request, working with the results, and then making a second request for more specific information. I show that practice in Chapter 3. For example, suppose I need to get the name and status of all members attending an event. Using the API, I would call events.getMembers to get all members attending a specific event. Next, I would take that array of members and use users.getInfo to get the name and status for each of them. In contrast, with FQL, these two requests can be done inside a single complex query (using the IN clause for a subquery). FQL is Web-language neutral. The Facebook API has several versions, each of which is written for a particular programming language, such as PHP, Java, or Ruby on Rails. Therefore, if you work in multiple programming environments, your Facebook code must be specific to each one. In contrast, because FQL is programming-language independent, you can use the same FQL statements anywhere. In the end, even if you prefer to spend most of your time working with the API, you may find specific occasions in which you want to take advantage of the efficiency of FQL. 122 Part II: Poking the API Database terminology If you are new to the world of databases, here’s a quick primer to the terminology used. A database consists of one or more tables of data. Much like a spreadsheet, a table contains fields (columns) that capture different types of info and records (rows) of actual data. 10_277959-ch05.qxp 5/5/08 11:26 AM Page 122 Making an FQL statement To make an FQL query, use the fql.query API method. Its syntax in PHP looks like this: $result_set = $facebook->api_client->fql_query(“SELECT name FROM user WHERE uid=665127078”); The fql_query method sends the FQL query string to Facebook for processing. The results are returned to the PHP client as an array. Note that you can also return results in XML and JSON format as well for other clients. The array result set you’d get back in PHP would be: ( [0] => Array ( [name] => Rich Wagner ) ) You can then work with the result set just like you would with an array obtained from a Facebook API call. (See Chapter 3 for complete details on working with result sets.) Note that because Facebook manages the database connections for you, you do not need to open and close databases as part of your application. Differences between SQL and FQL Although FQL is based on SQL syntax, the two are not the same. SQL is designed to be a flexible query language in a variety of contexts. FQL, on the other hand, is a query language for a specific, very targeted data set. Therefore, FQL has some limitations compared to SQL that you need to be aware of, particularly if you already know SQL. These include the following: Chapter 5: Exploring FQL: Facebook Query Language 123 It’s a matter of pronunciation SQL is usually pronounced ess-que-ell, though some prefer to call it sequel. However, I don’t think the same carries over too well in the Facebook Platform. You should pronounce FQL like eff-que-ell, which sounds much more tasteful than the alternative fequel. 10_277959-ch05.qxp 5/5/08 11:26 AM Page 123 SELECT * is not allowed. You need to specify all the fields by name in which you want to include in the result set. The FROM clause can only include a single table. At least one field in the WHERE clause must be classified as indexable. JOIN is not supported (though IN subqueries are). The GROUPBY, ORDER BY, COUNT, and LIMIT keywords are not supported. The BETWEEN and LIKE operators are not supported. Because you have read-only access, you obviously cannot use keywords like UPDATE, DELETE, INSERT INTO, or CREATE TABLE. Writing a Basic Query An FQL query has at least three parts to it: SELECT [fields] FROM [table] WHERE [conditions] Take a look at each of the parts: The SELECT clause lists the fields that you want to include in the result set. The FROM clause identifies the table in which you want to search. (Table 5-1 lists each of the 16 tables available.) The WHERE clause specifies the conditions that need to be met to create a match. As with all FQL keywords, you don’t have to capitalize SELECT, FROM, or WHERE. The FQL constructs are case insensitive. However, it is common practice to capitalize keywords to make the



 query itself more readable. Table 5-1 Facebook Tables Table Contains Info About album Photo albums cookies Cookies event Events 124 Part II: Poking the API 10_277959-ch05.qxp 5/5/08 11:26 AM Page 124 Table Contains Info About event_member Invited members of an event friend Friends of a user friend_request Requests to be friends with current user friendlist Friend lists for current user friendlist_member Members of a friend list of current user group Groups group_member Members of a group listing Facebook Marketplace listing page Facebook pages page_fan Fans of a Facebook page photo Photos photo_tag Photo tags user Users To demonstrate, if you want to return the name of a user whose user ID is 665127078, here’s the query: SELECT name FROM user WHERE uid=665127078 Using fql.query, the results are returned to the calling application in one of three formats. As shown earlier, if you are using PHP, then you’ll receive the results as an array. However, if you working with other languages, you can specify the result format as a parameter in the API call (see Chapter 2). Plain XML displays the following: Rich Wagner If you use JSON format, it outputs as [{“name”:”Rich Wagner”}] Chapter 5: Exploring FQL: Facebook Query Language 125 10_277959-ch05.qxp 5/5/08 11:26 AM Page 125 Finally, as I show earlier in the chapter, the array-based output for PHP is Array ( [0] => Array ( [name] => Rich Wagner ) ) Every FQL query must contain at least one field in the WHERE clause that can be classified as indexable. If you specify anything else, you get an error. (I mention this earlier in the section “Differences between SQL and FQL,” but let me go into greater detail.) The indexable fields are typically the unique IDs of the table (such as uid or pid). Table 5-2 lists each of the indexable fields for the tables. Table 5-2 Indexable Fields Table Indexable Fields album aid, cover_pid, owner cookies uid event eid event_member uid, eid friend uid1, uid2 friend_request uid_to friendlist owner friendlist_member flid group gid group_member uid, gid listing listing_id, poster page page_id, name page_fan uid photo pid, aid photo_tag pid, subject user uid, name 126 Part II: Poking the API 10_277959-ch05.qxp 5/5/08 11:26 AM Page 126 The reason behind the indexable field requirement is simple. This requirement means that people cannot simply harvest random data (such as all the users with the first name of Reggie) from the Facebook database. You need to have some specific data points (such as a particular user or event) to fetch information. You can add multiple fields in the SELECT clause simply by separating them with a comma. For example: SELECT name, movies FROM user WHERE uid=665127078 This query returns the name and list of favorite movies of the user 665127078. Changing the field order in the result set Because you usually reference a piece of data by its field name instead of its field index, the order in which the fields are returned is often less important within the Facebook Platform context. However, using FQL, you can alter the order of the fields based on the order in which you list the fields in your query. For example, the following query returns the name and favorite movie list of a movie fan: SELECT movies, name FROM user WHERE uid=665127078 If you are using PHP, the fuller example would look like: $uid = “665127078”; $FQL = “SELECT movies, name FROM user WHERE uid=$uid”; $result_set = $facebook->api_client->fql_query($FQL); echo “
”;
print_r($result_set);
echo “

”;
Although movies normally appears below the name in the actual user table,
the result set switches the order. Here are the XML results:



Casablanca, The Shawshank Redemption, It’s A Wonderful Life,
Babette’s Feast, Field of Dreams, Band of Brothers, Chariots of
Fire, Amélie (Le Fabuleux destin d’Amélie Poulain), Henry V (1989
version), The Truman Show
Rich Wagner


Chapter 5: Exploring FQL: Facebook Query Language 127
10_277959-ch05.qxp 5/5/08 11:26 AM Page 127
Or, results using PHP would be as follows:
(
[0] => Array
(
[movies] => Casablanca, The Shawshank Redemption, It’s A Wonderful
Life, Babette’s Feast, Field of Dreams, Pride & Prejudice (A&E
Version), Band of Brothers (HBO), Chariots of Fire, Amélie (Le
Fabuleux destin d’Amélie Poulain), Groundhog Day, Braveheart,
Princess Bride, Les Miserables (1998 version), Signs, The Lord of
the Rings trilogy, Benny & Joon, Vertigo, Sense & Sensibility,
The Count of Monte Cristo, A Little Princess, Double Indemnity,
Forrest Gump, The Incredibles, The African Queen, Henry V (1989
version), The Truman Show
[name] => Rich Wagner
)
)
Dealing with array-type fields
As I discuss in Chapter 2, some of the fields are containers (or arrays) of
data. For example, the event table has a venue field that actually contains
six pieces of site-related data, including city, state, country, latitude,
and longitude. You can retrieve the entire venue array with the following
statement:
SELECT eid, venue FROM event WHERE eid=101022926233
However, if you want to retrieve just a single venue-related field, you can use
dot notation to reference the subfield. For example, if you wanted to just
retrieve the city, you could modify the statement as follows:
SELECT eid, venue.city FROM event WHERE eid=101022926233
Using operators in the WHERE clause
In the examples I have shown so far, the WHERE conditions have always used
the equal sign to specify the matches, such as this:
WHERE pid=12920201292
However, FQL allows you to use several other operators to add power to your
queries. Consider a scenario in which you want to retrieve all the events of a
128 Part II: Poking the API
10_277959-ch05.qxp 5/5/08 11:26 AM Page 128
user 664567292 that have an eid of less than or equal to 7630890908. Here’s
the query:
SELECT eid FROM event_member WHERE uid=664567292 AND eid<= 7630890908
The <= operator is used to retrieve all the eids that are less than or equal to
7630890908. This query also uses the AND operator to add a Boolean condition to the statement — ensuring that both conditions are met. Table 5-3 lists
all the available operators.
Table 5-3 WHERE Operators
Operator Description
= Equal to
<> Not equal to
> Greater than
>= Greater than or equal to
< Less than
<= Less than or equal to
AND, OR, NOT Boolean operators
IN Used to create a subquery when an exact value is known
for one of indexable fields
Writing More Complex Queries
with the IN Operator
Shakespeare’s Hamlet may have featured a “play within a play,” but that’s old
school now. Facebook’s FQL can top that with its support for “a query within
a query.” You connect queries with the IN clause.
For example, suppose you wanted to retrieve the name and location of all the
events attached to a particular Facebook page (8645672921). The problem is
that because the event table has only eid as an indexable field, you cannot
use the page ID in a query on the event table.
Chapter 5: Exploring FQL: Facebook Query Language 129
10_277959-ch05.qxp 5/5/08 11:26 AM Page 129
However, the event_member table provides the link you are looking for. I can
use the following query to retrieve the eids that are linked with the page ID:
SELECT eid FROM event_member WHERE uid= 8645672921
In PHP, the fuller example would be:
$uid = “8645672921”;
$FQL = “SELECT eid FROM event_member WHERE uid=$uid”;
$result_set = $facebook->api_client->fql_query($FQL);
echo “
”;
print_r($result_set);
echo “
”; Here’s the XML output: 7688563787 9542531788 9610357779 7714358414 20803947584 9405416779 11573937673 9374611460 7630890908 130 Part II: Poking the API 10_277959-ch05.qxp 5/5/08 11:26 AM Page 130 The PHP result set would look like: Array ( [0] => Array ( [eid] => 7352524755 ) [1] => Array ( [eid] => 11501651899 ) [2] => Array ( [eid] => 7902052238 ) [3] => Array ( [eid] => 8181301283 ) [4] => Array ( [eid] => 8071913190 ) [5] => Array ( [eid] => 9536366299 ) [6] => Array ( [eid] => 8169259038 ) [7] => Array ( [eid] => 20951632704 ) [8] => Array ( [eid] => 7552445899 ) [9] => Array Chapter 5: Exploring FQL: Facebook Query Language 131 10_277959-ch05.qxp 5/5/08 11:26 AM Page 131 ( [eid] => 20424183216 ) [10] => Array ( [eid] => 7688563787 ) [11] => Array ( [eid] => 9542531788 ) [12] => Array ( [eid] => 9610357779 ) [13] => Array ( [eid] => 7714358414 ) [14] => Array ( [eid] => 20803947584 ) [15] => Array ( [eid] => 9405416779 ) [16] => Array ( [eid] => 11573937673 ) [17] => Array ( [eid] => 9374611460 ) [18] => Array ( [eid] => 7630890908 ) [19] => Array 132 Part II: Poking the API 10_277959-ch05.qxp 5/5/08 11:26 AM Page 132 ( [eid] => 8082232798 ) [20] => Array ( [eid] => 9630616527 ) [21] => Array ( [eid] => 19725430231 ) [22] => Array ( [eid] => 7166054484 ) [23] => Array ( [eid] => 7562862350 ) [24] => Array ( [eid] => 8068494785 ) [25] => Array ( [eid] => 7179584756 ) [26] => Array ( [eid] => 8325821164 ) ) Now that you have the eids you need, you must feed each of them back into a query of the event table to retrieve its name and location. Fortunately, this is very easily done using the IN operator, which essentially creates a query within a query. Continuing with the event example: Here’s how you should construct its query using the IN operator: SELECT name, location FROM event WHERE eid IN (SELECT eid FROM event_member WHERE uid= 8645672921) Chapter 5: Exploring FQL: Facebook Query Language 133 10_277959-ch05.qxp 5/5/08 11:26 AM Page 133 Using PHP, the full example code is: $uid = “8645672921”; $FQL = “SELECT name, location “ . “FROM event “ . “WHERE eid “ . “IN (“ . “SELECT eid “ . “FROM event_member “ . “WHERE uid=$uid” . “)”; $result_set = $facebook->api_client->fql_query($FQL); echo “
”;
print_r($result_set);
echo “
”; When Facebook evaluates the query, the subquery (the query inside the IN parentheses) is performed first. Next, the outer query is then performed using all the eids returned from the subquery as eid values in the WHERE clause. When executed, the desired results are now returned: Stage 24: Walterboro, SC to Charleston, SC Charleston, SC Stage 23: Waynesboro, GA to Walterboro, SC Passing through the South Stage 22: Monroe, GA to Waynesboro, GA Georgia Stage 21: Chattanooga, TN to Atlanta, GA Georgia Stage 20: Murfreesboro, TN to Chattanooga, TN Tennessee 134 Part II: Poking the API 10_277959-ch05.qxp 5/5/08 11:26 AM Page 134 Rest Day in Nashville Nashville Stage 19: Huntingdon, TN to Nashville, TN Tennessee Stage 18: Malden, MO to Huntingdon, TN Crossing the Mississippi Stage 17: West Plains, MO to Poplar Bluff, MO Missouri In PHP, the results are: Array ( [0] => Array ( [name] => Stage 24: Walterboro, SC to Charleston, SC [location] => Charleston, SC ) [1] => Array ( [name] => Stage 23: Waynesboro, GA to Walterboro, SC [location] => Passing through the South ) [2] => Array ( [name] => Stage 22: Monroe, GA to Waynesboro, GA [location] => Georgia ) [3] => Array ( [name] => Stage 21: Birmingham, AL to Atlanta, GA [location] => Georgia ) [4] => Array ( [name] => Stage 20: Amory, MS to Birmingham, AL [location] => Mississippi Chapter 5: Exploring FQL: Facebook Query Language 135 10_277959-ch05.qxp 5/5/08 11:26 AM Page 135 ) [5] => Array ( [name] => Rest Day in Little Rock, AR [location] => Little Rock, AR ) [6] => Array ( [name] => Stage 19: Helena, AR to Tupelo, MS [location] => Mississippi ) [7] => Array ( [name] => Stage 18: Little Rock, AR to Helena, AR [location] => Arkansas ) [8] => Array ( [name] => Stage 17: Fort Smith, AR vicinity to Little Rock, AR [location] => Arkansas ) [9] => Array ( [name] => Stage 16: McAlester, OK to Fort Smith, AR [location] => Arkansas ) [10] => Array ( [name] => Stage 15: Oklahoma City, OK to McAlester, OK [location] => Oklahoma ) [11] => Array ( [name] => Stage 14: Woodward, OK to Oklahoma City, OK [location] => Oklahoma ) [12] => Array ( [name] => Stage 13: Guymon, OK to Woodward, OK [location] => Oklahoma ) [13] => Array 136 Part II: Poking the API 10_277959-ch05.qxp 5/5/08 11:26 AM Page 136 ( [name] => Stage 12: Clayton, NM to Guymon, OK [location] => Oklahoma ) [14] => Array ( [name] => Stage 11: Raton, NM to Clayton, NM [location] => New Mexico ) [15] => Array ( [name] => Rest Day in Denver/Colorado Springs [location] => Denver/Colorado Springs ) [16] => Array ( [name] => Stage 10: Mount Evans Summit [location] => Mountain Springs Church ) [17] => Array ( [name] => Stage 9: Buena Vista, CO to Colorado Springs, CO [location] => Colorado Springs ) [18] => Array ( [name] => Stage 8: Gunnison, CO to Buena Vista, CO [location] => Continental Divide ) [19] => Array ( [name] => Stage 7: Durango, CO to Ouray, CO [location] => Million Dollar Highway ) [20] => Array ( [name] => Stage 6: Kayenta, AZ to Four Corners, CO [location] => Monument Valley and Arizona ) [21] => Array ( [name] => Stage 5: Flagstaff, AZ to Tuba City, AZ Chapter 5: Exploring FQL: Facebook Query Language 137 10_277959-ch05.qxp 5/5/08 11:26 AM Page 137 [location] => Arizona ) [22] => Array ( [name] => Rest Day in Prescott, AZ [location] => Prescott, AZ ) [23] => Array ( [name] => Stage 4: Prescott, AZ to Flagstaff, AZ [location] => Arizona ) [24] => Array ( [name] => Stage 3: Quartzsite, AZ to Prescott, AZ [location] => Arizona ) [25] => Array ( [name] => Stage 2: Brawley, CA to Blythe, CA [location] => Brawley, CA to Blythe, CA ) [26] => Array ( [name] => Stage 1: Escondido, CA to Brawley, CA [location] => Escondido, CA to Brawley, CA ) ) Take a second example. If you want to get all the first names of friends who have the current application installed, you could use the following statement: SELECT first_name FROM user WHERE has_added_app=1 AND uid IN (SELECT uid2 FROM friend WHERE uid1=665127078) In this example, the subquery selects the total list of friends of user 665127078. This array of records is then drawn upon in the first query. All the users pulled from the subquery are checked to see whether they have the current application installed. For all the matches, their first names are output in the result set. Finally, if you want to get a list of all the pages that a particular user is a fan of, you could use the following query: 138 Part II: Poking the API 10_277959-ch05.qxp 5/5/08 11:26 AM Page 138 SELECT name, pic_small, type FROM page WHERE page_id IN (SELECT page_id FROM page_fan WHERE uid= 665127078) Once again, the subquery looks for all the page IDs that are associated with the specified user ID. These IDs are then used in the first query to get the page information for each of these matches. Using Special Functions FQL supports several in-query functions that you can call to manipulate your result set. These are listed in Table 5-4. You can insert these in the SELECT and WHERE clauses as you list your fields. Table 5-4 FQL Functions Function Description now() Outputs the current time rand() Produces a random number strlen(str) Returns the string length concat(str1, str2,...) Concatenates the enclosed strings substr(str, startPosition, Returns a substring of the string length) strpos(str, searchStr) Returns the position of searchStr in str (–1 if not found) lower(str) Converts the string to lowercase upper(str) Converts the string to uppercase For example, suppose you want to combine two user fields in the result set into one field: SELECT concat( first_name, ‘ loves the following movies: ‘, movies) FROM user WHERE uid= 665127078 Chapter 5: Exploring FQL: Facebook Query Language 139 10_277959-ch05.qxp 5/5/08 11:26 AM Page 139 Using this query in a full PHP example, here’s the code: $uid = “665127078”; $FQL = “SELECT concat(first_name, ‘ loves the following movies: ‘, movies) “ . “FROM user “ . “WHERE uid = $uid”; $result_set = $facebook->api_client->fql_query($FQL); echo “
”;
print_r($result_set);
echo “
”; Using the concat() function, the first_name field is combined with a text string and the movies field. The XML results are as follows: Rich loves the following movies: Casablanca, The Shawshank Redemption, It’s A Wonderful Life, Babette’s Feast, Field of Dreams, Pride & Prejudice (A&E Version), Band of Brothers (HBO), Chariots of Fire, Amélie (Le Fabuleux destin d’Amélie Poulain), Groundhog Day, Braveheart, Princess Bride, Les Miserables (1998 version), Signs, The Lord of the Rings trilogy, Benny & Joon, Vertigo, Sense & Sensibility, The Count of Monte Cristo, A Little Princess, Double Indemnity, Forrest Gump, The Incredibles, The African Queen, Henry V (1989 version), The Truman Show The PHP output is shown below: ( [0] => Array ( [anon] => Rich loves the following movies: Casablanca, The Shawshank Redemption, It’s A Wonderful Life, Babette’s Feast, Field of Dreams, Pride & Prejudice (A&E Version), Band of Brothers (HBO), Chariots of Fire, Amélie (Le Fabuleux destin d’Amélie Poulain), Groundhog Day, Braveheart, Princess Bride, Les Miserables (1998 version), Signs, The Lord of the Rings trilogy, Benny & Joon, Vertigo, Sense & Sensibility, The Count of Monte Cristo, A Little Princess, Double Indemnity, Forrest Gump, The Incredibles, The African Queen, Henry V (1989 version), The Truman Show ) ) 140 Part II: Poking the API 10_277959-ch05.qxp 5/5/08 11:26 AM Page 140 As you can see, when you use an FQL function in the SELECT clause, you are creating a derived attribute. FQL returns the field content into a field called anon. (See the nearby sidebar “JSON derides those attributes” if you plan to use derived attributes with JSON.) You can also use functions in the WHERE clause as well. In the following query, I am retrieving all the events of a Facebook page (8645672921) that have the string “Rest Day” in their name fields: SELECT name, location FROM event WHERE strpos(name, “Rest Day”) >=0 AND eid IN (SELECT eid FROM event_member WHERE uid= 8645672921) Here’s an example using PHP: $uid = “8645672921”; $FQL = “SELECT name, location “ . “FROM event “ . “WHERE strpos(name, \”Rest Day\”) >= 0 “ . “AND eid IN (“ . “SELECT eid “ . Chapter 5: Exploring FQL: Facebook Query Language 141 JSON derides those attributes A derived attribute is a field in the result set that does not map directly to a single field in the table being queried. Although FQL supports the use of derived attributes through FQL functions, be aware of a major limitation if you are working with JSON format in the output: JSON supports only one derived attribute per query. For example, consider the following dummy query: SELECT “anon1”, “anon2”, “anon3”, name FROM user WHERE uid=665127078 When XML is the output format, Facebook delivers the derived attributes, each packaged in an anon element: anon1 anon2 anon3 Rich Wagner In contrast, the following is output using JSON: [{“name”:”Rich Wagner”,”anon”:”anon3”}] As you can see, only the last derived attribute is retained. 10_277959-ch05.qxp 5/5/08 11:26 AM Page 141 “FROM event_member “ . “WHERE uid = $uid” . “)”; $result_set = $facebook->api_client->fql_query($FQL); echo “
”;
print_r($result_set);
echo “
”; The result set includes just 3 of the 22 events: Rest Day in Nashville Nashville Rest Day in Denver/Colorado Springs Denver/Colorado Springs Rest Day in Prescott, AZ Prescott, AZ In PHP, the result set is: Array ( [0] => Array ( [name] => Rest Day in Little Rock, AR [location] => Little Rock, AR ) [1] => Array ( [name] => Rest Day in Denver/Colorado Springs [location] => Denver/Colorado Springs ) [2] => Array ( [name] => Rest Day in Prescott, AZ [location] => Prescott, AZ ) ) 142 Part II: Poking the API 10_277959-ch05.qxp 5/5/08 11:26 AM Page 142 Chapter 6 Scripting with Facebook JavaScript In This Chapter Exploring FBJS Figuring out how to access the DOM using FBJS Picking up tips and techniques for using FBJS Getting animated with FBJS Animation Working with AJAX in your Facebook apps Not all technology names or acronyms are created equal. Some are legendary — Java and Windows quickly come to mind. Other names, on the other hand, glide from your tongue as awkwardly as a 16-year-old’s words on prom night. I would suggest that Facebook’s scripting language — FBJS (Facebook JavaScript) fits into that latter category. Both FBML and FQL sound much like their respective counterparts, HTML and SQL. In contrast, not only does FBJS not rhyme with JavaScript, but it is just plain hard to say — eff-bee-jay-ess. Fortunately, you can come up to speed working with FBJS about as quickly as it takes to pronounce the acronym. In this chapter, you explore the basics of FBJS and identify how it differs from standard JavaScript. You also explore FBJS Animation and AJAX to deliver interactive interfaces for your Facebook apps. Understanding the Facebook Platform Scripting Approach Although you can use standard JavaScript and AJAX in sandboxed iframe pages to your heart’s content, the Facebook Platform places restrictions over the amount of scripting capabilities you can add to the more tightly integrated FBML pages. 11_277959-ch06.qxp 5/5/08 11:27 AM Page 143 When you place scripting code in your pages, Facebook first needs to ensure that your code safely interacts inside the Facebook environment and does not cause any scoping issues (scope is the context in which a JavaScript script executes). As a result, before the FBML page is rendered, Facebook pre-processes the script by parsing through the code and prepending your application ID to the names of your functions and variables to create a private “scripting sandbox” for your app to have fun in. For example, consider the following JavaScript pseudo-code: var counter = 0; function showHide(activeItem) { if(activeItem) { activeItem.className = ‘inactiveItem’; var myId = activeItem.id.replace(/[^\d]/g,’’); } activeItem = this; this.className = ‘activeItem’; var myId = this.id.replace(/[^\d]/g,’’); } When it’s embedded in an FBML page, Facebook transforms it to var 4f83a8adc579bb26ae5b4630dc9eefb8_counter = 0; function 4f83a8adc579bb26ae5b4630dc9eefb8_ showHide(4f83a8adc579bb26ae5b4630dc9eefb8_activeItem) { if(4f83a8adc579bb26ae5b4630dc9eefb8_activeItem) { 4f83a8adc579bb26ae5b4630dc9eefb8_activeItem.className = ‘inactiveItem’; var 4f83a8adc579bb26ae5b4630dc9eefb8_myId = 4f83a8adc579bb26ae5b4630dc9eefb8_activeItem.id.replace(/[^\d]/g,’’ ); } 4f83a8adc579bb26ae5b4630dc9eefb8_activeItem = this; this.className = ‘activeItem’; var 4f83a8adc579bb26ae5b4630dc9eefb8_myId = this.id.replace(/[^\d]/g,’’); } You don’t do any of the name transformation yourself. Facebook does it all behind the scenes for you. The advantage to this “scripting sandbox” approach is that it frees you from the need to be careful about breaking Facebook shell functionality as you script away inside your canvas page or profile box. Accessing the DOM Although FBJS provides most of the same Document Object Model (DOM)- related methods that you are used to in JavaScript, Facebook’s scripting model fundamentally changes the way in which you can access DOM properties. 144 Part II: Poking the API 11_277959-ch06.qxp 5/5/08 11:27 AM Page 144 Instead of accessing properties directly through dot notation, FBJS requires you to call a get/set method instead. Therefore, although JavaScript allows custForm=document.getElementById(‘cust_form’); custForm.action=’http://www.richwagnerwords.com/facebook/process.php’; in FBJS, you would need to use the setAction method instead, like this: custForm=document.getElementById(‘cust_form’); custForm.setAction(‘http://www.richwagnerwords.com/facebook/process.php’); Table 6-1 lists all the get/set methods for accessing DOM properties. Table 6-1 Get/Set Methods for Accessing DOM Properties DOM Property Get Set accessKey getAccessKey setAccessKey action getAction setAction checked getChecked setChecked childNodes getChildNodes className getClassName setClassName clientHeight getClientHeight clientWidth getClientWidth cols getCols setCols dir getDir setDir disabled getDisabled setDisabled firstChild getFirstChild form getForm href getHref setHref id getId setId innerFBML setInnerFBML innerHTML setInnerXHTML innerText/ setTextValue textContent lastChild getLastChild (continued) Chapter 6: Scripting with Facebook JavaScript 145 11_277959-ch06.qxp 5/5/08 11:27 AM Page 145 Table 6-1 (continued) DOM Property Get Set location setLocation name getName setName nextSibling getNextSibling offsetHeight getOffsetHeight offsetWidth getOffsetWidth parentNode getParentNode previousSibling getPreviousSibling readOnly getReadOnly setReadOnly rows getRows setRows scrollHeight getScrollHeight scrollLeft getScrollLeft setScrollLeft scrollTop getScrollTop setScrollTop scrollWidth getScrollWidth selected getSelected setSelected selectedIndex getSelectedIndex setSelectedIndex src getSrc setSrc style getStyle setStyle tabIndex getTabIndex setTabIndex tagName getTagName target getTarget setTarget title getTitle setTitle type getType setType value getValue setValue (None) getAbsoluteTop (None) getAbsoluteLeft (None) getRootElement 146 Part II: Poking the API 11_277959-ch06.qxp 5/5/08 11:27 AM Page 146 Setting the Content of DOM Elements For security reasons, FBJS does not permit full access to setting the innerHTML property of a DOM element. However, there are three alternatives that you can use: setInnerXHTML(), setInnerFBML(), and setInnerText(). The next few sections elaborate on these alternatives. setInnerXHTML() The setInnerXHTML() method is the FBJS equivalent to setting innerHTML. The difference is that the string is preprocessed by Facebook before it renders it. For example, in the following code, an HTML tale is created using the setInnerXHTML method: var friend_div = document.getElementById(‘friends’); friend_div.setInnerXHTML(“
NameNetwork
” ); Facebook makes sure that the content inside of the markup string is safe and then displays the results inside of the friends div element. setInnerFBML() You can also use the setInnerFBML() method to insert HTML or FBML content into a DOM object. However, this method requires the parameter to be an fb:js-string object, not an ordinary JavaScript string literal or object variable. Here’s an example, which renders the name of the profile owner inside of a div:
The fb:name content is assigned to the str1 variable, which is used in the setInnerFBML call. Chapter 6: Scripting with Facebook JavaScript 147 11_277959-ch06.qxp 5/5/08 11:27 AM Page 147 setInnerText() If you only need to assign ordinary text and no HTML or FBML markup to your DOM object, you can use the setInnerText() method: var friend_div = document.getElementById(‘friends’); friend_div.setInnerText(“We are glad that you have 1,202 friends. But how do you keep in touch with all of them?”);
Note that calling setInnerText() deletes any child nodes that were associated with the object. Setting Styles through FBJS FBJS allows you to programmatically set the styles of elements using the setStyle() method. To define a single style, you can use the following syntax: setStyle(property, value) For example, to set the margin property of the friendDiv object, you would use friendDiv.setStyle(‘margin’, ‘0’); To define multiple styles, you can use property-value pairs: setStyle( {property1: value1, property2: value2, ...}) For example, friendDiv.setStyle({margin: ‘0’, width’, ‘320px’}); However, there are caveats to setStyle(). First, you need to transform hyphenated style property names into lower camel case — in other words, this sort of syntax — lowerCamelCase. To illustrate: text-align becomes textAlign font-size becomes fontSize margin-right becomes marginRight 148 Part II: Poking the API 11_277959-ch06.qxp 5/5/08 11:27 AM Page 148 Second, when setting values, be sure that you always append the unit of measurement to the value. For example: friendDiv.setStyle(‘lineHeight’, ‘1.7’); // <-- Bad :( friendDiv.setStyle(‘lineHeight’, ‘1.7em’); // <-- Good :) friendDiv.setStyle(‘lineHeight’, ‘14px’); // <-- Good :) FBJS allows you to programmatically work with class styles that you have already defined in your code. For example, if you have a .redTable class defined in an embedded style sheet, you can associate a DOM element with it by adding an existing a class style: this.addClassName(‘redTable’); Or, to remove a style: this.removeClassName(‘redTable’); To check to see whether a class is assigned to an element, use hasClassName(): if (this.hasClassName(‘redTable’)) { this.addClassName(‘shinyRedTable’); } Including External JavaScript Files on Canvas Pages In addition to inline scripts, you can also include external .js files on canvas pages (although at the time of this book’s publication, you could not include them on profile boxes). Using external script files offers two significant benefits. First, using them makes your code easier to manage and maintain in separating the script’s “logic layer” from your presentation markup. Second, Facebook caches external script files, minimizing the response time for your application. You can reference an external .js file just like you do with a normal Web page — via the script tag’s src attribute. For example: Chapter 6: Scripting with Facebook JavaScript 149 11_277959-ch06.qxp 5/5/08 11:27 AM Page 149 Because it caches the external file content, Facebook puts two reasonable limitations on their usage: Your limit to the total number of external script files you can include across all of your apps is 1,000. (I know, it will be hard to keep within that limitation, but do your best.) Though not a likely scenario, do not reference a common script file by using a unique URL for each user. If you do, Facebook ends up caching duplicate instances of the same script. Helpful Tips When Using FBJS The following are tips and techniques that you should keep in mind when you work with FBJS: FBJS events are not processed inside of your app’s profile box when the profile itself loads. Instead, your profile box script needs to wait until a user “activates” the box itself — typically by clicking the box and triggering an onclick or onfocus event. FBJS supports the addEventListener and removeEventListener methods for attaching and detaching events. However, the useCapture parameter of the addEventListener is not supported. You can create the FBML element fb:swf programmatically through the document.createElement method. For example: var shockMe = document.createElement(‘fb:swf’); At the time of writing, the createElement method can only be used for the Flash/Shockwave element and nothing else. You can get/set text selection in form textboxes using getSelection() and setSelection(start, end). Like setInnerText(), setTextValue() requires a string passed as a parameter and does not perform markup or type conversions. Using the FBJS Animation Library As part of its scripting support, Facebook provides application developers an easy way to add animation to their application user interfaces through its FBJS Animation library. You can do several types of animation, including 150 Part II: Poking the API 11_277959-ch06.qxp 5/5/08 11:27 AM Page 150 Tweening CSS attributes of an object Hide and show block-level elements Shrink and expand block-level elements A scripting object called Animation is used to perform all of the animations. Animations are achieved by “chaining” together a series of changes that you want to occur within a given animation. All of the animations for a given call to Animation occur on the same DOM element. The Animation uses the methods shown in Table 6-2. Table 6-2 FBJS Animation Methods Method Description to(property, value) Defines a stopping point in the animation. You can chain together multiple to() methods within a given animation sequence to transform multiple properties of an object. go() Closes the animation chain and tells the Animation object to begin the animation. from(property, value) Optionally used to define an initial starting point of the animation. If no from() methods are specified, the current property settings are considered the point of origin. by(increment) Increments a CSS property value by the specified amount. duration(timeLength) Specifies the amount of time (in milliseconds) in which to perform the animation. (If you do not specify a duration time, the animation lasts for 1,000 ms.) ease(easeFunction) Changes the ease rate of the animation. Builtin ease functions include Animation. ease.begin (slows beginning), Animation.ease.end (slows end), Animation.ease.both (slows both). show() Makes a hidden element (display:none) visible at the start of an animation. (continued) Chapter 6: Scripting with Facebook JavaScript 151 11_277959-ch06.qxp 5/5/08 11:27 AM Page 151 Table 6-2 (continued) Method Description hide() Sets the element’s style to display: none when animation is completed. blind() Prevents unwanted text wrapping from occurring during an animation of a text container. Use only when the block element starts or ends hidden. checkpoint() Creates a checkpoint inside of an animation. All steps in the animation chain before the checkpoint() call are executed before continuing to the next set of steps. You can add multiple checkpoints() in a given animation. The next few sections explore the different ways in which the FBJS Animation library can be used. Tweening animation Tweening is an animation term that refers to the gradual change that takes place between a starting point and ending point of an object. Tweening can refer either to the motion or a state change of an object. A few examples help explain the concept of tweening and how you can achieve it in FBJS. Suppose you want to increase the font size of an a link when it is clicked. Here’s the markup code I use:
The a link’s onclick handler calls the Animation object and passes itself (this) as the parameter. The Animation object knows that all animation operations will be performed on the a link. The tweening animation begins with the called object’s current CSS attributes. The to() method specifies the value of the CSS property at the end of the animation. It uses a property name and value pair as its two parameters: Animation.to(property, value) 152 Part II: Poking the API 11_277959-ch06.qxp 5/5/08 11:27 AM Page 152 In the example, the to() specifies a stopping point for the animation after the font-size property grows to 34px. The go() is added onto the end to tell the Animation object that the animation chain is closed. Figures 6-1 and 6-2 show the start and end points of the animation. You can chain together multiple property changes during the same animation by chaining together to() methods. In the code that follows, the background color is changed to gray at the same time that the font size grows: Figure 6-2: Ending point of tweening. Figure 6-1: Starting point of tweening animation. Chapter 6: Scripting with Facebook JavaScript 153 Got animation? The FBJS Animation library has made available an open source version that you can use in non-Facebook apps. You can download this version at developers.facebook.com/ animation. Note that the open-source version requires that you run in standards-compliant mode rather than quirks mode. 11_277959-ch06.qxp 5/5/08 11:27 AM Page 153 Watch me grow and change my background with a single click! Figure 6-3 shows the end result of the animation. (The starting point is just like it was back in Figure 6-1.) If I add in a from() method to the chain, the animation starts with the property state defined with it and then goes to the ending states of the to() methods. For example, in the code below, the link background-color initially turns red before the tweening goes to dark gray: Watch me grow and change my background with a single click! When the from() method immediately follows a to() that references the same CSS property, you can drop the property declaration as a parameter in the from() call. Given that, the following two lines are equivalent: // Full declaration Animation(this).to(‘backgroundColor’, ‘#333333’).from(‘backgroundColor’, ‘#ff0000’).go(); // Shortcut declaration Animation(this).to(‘backgroundColor’, ‘#333333’).from(‘#ff0000’).go(); Figure 6-3: Background and font size are both changed during the tween animation. 154 Part II: Poking the API 11_277959-ch06.qxp 5/5/08 11:27 AM Page 154 You can add multiple from() methods to the chain to set multiple property states. For example: Watch me grow and change my background with a single click! You can also give the appearance of motion animation by adjusting the positioning of an element. For example, the following example moves the div container of the link by adjusting its left and top margin:
The animation begins in the same position as that shown in Figure 6-1, but ends one second later in the new position shown in Figure 6-4. Figure 6-4: FBJS Animation enables you to move objects onscreen. Chapter 6: Scripting with Facebook JavaScript 155 11_277959-ch06.qxp 5/5/08 11:27 AM Page 155 Adjusting the speed and ease of the animation You can add the duration() method onto the chain if you would like to change the running time of the animation. The following code changes the motion tweening to take place over the course of three seconds: Animation(document.getElementById(‘div1’)).to(‘marginLeft’, ‘100px’).to(‘marginTop’, ‘200px’).duration(3000).go(); Animations normally transpire at the same rate from start to finish. However, the ease() method enables you to speed up or slow down an animation at points within it. The built-in ease functions are as follows: Animation.ease.begin starts the animation slowly and ends it quickly. Animation.ease.end starts quickly and ends more slowly. Animation.ease.both slows down the animation at the beginning and end, but speeds it up during the middle. The following animation, for example, starts slow and ends at a turbo level: Animation(document.getElementById(‘div1’)).to(‘marginLeft’, ‘100px’).to(‘marginTop’, ‘200px’).ease(Animation.ease.begin).go(); Adjusting the size and visibility of block-level events You can use the FBJS Animation library to animate changes to the sizing and visibility of block-level events. To illustrate, say that I have a headline and start of a news story that I display on a canvas page. However, the full text of the story is hidden unless the user clicks a link to show the whole thing. I can animate this process by using a combination of chained methods of the FBJS Animation library. I begin by setting up the news story text, consisting of h3, p, and div elements:

Captain Amazing Saves the Day

An amazing boy who refers to himself as “Captain Amazing” singlehandedly rescued a dozen firemen when they were trapped in a glue factory.
156 Part II: Poking the API 11_277959-ch06.qxp 5/5/08 11:27 AM Page 156 Ut sit amet libero. Sed facilisis, tortor vitae aliquam tincidunt, purus nisi suscipit dolor, nec egestas elit mi vel augue. Maecenas eget nulla. In hac habitasse platea dictumst. Mauris at quam non odio nonummy aliquet. Donec sodales fermentum sem. In non magna at turpis feugiat tincidunt. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Etiam rhoncus adipiscing dui. Curabitur fermentum leo in nisi. Praesent vulputate, orci sed ultrices venenatis, dolor turpis ultrices dolor, vel bibendum dui magna ultricies libero. Suspendisse rhoncus dignissim orci. Quisque rutrum.
Notice that the div is hidden at the start because its display CSS property is set to none. I can then add an a link to the end of the top paragraph that triggers the animation in its onclick handler: Animation(document.getElementById(‘news’)).to(‘height’, ‘auto’).from(‘height’, ‘0px’).to(‘width’, ‘auto’).from(‘width’, ‘0px’).to(‘opacity’, 1).from(‘opacity’, 0).blind().show().duration(600).go(); return false; This animation simultaneously tweens the appearance of the div element’s height, width, and opacity. The height and width properties go from 0px to auto, enabling it to expand as needed based on its contents. The opacity starts at 0 and then ends at the full value of 1. Because the div is hidden at the start, I use blind() to ensure that the text wrapping of the div’s content is handled automatically by the Animation object during the animation. Also, I need to use show() to toggle the visibility of the hidden div. Here is the full source of the example:

Captain Amazing Saves the Day

An amazing boy who refers to himself as “Captain Amazing” singlehandedly rescued a dozen firemen when they were trapped in a glue factory. Read full story >>
Chapter 6: Scripting with Facebook JavaScript 157 11_277959-ch06.qxp 5/5/08 11:27 AM Page 157 Ut sit amet libero. Sed facilisis, tortor vitae aliquam tincidunt, purus nisi suscipit dolor, nec egestas elit mi vel augue. Maecenas eget nulla. In hac habitasse platea dictumst. Mauris at quam non odio nonummy aliquet. Donec sodales fermentum sem. In non magna at turpis feugiat tincidunt. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Etiam rhoncus adipiscing dui. Curabitur fermentum leo in nisi. Praesent vulputate, orci sed ultrices venenatis, dolor turpis ultrices dolor, vel bibendum dui magna ultricies libero. Suspendisse rhoncus dignissim orci. Quisque rutrum.
You can also animate hiding a block-level element on your page. Suppose, for example, you decide to add an advertisement to your Facebook application, but in an act of unimaginable compassion to your users, you allow them to hide the ad. But instead of just making it disappear, you would like the ad to disappear on one last blaze of animated glory. Here’s the basic div-based ad (shown in Figure 6-5):

Hail to Your Cell Phone


Download your free, 12-minute long ring tone of Hail to the Chief, one of the most super amazing deals you have ever encountered on the web. With each new call, you too can feel as important and powerful as the President.
Hide this ad
Figure 6-5: Basic ad before animation. 158 Part II: Poking the API 11_277959-ch06.qxp 5/5/08 11:27 AM Page 158 An onclick event handler for the link is, once again, used to trigger the animation: Hide this ad The animation begins at the default state of the div and then gradually (over a three-second time span) tweens the height, width, and opacity properties (see Figure 6-6). The hide() method is used to set the display CSS property to none at the end of the animation (see Figure 6-7). Working with AJAX in FBJS AJAX, which stands for Asynchronous JavaScript and XML, is the capability of including content in a Web page in a way in which JavaScript obtains data from the server and displays it without refreshing the entire page. FBJS provides an AJAX object that enables you to add AJAX capabilities to your Facebook applications. Table 6-3 displays the members of the FBJS implementation of the AJAX object. Figure 6-7: Now you see it, now you don’t. Figure 6-6: Disappearing ad in the middle of animation. Chapter 6: Scripting with Facebook JavaScript 159 11_277959-ch06.qxp 5/5/08 11:27 AM Page 159 Table 6-3 FBJS AJAX Object Members Member Type Description requireLogin Property Specifies whether the user needs to be logged into your app before an AJAX call is made. responseType Property Indicates the response type (Ajax.RAW, Ajax.JSON, or Ajax.FBML). useLocalProxy Property Specifies whether to use fb:localproxy to call your server. (Valid for RAW and JSON response types.) (In beta at the time of writing.) Ajax.RAW Property Server response in its native unprocessed state. Ajax.JSON Property Server response converted into a JSON object. Ajax.FBML Property Server response returned as FBML content. post(url, query) Method Initiates an AJAX post. abort() Method Halts an AJAX post. ondone(data) Event Event handler that is triggered when the AJAX call returns. The format of data depends on the responseType property. onerror Event Event handler that is called if a problem occurs in the middle of an AJAX operation. Consider the following PHP-based example, which demonstrates how you can use the FBJS version of AJAX in your Facebook apps. The purpose of this example is to provide dynamic feedback to the users based on the link that they click on in the canvas page. To begin, I start with basic FBML content for the page:

Your Customer Survey Is Now Complete

160 Part II: Poking the API 11_277959-ch06.qxp 5/5/08 11:27 AM Page 160 Thanks for taking our free customer survey! Your input will help us make a better application and sell more ads to bombard you with. Your survey was completed in only 3hr. 47min, which is 7% less time than the average time most high schoolers take to finish their SAT tests.
” firstname=”true” useyou=”false”/>, as a token of appreciation for taking our brief survey, which of the following complimentary gifts would you like to receive today?
Make your selection please before continuing.

When the user clicks on one of the three list items, I use AJAX to fill in a response in the userchoice div. The next step is to define the FBJS function that performs the AJAX functionality. Here’s the code: After defining the ajax object and assigning properties, the ondone handler is set. This function, called when the server responds to the AJAX request, Chapter 6: Scripting with Facebook JavaScript 161 11_277959-ch06.qxp 5/5/08 11:27 AM Page 161 updates the FBML content of the specified DOM element. The 

No comments