Guia COMPLETO para integração do Facebook com fos userbundle no symfony 2.1

visão global

  1. Introdução
  2. Suposições
  3. Configurando o pacote de usuário FOS
  4. Configurando FOS Facebook Bundle

Introdução

Demorei um pouco para implementar esses 3 pacotes juntos. Configurar o pacote do usuário não era o problema, mas integrar os pacotes do Facebook e do Twitter era um pé no saco. Especialmente o do Twitter, este foi um pouco obsoleto e eu precisei adicionar um setter na própria biblioteca para criar URLS de retorno de chamada dinâmico.

Este tutorial cobre os seguintes aspectos da integração:

  • Criar Conta
  • Conecte-se a uma conta

Suposições

Para este tutorial, presumo que você tenha configurado algumas coisas:

  • Symfony 2 instalado (e totalmente executado)
  • Configurar um banco de dados
  • Pode executar comandos na linha de comando
  • Compositor de configuração
  • Você usa o Doctrine para salvar seus dados

Configurando o pacote de usuário FOS

Então, vamos começar a integração. A primeira coisa que queremos fazer é instalar o FOS Userbundle. Este pacote será nosso provedor de usuários base. É mais fácil começar com este pacote porque ele possui a maioria dos recursos que só precisam de extensão dos outros 2 pacotes.

A primeira coisa que vamos fazer é adicionar as referências do compositor:

{
"require": {
"friendsofsymfony/user-bundle": "*"
}
}

Agora execute a ferramenta composer para atualizar suas dependências e o compose fará o download da versão mais recente do pacote de usuário FOS para a pasta de fornecedores.
A próxima coisa que queremos fazer é habilitar o pacote em nosso kernel.

<?php

// app/AppKernel.php

public function registerBundles()
{
$bundles
= array(
// ...
new FOSUserBundleFOSUserBundle(),
);
}

Se estiver correto, não tente ir ao site ainda, pois ainda não configuramos nosso bundle, teremos várias exceções. O que agora temos que fazer é configurar uma entidade de usuário em um pacote recém-criado. Para criar um pacote execute o comandogenerate:bundle

A entidade do usuário

Para este tutorial, vou usar o exemplo da classe de entidade do usuário do fos userbundle:

<?php
// src/My/UserBundle/Entity/User.php

namespace AcmeUserBundleEntity;

use FOSUserBundleEntityUser as BaseUser;
use DoctrineORMMapping as ORM;

/**
* @ORMEntity

* @ORMTable(name="fos_user")

*/

class User extends BaseUser
{
/**
* @ORMId

* @ORMColumn(type="integer")

* @ORMGeneratedValue(strategy="AUTO")

*/

protected $id;

public function __construct()
{
parent
::__construct();
// your own logic
}
}

Como estendemos de um usuário base, já temos alguns campos definidos (funções, nome de usuário, …) então apenas adicionamos um ID à classe para salvar nosso usuário no banco de dados. Depois de fazer isso, passamos para a configuração.

Configuração

Para configurar nossa entidade, precisamos definir isso em config.yml. Abra o config.yml (em app / config / config.yml) e adicione esta configuração:

# Fos userbundle configuration.
fos_user
:
db_driver
: orm
firewall_name
: main
user_class
: MyUserBundleEntityUser

Como você pode ver em nossa configuração, precisamos adicionar um novo firewall chamado main . Para fazer isso, precisamos abrir o security.yml (app / config / security.yml).
Se você abriu este arquivo, a primeira coisa que faremos é configurar um provedor de usuário. Isso é necessário para definir a origem de nossos usuários.
Para fazer isso, adicione esse valor aos provedores:

providers:
fos_userbundle
:
id
: fos_user.user_provider.username

Também precisamos definir um codificador para hash nossas senhas:

encoders:
FOSUserBundleModelUserInterface
: sha512

Agora que fizemos isso, podemos configurar nosso firewall real.

main:
pattern
: ^/
form_login
:
provider
: fos_userbundle
csrf_provider
: form.csrf_provider
login_path
: /user/login
use_forward
: false
check_path
: /user/login_check
failure_path
: null
logout
:
path
: /user/logout
anonymous
: ~
remember_me
:
key
: mySuperDuperKey
lifetime
: 4147200
path
: /
domain
: ~

Agora configuramos nosso firewall! É isso, mas ainda não estávamos prontos. No momento ainda não tínhamos regras de acesso. Então, vamos configurar esses:

access_control:
- { path: ^/user/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/secured, role: ROLE_USER }

Nessas regras de acesso configuramos que o login deve ser aberto para usuários anônimos e que o caminho / protegido só pode ser acessado por pessoas que estão logadas. O protegido é apenas uma página padrão que você pode criar e colocar seu roteamento para esse endereço .

