sammyk/laravel-facebook-sdk

为Laravel & Lumen 5.x提供完全单元测试的Facebook SDK v5集成

3.5.0 2017-07-23 00:05 UTC

README

Build Status Latest Stable Version Total Downloads License

这是一个用于将Facebook SDK v5轻松集成到Laravel和Lumen 5.0、5.1、5.2和5.3的完全单元测试的包。

本包适用于Laravel和Lumen 5.0、5.1、5.2和5.3

Laravel 5

Lumen 5

我真的应该使用Laravel Socialite吗?

Laravel 5内置了对Socialite的支持,允许您使用OAuth 2.0提供程序进行身份验证。Facebook登录使用OAuth 2.0,因此Socialite支持Facebook登录。

如果您只需要对应用程序进行身份验证并获取用户访问令牌以提取用户的基本数据,那么Socialite或The PHP League的Facebook OAuth客户端应该可以满足您的需求。

但是,如果您需要以下任何功能,您将需要将Facebook PHP SDK与该包集成

安装

将Laravel Facebook SDK包添加到您的composer.json文件中。

composer require sammyk/laravel-facebook-sdk

自动发现:从版本3.5.0开始,Laravel Facebook SDK支持自动发现,适用于Laravel 5.5及以上版本。

服务提供者

在您的应用程序配置中,将LaravelFacebookSdkServiceProvider添加到提供者数组中。

'providers' => [
    SammyK\LaravelFacebookSdk\LaravelFacebookSdkServiceProvider::class,
    ];

对于Lumen,将提供者添加到您的bootstrap/app.php文件中。

$app->register(SammyK\LaravelFacebookSdk\LaravelFacebookSdkServiceProvider::class);

门面(可选)

如果您想使用门面,请将其添加到应用程序配置中的别名数组中。

'aliases' => [
    'Facebook' => SammyK\LaravelFacebookSdk\FacebookFacade::class,
    ];

但是,有更好的方法来使用此包,那就是不使用门面

IoC容器

IoC容器会自动为您解决LaravelFacebookSdk的依赖项。您可以通过多种方式从IoC容器中获取LaravelFacebookSdk的实例。

// Directly from the IoC
$fb = App::make('SammyK\LaravelFacebookSdk\LaravelFacebookSdk');
// Or in PHP >= 5.5
$fb = app(SammyK\LaravelFacebookSdk\LaravelFacebookSdk::class);

// From a constructor
class FooClass {
    public function __construct(SammyK\LaravelFacebookSdk\LaravelFacebookSdk $fb) {
       // . . .
    }
}

// From a method
class BarClass {
    public function barMethod(SammyK\LaravelFacebookSdk\LaravelFacebookSdk $fb) {
       // . . .
    }
}

// Or even a closure
Route::get('/facebook/login', function(SammyK\LaravelFacebookSdk\LaravelFacebookSdk $fb) {
    // . . .
});

配置文件

注意:从版本3.4.0开始,发布配置文件是可选的,只要您设置了必需的配置值

此外请注意: 配置文件中包含一个默认的Graph API版本,该版本会定期更新到最新稳定版本,这可能会导致在您更新此包的次要或修补程序版本时发生破坏性更改。建议您仍然发布配置文件,并自行在适当的时间更新Graph API版本,以防止破坏应用程序。

Facebook中创建应用程序后,您需要提供应用程序ID和密钥。在Laravel中,您可以使用artisan命令发布配置文件。

$ php artisan vendor:publish --provider="SammyK\LaravelFacebookSdk\LaravelFacebookSdkServiceProvider" --tag="config"

文件在哪里? Laravel 5会将配置文件发布到/config/laravel-facebook-sdk.php

Lumen中,您需要手动将配置文件从/src/config/laravel-facebook-sdk.php复制到您的基项目目录中的配置文件夹。Lumen默认没有/config文件夹,所以如果您还没有创建,您需要创建它。

必需的配置值

您需要使用您的应用程序ID和密钥更新配置文件中的app_idapp_secret值。

默认情况下,配置文件将查找环境变量以获取您的应用程序ID和密钥。建议您使用环境变量存储此信息,以保护您的应用程序密钥免受攻击者侵害。请确保更新您的/.env文件,包含您的应用程序ID和密钥。

