いいね！したFacebookページのフィードを定期的にMongoDBに保存する

PHP v5.6
Facebook SDK for PHP v4.0


あんまり需要なさそうですが、自分でいいね！したFacebookページのフィードをまとめてみたいなーと思って、いろいろ探ってみました。

RSS登録すればいいんですけど最近RSSあんまり使ってないし、いいね！の方が気楽だしってことで

定期的に実行させるにはcron等を使ってphpを自動実行させ、その中でFacebook APIを実行させるのですが、Facebook APIを実行するにあたってアクセストークンが必要になるので、事前にユーザーにFacebookアプリ認証画面通してユーザー情報にアクセスができるようにしておく必要があります。

大枠の流れ

**フロント側**

 - Facebookアプリを通してユーザー情報にアクセスできるようにする
 - 有効期限を延長したアクセストークンを取得してDBへ保存

**バッチ側**

 - 登録したアクセストークンを使っていいね！しているFacebookページ一覧を取得
 - それぞれいいね！しているFacebookページのフィードを取得



# Facebookアプリを通してユーザー情報にアクセスできるようにする

Facebook SDK for PHP v4.0で「いいね！」をしたFacebookページを取得
http://qiita.com/ykyk1218/items/9c315efbd14f6316318c

上のURLの中でFacebookアプリを通してアクセストークンを取得する方法を記載してあります。
しかーし、ここで問題が、、、

# 有効期限を延長したアクセストークンを取得してDBへ保存

通常のアクセストークンだとせいぜい1時間ぐらいしか時間がもちません。
そこでアクセストークンの有効期限を延長する必要があります。

## 有効期限を延長したアクセストークンの取得
Facebook APIで有効期限を延長したアクセストークンを取得するには`grant_type=fb_exchange_token`というパラメータを付与してあげる必要があります。

Facebook SDK for PHPを使う場合は`getLongLivedSession()`を使う事で取得できます。

```php
FacebookSession::setDefaultApplication(APP_ID, APP_SECRET);
$helper  = new FacebookRedirectLoginHelper('<facebook認証をした後の戻り先URL（今回はこのURLに戻ってくることとします）>');
$session = $helper->getSessionFromRedirect();

if($session) {
	/* ここで有効期限を延長させてます */
	$longSession = $session->getLongLivedSession()
	$accessToken = $longSession->getToken();
}
```

これで有効期限が延長されたアクセストークンが返却されます。
ただ、延長されたといっても60日なので、有効期限が切れたばあいはアプリ側でうまいこと
調整する必要があります。

## SDKの中身をちょっと見る
ついでにSDKの中でどんなことしているのか見てみます。

こいつがまず呼ばれる関数の処理

```php:vendor/facebook/php-sdk-v4/src/Facebook/FacebookSession.php

public function getLongLivedSession($appId = null, $appSecret = null)
{
	$longLivedAccessToken = $this->accessToken->extend($appId, $appSecret);
	return new static($longLivedAccessToken, $this->signedRequest);
}
```

上記の`$this->accessToken->extend`の部分

```vendor/facebook/php-sdk-v4/src/Facebook/Entities/AccessToken.php
public function extend($appId = null, $appSecret = null)
{
	$params = array(
		'grant_type' => 'fb_exchange_token',
		'fb_exchange_token' => $this->accessToken,
	);

	return static::requestAccessToken($params, $appId, $appSecret);
}
```
こんな感じになっていてfb_exchange_tokenを使っているなーってすぐわかりますね！

## MongoDBへアクセストークンを保存

### MongoDBのインストール
http://qiita.com/ykyk1218/items/1c1824e77fb8af78bcf5

### PHPのMongoDB用ドライバのインストール

```bash
$ sudo pecl install mongo
```

もしくは下記を参考にインストールします
http://andres.jaimes.net/876/setup-mongo-php-module-centos-6/

### アクセストークンをDBに保存
```php
$con = new MongoClient();
$db  = $con->selectDB(<データベース名>);
$col = $db->createCollection("<コレクション名>");

//どのユーザーかわかるように、FacebookユーザーのIDもついでに保存しておく
$request_me  = new FacebookRequest($session, 'GET', '/me');
$response_me = $request_me->execute();
$graphObj = $response_me->getGraphObject();
$userId   = $graphObj->getProperty('id');

$data = array(
	'member_id'    => $userId,
	'access_token' => $accessToken
);
$ret = $col->insert($data);
```

