如何将 Facebook PHP SDK 与 Laravel 5.4 集成?

How to integrate Facebook PHP SDK with Laravel 5.4?

我正在寻找一种在 github 上集成 Facebook PHP SDK with Laravel 5.4. Essentially, I wanted to make it available as a service within my Laravel app. Of course there is SammyK/LaravelFacebookSdk 的简单方法。但出于某种原因,我不想使用它。我觉得设置添加了另一个我必须理解的层,并在限制范围内工作。

还有Laravel自己的Socialite包。但这本质上仅用于简单的身份验证。如果我要上传照片、评论、批量请求,这些都是不可能的。 Socialite 不使用 Facebook 的 PHP SDK 作为依赖项。它使用 Guzzle 包直接 API 仅请求身份验证。

因为没有简单的SO答案直接集成Facebook SDK,步骤最少,所以我想我会写这个。

首先,在项目的根文件夹中编辑 composer.json 以包含 Facebook SDK:

{
  "require" : {
    "facebook/graph-sdk" : "~5.0"
  }
}

下一步 运行 composer update 在 shell 提示下将 sdk 拉入供应商文件夹。

现在我们想在我们的应用程序中使用 Facebook SDK 作为服务提供商。但在此之前,让我们设置 app_idapp_secretdefault_graph_version,它们是向 Facebook API 发出请求时所需的参数。 app_id和app_secret可以在Facebook Developers网站注册获得。

从 Facebook 获得这些凭据后,我们现在将编辑项目根文件夹中的 .env 文件。将它们添加到末尾:

FACEBOOK_APP_ID=xxxxxxxxxxxxxxxx
FACEBOOK_APP_SECRET=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
FACEBOOK_DEFAULT_GRAPH_VERSION=v2.8

将 xxx.. 替换为提供给您的值。请注意,变量名称只是我自己创建的。您可以随意命名它们。我们现在必须使用这些变量来设置一个单独的配置文件。我们需要这样做,以便我们可以使用 Laravel 的 config() 辅助函数来检索应用程序中我们想要的任何位置的值。因此,让我们在配置文件夹中创建 facebook.php 并添加以下内容:

<?php

return [
    'config' => [
        'app_id' => env('FACEBOOK_APP_ID', null),
        'app_secret' => env('FACEBOOK_APP_SECRET', null),
        'default_graph_version' => env('FACEBOOK_DEFAULT_GRAPH_VERSION', 'v2.8'),
    ],
];

通过这个简单的设置,我们现在可以从应用程序的任何位置调用 config('facebook.config')。它将 return 数组以及从 .env 文件匹配的相关值。

现在让我们将其设置为服务提供商,这样我们就不必在每次调用 Facebook API 时检索这些凭据并构建新的 Facebook object .

在Laravel 5.4中打开文件app\Providers\AppServiceProvider.php。如果您没有此文件或想制作一个单独的文件,那么您可以在 shell:

创建一个新的服务提供商

php artisan make:provider FacebookServiceProvider

我们现在可以在 Providers 文件夹中编辑 FacebookServiceProvider.php。唯一的区别是我们需要在 config/app.php 文件中注册它。您可以在 $providers 数组的末尾添加以下内容:

App\Providers\FacebookServiceProvider::class,

要继续相关代码,在 AppServiceProvider.php 或我们新的 FacebookServiceProvider.php 中,我们首先包括:use Facebook\Facebook; 在顶部。然后在 register() method 添加以下内容:

$this->app->singleton(Facebook::class, function ($app) {
            return new Facebook(config('facebook.config'));
        });

您可能会注意到我将 class 绑定为 singleton,因为对于我的应用程序,我想重用服务容器中的相同 object。 Laravel 提供了 other types of bindings,您可能想要查看。

整个代码如下所示(我正在使用 AppServiceProvider.php):

<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use Facebook\Facebook;

class AppServiceProvider extends ServiceProvider
{
    /**
     * Bootstrap any application services.
     *
     * @return void
     */
    public function boot()
    {
        //
    }

    /**
     * Register any application services.
     *
     * @return void
     */
    public function register()
    {
        $this->app->singleton(Facebook::class, function ($app) {
            return new Facebook(config('facebook.config'));
        });
    }
}

就是这样。我们现在可以使用 Facebook 作为应用程序的服务。我们现在可以 inject the facebook object wherever we would like to use it within our app. From here on one can simply follow instructions from Facebook documentation 调用他们的 API.'

额外的东西,举个例子:

在继续之前,我想提一下,我发现 Symfony 的人们编写的 post 系列对理解 Service Container 和 [=38 的概念非常有帮助=].你可以find them here

现在让我们尝试做一个基本的操作,比如从 facebook 中检索一个人的名字。为了获取有关 facebook 用户的信息,我们需要通过发送基本参数来调用 Facebook API:用户的 facebook id 以及访问令牌。我们分别称它们为 uidaccess_token。您需要使用 Facebook 的一种方法来检索这些内容:

  1. FacebookRedirectLoginHelper - 当您希望从服务器端进行 Facebook 身份验证时。
  2. FacebookCanvasHelper - 用于客户端 canvas 基于应用程序身份验证
  3. FacebookJavaScriptHelper - 用于客户端javascript 身份验证