FACEBOOK_APP_ID=1234567890
FACEBOOK_APP_SECRET=SomeFooAppSecret

通过重定向进行用户登录示例

以下是一个使用重定向方法登录用户的完整示例。

此示例还演示了如何交换短生命周期的访问令牌和长生命周期的访问令牌,并在条目不存在时将用户保存到您的users表中。

最后,它会使用Laravel内置的用户身份验证登录用户。

Lumen中的会话:“从重定向登录”功能依赖于会话来存储CSRF令牌。由于Lumen 5.2+中没有会话,您需要使用不同的方法来获取访问令牌。为了测试,您可以从Graph API Explorer获取访问令牌(请确保从“应用程序”下拉菜单中选择您的应用程序)。

// Generate a login URL
Route::get('/facebook/login', function(SammyK\LaravelFacebookSdk\LaravelFacebookSdk $fb)
{
    // Send an array of permissions to request
    $login_url = $fb->getLoginUrl(['email']);

    // Obviously you'd do this in blade :)
    echo '<a href="' . $login_url . '">Login with Facebook</a>';
});

// Endpoint that is redirected to after an authentication attempt
Route::get('/facebook/callback', function(SammyK\LaravelFacebookSdk\LaravelFacebookSdk $fb)
{
    // Obtain an access token.
    try {
        $token = $fb->getAccessTokenFromRedirect();
    } catch (Facebook\Exceptions\FacebookSDKException $e) {
        dd($e->getMessage());
    }

    // Access token will be null if the user denied the request
    // or if someone just hit this URL outside of the OAuth flow.
    if (! $token) {
        // Get the redirect helper
        $helper = $fb->getRedirectLoginHelper();

        if (! $helper->getError()) {
            abort(403, 'Unauthorized action.');
        }

        // User denied the request
        dd(
            $helper->getError(),
            $helper->getErrorCode(),
            $helper->getErrorReason(),
            $helper->getErrorDescription()
        );
    }

    if (! $token->isLongLived()) {
        // OAuth 2.0 client handler
        $oauth_client = $fb->getOAuth2Client();

        // Extend the access token.
        try {
            $token = $oauth_client->getLongLivedAccessToken($token);
        } catch (Facebook\Exceptions\FacebookSDKException $e) {
            dd($e->getMessage());
        }
    }

    $fb->setDefaultAccessToken($token);

    // Save for later
    Session::put('fb_user_access_token', (string) $token);

    // Get basic info on the user from Facebook.
    try {
        $response = $fb->get('/me?fields=id,name,email');
    } catch (Facebook\Exceptions\FacebookSDKException $e) {
        dd($e->getMessage());
    }

    // Convert the response to a `Facebook/GraphNodes/GraphUser` collection
    $facebook_user = $response->getGraphUser();

    // Create the user if it does not exist or update the existing entry.
    // This will only work if you've added the SyncableGraphNodeTrait to your User model.
    $user = App\User::createOrUpdateGraphNode($facebook_user);

    // Log the user into Laravel
    Auth::login($user);

    return redirect('/')->with('message', 'Successfully logged in with Facebook');
});

有关用户身份验证的更多详细信息,请参阅Facebook登录

向Facebook发送请求

对Facebook的请求是通过Graph API发出的。此包是官方Facebook PHP SDK v5的Laravel包装器,因此所有官方SDK中可用的方法也都可在本包中使用。

获取用户信息

以下代码片段将检索代表已登录用户的用户节点

try {
  $response = $fb->get('/me?fields=id,name,email', 'user-access-token');
} catch(\Facebook\Exceptions\FacebookSDKException $e) {
  dd($e->getMessage());
}

$userNode = $response->getGraphUser();
printf('Hello, %s!', $userNode->getName());

有关更多关于get()方法的说明。

Facebook登录

当我们说“通过Facebook登录”时,我们的真正意思是“获取一个用户访问令牌以代表用户调用Graph API”。这是通过Facebook通过OAuth 2.0完成的。有多种方法可以使用Facebook PHP SDK中的“辅助函数”通过Facebook登录用户。

支持的四种登录方法是

  1. 通过重定向登录 (OAuth 2.0)
  2. 通过JavaScript登录 (带有JS SDK Cookie)
  3. 通过应用画布登录 (带有签名请求)
  4. 通过页面标签登录 (带有签名请求)

