使用Facebook的实时更新和订阅API

Facebook’s real-time updates allow us to declare our interest in certain information about our users and have Facebook notify us when it changes instead of us continually querying the API. Although Facebook recommends we retain a minimum amount of user data, it’s prudent to maintain a balance. Indeed, it makes more sense to store certain information, such as a user’s name and her list of friends, than to continually query for it. Subscribing to information updates helps ensure that our data always stays current.

Facebook的实时更新使我们可以声明对某些有关用户的信息的兴趣,并让Facebook在更改时通知我们,而不用我们不断查询API。 尽管Facebook建议我们保留最少数量的用户数据,但保持平衡是审慎的做法。 确实,存储某些信息(例如用户的姓名和她的朋友列表)比不断查询信息更有意义。 订阅信息更新有助于确保我们的数据始终保持最新。

There are of course other uses for real-time updates – and indeed we’ll take a look at one in this article: an example application which emails people when others “unfriend” them. (Actually that’s not strictly true – it tells you when someone is no longer your “friend” regardless of who did the unfriending.)

当然,实时更新还有其他用途-的确,我们将在本文中介绍一个例子:一个示例应用程序,当其他人“与之成为好友”时,它会向人们发送电子邮件。 (实际上,这并不是严格意义上的,它告诉您何时不再是您的“朋友”,无论谁取消了朋友。)

入门 (Getting Started)

If you’ve ever built a Facebook application, registering the app will be second nature for you. Go to https://developers.facebook.com, click Apps, then click Create New App, and follow the instructions. Save the application ID and secret because you’ll need that later to establish the connection to Facebook.

如果您曾经构建过Facebook应用程序,那么注册该应用程序将是您的第二选择。 转到https://developers.facebook.com ,单击“应用程序”,然后单击“创建新应用程序”,然后按照说明进行操作。 保存应用程序ID和密码,因为稍后需要使用它来建立与Facebook的连接。

Also, the application will need to be publicly accessible for Facebook to “ping” it. You might want to consider using a service such as AppFog, Heroku, or PagodaBox, or simply host it yourself on an accessible server.

而且,该应用程序将需要公开访问,Facebook才能对其进行“ ping”操作。 您可能要考虑使用诸如AppFog,Heroku或PagodaBox之类的服务,或者只是将其自己托管在可访问的服务器上。

Once you have registered with Facebook and know where you will host the app, a good point to start is to either download a skeleton project for your favorite framework (this article uses this Slim-based skeleton by Timothy Boronczyk) or you can download the article’s example code from GitHub.

在Facebook上注册并知道将在哪里托管该应用程序之后,一个好的开始是下载最喜欢的框架的框架项目(本文使用Timothy Boronczyk 的基于Slim的框架 ),也可以下载本文的框架来自GitHub的示例代码

First, we need to make a few additions to skeleton’s composer.json file. We’ll install three additional libraries: the Facebook SDK to access their API, PHPMailer to easily send emails, and a port of the popular JavaScript library Underscore.

首先,我们需要对skeleton的composer.json file进行一些补充。 我们将安装三个附加库:用于访问其API的Facebook SDK,用于轻松发送电子邮件PHPMailer,以及流行JavaScript库Underscore的端口。

{
    "require": {
        ...
        "facebook/php-sdk": "dev-master",
        "phpmailer/phpmailer": "dev-master",
        "underscore/underscore.php": "dev-master"
    }
}

Next, create a configuration file by copying config/config.php.example to config/config.php, set your specific database credentials, and make the following additions:

接下来,通过复制创建一个配置文件config/config.php.exampleconfig/config.php ,设置特定的数据库凭证,并作如下补充:

'facebook.app_id'       => 'FACEBOOK-APP-ID',
'facebook.app_secret'   => 'FACEBOOK-APP-SECRET',
'facebook.verify_token' => 'FACEBOOK-VERIFY-TOKEN',
    
'smtp.host'             => 'SMTP-HOST',
'smtp.port'             => SMTP-PORT,
'smtp.encryption'       => 'tls', // or 'ssl'
'smtp.username'         => 'SMTP-USERNAME',
'smtp.password'         => 'SMTP-PASSWORD',
'smtp.from_address'     => 'no-reply@example.com',
'smtp.from_name'        => 'SitePoint Facebook Real-Time Tutorial',

Obviously you’ll need to provide your own values as you go along.

显然,您在进行过程中需要提供自己的值。

