<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>hudson@work &#187; facebook</title>
	<atom:link href="http://hudson.su/tag/facebook/feed/" rel="self" type="application/rss+xml" />
	<link>http://hudson.su</link>
	<description>статьи о web-разработке, менеджменте IT проектов и контроле качества</description>
	<lastBuildDate>Fri, 20 Jan 2012 13:15:39 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	
		<item>
		<title>Symfony 2 &amp; Facebook: часть 1, первый проект</title>
		<link>http://hudson.su/2010/09/06/symfony-2-facebook-part-1-first-project/</link>
		<comments>http://hudson.su/2010/09/06/symfony-2-facebook-part-1-first-project/#comments</comments>
		<pubDate>Mon, 06 Sep 2010 15:27:02 +0000</pubDate>
		<dc:creator>hudson</dc:creator>
				<category><![CDATA[Профессиональное]]></category>
		<category><![CDATA[facebook]]></category>
		<category><![CDATA[facebook graph api]]></category>
		<category><![CDATA[facebook php sdk]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[Symfony2]]></category>

		<guid isPermaLink="false">http://hudson.su/?p=1568</guid>
		<description><![CDATA[Уже совсем скоро (хочется надеяться) будет выпущен первый релиз Symfony 2. А по сему пробуем применить его на практике. Этим постом я планирую начать небольшую серию публикаций про работу с Symfony 2 и Facebook на php 5.3. Что из этого выйдет &#8211; время покажет, а пока делаем первые шаги: Для начала качаем sandbox Symfony 2: [...]]]></description>
			<content:encoded><![CDATA[<p>Уже совсем скоро (хочется надеяться) будет выпущен первый релиз <strong>Symfony 2</strong>. А по сему пробуем применить его на практике.</p>
<p>Этим постом я планирую начать небольшую серию публикаций про работу с <strong>Symfony 2</strong> и <strong>Facebook</strong> на php 5.3. Что из этого выйдет &#8211; время покажет, а пока делаем первые шаги:</p>
<p><span id="more-1568"></span>Для начала качаем sandbox Symfony 2: <a href="http://symfony-reloaded.org/downloads/sandbox_2_0_PR2.zip" target="_blank">http://symfony-reloaded.org/downloads/sandbox_2_0_PR2.zip</a></p>
<p>Я экспериментирую на локальной машине с <strong>denwer</strong>&#8216;ом. У вас може быть <strong>XAMPP</strong>, <strong>MAMP </strong>или любая другая работоспособная комбинация из вебсервера и PHP 5.3.2+ (у меня установлен <strong>PHP 5.3.3</strong>).</p>
<p>Создаем директорию z:hometest-project.com и распаковываем содержимое сандбокса в нее. Получится примерно следующее:</p>
<pre>z:hometest-project.com
    hello
    src
    web</pre>
<p>Web не мудрствуя лукаво переименуем в www чтобы не возиться с ручным конфигом и hosts (в этом случае денвер сделает все настройки самостоятельно. Иначе &#8211; вам придется сделать их самим, что займет лишние 2 минуты). Не забываем рестартануть денвера.</p>
<p>Далее качаем с <strong>github </strong>тем или иным способом <strong>facebook php sdk</strong>: <a href="http://github.com/facebook/php-sdk/" target="_blank">http://github.com/facebook/php-sdk/</a>. Нас оттуда интересует файл src/facebook.php. Сразу предупрежу &#8211; я поступил немного грязно и разделил этот класс на два (их там и есть два, собственно <strong>Facebook </strong>и <strong>FacebookApiExcetion</strong>), а также разместил в namespace Facebook. Выглядит это примерно так:</p>
<pre>// src/vendor/facebook/Facebook/Facebook.php
&lt;?php

namespace Facebook;

if (!function_exists('curl_init')) {
  throw new Exception('Facebook needs the CURL PHP extension.');
}
if (!function_exists('json_decode')) {
  throw new Exception('Facebook needs the JSON PHP extension.');
}

/**
 * Provides access to the Facebook Platform.
 *
 * @author Naitik Shah &lt;naitik@facebook.com&gt;
 */
class Facebook
{
</pre>
<p>И вот так:</p>
<pre>// src/vendor/facebook/Facebook/FacebookApiException.php
&lt;?php

namespace Facebook;

/**
 * FacebookApiException
 *
 * @author     dmitry.bykadorov@gmail.com
 * @version    SVN: $Id:  $
 */

/**
 * Thrown when an API call returns an exception.
 *
 * @author Naitik Shah &lt;naitik@facebook.com&gt;
 */
class FacebookApiException extends Exception
{
</pre>
<p>Обратите внимание на строку <code>class FacebookApiException extends <strong>Exception</strong></code>. Мы используем стандартный класс Exception, который не принадлежит никакому пространству имен. Ну или принадлежит корневому (стандартному) пространству имен. Честно говоря я пока не очень продвинут в работе с неймспейсами, поэтому в терминологии могу ошибаться ).</p>
<p>Как вы можете видеть, файлы классов расположены в директории src/vendor/facebook/Facebook/. Разработчики Symfony 2 рекомендуют не включать сторонние библиотеки в бандлы или приложения &#8211; думаю стоит к этому прислушаться. Поэтому после размещения библиотеки в нашем приложении нам нужно зарегистрировать его в автолоадере. Для того открываем файл src/autoload.php и добавляем туда наш неймспейс в функцию registerNamespaces:</p>
<pre>// src/autoload.php
//...

$loader = new UniversalClassLoader();

$loader-&gt;registerNamespaces(array(
  'Symfony'                    =&gt; __DIR__.'/vendor/symfony/src',
  'Application'                =&gt; __DIR__,
  'Bundle'                     =&gt; __DIR__,
  'Doctrine\Common'           =&gt; __DIR__.'/vendor/doctrine/lib/vendor/doctrine-common/lib',
  'Doctrine\DBAL\Migrations' =&gt; __DIR__.'/vendor/doctrine-migrations/lib',
  'Doctrine\ODM\MongoDB'     =&gt; __DIR__.'/vendor/doctrine-mongodb/lib',
  'Doctrine\DBAL'             =&gt; __DIR__.'/vendor/doctrine/lib/vendor/doctrine-dbal/lib',
  'Doctrine'                   =&gt; __DIR__.'/vendor/doctrine/lib',
  'Zend'                       =&gt; __DIR__.'/vendor/zend/library',
  'Facebook'                   =&gt; __DIR__.'/vendor/facebook/lib',
));
//...
</pre>
<p>Для реализации тестового примера нам потребуется одна страница, поэтому структуру сандбокса с application bundle <strong>hello</strong> мы менять не будем (кроме того я пока не придумал зачем нам нужен будет бандл Facebook, поэтому это будет тема следующей статьи).</p>
<p>Создаем iframe приложение Facebook в разделе разработчиков (на этом отдельно не останавливаюсь, статью напишу <strong>если попросите</strong>). Оттуда нам нужны параметры приложения &#8211; app_id, api_key, app_secret. Второй параметр для <strong>oAuth </strong>не используется, но может будет полезен впоследствии. Наши настройки мы внесем в файл настроек hello/config/config.yml:</p>
<pre># hello/config/config.yml
parameters:
    kernel.include_core_classes: false

    # facebook
    facebook.credentials.app_id: &lt;ваш_app_id&gt;
    facebook.credentials.api_key: &lt;ваш_api_key&gt;
    facebook.credentials.app_secret: &lt;ваш_app_secret&gt;
    facebook.url.canvas:  # будет нужен потом
    facebook.url.site:  # будет нужен потом
</pre>
<p>Модифицируем HelloController:</p>
<pre>// src/Application/HelloBundle/Controller/HelloController.php
&lt;?php

  namespace ApplicationHelloBundleController;

  use Facebook;
  use SymfonyFrameworkFoundationBundleController;

  class HelloController extends Controller
  {
    public function indexAction($name)
    {
      $app_id = $this-&gt;container-&gt;getParameter('facebook.credentials.app_id');
      $api_key = $this-&gt;container-&gt;getParameter('facebook.credentials.api_key');
      $app_secret = $this-&gt;container-&gt;getParameter('facebook.credentials.app_secret');

      // у меня локально без отключения проверки сертификатов не заработало
      FacebookFacebook::$CURL_OPTS[CURLOPT_SSL_VERIFYHOST] = 0;
      FacebookFacebook::$CURL_OPTS[CURLOPT_SSL_VERIFYPEER] = 0;

      $facebook = new FacebookFacebook(
      array(
        'appId' =&gt; $app_id,
        'secret' =&gt; $app_secret,
        'cookie' =&gt; true,
      ));
      $session = $facebook-&gt;getSession();

      $me = null;
      $uid = null;

      // Обращение к API на основе сессии
      if ($session)
      {
        try
        {
          $uid = $facebook-&gt;getUser();
          $me = $facebook-&gt;api('/me');
        }
        catch ( FacebookFacebookApiException $e )
        {
          // пока ничего не делаем
        }
      }

      // login или logout url будут нужны в зависимости от статуса авторизации пользователя
      $logoutUrl = '';
      $loginUrl = '';
      if ($me)
      {
        $logoutUrl = $facebook-&gt;getLogoutUrl();
      }
      else
      {
        $loginUrl = $facebook-&gt;getLoginUrl();
      }

      // Обращение к API без сессии
      $user = $facebook-&gt;api('/dmitry.bykadorov');

      return $this-&gt;render(
        'HelloBundle:Hello:index',
        array(
          'facebook' =&gt; $facebook,
          'session' =&gt; $session,
          'me' =&gt; $me,
          'logoutUrl' =&gt; $logoutUrl,
          'loginUrl' =&gt; $loginUrl,
          'user' =&gt; $user,
          'uid' =&gt; $uid,
        )
      );
    }
  }
</pre>
<p>Теперь нужно модифицировать шаблон: добавить в него декоратор (layout &#8211; он теперь не цепляется по умолчанию) и сформируем шаблон:</p>
<pre>&lt;?php
&lt;!-- src/Application/HelloBundle/Resources/views/Hello/index.php --&gt;
/**
 *
 */

?&gt;

&lt;?php $view-&gt;extend('HelloBundle::layout') ?&gt;

&lt;div id="fb-root"&gt;&lt;/div&gt;

&lt;style&gt;
 body {
 font-family: 'Lucida Grande', Verdana, Arial, sans-serif;
 }
 h1 a {
 text-decoration: none;
 color: #3b5998;
 }
 h1 a:hover {
 text-decoration: underline;
 }
&lt;/style&gt;

&lt;script&gt;
 window.fbAsyncInit = function()
 {
 FB.init({
 appId   : '&lt;?php echo $facebook-&gt;getAppId(); ?&gt;',
 session : &lt;?php echo json_encode($session); ?&gt;, // не фетчим сессию если она уже есть в PHP
 status  : true, // разрешаем проверку логина
 cookie  : true, // разрешаем куки для проверки сессии
 xfbml   : true // разрешаем парсинг XFBML
 });

 // если юзер вошел надо обновить страницу
 FB.Event.subscribe('auth.login', function()
 {
 window.location.reload();
 });
 };

 (function() {
 var e = document.createElement('script');
 e.src = document.location.protocol + '//connect.facebook.net/en_US/all.js';
 e.async = true;
 document.getElementById('fb-root').appendChild(e);
 }());
&lt;/script&gt;

&lt;h1&gt;&lt;a href="example.php"&gt;php-sdk&lt;/a&gt;&lt;/h1&gt;

&lt;?php if ($me): ?&gt;
&lt;a href="&lt;?php echo $logoutUrl; ?&gt;"&gt;
 &lt;img src="http://static.ak.fbcdn.net/rsrc.php/z2Y31/hash/cxrz4k7j.gif"&gt;
&lt;/a&gt;
&lt;?php else: ?&gt;
&lt;div&gt;
 С использованием JavaScript &amp;amp; XFBML:
 &lt;fb:login-button&gt;&lt;/fb:login-button&gt;
&lt;/div&gt;
&lt;div&gt;
 Без JavaScript &amp;amp; XFBML:
 &lt;a href="&lt;?php echo $loginUrl; ?&gt;"&gt;
 &lt;img src="http://static.ak.fbcdn.net/rsrc.php/zB6N8/hash/4li2k73z.gif"&gt;
 &lt;/a&gt;
&lt;/div&gt;
&lt;?php endif ?&gt;

&lt;h3&gt;Сессия&lt;/h3&gt;
&lt;?php if ($me): ?&gt;
&lt;pre&gt;&lt;?php print_r($session); ?&gt;&lt;/pre&gt;

&lt;h3&gt;Вы&lt;/h3&gt;
&lt;img src="https://graph.facebook.com/&lt;?php echo $uid; ?&gt;/picture"&gt;
&lt;?php echo $me['name']; ?&gt;

&lt;h3&gt;Ваш юзерский объект&lt;/h3&gt;
&lt;pre&gt;&lt;?php print_r($me); ?&gt;&lt;/pre&gt;
&lt;?php else: ?&gt;
&lt;strong&gt;&lt;em&gt;Вы не подключены.&lt;/em&gt;&lt;/strong&gt;
&lt;?php endif ?&gt;

&lt;h3&gt;Юзер&lt;/h3&gt;
&lt;img src="https://graph.facebook.com/dmitry.bykadorov/picture"&gt;
&lt;?php echo $user['name']; ?&gt;</pre>
<p>Ну и немного подправим декорирующий шаблон:</p>
<pre>&lt;!-- src/Application/HelloBundle/Resources/views/layout.php --&gt;
&lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"&gt;
&lt;html xmlns:fb="http://www.facebook.com/2008/fbml"&gt;
&lt;head&gt;
 &lt;meta http-equiv="Content-Type" content="text/html; charset=utf-8"/&gt;
&lt;/head&gt;
&lt;body&gt;

 &lt;table style="width:700px;margin-left:auto;margin-right:auto;" border="1"&gt;
 &lt;tr&gt;
 &lt;td&gt;
 &lt;?php $view-&gt;slots-&gt;output('_content') ?&gt;
 &lt;/td&gt;
 &lt;/tr&gt;
 &lt;/table&gt;

&lt;/body&gt;
&lt;/html&gt;
</pre>
<p>В результате мы поимеем примерно следующее:</p>
<p><a href="http://hudson.su/files/2010/09/symfony2_facebook_c1_01.png" rel='lightbox'><img class="alignnone size-medium wp-image-1596" src="http://hudson.su/files/2010/09/symfony2_facebook_c1_01-266x300.png" alt="" width="266" height="300" /></a></p>
<p>So&#8230; рубикон перейден, надеюсь мы с вами тут же на бережку не разляжемся а двинемся дальше )) </p>
<p>Have fun! ))</p>
]]></content:encoded>
			<wfw:commentRss>http://hudson.su/2010/09/06/symfony-2-facebook-part-1-first-project/feed/</wfw:commentRss>
		<slash:comments>16</slash:comments>
		</item>
		<item>
		<title>Symfony 2009 advent calendar: день 19 – Разработка для Facebook (часть 2)</title>
		<link>http://hudson.su/2009/12/29/symfony-2009-advent-calendar-day19-facebook-p2/</link>
		<comments>http://hudson.su/2009/12/29/symfony-2009-advent-calendar-day19-facebook-p2/#comments</comments>
		<pubDate>Tue, 29 Dec 2009 12:24:54 +0000</pubDate>
		<dc:creator>hudson</dc:creator>
				<category><![CDATA[Профессиональное]]></category>
		<category><![CDATA[facebook]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[symfony]]></category>
		<category><![CDATA[symfony advent calendar'09]]></category>

		<guid isPermaLink="false">http://hudson.su/?p=685</guid>
		<description><![CDATA[Symfony 2009 advent calendar &#8211; это 24 урока продвинутого уровня о symfony. Все уроки представлены на 5 языках: английском, французском, испанском, итальянском, японском. Русского нет, это досадное упущение постараюсь исправить. Перевод статьи Symfony 2009 Advent Calendar: Developing for Facebook (part 2). Facebook Connect О том как Facebook Connect работает и о различных стратегиях интеграции Facebook [...]]]></description>
			<content:encoded><![CDATA[<blockquote><p><strong>Symfony 2009 advent calendar</strong> &#8211; это 24 урока <strong>продвинутого</strong> уровня о symfony. Все уроки представлены на 5 языках: английском, французском, испанском, итальянском, японском. Русского нет, это досадное упущение постараюсь исправить.</p></blockquote>
<p>Перевод статьи <a href="http://www.symfony-project.org/advent_calendar/19/en" target="_blank"><strong>Symfony 2009 Advent Calendar: Developing for Facebook (part 2)</strong></a>.</p>
<p><span id="more-685"></span></p>
<h3>Facebook Connect</h3>
<h4>О том как Facebook Connect работает и о различных стратегиях интеграции</h4>
<p>Facebook Connect, прежде всего, предоставляет свою сессию для совместного доступа вашему вебсайту. Работа этой функции становится возможной благодаря копированию аутентификационных кукисов с Facebook через iframe на сайте, который указывает на страницу Facebook, которая в свою очередь открывает iframe на сайт. Для этого, Facebook Connect должен иметь доступ к вебсайту, что делает невозможным использовать или тестировать Facebook Connect на локальном сервере или в интранет-сети. Входной точкой является xd_receiver.htm, который поставляется с <strong>sfFacebookConnectPlugin</strong>&#8216;ом. Не забудьте выполнить таск <strong>plugin:publish-assets</strong> для того чтобы этот файл стал доступен.</p>
<p>Когда подготовительные процедуры выполнены, официальная библиотека Facebook получает возможность использовать сессию Facebook. В дополнение к этому, sfFacebookConnectPlugin создает пользователя sfGuard, связанного с сессией Facebook и который прочно связан с существующим сайтом на symfony. Вот почему плагин по умолчанию редиректит пользователя на экшен <strong>sfFacebookConnectAuth/signIn</strong> после того как тот нажмет кнопку Facebook Connect и сессия пройдет валидацию. Первым делом плагин ищет существующего пользователя с тем же UID&#8217;ом или с тем же хэшем Email (см. также раздел &#8220;<a href="#connect_existing_users">Сопоставляем существующих пользователей с их аккаунтами на Facebook</a>&#8221; в конце этой статьи). Если пользователь не найден, создается новый пользователь.</p>
<p>Другая стратегия регистрации заключается в том, чтобы не создавать пользователей напрямую, а сначала перенаправлять их на особую форму регистрации. На этой форме, с использованием сессии Facebook, например, можно сразу заполнить базовую информацию. Для того чтобы это сделать, можно добавить следующий код в форму регистрации:</p>
<pre>public function setDefaultsFromFacebookSession()
{
  if ($fb_uid = sfFacebook::getAnyFacebookUid())
  {
    $ret = sfFacebook::getFacebookApi()-&gt;users_getInfo(
      array(
        $fb_uid
      ),
      array(
        'first_name',
        'last_name',
      )
    );

    if ($ret &amp;&amp; count($ret)&gt;0)
    {
      if (array_key_exists('first_name', $ret[0]))
      {
        $this-&gt;setDefault('first_name',$ret[0]['first_name']);
      }
      if (array_key_exists('last_name', $ret[0]))
      {
        $this-&gt;setDefault('last_name',$ret[0]['last_name']);
      }
    }
  }
</pre>
<p>Для использования второй стратегии, просто разрешите в app.yml редирект после Facebook Connect и маршрут, который необходимо будет использовать.</p>
<pre># default values
all:
  facebook:
    redirect_after_connect: true
    redirect_after_connect_url: '@register_with_facebook'
</pre>
<h4>Фильтр Facebook Connect</h4>
<p>Другая важная особенность Facebook Connect заключается в том, что пользователи Facebook, при просмотре интернет-страниц, зачастую уже залогинены в Facebook. Вот тут во всю раскрывается сила <strong>sfFacebookConnectRememberMeFilter</strong>. Если пользователь, зашедший на ваш вебсайт, уже залогинен в Facebook, sfFacebookConnectRememberMeFilter автоматически залогинит его на вашем сайте, примерно так, как это делает фильтр &#8220;Remember me&#8221;.</p>
<pre>$sfGuardUser = sfFacebook::getSfGuardUserByFacebookSession();
if ($sfGuardUser)
{
  $this-&gt;getContext()-&gt;getUser()-&gt;signIn($sfGuardUser, true);
}
</pre>
<p>Тем не менее, эта плюшка имеет один серьёзный недостаток: пользователи не могут разлогиниться, пока они залогинены на Facebook (т.к. они каждый раз будут логиниться автоматически). Используйте эту фичу осторожно (ну или на свой страх и риск).</p>
<h4>Как избежать фатального JavaScript бага в IE</h4>
<p>Один из самых ужасных багов, который вы можете получить на вебсайте, является <strong>ошибка &#8220;Operation aborted&#8221; в IE</strong>, которая запросто рушит рендеринг вебсайта на стороне клиента. Это следствие кривого движка рендеринга в IE6/7, который может обрушиться, если вы добавляете DOM-элементы к элементу BODY из скрипта, который не является прямым потомком элемента BODY. К несчастью, это как правило случается, если вы загружаете JavaScript для Facebook Connect и не озаботились загрузить его напрямую из body в конце документа. Тем не менее, этого легко избежать при помощи slot&#8217;ов. Используйте slot для того чтобы включить скрипт Facebook Connect&#8217;а когда это необходимо, в ваш шаблон лэйаута в конце документа (перед тагом &lt;/body&gt;):</p>
<pre>// in a template that uses a XFBML tag or a Facebook Connect button
slot('fb_connect');
include_facebook_connect_script();
end_slot();

// just before &lt;/body&gt; in the layout to avoid problems in IE
if (has_slot('fb_connect'))
{
  include_slot('fb_connect');
}
</pre>
<h3>Лучшие практики для Facebook-приложений</h3>
<p>Благодаря sfFacebookConnectPlugin, интеграция с sfGuardPlugin выполняется гладко и выбор, будет ли приложение FBML, IFrame или Facebook Connect, можно отложить на последок. Для того чтобы мы могли двигаться дальше и создавать навороченные приложения для Facebook, ниже приведено несколько важных советов.</p>
<h4>Используйте environments в symfony для того чтобы поднять несколько тестовых серверов для Facebook Connect</h4>
<p>Очень важный аспект философии symfony заключается в быстрой отладке и качественном тестировании приложения. Интеграция с Facebook может серьезно осложнить эту задачу, так как почти все время будет требоваться соединение с Internet для коммуникаций с сервером Facebook и открытый 80й порт для обмена аутентификационными кукисами. Кроме того, есть еще ограничение: приложение, использующее Facebook Connect может быть подключено только к одному хосту. Это становится настоящей проблемой, когда приложение разрабатывается на одной машине, тестируется на другой, помещается в пре-продакшн на третий сервер и, наконец, используется в продуктовом окружении на четвертом.</p>
<p>В этой ситуации наиболее простым решением будет создать приложение для каждого сервера и создать в symfony environment для каждого из них. В симфони это делается легким движением руки ))) делаем копию frontend_dev.php (или его аналога), в frontend_preprod.php и редактируем строку во вновь созданном файле для изменения dev environment&#8217;а на новый, который мы назовем preprod:</p>
<pre>// заменяем энвайронмент на новый:
$configuration = ProjectConfiguration::getApplicationConfiguration('frontend', 'preprod', true);
</pre>
<p>Далее, вносим поправки в app.yml для того чтобы сконфигурировать новое Facebook-приложение для каждого нового окружения:</p>
<pre>prod:
  facebook:
    api_key: xxx
    api_secret: xxx
    api_id: xxx

dev:
  facebook:
    api_key: xxx
    api_secret: xxx
    api_id: xxx

preprod:
  facebook:
    api_key: xxx
    api_secret: xxx
    api_id: xxx</pre>
</pre>
<p>Теперь приложение тестируемо на каждом отдельном сервере с использованием соответствующей среде frontend_xxx.php точки входа.</p>
<h4>Используйте систему логгирования symfony для отладки FBML</h4>
<p>Решение с переключением лэйаутов позволяет разрабатывать и тестировать большинство FBML приложений вне сайта Facebook. Тем не менее, финальный тест внутри Facebook может выдавать какие-то невразумительные ошибки. Действительно, основная проблема рендеринга FBML напрямую в Facebook заключается в том, что ошибка 500 обрабатывается и заменяется на не очень информативную стандартную страницу с ошибкой. Кроме того, web debug toolbar, не отображается в Facebook-фрейме. К счастью, на помощь к нам приходит замечательная система логгирования symfony. sfFacebookConnectPlugin автоматически логгирует многие важные действия, вы также с легкостью можете добавить строки в файл лога в любой точке приложения:</p>
<pre>if (sfConfig::get('sf_logging_enabled'))
{
  sfContext::getInstance()-&gt;getLogger()-&gt;info($message);
}
</pre>
<h4>Используйте Proxy во избежание ошибочных редиректов Facebook</h4>
<p>Имеется странный баг Facebook, который заключается в том, что когда Facebook Connect сконфигурирован в приложении, Facebook Connect рассматривается как домашняя директория (home) приложения. Не смотря на то, что home может быть сконфигурирован, он должен быть расположен на том же домене, что и Facebook Connect хост. Таким образом, не существует другого решения кроме как сдаться и сконфигурировать home как простой экшен, осуществляющий перенаправление куда потребуется. Следующий код осуществляет перенаправление на Facebook-приложение:</p>
<pre>public function executeRedirect(sfWebRequest $request)
{
  return $this-&gt;redirect('http://apps.facebook.com'.sfConfig::get('app_facebook_app_url'));
}
</pre>
<h4>Используйте fb_url_for() хелпер</h4>
<p>Для создания универсального приложения, которое может быть использовано как FBML внути Facebook или XFBML в iframe в любую (даже последнюю минуту), необходимо уделить внимание проблеме маршрутизации:</p>
<ul>
<li>Для FBML-приложения, ссылки внутри приложения должны указывать на /app-name/symfony-route;</li>
<li>Для iframe приложения важно передавать информацию о сессии facebook при переходе между страницами.</li>
</ul>
<p>Плагин sfFacebookConnectPlugin предоставляет особый хелпер, который решает указанные проблемы, это <strong>fb_url_for()</strong> хелпер.</p>
<h4>Редиректы внутри FBML приложения</h4>
<p>Symfony разработчики быстро привыкают выполнять редирект после успешного POST, и это в общем-то хорошая практика для избежания дублирования данных в БД. Перенаправление в FBML апликации же не работает как ожидается. Вместо этого необходимо применять специализированный таг <code>&lt;fb:redirect&gt;</code> для того чтобы Facebook выполнил редирект. Для сохранения независимости от контекста (FBML таг или обычный редирект) существует специальный статический метод в классе sfFacebook, который может быть использован, например, в экшене сохранения формы:</p>
<pre>
if ($form-&gt;isValid())
{
  $form-&gt;save();
  return sfFacebook::redirect($url);
}
</pre>
<h4 id="connect_existing_users">Сопоставляем существующих пользователей с их аккаунтами на Facebook</h4>
<p>Одна из целей Facebook Connect - упростить регистрацию для новых пользователей. Кроме того, интерес также может представлять сопоставление существующих пользователей с их аккаунтами на Facebook как для получения более полной информации о них, так и для взаимодействия с их фидом. Эта вспомогательная цель может быть достигнута двумя способами:</p>
<ul>
<li>Заставить существующих пользователей кликать на кнопку Connect with Facebook. Экшен sfFacebookConnectAuth/signIn не будет создавать нового пользователя, если он уже залогинен, он лишь будет сохранять данные Faceook пользователя для текущего sfGuard-пользователя. Это просто.</li>
<li>Использовать Facebook-систему распознавания email'ов. Когда пользователь использует Facebook Connect на сайте, Facebook может предоставить специальный хэш его email-адреса, который можно сравнить с хэшами в существующей базе данных для того чтобы определить аккаунт, принадлежащий пользователю. Тем не менее, по соображениям безопасности, Facebook предоставляет указанные хэши только если пользователь был зарегистрирован ранее при помощи его API! Таким образом, необходимо регистрировать email'ы всех новых пользователей, чтобы можно было опознать их впоследствии. Этим и занимается таск <strong>registerUsers</strong>. Этот таск должен запускаться как минимум каждую ночь для того чтобы регистрировать вновь созданных пользователей. В принципе, вы также можете использовать метод registerUsers из класса sfFacebookConnect: <code>sfFacebookConnect::registerUsers(array($sfGuardUser));</code></li>
</ul>
<h3>Что дальше?</h3>
<p>Надеюсь эта статья выполнит свое назначение: поможет вам начать разработку приложения для Facebook c использованием symfony и пояснит основные возможности разработки для Facebook. Тем не менее sfFacebookConnectPlugin не заменяет собой Facebook API, поэтому чтение оригинальной <a href="http://developers.facebook.com/" target="_blank">документации</a> никто не отменял, тем более что там вы можете получить самую полную информацию о разработке для facebook.</p>
<p>В заключение, хочу выразить благодарность сообществу symfony за качество и отзывчивость, в особенности тем, кто уже внес вклад в развитие sfFacebookConnectPlugin своими комментариями и патчами: Damien Alexandre, Thomas Parisot, Maxime Picaud, Alban Creton (и да простят меня все кого я тут не упомянул ))) И, конечно же, если вам есть что сказать или дополнить, не колеблясь вносите свой вклад!</p>
]]></content:encoded>
			<wfw:commentRss>http://hudson.su/2009/12/29/symfony-2009-advent-calendar-day19-facebook-p2/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Инвайт-диалог стандартными средствами facebook</title>
		<link>http://hudson.su/2009/12/24/invite-dialog-by-standard-facebook-tools/</link>
		<comments>http://hudson.su/2009/12/24/invite-dialog-by-standard-facebook-tools/#comments</comments>
		<pubDate>Thu, 24 Dec 2009 13:18:26 +0000</pubDate>
		<dc:creator>hudson</dc:creator>
				<category><![CDATA[Профессиональное]]></category>
		<category><![CDATA[facebook]]></category>
		<category><![CDATA[fbjs]]></category>
		<category><![CDATA[fbml]]></category>
		<category><![CDATA[php]]></category>

		<guid isPermaLink="false">http://hudson.su/?p=664</guid>
		<description><![CDATA[Для facebook приложений есть много полезных виральных фич, которые позволяют быстро наращивать пользовательскую базу вашего приложения. Одной из таких фич является форма приглашения друзей (invite). Изначально она формируется при помощи FBML тагов и выглядит вполне себе круто и функционально. Но у данной формы есть один недостаток &#8211; для того чтобы перейти к ней, пользователь должен [...]]]></description>
			<content:encoded><![CDATA[<p>Для facebook приложений есть много полезных виральных фич, которые позволяют быстро наращивать пользовательскую базу вашего приложения. Одной из таких фич является форма приглашения друзей (invite). Изначально она формируется при помощи FBML тагов и выглядит вполне себе круто и функционально. Но у данной формы есть один недостаток &#8211; для того чтобы перейти к ней, пользователь должен уйти с основной странички приложения (особенно если это flash/flex приложение, при возврате придется его перезагрузить, переинициализировать и т.п.).</p>
<p>Ниже приводится простой способ отобразить диалог с формой инвайта, который не будет обновлять страницу при сабмите или отказе от него.</p>
<p><span id="more-664"></span></p>
<p>Первым шагом необходимо подготовить для отображения форму инвайта:</p>
<pre>&lt;fb:js-string var="dialog_fbml"&gt;
&lt;div id="dialog-content" style=""&gt;
    &lt;fb:request-form
        method="POST"
        invite="true"
        type="sample network"
        content="Blah blah blah &lt;fb:req-choice url='http://www.facebook.com/' label='Check out this network!' /&gt;"
    &gt;
    &lt;fb:multi-friend-selector
        showborder="false"
        actiontext="Invite your friends to this network."
        max="4"
        rows="2"
        cols="2"
    /&gt;
    &lt;/fb:request-form&gt;
&lt;/div&gt;
&lt;/fb:js-string&gt;</pre>
<p>Пару слов о том что мы тут делаем: <strong>fb:js-string</strong> необходим для того чтобы мы могли отобразить диалог с формой &#8211; иначе FBJS не умеет. Данная строка (т.е. наша форма) будет при отображении страницы фейсбуком помещена в переменную <strong>dialog_fbml</strong>, которая в последствии будет использоваться при отображении диалога. Сама форма составлена в общем-то по эталону из документации (подробнее можно прочесть в <a href="http://wiki.developers.facebook.com/index.php/Fb:request-form" target="_blank">документации fb:request-form</a>).</p>
<p>Двигаемся дальше. Форму нам надо отобразить. Для этого нам нужна JS функция. И еще одна для сабмита формы:</p>
<pre>var inviteDialog;
function showInviteDialog(title) {
    inviteDialog = new Dialog();
    inviteDialog.showChoice(title, dialog_fbml);
}
function inviteAction(){
    inviteDialog.hide();
}</pre>
<p>Тут тоже все просто. Зачем и как мы будем использовать inviteAction расскажу чуть позже, а пока разместим на канвасе кнопку, которая будет отображать наш диалог:</p>
<pre>&lt;input
    id      = "pop-dialog-shower"
    type    = "button"
    value   = "Invite!"
    onclick = "showInviteDialog('Invite!');"
/&gt;</pre>
<p>Ок. Давайте взглянем как это выглядит (диалог показать мы уже можем):<br />
<a rel="lightbox" href="http://hudson.su/wp-content/blogs.dir/1/files/2009/12/fb-invite-01.jpg" target="_blank"><img class="alignnone size-medium wp-image-668" src="http://hudson.su/wp-content/blogs.dir/1/files/2009/12/fb-invite-01-300x292.jpg" alt="" width="300" height="292" /></a></p>
<p>Это уже что-то! Однако есть два проблемных участка, выделенных маркером и пронумерованных:</p>
<p>1. Форма инвайта неприлично больше диалога и выезжает за его границу. Это будем решать при помощи строенных стилей, которые перекроют оригинальные фейсбуковские. Мы хотим расширить окно диалога до размера формы. Добавим в нашу страницу тег &lt;style&gt; и следующий стиль для нее:</p>
<pre>.generic_dialog_popup {
    width:650px;
}</pre>
<p>2. Стандартные кнопки диалога. К сожалению диалог без кнопок создать нельзя, поэтому мешающие нам кнопки мы попросту спрячем. Для этого добавим на страницу такой стиль:</p>
<pre>.contextual_dialog .dialog_buttons .inputbutton,.inputsubmit {
    display:none;
}</pre>
<p>Загружаем изменения на сервер, смотрим и радуемся. Теперь форма и диалог производят впечатление единого целого.</p>
<p>Но! Если мы отправим приглашение или даже откажемся от него (какое кощунство )))) то страница все-равно перегрузится. Добивались мы совсем не этого, посему нам остался заключительный финт ушами, в котором нам поможет функция <strong>inviteAction</strong>. Но перед тем как я продемонстрирую код, который нам нужно написать, пара комментариев о том как это будет работать:</p>
<ul>
<li>Во-первых, для достижения необходимого эффекта (инвайта без перезагрузки страницы) нам нужно воспользоваться одной мало используемой возможностью форм &#8211; а именно, возможностью указывать в качестве action &#8211; javascript функцию (аналогично это работает в href ссылок). Если честно, пробовал я это наугад, проверил в основных браузерах &#8211; и это работало. При этом, facebook не предоставляет по большому счету никаких возможностей по управлению поведением формы инвайта, за исключением action и method. Собственно для этих целей и определена функция inviteAction.</li>
<li>Во-вторых, если мы напрямую укажем inviteAction в качестве экшена формы, то при попытке сабмита или отказа мы увидим ошибку что такой функции нет. Происходит это потому, что facebook при рендеринге страницы парсит весь встроенный JS код и подменяет имена функций и переменных, используя app_id. При этом, видимо, не ожидается что внутри fb:request-form будет использоваться функция и соответственно подмена имени не происходит. Поэтому не поленитесь узнать свой app_id на <a href="http://www.facebook.com/developers/" target="_blank">страничке разработчиков</a> и модифицируйте имя функции по правилу <strong>a[app_id]_inviteAction()</strong>. Например <strong>a123456_inviteAction()</strong>.</li>
</ul>
<p>Итак, у нас был FBML таг формы запроса:</p>
<pre>    &lt;fb:request-form
        method="POST"
        invite="true"
        type="sample network"
        content="Blah blah blah &lt;fb:req-choice url='http://www.facebook.com/' label='Check out this network!' /&gt;"
    &gt;</pre>
<p>Для достижения нашей цели мы должны добавить параметр action оговоренного ранее вида (app_id конечно же выдуманный, поэтому не забывайте его заменять на актуальный для вашего приложения):</p>
<pre>    &lt;fb:request-form
        method="POST"
        invite="true"
        action="javascript:a123456_inviteAction()"
        type="sample network"
        content="Blah blah blah &lt;fb:req-choice url='http://www.facebook.com/' label='Check out this network!' /&gt;"
    &gt;</pre>
<p>Обновите код &#8211; и наслаждайтесь )</p>
<p><a rel="lightbox" href="http://hudson.su/wp-content/blogs.dir/1/files/2009/12/fb-invite-02.jpg" target="_blank"><img class="alignnone size-medium wp-image-676" src="http://hudson.su/wp-content/blogs.dir/1/files/2009/12/fb-invite-02-300x265.jpg" alt="" width="300" height="265" /></a></p>
<p>Итак, эта форма имеет следующие достоинства:</p>
<ul>
<li>Инвайт без перезагрузки страницы.</li>
<li>Удобство для пользователя.</li>
<li>Фокус пользователя не рассеивается на перемещение по страницам.</li>
<li>Быстро отображается (т.к. загружена заранее).</li>
</ul>
<p>А также недостатки:</p>
<ul>
<li>При передаче традиционным способом еще передается список ids &#8211; кому отправлен инвайт. В этом случае он теряется (во всяком случае я пока не вижу способа как его можно получить &#8211; если подскажете, будет вообще здорово ))).</li>
<li>Это все один сплошной хак ))) сам facebook такого функционала не предоставляет.</li>
</ul>
<p>И еще один момент, который ни достоинство ни недостаток:</p>
<ul>
<li>Т.к. action поидее должен использоваться для редиректов и некоторых последействий, то, чтобы не терять такой возможности, можно вызвать нужный URL через Ajax.post внутри функции inviteAction.</li>
</ul>
<p>Меня это соотношение устраивает. Если у вас есть какие-то замечания и комментарии &#8211; можете оставлять их ниже.</p>
<p>P.S. на самом деле видов диалогов в facebook &#8211; два, POP, как в данном примере и CONTEXTUAL &#8211; который можно привязать к контексту (например какому-то элементу по ID). Второй тип диалога мне нравился больше, но увы, форма инвайта в нем не работает. По этому поводу я засабмитил баг <a href="http://bugs.developers.facebook.com/show_bug.cgi?id=7894" target="_blank">7894</a>, но не уверен в том что в ближайшем будущем у кого-то до него дойдут руки.</p>
<p>P.P.S. Source code for this article (<strong>how to place facebook invite in standard dialog</strong>) you can download by clicking <a href="http://hudson.su/wp-content/blogs.dir/1/files/2009/12/fb_invite_in_dialog.txt" target="_blank">here</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://hudson.su/2009/12/24/invite-dialog-by-standard-facebook-tools/feed/</wfw:commentRss>
		<slash:comments>20</slash:comments>
		</item>
		<item>
		<title>Symfony 2009 advent calendar: день 18 &#8211; Разработка для Facebook (часть 1)</title>
		<link>http://hudson.su/2009/12/22/symfony-2009-advent-calendar-day18-facebook-p1/</link>
		<comments>http://hudson.su/2009/12/22/symfony-2009-advent-calendar-day18-facebook-p1/#comments</comments>
		<pubDate>Tue, 22 Dec 2009 01:14:14 +0000</pubDate>
		<dc:creator>hudson</dc:creator>
				<category><![CDATA[Профессиональное]]></category>
		<category><![CDATA[facebook]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[symfony]]></category>
		<category><![CDATA[symfony advent calendar'09]]></category>

		<guid isPermaLink="false">http://hudson.su/?p=636</guid>
		<description><![CDATA[Symfony 2009 advent calendar &#8211; это 24 урока продвинутого уровня о symfony. Все уроки представлены на 5 языках: английском, французском, испанском, итальянском, японском. Русского нет, это досадное упущение постараюсь исправить. Перевод статьи Symfony 2009 advent calendar: day 18 &#8211; Developing for Facebook (part 1). Предисловие: тема разработки для facebook меня достаточно серьезно заинтересовала с полгода [...]]]></description>
			<content:encoded><![CDATA[<blockquote><p><strong>Symfony 2009 advent calendar</strong> &#8211; это 24 урока <strong>продвинутого</strong> уровня о symfony. Все уроки представлены на 5 языках: английском, французском, испанском, итальянском, японском. Русского нет, это досадное упущение постараюсь исправить.</p></blockquote>
<p>Перевод статьи <a href="http://www.symfony-project.org/advent_calendar/18/en" target="_blank"><strong>Symfony 2009 advent calendar: day 18 &#8211; Developing for Facebook (part 1)</strong></a>.</p>
<p>Предисловие: тема <strong>разработки для facebook</strong> меня достаточно серьезно заинтересовала с полгода назад. После неудачной попытки доложиться по этому вопросу на phpconf&#8217;09 (тянул до последнего и не попал в программу))) я хотел систематизировать свои наблюдения и наработки в виде серии статей, однако все было недосуг. И вот, вижу данный урок на symfony, основном моем инструменте последних лет. Поэтому, немного сломав порядок перевода статей advent calendar, задуманный мной, начну с освещения этого интересного материала.</p>
<p><span id="more-636"></span></p>
<p>Facebook, со своими 350 миллионами пользователей, де-факто стал стандартом для социальных сетей. Одна из его самых интересных особенностей &#8211; это <strong>Facebook Platform</strong>, API, который позволяет сторонним разработчикам разрабатывать приложения как &#8220;внутри&#8221; facebook, так и подключать другие сайты и с системой аутентификации facebook (т.н. facebook connect).</p>
<p>Так как фронтэнд facebook&#8217;а написан на PHP, неудивительно, что официальная клиентская библиотека к этому API также написана на PHP. Этот факт делает symfony логичным выбором для быстрой разработки качественных приложенией для facebook, а также facebook connect&#8217;ед сайтов. Более того, разработка для facebook реально показывает, как вы можете пользоваться всей мощью функциональности symfony, экономя драгоценное время при сохранении высокого качества.</p>
<p>Далее мы рассмотрим следующие вопросы: после краткого описания что из себя представляет facebook api и с чем его едят. Мы расскажем как лучше всего использовать symfony при разработке facebook приложений, как получить отдачу от коммьюнити с помощью sfFacebookPlugin, продемонстрируем все на простом приложении &#8220;Hello you!&#8221; и, наконец, дадим несколько советов, как решить наиболее типичные проблемы.</p>
<h3>Разработка для facebook</h3>
<p>Не смотря на то, что API в целом одинаков в обоих случаях, есть два различных юзкейса: мы можем создать приложение &#8220;внутри&#8221; facebook или же реализовать facebook connect на внешнем сайте.</p>
<h4>Facebook приложения</h4>
<p>Facebook приложения &#8211; это веб приложения внутри facebook. Их основной плюс в том, что они напрямую встраиваются в социальную сеть с более чем 300 миллионами пользователей, что позволяет приложению наращивать посещаемость потрясающими темпами с использованием виральных (вирусных) технологий. Последний и наиболее успешный пример &#8211; Farmville, с 60 миллионами активных пользователей ежемесячно и с 2 миллионами поклонников, и эти показатели достигнуты всего за несколько месяцев! Эти цифры эквиваленты населению Франции, которое ежемесячно возвращается для того чтобы работать на своей виртуальной ферме. Facebook приложения взаимодействуют с сайтом и социальной сетью различными способами. Ниже приведены различные варианты того, где приложение может появиться:</p>
<h4>Канвас (the Canvas)</h4>
<blockquote><p>Канвас переводится с английского как холст, но в среде русскоговорящих разработчиков более принято прямое произношение. Я буду далее использовать именно его. // hudson</p></blockquote>
<p>Итак, канва, это, как правило, основная часть приложения. Это небольшой вебсайт, встраиваемый внутрь фрейма facebook.</p>
<h4>Вкладка профиля (the Profile Tab)</h4>
<p>Приложение также может располагаться на вкладке пользовательского профайла или фанатской страницы. Имеются следующие ограничения:</p>
<ul>
<li>Только одна страница. Нет возможности определять ссылки на внутренние страницы приложения на вкладке.</li>
<li>Никой динамики в виде flash или JavaScript во время загрузки. Для динамической функциональности приложение должно ожидать пользовательского действия на странице (клика на ссылку или кнопку)</li>
</ul>
<h4>Ящик профиля (the Profile Box)</h4>
<p>Эта функциональность осталась от старых версий facebook и фактически мало кем используется. Этот атавизм можно найти на вкладке &#8220;Boxes&#8221; пользовательского профайла.</p>
<h4>Дополнительная информационная вкладка (the Information Tab&#8217;s Addendum)</h4>
<p>Некоторая статическая информация, привязанная к тому или иному пользователю и приложение могут отображаться на информационной вкладке пользовательского профайла.</p>
<h4>Публикация заметок и новостной поток (Publishing Notices and in the News Stream)</h4>
<p>Приложение может публиковать новости, ссылки, картинки, видеоролики в потоке новостей (news stream) на стенах друзей или напрямую модифицировать статус пользователя.</p>
<blockquote><p>Не смотря на то, что нотификациям и стриму (и прочим виральным техникам) уделено несколько слов сейчас и еще несколько слов будет ниже, на самом деле они заслуживают как минимум отдельной статьи. Если вам будет интересно &#8211; пишите мне на email или оставьте комментарий. //hudson</p></blockquote>
<h4>Информационная страница (the Information page)</h4>
<p>Это аналог профайла, но для приложения. Эта страница создается автоматически. Это место, где создатель приложения может контактировать с пользователями обычным на facebook образом. Данная страница должна больше заботить маркетологов, нежели девелоперов )))</p>
<h4>Facebook Connect</h4>
<p>Непереводимая игра слов (прим.пер.) ))) Коннект позволяет подключить к любому вебсайту кое-что из немалой функциональности facebook и предоставить ее пользователям сайта. Уже подключенные сайты легко распознать по большой синей кнопке с текстом &#8220;connect with facebook&#8221;. Наиболее известные из них &#8211; digg.com, cnet.com, netvibes.com, yelp.com и т.д. Ниже перечислены четыре основные причины для того чтобы подключить ваш сайт к facebook:</p>
<ul>
<li><strong>Аутентификация одним кликом</strong>. Как и OpenID, Facebook Connect предоставляет вашему сайту автоматический логин с использованием facebook сессии. Когда соединение между вебсайтом и facebook установлено пользователем, автоматически создается facebook сессия, тем самым сохраняется время разработчика, которое он потратил бы на свою собственную систему аутентификации.</li>
<li><strong>Получение более детальной информации о пользователе</strong>.Другой полезной особенностью facebook connect является количество данных о пользователе, которые можно получить. Обычно пользователи оставляют на сайтах немного информации о себе, connect позволяет быстро получить дополнительную информацию о пользователе, например пол, местоположение, возраст, аватарку и т.д. Важно помнить, что <strong>facebook запрещает хранить персональную информацию пользователей</strong>, если пользователь не дал на то явного согласия. С другой стороны можно пользоваться этими данными, не храня их у себя (просто запрашивать у facebook, когда необходимо).</li>
<li><strong>Виральные коммуникации с использованием новостной ленты</strong>. Возможность взаимодействия с новостной лентой пользователя, приглашать друзей или публиковать что-либо на стенах друзей позволяет вебсайту использовать весь виральный потенциал facebook. Любой сайт с социальным уклоном может получить реальную выгоду от этих возможностей, до тех пор пока публикуемая информация имеет социальное значение и может заинтересовать друзей и друзей друзей.</li>
<li><strong>Преимущества уже существующей социальной сети</strong>. Для вебсайтов, которые зависят от социальной сети (типа сети друзей или знакомых), цена построения первого коммьюнити с достаточным числом связей между пользователями, как правило, очень высока. Предоставляя простой доступ к списку друзей пользователя, facebook connect значительно снижает эту цену, устраняя необходимость искать друзей пользователей.</li>
</ul>
<h3>Настройка первого проекта, с использованием sfFacebookConnectPlugin</h3>
<h4>Создание приложения</h4>
<p>Для начала нужен аккаунт на facebook, с установленным приложением <a href="http://www.facebook.com/developers" target="_blank">Developer</a>. Для того чтобы создать приложение, достаточно указать его имя.</p>
<h4>Установка и настройка sfFacebookConnectPlugin</h4>
<p>Следующим шагом будет связка пользователей facebook с пользователями <strong>sfGuard</strong>. Это основное свойство <strong>sfFacebookConnectPlugin</strong>. После установки плагина его обязательно нужно сконфигурировать, но это не сложно. <strong>API key</strong>, <strong>application secret</strong> и <strong>application ID</strong> должны быть определены в файле app.yml:</p>
<pre># default values
all:
  facebook:
    api_key: xxx
    api_secret: xxx
    api_id: xxx
    redirect_after_connect: false
    redirect_after_connect_url: ''
    connect_signin_url: 'sfFacebookConnectAuth/signin'
    app_url: '/my-app'
    guard_adapter: ~
    js_framework: none # none, jQuery or prototype.

  sf_guard_plugin:
    profile_class: sfGuardUserProfile
    profile_field_name: user_id
    profile_facebook_uid_name: facebook_uid # WARNING this column must be of type varchar! 100000398093902 is a valid uid for example!
    profile_email_name: email
    profile_email_hash_name: email_hash

  facebook_connect:
    load_routing:     true
    user_permissions: []
</pre>
<blockquote><p>Для более ранних версий symfony (&lt;1.3), необходимо установить опцию load_routing в false, так как она используется новой системой маршрутизации.</p></blockquote>
<h4>Настройка facebook приложения</h4>
<p>Если ваш проект это facebook приложение (а не connect&#8217;ed сайт), тогда есть только один необходимый параметр, который вам нужно указать &#8211; этот параметр <strong>app_url</strong>, который указывает относительный путь к приложению на facebook. Например, для приложения <em>http://apps.facebook.com/my-app</em>, значением app_url будет <em>/my-app</em></p>
<h4>Настройка facebook connect сайта</h4>
<p>Если ваш проект &#8211; это сайт с facebook connect, следующие конфигурационные параметры могут быть оставлены со значениями по-умолчанию в большинстве случаев:</p>
<ul>
<li><strong>redirect_after_connect</strong> &#8211; позволяет изменить поведение сайта после клика на кнопку &#8220;connect with facebook&#8221;. По умолчанию, плагин воспроизводит поведение sfGuardPlugin после регистрации.</li>
<li><strong>js_framework</strong> &#8211; может быть использован для указания js фреймворка, который вы будете использовать. Очень рекомендуем воспользоваться этой возможностью и остановить выбор каком либо из фреймворков типа jQuery, так как JavaScript в Facebook Connect очень тяжел и может вызывать фатальные сбои (!) в IE6 будучи не загруженным в нужный момент.</li>
<li><strong>user_permissions</strong> &#8211; массив прав доступа, которые будут присвоены новому facebook пользователю.</li>
</ul>
<h4>Коннектим sfGuard с Facebook</h4>
<p>Связь между пользователем facebook осуществляется благодаря использованию колонки <strong>facebook_uid</strong> в таблице <strong>Profile</strong>. Плагин полагает что связь между sfGuardUser и его профайлом с использованием метода getProfile(). Это поведение по-умолчанию для sfPropelGuardPlugin, но для sfDoctrineGuardPlugin потребуется немного <del>танцев с бубном</del> донастроить. Вот возможная схема для <strong>Propel</strong>:</p>
<pre># schema.yml
sf_guard_user_profile:
  _attributes: { phpName: UserProfile }
  id:
  user_id:            { type: integer, foreignTable: sf_guard_user, foreignReference: id, onDelete: cascade }
  first_name:         { type: varchar, size: 30 }
  last_name:          { type: varchar, size: 30 }
  facebook_uid:       { type: varchar, size: 20 }
  email:              { type: varchar, size: 255 }
  email_hash:         { type: varchar, size: 255 }
  _uniques:
    facebook_uid_index: [facebook_uid]
    email_index:        [email]
    email_hash_index:   [email_hash]
</pre>
<p>А вот для <strong>Doctrine</strong>:</p>
<pre>sfGuardUserProfile:
  tableName:     sf_guard_user_profile
  columns:
    user_id:          { type: integer(4), notnull: true }
    first_name:       { type: string(30) }
    last_name:        { type: string(30) }
    facebook_uid:     { type: string(20) }
    email:            { type: string(255) }
    email_hash:       { type: string(255) }
  indexes:
    facebook_uid_index:
      fields: [facebook_uid]
      unique: true
    email_index:
      fields: [email]
      unique: true
    email_hash_index:
      fields: [email_hash]
      unique: true
  relations:
    sfGuardUser:
      type: one
      foreignType: one
      class: sfGuardUser
      local: user_id
      foreign: id
      onDelete: cascade
      foreignAlias: Profile
</pre>
<blockquote><p>А что делать если проект использует Doctrine и foreignAlias не Profile? В этом случае плагин работать не будет. Но простой метод getProfile() в классе sfGuardUser.class.php, который ссылается на таблицу Profile &#8211; решит эту проблему!</p></blockquote>
<p>Имейте в виду, что колонка facebook_uid должна быть VARCHAR, так как новые профайлы facebook имеют uids свыше 10^15. Лучше жить спокойно с индексированным VARCHAR, чем пытаться заставить работать BIGINT с различными ORM.</p>
<p>Другие две колонки менее важны: email и email_hash нужны только для случая facebook connect сайта с уже существующими пользователями. В этом случае facebook предоставляет сложный процесс сопоставления существующих аккаунтов с новыми аккаунтами facebook connect с использованием хэша email. Мы постарались упростить этот процесс при помощи CLI таска, который поставляется вместе с sfFacebookConnectPlugin. Этот таск будет описан в конце статьи.</p>
<h4>Выбор между FBML и XFBML разрешен в пользу symfony ))</h4>
<p>Теперь, когда все настроено, мы можем начать кодировать собственно приложение. Facebook предоставляет много специальных тагов, которые отвечают за рендеринг всего функционала, например форму &#8220;invite friends&#8221;, или полнофункциональную систему комментирования. Эти таги называются <strong>FBML</strong> или <strong>XFBML</strong> тагами. FBML и XFBML таги в целом похожи, но от выбора между ними зависит, будет ли приложение рендериться внутри facebook или нет. Если проект &#8211; это сайт с Facebook connect, ваш выбор только XFBML. Если же это Facebook приложение, есть две возможности:</p>
<ul>
<li>Встроить приложение в IFrame внутри страницы facebook-приложения и использовать XFBML внутри IFrame/</li>
<li>Встроить приложение &#8220;прозрачно&#8221; и использовать FBML.</li>
</ul>
<p>Facebook поощряет разработчиков в использовании их &#8220;прозрачного встраивания&#8221; или, как его чаще называют, &#8220;FBML applicaton&#8221;. В самом деле, такие приложения предоставляют ряд интересных особенностей:</p>
<ul>
<li>Никаких IFrame&#8217;ов, которые всегда усложняют управление</li>
<li>FBML таги автоматически интерпретируются на стороне facebook и позволяют отображать данные пользователя без необходимости заранее получать их от facebook</li>
<li>Нет необходимости явно передавать facebook сессию со страницы на страницу</li>
</ul>
<p>Тем не менее, FBML имеет и недостатки:</p>
<ul>
<li>Любой JavaScript должен быть определен непосредственно внутри страницы, что делает невозможным использование сторонних библиотек типа googlemaps, jquery или системы сбора статистики, отличной от Google Analytics, которая официально поддерживается facebook (к слову сказать, в версии FBJS2 обещают добавить поддержку сторонних JS библиотек в FBML приложения // прим. пер.)</li>
<li>Считается, что FBML таги позволяют ускорить работу приложения, по сравнению с тем, если бы вы пользовались вызовами API. Тем не менее, если приложение не сложное, хостинг на своем сервере будет быстрее.</li>
<li>FBML приложения сложнее отлаживать, в том числе 500я ошибка перехватывается faceook&#8217;ом и заменяется на стандартную.</li>
</ul>
<blockquote><p>Согласен с тем что отладка FBML приложения более сложная. Тем не менее, недавно я переводил 30 лучших практик для symfony, там в частности рекомендовалось использовать логгеры вместо привычных многим PHP-разработчикам echo &amp; var_dump. Тут я могу порекомендовать то же самое. Это работает, не зависимо от того, пишите вы на symfony или же на каком-то самописном решении. А вот с JavaScript-отладкой готовьте матюгальник. Без шуток. Даже firebug почти не помогает. Все embedded скрипты парсятся и модифицируются на стороне facebook перед показом. В общем-то вы очень ограничены в выборе клиентских средств (см. FBJS). // hudson</p></blockquote>
<p>Итак, что же выбрать? Хорошая новость! Symfony и sfFacebookConnectPlugin не требуют от вас подобного выбора. Вы можете писать приложение независимо от его типа и переключаться с IFrame на FBML или на сайт с Facebook Connect имея в основе тот же код. Это возможно, так как технически основное различие между этими типами приложений в их шаблоне (layout), которые очень просто переключать в symfony. Вот вам примеры двух различных шаблонов:</p>
<p>Шаблон для FBML приложения:</p>
<pre>&lt;?php sfConfig::set('sf_web_debug', false); ?&gt;
&lt;fb:title&gt;&lt;?php echo sfContext::getInstance()-&gt;getResponse()-&gt;getTitle() ?&gt;&lt;/fb:title&gt;
&lt;?php echo $sf_content ?&gt;
</pre>
<p>Шаблон для IFrame/Facebook Connect сайта</p>
<pre>&lt;?php use_helper('sfFacebookConnect')?&gt;
&lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"&gt;
&lt;html xmlns="http://www.w3.org/1999/xhtml" xmlns:fb="http://www.facebook.com/2008/fbml"&gt;
  &lt;head&gt;
    &lt;?php include_http_metas() ?&gt;
    &lt;?php include_metas() ?&gt;
    &lt;?php include_title() ?&gt;
    &lt;script type="text/javascript" src="/sfFacebookConnectPlugin/js/animation/animation.js"&gt;&lt;/script&gt;
  &lt;/head&gt;
  &lt;body&gt;
    &lt;?php echo $sf_content ?&gt;
    &lt;?php echo include_facebook_connect_script() ?&gt;
  &lt;/body&gt;
&lt;/html&gt;
</pre>
<p>Для автоматического переключения между ними, просто добавьте следующий код в actions.class.php:</p>
<pre>public function preExecute()
{
  if (sfFacebook::isInsideFacebook())
  {
    $this-&gt;setLayout('layout_fbml');
  }
  else
  {
    $this-&gt;setLayout('layout_connect');
  }
}
</pre>
<p>Есть правда небольшое отличие между FBML и XFBML, которое не решается сменой лэйаута: FBML таги могут быть закрытыми, а XFBML нет. Т.о. всего лишь изменяем</p>
<pre>[html]
 &lt;fb:profile-pic uid="12345" size="normal" width="400" /&gt;
</pre>
<p>на</p>
<pre>[html]
 &lt;fb:profile-pic uid="12345" size="normal" width="400"&gt;&lt;/fb:profile-pic&gt;
</pre>
<p>Конечно, для того чтобы проделывать эти манипуляции, приложение должно быть настроено также и для Facebook Connect в настройках, даже если оно задумывалось лишь как FBML. Но огромная польза от этого заключается в возможности тестировать приложение локально!</p>
<p>Если вы создаете приложение для facebook и планируете использовать FBML таги, которых по большому счету не миновать по тем или иным причинам, елинственный способ протестировать приложение заключается в размещении кода онлайн и тестировать непосредственно на facebook. К счастью, благодаря Facebook Connect, XFBML таги могут рендериться вне facebook.com. И, как уже было сказано, единственное их отличие в layout&#8217;ах. Таким образом, это решение позволяет локально рендерить FBML таги, коль сокоро на локальной машине есть выход в интернет (с facebook так или иначе коннектиться придется).</p>
<p>К тому же, если ваша девелоперская среда доступна через интернет, сервер ли это или у вас домашний компьютер с белым IP и вебсервером на 80м порту, система аутентификации будет работать на facebook.com, благодаря Facebook Connect.</p>
<h4>Простейшее приложение</h4>
<p>При помощи нижеследующего кода в шаблоне мы закончим разработку нашего приложения &#8220;Hello you!&#8221;:</p>
<pre>
&lt;?php $sfGuardUser = sfFacebook::getSfGuardUserByFacebookSession(); ?&gt;
Hello &lt;fb:name uid="&lt;?php echo $sfGuardUser?$sfGuardUser-&gt;getProfile()-&gt;getFacebookUid():'' ?&gt;"&gt;&lt;/fb:name&gt;
</pre>
<p>sfFacebookConnectPlugin автоматически конвертирует пользователя facebook в пользователя sfGuard. Это позволяет быструю интеграцию существующего кода, основанного на sfGuardPlugin.</p>
<blockquote><p>Продолжение следует! ))<br />
P.S. Если вы прочитали статью до этого места &#8211; поставьте пожалуйста оценку. Или даже обоснуйте ее в комментарии. Вам не сложно, а мне приятно и наука на будущее ))</p></blockquote>
]]></content:encoded>
			<wfw:commentRss>http://hudson.su/2009/12/22/symfony-2009-advent-calendar-day18-facebook-p1/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
	</channel>
</rss>