通过重定向登录

将用户登录到您的应用程序的最常见方法之一是使用重定向URL。

思路是您生成一个唯一的URL,用户点击该URL。一旦用户点击链接,他们将被重定向到Facebook,要求他们授予您的应用程序请求的任何权限。一旦用户做出回应,Facebook将用户重定向回您指定的回调URL,无论是成功响应还是错误响应。

可以通过SDK的getRedirectLoginHelper()方法获取重定向辅助程序。

生成登录URL

您可以通过与Facebook PHP SDK v5相同的方式获取登录URL。

Route::get('/facebook/login', function(SammyK\LaravelFacebookSdk\LaravelFacebookSdk $fb) {
    $login_link = $fb
            ->getRedirectLoginHelper()
            ->getLoginUrl('https://exmaple.com/facebook/callback', ['email', 'user_events']);
    
    echo '<a href="' . $login_link . '">Log in with Facebook</a>';
});

但是,如果您在配置文件中设置了default_redirect_uri回调URL,则可以使用getLoginUrl()包装方法,该方法将默认回调URL(default_redirect_uri)和权限范围(default_scope)设置为配置文件中设置的任何内容。

$login_link = $fb->getLoginUrl();

或者,您可以传递权限和自定义回调URL到包装器,以覆盖默认配置。

注意:由于权限列表有时会发生变化,但回调URL通常保持不变,因此权限数组是getLoginUrl()包装方法中的第一个参数,这与SDK的getRedirectLoginHelper()->getLoginUrl($url, $permissions)方法的顺序相反。

$login_link = $fb->getLoginUrl(['email', 'user_status'], 'https://exmaple.com/facebook/callback');
// Or, if you want to default to the callback URL set in the config
$login_link = $fb->getLoginUrl(['email', 'user_status']);

从回调URL获取访问令牌

用户点击上面的登录链接并确认或拒绝应用程序权限请求后,他们将被重定向到指定的回调URL。

在回调URL上获取访问令牌的“SDK”标准方法如下

Route::get('/facebook/callback', function(SammyK\LaravelFacebookSdk\LaravelFacebookSdk $fb) {
    try {
        $token = $fb
            ->getRedirectLoginHelper()
            ->getAccessToken();
    } catch (Facebook\Exceptions\FacebookSDKException $e) {
        // Failed to obtain access token
        dd($e->getMessage());
    }
});

在LaravelFacebookSdk中有一个包装方法getRedirectLoginHelper()->getAccessToken(),名为getAccessTokenFromRedirect(),它默认将回调URL设置为laravel-facebook-sdk.default_redirect_uri配置值。

Route::get('/facebook/callback', function(SammyK\LaravelFacebookSdk\LaravelFacebookSdk $fb) {
    try {
        $token = $fb->getAccessTokenFromRedirect();
    } catch (Facebook\Exceptions\FacebookSDKException $e) {
        // Failed to obtain access token
        dd($e->getMessage());
    }
    
    // $token will be null if the user denied the request
    if (! $token) {
        // User denied the request
    }
});

通过JavaScript登录

如果您使用的是JavaScript SDK,则可以从由JavaScript SDK设置的Cookie中获取访问令牌。

默认情况下,JavaScript SDK不会设置Cookie,因此您必须在调用SDK时显式启用它,使用cookie: true

FB.init({
  appId      : 'your-app-id',
  cookie     : true,
  version    : 'v2.10'
});

使用FB.login()通过JavaScript SDK登录用户后,您可以从存储在由JavaScript SDK设置的Cookie中的签名请求中获取用户访问令牌。

Route::get('/facebook/javascript', function(SammyK\LaravelFacebookSdk\LaravelFacebookSdk $fb) {
    try {
        $token = $fb->getJavaScriptHelper()->getAccessToken();
    } catch (Facebook\Exceptions\FacebookSDKException $e) {
        // Failed to obtain access token
        dd($e->getMessage());
    }

    // $token will be null if no cookie was set or no OAuth data
    // was found in the cookie's signed request data
    if (! $token) {
        // User hasn't logged in using the JS SDK yet
    }
});

通过应用画布登录

TokenMismatchException: 默认Laravel安装将在您尝试在Facebook中查看您的应用程序时抛出TokenMismatchException查看如何修复此问题