Now add the following to include/services.php, which handles the dependency injections, so that our application has easy access to the Facebook SDK and PHPMailer libraries:

现在,将以下内容添加到include/services.php ,该文件处理依赖项注入,以便我们的应用程序可以轻松访问Facebook SDK和PHPMailer库:

$c['facebook'] = function($c) {
    $config = $c['config'];
    return new Facebook(array(
        'appId' =>  $config['facebook.app_id'], 
        'secret' => $config['facebook.app_secret'],
        'cookie' => true,
    ));
};

$c['phpmailer'] = function($c) {
    $config = $c['config'];

    $mail = new PHPMailer();

    $mail->IsSMTP();
    $mail->Host = $config['smtp.host'];
    $mail->Port = $config['smtp.port'];
    $mail->SMTPAuth = true;
    $mail->Username = $config['smtp.username'];
    $mail->Password = $config['smtp.password'];              
    $mail->SMTPSecure = $config['smtp.encryption'];

    $mail->From = $config['smtp.from_address'];
    $mail->FromName = $config['smtp.from_name'];

    $mail->WordWrap = 100;
    $mail->IsHTML(true);

    return $mail;
};

Finally, we need to set up our database schema. (I’m going to use MySQL, although NotORM – the ORM bundled with the skeleton application – works with any database that supports PDO.) There’s only one table:

最后,我们需要设置数据库架构。 (我将使用MySQL,尽管NotORM(与框架应用程序捆绑在一起的ORM)可与任何支持PDO的数据库一起使用。)只有一个表:

CREATE  TABLE users (
    id INTEGER NOT NULL AUTO_INCREMENT,
    fb_id VARCHAR(64),
    name VARCHAR(255) NOT NULL,
    email VARCHAR(255) NOT NULL,
    friends TEXT,
    fb_access_token VARCHAR(255),
    fb_access_token_expires INTEGER,

    PRIMARY KEY (id) ,
    UNIQUE INDEX fb_id_UNIQUE (fb_id ASC)
);

授权申请 (Authorizing the Application)

In order to act on the behalf of our users, we need to allow them to authorize our application. We’ll keep things simple and just create a page with a Facebook sign-in button. Clicking the button will redirect the user to Facebook where she will be asked to authorize the application before redirecting to a confirmation callback.

为了代表我们的用户行事,我们需要允许他们授权我们的应用程序。 我们将使事情变得简单,仅使用Facebook登录按钮创建一个页面。 单击该按钮会将用户重定向到Facebook,然后在重定向到确认回调之前将要求她授权该应用程序。

In the callback, we need to do a few things:

在回调中,我们需要做一些事情:

  • Exchange the code for an access token.

    将代码交换为访问令牌。
  • Exchange the access token for a long-term access token.

    将访问令牌交换为长期访问令牌。
  • Get the user’s information.

    获取用户的信息。
  • Retrieve a list of the user’s friends.

    检索用户的朋友列表。
  • Create a database record for the user, storing the long-term access token and the list of her friends.

    为用户创建一个数据库记录,存储长期访问令牌和她的朋友列表。

Facebook has done away with offline access, which we used to be able to get a user access token that lasted forever. Instead, we now request a long-term token. When I tried this, the token indicated it was valid for just under two months and the user will need to re-authenticate when (or just before) it expires. Reauthorization is beyond the scope off this article, but be aware that our application can’t operate forever without the user re-authorizing it.

Facebook已经取消了离线访问,而我们过去可以使用离线访问来获得永久存在的用户访问令牌。 相反,我们现在要求一个长期令牌。 当我尝试此操作时,令牌表明它在不到两个月的时间内有效,并且用户需要在其过期时(或之前)重新进行身份验证。 重新授权不在本文讨论范围之内,但是请注意,如果没有用户重新授权,我们的应用程序将无法永远运行。

First, add the route for the front page:

首先,为首页添加路线:

$app->get('/', function () use ($app, $c) {
    $url = $c['facebook']->getLoginUrl(array(
        'scope' => 'email',
        'redirect_uri' => $app->request()->getUrl() . '/confirm',
    ));

    $app->view()->setData(array(
        'login_url' => $url,
    ));

    $app->render('index.html');
});

All this route does is get the login URL – specifying that we need the user’s email address as an additional permission – and set the confirmation callback. The important stuff happens in the callback.

这一切都是通过获取登录URL(指定我们需要用户的电子邮件地址作为附加权限)并设置确认回调。 重要的事情发生在回调中。