# 登録したアクセストークンを使っていいね！しているFacebookページ一覧を取得
ここからはバッチ処理のはなし

大枠は
Facebook SDK for PHP v4.0で「いいね！」をしたFacebookページを取得
http://qiita.com/ykyk1218/items/9c315efbd14f6316318c

こちらを見ればわかると思います。
変更点としては下記の通り

```bash
$session = new FacebookSession($accessToken);
$request = new FacebookRequest($session, 'GET', '/me/likes');
```

上記のようにセッションを取得するときにアクセストークを使って取得するようにしましょう。

# それぞれいいね！しているFacebookページのフィードを取得
RSSフィードのURLがあるので、それを使ってあげればフィードを取得できます。
ただ、UserAgentを適当なブラウザのものにしないとだめっぽかったので、そこだけ注意ですね。

下記が登録したアクセストークンをMongoDBから取得するところから、Facebookページのフィードを登録するまで処理になります。

```php
<?php

require("vendor/autoload.php");

use Facebook\FacebookSession;
use Facebook\FacebookRequest;
use Facebook\GraphUser;
use Facebook\FacebookRequestException;
use Facebook\FacebookRedirectLoginHelper;

FacebookSession::setDefaultApplication(APP_ID, APP_SECRET);

//各ユーザーのアクセストークンを取得する
$con     = new MongoClient();
$db      = $con->selectDB(DATABASE);
$col     = $db->selectCollection(COLLECTION);
$cursor  = $col->find();

foreach($cursor as $obj) {
	//アクセストークンをもとにいいね！しているページ一覧を取得する
	//アクセストークンの期限が切れている場合はエラー
	$accessToken = $obj['access_token'];
	$memberId    = $obj['member_id'];
	$session = new FacebookSession($accessToken);
	try {
		$session->validate();
		$request = new FacebookRequest($session, 'GET', '/me/likes');
		$response = $request->execute();
		registFacebookPage($db, $response, $memberId);

	}catch(FacebookRequestException $fe) {
		//アクセストークンの有効期限切れとか不正なアクセストークンの場合
		unset($sesssion);
		echo $fe->getCode();
		echo $fe->getMessage();
		exit; 	
	}catch(\Exception $e) {
		echo $e->getMessage();
		exit;
	}
}


function registFacebookPage($db, $response, $memberId) {
	//フォーマットをjson形式で取得
	$facebookFeedUrl = 'https://www.facebook.com/feeds/page.php?format=json&id=';

	$facebookPages = array();
	$graphObjList = $response->getGraphObjectList();

	foreach($graphObjList as $r) {
		$pageId   = $r->getProperty('id');
		$pageName = $r->getProperty('name');
		$url = $facebookFeedUrl . $pageId;
		$ch = curl_init($url);

		//これやらないと直接表示出力される
		curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );

		//UA偽装しないとfacebookから怒られるので適当なブラウザのUAにしておく
		curl_setopt($ch, CURLOPT_USERAGENT, 'AppleWebKit/537.36 (KHTML, like Gecko) Chrome');

		$jsonFeed = curl_exec($ch);
		curl_close($ch);

		$feeds = json_decode($jsonFeed, true);
		$facebookPages[$pageId]['pageName'] = $pageName;
		$facebookPages[$pageId]['entries']  = $feeds['entries'];

		//mongodbに登録
		$colFeeds = $db->createCollection("feeds");

		foreach($feeds['entries'] as $entry) {
			$dataFeed = array(
				"member_id" => $memberId,
				"page_id"   => $pageId,
				"page_name" => $pageName,
				"title"     => $entry['title'],
				"feed_id"   => $entry['id'],
				"alternate" => $entry['alternate'],
				"categories"=> $entry['categories'],
				"published" => $entry['published'],
				"updated"   => $entry['updated'],
				"author"    => $entry['author'],
				"verb"      => $entry['verb'],
				"objects"   => $entry['objects'],
				"comments"  => $entry['comments'],
				"likes"     => $entry['likes'],
				"content"   => $entry['content']
			);
			//同じidの場合は登録しない
			$retCount = $colFeeds->find(array("feed_id" => $dataFeed['feed_id']))->count();
			if($retCount > 0) {
				//登録済み
				continue;
			}

			$ret = $colFeeds->insert($dataFeed);
			if($ret['OK'] != 1) {
				//登録エラー
				
			}
		}
	}
}
?>
```

あとはこのphpを定期的に実行してあげれば登録されていきます。
やっほー！