如果您的应用程序位于Facebook应用程序画布的上下文中,您可以从第一次页面加载时通过POST发送到您的应用程序的签名请求中获取访问令牌。

注意:画布辅助程序仅从Facebook接收到的签名请求数据中获取现有的访问令牌。如果访问您的应用程序的用户尚未授权您的应用程序或其访问令牌已过期,则getAccessToken()方法将返回null。在这种情况下,您需要使用重定向JavaScript登录用户。

使用SDK的画布辅助工具从已签名的请求数据中获取访问令牌。

Route::match(['get', 'post'], '/facebook/canvas', function(SammyK\LaravelFacebookSdk\LaravelFacebookSdk $fb) {
    try {
        $token = $fb->getCanvasHelper()->getAccessToken();
    } catch (Facebook\Exceptions\FacebookSDKException $e) {
        // Failed to obtain access token
        dd($e->getMessage());
    }

    // $token will be null if the user hasn't authenticated your app yet
    if (! $token) {
        // . . .
    }
});

页面标签登录

TokenMismatchException: 默认Laravel安装会在您尝试查看Facebook页面标签时抛出 TokenMismatchException查看如何修复此问题

如果您的应用位于Facebook页面标签的上下文中,这等同于应用画布,并且“从应用画布登录”方法也可以用来获取访问令牌。但页面标签在已签名的请求中还有其他数据。

SDK提供页面标签辅助工具,用于从页面标签上下文中的已签名的请求数据中获取访问令牌。

Route::match(['get', 'post'], '/facebook/page-tab', function(SammyK\LaravelFacebookSdk\LaravelFacebookSdk $fb) {
    try {
        $token = $fb->getPageTabHelper()->getAccessToken();
    } catch (Facebook\Exceptions\FacebookSDKException $e) {
        // Failed to obtain access token
        dd($e->getMessage());
    }

    // $token will be null if the user hasn't authenticated your app yet
    if (! $token) {
        // . . .
    }
});

其他授权请求

Facebook支持两种其他类型的授权URL - 重新请求和重新认证。

重新请求

重新请求(或重新请求?)会再次询问用户之前拒绝的权限。使用重新请求URL而不是仅使用正常登录链接进行重定向非常重要,因为

一旦有人拒绝了权限,登录对话框不会再次询问,除非您明确告诉对话框您正在重新请求被拒绝的权限。- Facebook 文档

您可以使用 getReRequestUrl() 方法生成重新请求URL。

$rerequest_link = $fb->getReRequestUrl(['email'], 'https://exmaple.com/facebook/login');
// Or, if you want to default to the callback URL set in the config
$rerequest_link = $fb->getReRequestUrl(['email']);

重新认证

重新认证 强迫用户通过再次输入他们的Facebook账户密码来确认他们的身份。在更改或查看您的Web应用程序上的敏感数据之前添加另一层安全措施很有用。

您可以使用 getReAuthenticationUrl() 方法生成重新认证URL。

$re_authentication_link = $fb->getReAuthenticationUrl(['email'], 'https://exmaple.com/facebook/login');
// Or, if you want to default to the callback URL set in the config
$re_authentication_link = $fb->getReAuthenticationUrl(['email']);
// Or without permissions
$re_authentication_link = $fb->getReAuthenticationUrl();

保存访问令牌

在大多数情况下,除非您计划在用户不浏览您的应用程序(例如,例如凌晨3点的CRON作业)时代表用户向Graph API发出请求,否则您不需要将访问令牌保存到数据库中。

获取访问令牌后,您可以将其存储在会话中以便用于后续请求。

Session::put('facebook_access_token', (string) $token);

然后在每个调用Graph API的脚本中,您可以从会话中提取令牌并将其设置为默认值。

$token = Session::get('facebook_access_token');
$fb->setDefaultAccessToken($token);

在数据库中保存从Facebook获取的数据

将接收到的Graph API数据保存到数据库有时可能是一项繁琐的任务。由于Graph API以可预测的格式返回数据,因此 SyncableGraphNodeTrait 可以使将数据保存到数据库变得容易。

任何实现了 SyncableGraphNodeTrait 的Eloquent模型都会应用 createOrUpdateGraphNode() 方法。此方法真正使直接从Facebook返回的数据创建或更新本地数据库变得简单。