Next, add the route for the confirmation callback:

接下来,添加确认回调的路由:

$app->get('/confirm', function () use ($app, $c) {
    $config = $c['config'];
    $facebook = $c['facebook'];

    // exchange the code for an access token
    $url = sprintf(
        'https://graph.facebook.com/oauth/access_token?client_id=%s&redirect_uri=%s&client_secret=%s&code=%s',
        $config['facebook.app_id'],
        urlencode($app->request()->getUrl() . '/confirm'),
        $config['facebook.app_secret'],
        $app->request()->get('code')
    );
    $response = file_get_contents($url);

    $params = null;
    parse_str($response, $params);
    $token = $params['access_token'];

    // exchange the access token for a long-term token
    $url = sprintf(
        'https://graph.facebook.com/oauth/access_token?grant_type=fb_exchange_token&client_id=%s&client_secret=%s&fb_exchange_token=%s',
        $config['facebook.app_id'],
        $config['facebook.app_secret'],
        $token
    );
    $response = file_get_contents($url);

    $params = null;
    parse_str($response, $params);
    $token = $params['access_token'];
    $tokenExpires = $params['expires'];

    // get the user's information
    $facebook->setAccessToken($token);
    $fb_user = $facebook->api('/me');
    $friends = $facebook->api('/me/friends');

    // create the database entry
    $c['db']->users->insert(array(
        'fb_id'   => $fb_user['id'],
        'name'    => $fb_user['name'],
        'email'   => $fb_user['email'],
        'friends' => serialize($friends['data']),
        'fb_access_token' => $token,
        'fb_access_token_expires' => $tokenExpires
    ));
});

There are a couple of limitations to this simple example:

这个简单的例子有两个限制:

  1. It assumes that the user agrees to grant our application access to her email address.

    它假设用户同意授予我们的应用程序对其电子邮件地址的访问权限。
  2. It assumes that the user hasn’t previously authorized the application.

    假定用户先前未授权该应用程序。

Feel free to extend the example by handling denials and build in some sort of mechanism to ask users to re-authorize when the token expires.

可以通过处理拒绝来扩展示例,并建立某种机制来要求用户在令牌到期时重新授权。

设置订阅 (Setting up Subscriptions)

Facebook’s Subscriptions API provides a REST interface to the application’s subscriptions. A subscription is simply a case of telling Facebook what real-time updates we want to receive. We specify the object – in our case, users – and the fields – friends – we’re interested in.

Facebook的Subscriptions API为应用程序的订阅提供REST接口。 订阅只是告诉Facebook我们想要接收哪些实时更新的一种情况。 我们指定了我们感兴趣的对象(在我们的情况下为用户)以及字段(朋友)。

Note that we subscribe to types of objects, not specific object themselves; a subscription for users means we’ll get update for anyone who’s authorised the application, not just a specific user. Also, real-time updates are limited to certain types of objects and a subset of their fields. Refer to the Realtime Updates documentation for more information.

注意,我们订阅对象的类型,而不是特定对象本身。 订阅用户意味着我们将获得获得该应用程序授权的任何人的更新,而不仅仅是特定用户。 同样,实时更新仅限于某些类型的对象及其字段的子集。 有关更多信息,请参考实时更新文档

There are two ways to set up subscriptions: through the developer portal on Facebook, and using the API. In either case, we need to have set a hub challenge callback first because Facebook pings this as part of the validation process.

设置订阅的方式有两种:通过Facebook上的开发人员门户网站和使用API​​。 无论哪种情况,我们都需要先设置一个集线器挑战回调,因为Facebook在验证过程中会对其进行ping操作。

Whenever we add or modify a subscription, Facebook verifies the subscription by making a GET request to a callback with the following parameters:

每当我们添加或修改订阅时,Facebook都会通过使用以下参数向回调发出GET请求来验证订阅:

  • hub.mode – the string “subscribe”

    hub.mode –字符串“ subscribe”

  • hub.challenge – a random string

    hub.challenge –随机字符串

  • hub.verify_token – the verify_token value we gave when we created the subscription

    hub.verify_token –创建订阅时提供的verify_token值

Our callback simply checks that hub.mode contains the string “subscribe”, that hub.verify_token matches the one we specified, and all being well simply outputs the value of hub.challenge.

我们的回调仅检查hub.mode包含字符串“ subscribe”, hub.verify_token匹配我们指定的字符串,并且一切都很好地只是输出hub.challenge的值。