Bom, agora é hora de importar as rotas do pacote de usuários fos. Abra o app / routing.yml e adicione isto (pode ser que o generate: bundle já tenha adicionado este roteamento a este arquivo.):

my_user_management:
resource
: "@MyUserManagementBundle/Resources/config/routing.yml"

Neste ponto, concluímos todas as etapas básicas para configurar o pacote. Agora vamos personalizar um pouco o roteamento.

Em primeiro lugar, vamos ter certeza de que nosso pacote pode herdar das fontes originais abrindo src / My / UserBundle / MyUserBundle.php. Adicione esta linha de código:

public function getParent()
{
return 'FOSUserBundle';
}

Agora crie um controlador neste pacote e nomeie-o SecurityController.

<?php

namespace MyUserBundleController;

use FOSUserBundleControllerSecurityController as BaseSecurityController;
use SymfonyComponentSecurityCoreSecurityContext;

class SecurityController extends BaseSecurityController
{
}

Depois de fazer isso, abra a pasta Resources / config e adicione ao roteamento:

fos_user_security_login:
defaults
: { _controller: MyUserBundle:Security:login }
pattern
: /user/login

fos_user_security_check
:
pattern
: /user/login_check
defaults
: { _controller: MyUserBundle:Security:check }

fos_user_security_logout
:
pattern
: /user/logout
defaults
: { _controller: MyUserBundle:Security:logout }

fos_user_profile
:
resource
: "@FOSUserBundle/Resources/config/routing/profile.xml"
prefix
: /user

fos_user_register
:
resource
: "@FOSUserBundle/Resources/config/routing/registration.xml"
prefix
: /registration

fos_user_resetting
:
resource
: "@FOSUserBundle/Resources/config/routing/resetting.xml"
prefix
: /account

fos_user_change_password
:
resource
: "@FOSUserBundle/Resources/config/routing/change_password.xml"
prefix
: /account

Ao fornecer os prefixos de rotas, podemos separar bem as diferentes páginas umas das outras. Você pode deixá-lo como está ou personalizá-lo de acordo com suas necessidades.

Depois de fazer isso, finalmente configuramos o pacote de usuários fos! Para testar isso, criei um usuário com acessórios . Não vou entrar em detalhes, mas aqui está um exemplo de como criar um usuário:

function load(ObjectManager $manager)
{
$userManager
= $this->container->get('fos_user.user_manager');

$user1
= $userManager->createUser();

$user1
->setUsername("user");
$user1
->setEmail("my@email.com");
$user1
->setEnabled(true);

$encoder
= $this->container->get('security.encoder_factory')->getEncoder($user1);
$encodedPass
= $encoder->encodePassword('userpass', $user1->getSalt());

$user1
->setPassword($encodedPass);

$userManager
->updateUser($user1);
}

Configurando FOS Facebook Bundle

Agora que configuramos o pacote de usuários fos, tudo ficará mais fácil. Para instalar o pacote fos facebook vamos adicionar algumas referências no arquivo composer.json:

"facebook/php-sdk": "3.2.0",
"friendsofsymfony/facebook-bundle": "dev-master",

Depois de adicionar essas linhas, executamos o comando composer update novamente e temos nossas novas bibliotecas. A primeira coisa que vamos fazer é iniciar nossa biblioteca no kernel. Abra o app / appKernel.php e adicione esta linha:

$bundles = array(
// other bundles ...
new FOSFacebookBundleFOSFacebookBundle(),
);

Se fizermos isso, é hora de fazer algumas configurações básicas. Abra o app / config / config.yml e adicione estas linhas:

# Facebook configuration
fos_facebook
:
alias: facebook
app_id
: xxxxxxxxxxx
secret
: xxxxxxxxxxx
cookie
: true
permissions
: [email, user_hometown, user_location]

Feito isso, vamos abrir o security.yml (app / config / security.yml) novamente. Como temos vários fornecedores no momento, vamos encadear esses. Para fazer isso, precisamos adicionar um provedor de cadeia e nosso provedor do Facebook:

providers:
chainprovider
:
chain
:
providers
: [fos_userbundle, fos_facebook_provider]
fos_facebook_provider
:
id
: fos_facebook.user.login

Agora, no firewall principal, vamos definir nossas regras do Facebook. Nesta seção, definimos onde nosso aplicativo está localizado e também definimos nosso url de retorno de chamada aqui.

main:
fos_facebook
:
app_url
: "http://apps.facebook.com/this-is-my-awesome-app"
server_url
: "http://mywebsite.com"
login_path
: /user/login
check_path
: /facebook/login_check
provider
: fos_facebook_provider
default_target_path
: /

A seção de firewall deve ser assim:

firewalls:
main
:
pattern
: ^/
fos_facebook
:
app_url
: "http://apps.facebook.com/this-is-my-awesome-app"
server_url
: "http://mywebsite.com"
login_path
: /user/login
check_path
: /facebook/login_check
provider
: fos_facebook_provider
default_target_path
: /
form_login
:
provider
: fos_userbundle
csrf_provider
: form.csrf_provider
login_path
: /user/login
use_forward
: false
check_path
: /user/login_check
failure_path
: null
logout
:
path
: /user/logout
anonymous
: ~
remember_me
:
key
: mySuperDuperKey
lifetime
: 45146
path
: /
domain
: ~

Como você pode ver na seção do provedor, o provedor do Facebook fos contém um ID. Este ID faz referência a um serviço que precisamos definir. Para fazer isso (presumo que você carregue os serviços de seu pacote), abra src / My / UserBundle / Resources / config / services.yml.

Adicione estas linhas de yaml:

services:
fos_facebook
.user.login:
class: MyUserBundleSecurityUserProviderFacebookProvider
arguments
:
facebook
: "@fos_facebook.api"
userManager
: "@fos_user.user_manager"
validator
: "@validator"
container
: "@service_container"

Ao fazer isso, estamos definindo nosso próprio provedor do Facebook. Agora que o definimos, devemos criá-lo também! Para fazer isso, crie uma estrutura de pastas: src / MyUserBundle / Security / User / Provider e crie um arquivo php neste diretório chamado FacebookProvider. Nesta aula, vamos adicionar este código:

<?php

namespace MyUserBundleSecurityUserProvider;

use SymfonyComponentSecurityCoreExceptionUsernameNotFoundException;
use Facebook;
use SymfonyComponentSecurityCoreExceptionUnsupportedUserException;
use SymfonyComponentSecurityCoreUserUserProviderInterface;
use SymfonyComponentSecurityCoreUserUserInterface;
use BaseFacebook;
use FacebookApiException;

class FacebookProvider implements UserProviderInterface
{
/**
* @var Facebook

*/

protected $facebook;
protected $userManager;
protected $validator;
protected $container;

public function __construct(BaseFacebook $facebook, $userManager, $validator, $container)
{
$this
->facebook = $facebook;

// Add this to not have the error "the ssl certificate is invalid."
Facebook::$CURL_OPTS[CURLOPT_SSL_VERIFYPEER] = false;
Facebook::$CURL_OPTS[CURLOPT_SSL_VERIFYHOST] = 2;

$this
->userManager = $userManager;
$this
->validator = $validator;
$this
->container = $container;
}

public function supportsClass($class)
{
return $this->userManager->supportsClass($class);
}

public function findUserByFbId($fbId)
{
return $this->userManager->findUserBy(array('facebookId' => $fbId));
}

public function findUserByUsername($username)
{
return $this->userManager->findUserBy(array('username' => $username));
}

public function connectExistingAccount()
{

try {
$fbdata
= $this->facebook->api('/me');
} catch (FacebookApiException $e) {
$fbdata
= null;
return false;
}

$alreadyExistingAccount
= $this->findUserByFbId($fbdata['id']);

if (!empty($alreadyExistingAccount)) {
return false;
}

if (!empty($fbdata)) {

$currentUserObj
= $this->container->get('security.context')->getToken()->getUser();

$user
= $this->findUserByUsername($currentUserObj->getUsername());

if (empty($user)) {
return false;
}

$user
->setFBData($fbdata);

if (count($this->validator->validate($user, 'Facebook'))) {
// TODO: the user was found obviously, but doesnt match our expectations, do something smart
throw new UsernameNotFoundException('The facebook user could not be stored');
}
$this
->userManager->updateUser($user);

return true;
}

return false;

}

public function loadUserByUsername($username)
{
$user
= $this->findUserByFbId($username);

try {
$fbdata
= $this->facebook->api('/me');
} catch (FacebookApiException $e) {
$fbdata
= null;
}

if (!empty($fbdata)) {
if (empty($user)) {
$user
= $this->userManager->createUser();
$user
->setEnabled(true);
$user
->setPassword('');
}

if($user->getUsername() == '' || $user->getUsername() == null)
{
$user
->setUsername($username . '@facebook.com');
}

$user
->setFBData($fbdata);

if (count($this->validator->validate($user, 'Facebook'))) {
// TODO: the user was found obviously, but doesnt match our expectations, do something smart
throw new UsernameNotFoundException('The facebook user could not be stored');
}
$this
->userManager->updateUser($user);
}

if (empty($user)) {

// TODO: the user was found obviously, but doesnt match our expectations, do something smart
throw new UsernameNotFoundException('The facebook user could not be stored');

}

return $user;
}

public function refreshUser(UserInterface $user)
{
if (!$this->supportsClass(get_class($user)) || !$user->getFacebookId()) {
throw new UnsupportedUserException(sprintf('Instances of "%s" are not supported.', get_class($user)));
}

return