use SammyK\LaravelFacebookSdk\SyncableGraphNodeTrait;

class Event extends Eloquent {
    use SyncableGraphNodeTrait;
}

例如,如果您有一个名为 Event 的Eloquent模型,以下是您可能如何从Graph API获取特定事件并将其作为新条目插入数据库或更新具有新信息的现有条目的方法。

$response = $fb->get('/some-event-id?fields=id,name');
$eventNode = $response->getGraphEvent();

// A method called createOrUpdateGraphNode() on the `Event` eloquent model
// will create the event if it does not exist or it will update the existing
// record based on the ID from Facebook.
$event = Event::createOrUpdateGraphNode($eventNode);

createOrUpdateGraphNode() 将自动将返回的字段名称映射到数据库中的列名称。例如,如果您的 events 表的列名称与事件 节的字段名称不匹配,您可以映射字段

字段映射

由于您的数据库中的列名称可能不匹配Graph节点的字段名称,因此您可以使用 $graph_node_field_aliases 静态变量在您的 User 模型中映射字段名称。

数组的 是 Graph 节点上字段的名称。数组的 是本地数据库中列的名称。

use SammyK\LaravelFacebookSdk\SyncableGraphNodeTrait;

class User extends Eloquent implements UserInterface
{
    use SyncableGraphNodeTrait;
    
    protected static $graph_node_field_aliases = [
        'id' => 'facebook_user_id',
        'name' => 'full_name',
        'graph_node_field_name' => 'database_column_name',
    ];
}

指定 "可填充" 字段

默认情况下,createOrUpdateGraphNode() 方法会尝试将节点的所有字段插入到数据库中。但有时 Graph API 会返回您未明确请求且不存在于您数据库中的字段。在这种情况下,我们可以使用 $graph_node_fillable_fields 属性将特定字段列入白名单。

use SammyK\LaravelFacebookSdk\SyncableGraphNodeTrait;

class Event extends Eloquent
{
    use SyncableGraphNodeTrait;
    
    protected static $graph_node_fillable_fields = ['id', 'name', 'start_time'];
}

使用数据库列的名称。 例如,如果您已将数据库中的 id 字段别名为 facebook_id 列,则需要在您的 $graph_node_fillable_fields 数组中指定 facebook_id

嵌套字段映射

由于 Graph API 会将某些字段作为其他节点/对象返回,因此您可以使用 Laravel 的 array_dot() 表示法 来引用这些字段。

例如,您可以向 /me/events 端点发出请求,循环遍历所有事件并将它们保存到您的 Event 模型中。事件节点 将返回作为 位置节点place.location 字段。响应数据可能如下所示

{
  "data": [
    {
      "id": "123", 
      "name": "Foo Event", 
      "place": {
        "location": {
          "city": "Dearborn", 
          "state": "MI", 
          "country": "United States", 
          . . .
        }, 
        "id": "827346"
      }
    },
    . . .
  ]
}

假设您有一个这样的事件表

Schema::create('events', function(Blueprint $table)
{
    $table->increments('id');
    $table->bigInteger('facebook_id')->nullable()->unsigned()->index();
    $table->string('name')->nullable();
    $table->string('city')->nullable();
    $table->string('state')->nullable();
    $table->string('country')->nullable();
});

以下是您如何将嵌套字段映射到您的 Event 模型中的数据库表中的方法

use SammyK\LaravelFacebookSdk\SyncableGraphNodeTrait;

class Event extends Eloquent
{
    use SyncableGraphNodeTrait;
    
    protected static $graph_node_field_aliases = [
        'id' => 'facebook_id',
        'place.location.city' => 'city',
        'place.location.state' => 'state',
        'place.location.country' => 'country',
    ];
}

日期格式

Facebook PHP SDK 将大多数日期格式转换为 DateTime 实例。当您想将日期/时间值插入数据库时(例如,事件节点start_time 字段),这可能会出现问题。

默认情况下,SyncableGraphNodeTrait 将所有 DateTime 实例转换为以下 date() 格式

Y-m-d H:i:s

这应该是大多数关系型数据库上大多数情况下的正确格式。但此格式缺少时区,这可能对您的应用程序很重要。此外,如果您以不同的格式存储日期/时间值,您可能需要自定义 DateTime 实例转换为的格式。为此,只需在您的模型中添加一个 $graph_node_date_time_to_string_format 属性并将其设置为任何 有效的日期格式