$app->get('/subscriptions', function () use ($app, $c) {
    $req = $app->request();
    $verify = $c['config']['facebook.verify_token'];
    if ($req->get('hub_mode') == 'subscribe' && $req->get('hub_verify_token') == $verify) {
        echo $req->get('hub_challenge');
    }
});

To set up a subscription through the developer portal, go to your application and click “Realtime updates” under “Settings” on the left-hand side. Where it says “Please select an Object you would like to subscribe to”, select “user” and click Confirm. You’ll now see the following form:

要通过开发人员门户网站设置订阅,请转到您的应用程序,然后单击左侧“设置”下的“实时更新”。 如果显示“请选择您要订阅的对象”,请选择“用户”,然后单击“确认”。 现在,您将看到以下表格:

realtime-updates-01

Enter “friends” as the object fields, “<YOUR-URL>/subscriptions” for the callback endpoint, and enter a random string for the verify token (also record this random string in your configuration file).

在对象字段中输入“ friends”,在回调端点中输入“ <YOUR-URL> / subscriptions”,并为验证令牌输入一个随机字符串(还将此随机字符串记录在您的配置文件中)。

Alternatively, to set up a subscription using the Subscriptions API, make a POST request to https://graph.facebook.com/<APP_ID>/subscriptions. For example:

或者,要使用Subscriptions API设置订阅,请向https://graph.facebook.com/<APP_ID>/subscriptions发出POST请求。 例如:

$facebook->api(
    $config['facebook']['app_id'] . '/subscriptions',
    'POST',
    array(
        'object'       => 'user',
        'fields'       => 'friends',
        'callback_url' => 'http://www.example.com/subscriptions',
        'verify_token' => $config['facebook']['verify_token']
    )
);

处理实时更新 (Processing Real-Time Updates)

The subscription updates themselves are POSTed to our application in JSON format. To process them, we need to set up a callback that verifies the request’s authenticity and then updates our local data.

订阅更新本身以JSON格式发布到我们的应用程序中。 要处理它们,我们需要设置一个回调来验证请求的真实性,然后更新我们的本地数据。

The code coming up in the next sections all resides in the POST callback:

下一部分中出现的代码均位于POST回调中:

$app->post('/subscriptions', function () use ($app, $c) {

验证POST请求 (Verifying the POST Request )

It’s strongly advised to verify that the POST request actually came from Facebook. Luckily, this is relatively straightforward. When making the request, Facebook will generate a signature based on the body of the request and our application secret and include it in the X-HUB-SIGNATURE header. We simply check that the header is set, generate the signature ourself, and compare the two.

强烈建议验证POST请求是否确实来自Facebook。 幸运的是,这相对简单。 发出请求时,Facebook将基于请求的主体和我们的应用程序秘密生成签名,并将其包含在X-HUB-SIGNATURE标头中。 我们只需检查标头已设置,自己生成签名并比较两者即可。

$req = $app->request();
$headers = $req->headers();

$signature = $headers['X_HUB_SIGNATURE'];
$body = $req->getBody();

$expected = 'sha1=' . hash_hmac('sha1', $body, $facebook->getApiSecret());

if ($signature != $signature) {
	exit();
}

The signature is generated by taking the request body, adding “sha1=” to it the start of it, and then hashing it with SHA1 using the application secret. Once we get this far, we know that the request came from Facebook, so we can proceed onto processing it.

签名是通过以下方式生成的:将请求正文添加到请求正文的开头,然后在其上添加“ sha1 =”,然后使用应用程序密钥将其与SHA1进行哈希处理。 到此为止,我们知道请求来自Facebook,因此我们可以继续进行处理。

处理更新 (Processing the Update)

The update tells us what has changed, but not what it changed to. It’s the developer’s responsibility to query the API to find out the new value. Here’s an example of a notification that tells us that the user identified by 123456789 has friend changes:

此更新告诉我们已更改的内容,但未更改的内容。 查询API以找出新值是开发人员的责任。 这是通知的示例,该通知告诉我们123456789所标识的用户的朋友已更改:

{
  "object": "user",
  "entry": [
    {
      "uid": "123456789",
      "id": "123456789",
      "time": 1374846331,
      "changed_fields": [
        "friends"
      ]
    }
  ]
}

It’s possible that we’ll receive a single update at a time or a bunch of updates together – but always for the same object type. That is, we wouldn’t get user updates and page updates in the same request. As such, we need to check the object type first and then iterate through the actual objects to see what fields have changed.

我们可能一次会收到一个更新,也可能会一起收到一堆更新-但总是针对相同的对象类型。 也就是说,我们不会在同一请求中获得用户更新和页面更新。 因此,我们需要先检查对象类型,然后遍历实际对象以查看哪些字段已更改。

$updates = json_decode($body, true);
if ($updates['object'] == 'user') { 
    foreach ($updates['entry'] as $entry) {
        $uid = $entry['uid'];
        foreach ($entry['changed_fields'] as $field) {
            if ($field == 'friends') {
                ...

We’re only interested in the friends field, but of course you can subscribe to other fields (in which case a switch statement might be better).

我们只对friends字段感兴趣,但是您当然可以订阅其他字段(在这种情况下,使用switch语句可能会更好)。

For each user object, we can retrieve our saved information from the database using the Facebook UID we just grabbed from the request.

对于每个用户对象,我们可以使用刚从请求中获取的Facebook UID从数据库中检索保存的信息。

$user = $db->users('fb_id = ?', $uid)->fetch();
if ($user) {
    $data = unserialize($user['friends']);

The unserialized friends data looks something like this:

未序列化的Friends数据如下所示:

array(123) {
  [0]=>
  array(2) {
    ["name"]=>
    string(11) "Mick Jagger"
    ["id"]=>
    string(7) "123456789"
  }
  [1]=>
  array(2) {
    ["name"]=>
    string(10) "Keith Richards"
    ["id"]=>
    string(8) "987654321"
  }
...

All we want is an array of the ID’s, so here’s where Underscore comes in, specifically the pluck() function:

我们想要的只是ID的数组,因此下面是Underscore的出现位置,特别是pluck()函数:

$friendIDs = __::pluck($data, 'id');

We are using the Facebook API on behalf of the user so we need set the access token to what we have saved previously in the database. Then we can grab an up-to-date list of friends and run pluck() again it so we have an array to compare with. We simply determine the IDs that are missing from the up-to-date list and then use the API to fetch the corresponding names.

我们正在代表用户使用Facebook API,因此我们需要将访问令牌设置为之前保存在数据库中的令牌。 然后,我们可以获取最新的朋友列表,然后再次运行pluck() ,以便我们可以与之进行比较。 我们只需确定最新列表中缺少的ID,然后使用API​​来获取相应的名称。

$facebook->setAccessToken($user['fb_access_token']);
$response = $facebook->api('/me/friends');
$friendsData = $response['data'];

$newFriendIDs = __::pluck($friendsData, 'id');
$removedIDs = array_diff($friendIDs, $newFriendIDs);

if (count($removedIDs)) {
    $html = '<p>The following people have un-friended you:</p>';
    $html .= '<ul>';
    foreach ($removedIDs as $id) {
        $friend = $facebook->api($id);
        $html .= '<ul>' . $friend['name'] . '</li>';
    }
    $html .= '</ul>';

    $mail = $c['phpmailer'];
    $mail->AddAddress($user['email'], $user['name']);
    $mail->Subject = 'Someone has un-friended you on Facebook!';
    $mail->Body = $html;
    $mail->Send();
}

There’s one last thing and we’re done; we need to save the latest friends list to the database so that we can use it in future comparisons.

最后一件事,我们完成了; 我们需要将最新的好友列表保存到数据库中,以便我们在以后的比较中使用它。

$user->update(array(
    'friends' => serialize($friendsData),
));

摘要 (Summary)

We looked at Facebook’s Realtime Updates and Subscriptions API in this article and saw how incredibly useful they are for keeping locally-stored data up to date by letting us fetch updated data when there’s a change. We demonstrated how they can be used by building an example application that monitors a user’s friends list and lets her know when a change has occurred. Be sure to let me know in the comments if you have other ideas for how they can be useful!

我们在本文中研究了Facebook的Realtime Updates and Subscriptions API,并看到它们对于在发生更改时让我们获取更新的数据来保持本地存储的数据保持最新状态非常有用。 我们通过构建一个示例应用程序来演示如何使用它们,该应用程序监视用户的朋友列表并在更改发生时通知她。 如果您对如何使用它们还有其他想法,请务必在评论中告知我!

Image via Fotolia

图片来自Fotolia

翻译自: https://www.sitepoint.com/using-facebooks-realtime-updates-and-subscription-api/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值