您可以按照 Facebook getting started 指南中提供的步骤设置所需的身份验证类型。

我的应用程序非常简单,所以我使用了客户端 javascript 身份验证。我也同时使用 jquery。由于我使用 Laravel 的 blade 引擎,我的 javascript 直接嵌入到视图文件中,这样我就可以包含 Laravel 的 csrf_token() 并使用其他url() 等辅助函数。客户端 javascript 如下所示。请记住将 appId 替换为您的值并将文件另存为 login.blade.php.

<html>
<head>
    <link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet" type="text/css"/>
</head>
<body>
<button id="btn-login" type="button" class="btn btn-primary btn-lg">
    <span> Login with Facebook</span>
</button>

<script>
$(document).ready(function() {
    $.ajaxSetup({ cache: true }); // since I am using jquery as well in my app
    $.getScript('//connect.facebook.net/en_US/sdk.js', function () {
        // initialize facebook sdk
        FB.init({
            appId: 'xxxxxxxxxxxxxxxx', // replace this with your id
            status: true,
            cookie: true,
            version: 'v2.8'
        });

        // attach login click event handler
        $("#btn-login").click(function(){
            FB.login(processLoginClick, {scope:'public_profile,email,user_friends', return_scopes: true});  
        });
    });

// function to send uid and access_token back to server
// actual permissions granted by user are also included just as an addition
function processLoginClick (response) {    
    var uid = response.authResponse.userID;
    var access_token = response.authResponse.accessToken;
    var permissions = response.authResponse.grantedScopes;
    var data = { uid:uid, 
                 access_token:access_token, 
                 _token:'{{ csrf_token() }}', // this is important for Laravel to receive the data
                 permissions:permissions 
               };        
    postData("{{ url('/login') }}", data, "post");
}

// function to post any data to server
function postData(url, data, method) 
{
    method = method || "post";
    var form = document.createElement("form");
    form.setAttribute("method", method);
    form.setAttribute("action", url);
    for(var key in data) {
        if(data.hasOwnProperty(key)) 
        {
            var hiddenField = document.createElement("input");
            hiddenField.setAttribute("type", "hidden");
            hiddenField.setAttribute("name", key);
            hiddenField.setAttribute("value", data[key]);
            form.appendChild(hiddenField);
         }
    }
    document.body.appendChild(form);
    form.submit();
}

</script>
</body>
</html>

如果您的应用需要一组不同的用户权限,请编辑 scope 字段。对于所有可用的 facebook 权限 see here.

所以基本上,我的代码有一个登录按钮,当用户单击它时,javascript sdk 启动并弹出一个登录 window。用户登录后,我 post 将数据返回服务器,就好像正在 posted 表单一样。

现在回到服务器端,因为我们有 uidaccess_token 我们可以将它们存储到数据库,然后从我们的服务器对 Facebook API 进行简单调用。在routes/web.php设置路由接收表单数据:

Route::post('login', 'FacebookUser@store');

在 shell 处创建 FacebookUser 控制器:

php artisan make:controller FacebookUser

而controller的代码如下:

<?php

namespace App\Http\Controllers;

use Request;
use App\User; // you need to define the model appropriately
use Facebook\Facebook;

class FacebookUser extends Controller
{
    public function store(Facebook $fb) //method injection
    {
        // retrieve form input parameters
        $uid = Request::input('uid');
        $access_token = Request::input('access_token');
        $permissions = Request::input('permissions');

        // assuming we have a User model already set up for our database
        // and assuming facebook_id field to exist in users table in database
        $user = User::firstOrCreate(['facebook_id' => $uid]); 

        // get long term access token for future use
        $oAuth2Client = $fb->getOAuth2Client();

        // assuming access_token field to exist in users table in database
        $user->access_token = $oAuth2Client->getLongLivedAccessToken($access_token)->getValue();
        $user->save();

        // set default access token for all future requests to Facebook API            
        $fb->setDefaultAccessToken($user->access_token);

        // call api to retrieve person's public_profile details
        $fields = "id,cover,name,first_name,last_name,age_range,link,gender,locale,picture,timezone,updated_time,verified";
        $fb_user = $fb->get('/me?fields='.$fields)->getGraphUser();
        dump($fb_user);
    }    
}

请注意,通过使用 setDefaultAccessToken(),可以在应用程序代码库的后续处理的任何部分从服务容器中检索 $fb object。 $fb 可以直接用于发出 Facebook API 请求。无需使用 app_id、app_secret 再次构建 $fb,也无需在期间为当前用户再次设置访问令牌当前 request-response 生命周期。这是因为 $fb 是单例的,因此在当前 request-response 生命周期中为 Facebook 服务调用服务容器时,相同的 object 被 returned。

如果你无法通过方法注入获取$fbobject,那么还有other ways of resolving it。我个人最喜欢使用 resolve() 助手,因为它的工作与 class.

中的 object 上下文或静态函数无关
$fb = resolve('Facebook\Facebook');