use SammyK\LaravelFacebookSdk\SyncableGraphNodeTrait;

class Event extends Eloquent
{
    use SyncableGraphNodeTrait;
    
    protected static $graph_node_date_time_to_string_format = 'c'; # ISO 8601 date
}

在Laravel中登录用户

Laravel Facebook SDK 使您可以使用 Laravel 内置的身份验证驱动程序轻松地登录用户。

更新用户表

为了使 Facebook 身份验证与 Laravel 内置的身份验证一起工作,您需要将 Facebook 用户的 ID 存储在您的用户表中。

自然地,您需要为要保留的用户的其他任何信息创建一个列。

如果您需要在用户不在浏览您的应用程序时(如凌晨3点的 cron 作业)代表用户发出请求,则可以将访问令牌存储在数据库中。但通常您不需要在数据库中存储访问令牌。

您需要生成一个迁移来修改您的 users 表并添加任何新列。

注意:请确保将 <name-of-users-table> 更改为您的用户表名称。

$ php artisan make:migration add_facebook_columns_to_users_table --table="<name-of-users-table>"

现在更新迁移文件以包括您要在用户上保存的新字段。至少您需要保存 Facebook 用户 ID。

use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class AddFacebookColumnsToUsersTable extends Migration
{
    public function up()
    {
        Schema::table('users', function(Blueprint $table)
        {
            // If the primary id in your you user table is different than the Facebook id
            // Make sure it's an unsigned() bigInteger()
            $table->bigInteger('facebook_user_id')->unsigned()->index();
            // Normally you won't need to store the access token in the database
            $table->string('access_token')->nullable();
        });
    }

    public function down()
    {
        Schema::table('users', function(Blueprint $table)
        {
            $table->dropColumn(
                'facebook_user_id',
                'access_token'
            );
        });
    }
}

别忘了运行迁移。

$ php artisan migrate

如果您打算将Facebook用户ID作为主键,请确保您有一个名为 id 的列,它是一个无符号的大整数并且已建立索引。如果您将Facebook ID存储在不同的字段中,请确保该字段存在于数据库中,并在您的模型中将它映射到自定义的ID名称。

如果您正在使用Eloquent ORM并在数据库中存储访问令牌,请确保在您的 User 模型中隐藏 access_token 字段,以避免暴露。

别忘了将 SyncableGraphNodeTrait 添加到您的用户模型中,以便您可以将模型与从Graph API返回的数据同步。

# User.php
use SammyK\LaravelFacebookSdk\SyncableGraphNodeTrait;

class User extends Eloquent implements UserInterface {
    use SyncableGraphNodeTrait;

    protected $hidden = ['access_token'];
}

在Laravel中登录用户

用户使用Facebook登录并从Graph API获取用户ID后,您可以通过将登录用户的 User 模型传递给 Auth::login() 方法来在Laravel中登录用户。

class FacebookController {
    public function getUserInfo(SammyK\LaravelFacebookSdk\LaravelFacebookSdk $fb) {
       try {
           $response = $fb->get('/me?fields=id,name,email');
       } catch (Facebook\Exceptions\FacebookSDKException $e) {
           dd($e->getMessage());
       }
       
       // Convert the response to a `Facebook/GraphNodes/GraphUser` collection
       $facebook_user = $response->getGraphUser();
       
       // Create the user if it does not exist or update the existing entry.
       // This will only work if you've added the SyncableGraphNodeTrait to your User model.
       $user = App\User::createOrUpdateGraphNode($facebook_user);
       
       // Log the user into Laravel
       Auth::login($user);
    }
}

处理多个应用程序

如果您想在同一脚本中使用多个Facebook应用或者想在运行时调整设置,您可以使用自定义设置创建一个新的 LaravelFacebookSdk 实例。

Route::get('/example', function(SammyK\LaravelFacebookSdk\LaravelFacebookSdk $fb) {
    // All the possible configuration options are available here
    $fb2 = $fb->newInstance([
      'app_id' => env('FACEBOOK_APP_ID2'),
      'app_secret' => env('FACEBOOK_APP_SECRET2'),
      'default_graph_version' => 'v2.10',
      // . . .
    ]);
});

