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

时间:2017-03-22 07:48:53

标签: laravel facebook-php-sdk laravel-5.4

我正在寻找一种简单的方法来将Facebook PHP SDK与Laravel 5.4集成。基本上,我想在我的Laravel应用程序中将其作为服务提供。当然github上有SammyK/LaravelFacebookSdk。但出于某种原因,我并不想使用它。我觉得设置是添加了另一个我必须理解的层,并且在约束条件下工作。

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

由于没有简单的SO答案可以用最少的步骤直接整合Facebook SDK,我想我会写这个。

1 个答案:

答案 0 :(得分:63)

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

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

接下来在shell提示符下运行composer update以将sdk拉入供应商文件夹。

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

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

FACEBOOK_APP_ID=xxxxxxxxxxxxxxxx
FACEBOOK_APP_SECRET=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
FACEBOOK_DEFAULT_GRAPH_VERSION=v2.8

将xxx ..替换为提供给您的值。请注意,变量名称只是我自己创建的。无论你喜欢什么,都可以为它们命名。我们现在必须使用这些变量来设置单独的配置文件。我们需要这样做,以便我们可以使用Laravel的config()辅助函数在应用程序中的任何位置检索值。因此,让我们在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')。它将返回数组以及.env文件中匹配的相关值。

现在让我们将其设置为服务提供商,这样我们就不必在每次打电话给Facebook API时都检索这些凭据并构建新的Facebook对象。

在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'));
        });

您可能会注意到我将该类绑定为singleton,因为对于我的应用程序,我希望从服务容器中重用相同的对象。 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在我们想要在我们的应用程序中使用它的facebook对象。从这里开始,您只需按照Facebook documentation中的说明调用其API即可。&#39;

额外的东西,就像一个例子:

在继续之前,我想提一下,我发现Symfony的人写的一系列帖子对于理解Service ContainerDependency Injection的概念非常有帮助。你可以find them here

现在让我们尝试进行一项基本操作,例如从Facebook检索一个人的姓名。为了获得关于facebook用户的信息,我们需要通过发送基本参数来调用Facebook API:用户的facebook id以及访问令牌。我们分别调用这些uidaccess_token。您需要使用Facebook的一种方法检索这些:

  1. FacebookRedirectLoginHelper - 当您希望从服务器端进行Facebook身份验证时。
  2. FacebookCanvasHelper - 用于基于客户端画布的应用程序身份验证
  3. FacebookJavaScriptHelper - 用于客户端javascript身份验证
  4. 您可以按照Facebook getting started指南中提供的步骤设置所需的身份验证类型。

    我的应用非常简单,因此我使用了客户端javascript身份验证。我也在旁边使用jquery。由于我使用Laravel的刀片引擎,我的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启动并弹出一个登录窗口。一旦用户登录,我就会将数据发布回服务器,就好像正在发布表单一样。

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

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

    在shell中创建FacebookUser控制器:

    php artisan make:controller FacebookUser
    

    控制器的代码如下:

    <?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对象。 $ fb可以直接用于发出Facebook API请求。无需使用app_id,app_secret再次构建$ fb,并且无需在当前请求 - 响应生命周期内为当前用户再次设置访问令牌。这是因为$ fb是单例,因此在当前请求 - 响应生命周期中为Facebook服务调用服务容器时会返回相同的对象。

    如果您无法进行方法注入以获取$ fb对象,则有other ways of resolving it。我个人最喜欢的是使用resolve()助手,因为无论对象上下文或类中的静态函数如何,它都可以工作。

    $fb = resolve('Facebook\Facebook');