错误处理

Facebook PHP SDK会抛出 Facebook\Exceptions\FacebookSDKException 异常。每当Graph API返回错误响应时,SDK会抛出一个继承自 Facebook\Exceptions\FacebookSDKExceptionFacebook\Exceptions\FacebookResponseException。如果您抛出了 Facebook\Exceptions\FacebookResponseException,您可以通过调用 getPrevious() 方法获取与错误相关的特定异常。

try {
    // Stuffs here
} catch (Facebook\Exceptions\FacebookResponseException $e) {
    $graphError = $e->getPrevious();
    echo 'Graph API Error: ' . $e->getMessage();
    echo ', Graph error code: ' . $graphError->getCode();
    exit;
} catch (Facebook\Exceptions\FacebookSDKException $e) {
    echo 'SDK Error: ' . $e->getMessage();
    exit;
}

LaravelFacebookSdk不会抛出任何自定义异常。

故障排除

在使用canvas应用时遇到TokenMismatchException

如果您的应用在应用画布或页面标签的上下文中提供服务,您在尝试在Facebook上查看应用时可能会看到 TokenMismatchException 错误。这是因为Facebook会通过发送包含 signed_request 参数的POST请求来渲染您的应用,由于Laravel 5为每个非读取请求启用了CSRF保护,因此触发了错误。

虽然可以完全禁用此功能,但绝对不建议这样做,因为CSRF保护是网站的重要安全功能,并且应该默认在每条路由上启用。

在Laravel 5.1 & 5.2中禁用CSRF

将异常添加到您的canvas端点中的 $except 数组,在您的 app\Http\Middleware\VerifyCsrfToken.php 文件中。

<?php

namespace App\Http\Middleware;

use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as BaseVerifier;

class VerifyCsrfToken extends BaseVerifier
{
    /**
     * The URIs that should be excluded from CSRF verification.
     *
     * @var array
     */
    protected $except = [
        'facebook/canvas',
        'facebook/page-tab',
        // ... insert all your canvas endpoints here
    ];
}

在Laravel 5.0中禁用CSRF

在Laravel 5.0中禁用CSRF验证有些复杂,但有一篇文章解释了如何在Laravel 5.0中禁用特定路由的CSRF保护

在您的 app\Http\Middleware\VerifyCsrfToken.php 文件中,添加一个 excludedRoutes() 方法。然后创建一个包含您画布应用或页面标签端点的路由数组。完整的文件如下所示

<?php namespace App\Http\Middleware;

use Closure;
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as BaseVerifier;
use Illuminate\Session\TokenMismatchException;

class VerifyCsrfToken extends BaseVerifier
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     *
     * @throws TokenMismatchException
     */
    public function handle($request, Closure $next)
    {
        if ($this->isReading($request) || $this->excludedRoutes($request) || $this->tokensMatch($request)) {
            return $this->addCookieToResponse($request, $next($request));
        }

        throw new TokenMismatchException;
    }

    /**
     * Ignore CSRF on these routes.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return bool
     */
    private function excludedRoutes($request)
    {
        $routes = [
          'my-app/canvas',
          'my-app/page-tab',
          // ... insert all your canvas endpoints here
        ];

        foreach($routes as $route){
            if ($request->is($route)) {
                return true;
            }
        }

        return false;
    }
}

在保存用户时遇到QueryException

如果您正在使用MySQL,您可能会在用 createOrUpdateGraphNode() 将用户保存到数据库时遇到 QueryException

QueryException in Connection.php line 754:
SQLSTATE[HY000]: General error: 1364 Field 'password' doesn't have a default value

这是因为默认情况下启用了严格模式,将 sql_mode 设置为包含 STRICT_TRANS_TABLES。由于我们不需要为通过Facebook登录的用户设置密码,该字段将保持为空。解决这个错误的办法是将您的 config/database.php 文件中的MySQL diver的 strict 设置为 false

测试

测试是用 phpunit 编写的。您可以通过以下命令从项目目录的根目录运行测试。

$ ./vendor/bin/phpunit

贡献

有关详细信息,请参阅 CONTRIBUTING

鸣谢

该软件包由 Sammy Kaye Powers 维护。查看完整的 贡献者列表

许可证

MIT 许可证 (MIT)。有关更多信息,请参阅 许可证文件