in Профессиональное

Symfony 2009 advent calendar: день 18 – Разработка для Facebook (часть 1)

Symfony 2009 advent calendar – это 24 урока продвинутого уровня о symfony. Все уроки представлены на 5 языках: английском, французском, испанском, итальянском, японском. Русского нет, это досадное упущение постараюсь исправить.

Перевод статьи Symfony 2009 advent calendar: day 18 – Developing for Facebook (part 1).

Предисловие: тема разработки для facebook меня достаточно серьезно заинтересовала с полгода назад. После неудачной попытки доложиться по этому вопросу на phpconf’09 (тянул до последнего и не попал в программу))) я хотел систематизировать свои наблюдения и наработки в виде серии статей, однако все было недосуг. И вот, вижу данный урок на symfony, основном моем инструменте последних лет. Поэтому, немного сломав порядок перевода статей advent calendar, задуманный мной, начну с освещения этого интересного материала.

Facebook, со своими 350 миллионами пользователей, де-факто стал стандартом для социальных сетей. Одна из его самых интересных особенностей – это Facebook Platform, API, который позволяет сторонним разработчикам разрабатывать приложения как “внутри” facebook, так и подключать другие сайты и с системой аутентификации facebook (т.н. facebook connect).

Так как фронтэнд facebook’а написан на PHP, неудивительно, что официальная клиентская библиотека к этому API также написана на PHP. Этот факт делает symfony логичным выбором для быстрой разработки качественных приложенией для facebook, а также facebook connect’ед сайтов. Более того, разработка для facebook реально показывает, как вы можете пользоваться всей мощью функциональности symfony, экономя драгоценное время при сохранении высокого качества.

Далее мы рассмотрим следующие вопросы: после краткого описания что из себя представляет facebook api и с чем его едят. Мы расскажем как лучше всего использовать symfony при разработке facebook приложений, как получить отдачу от коммьюнити с помощью sfFacebookPlugin, продемонстрируем все на простом приложении “Hello you!” и, наконец, дадим несколько советов, как решить наиболее типичные проблемы.

Разработка для facebook

Не смотря на то, что API в целом одинаков в обоих случаях, есть два различных юзкейса: мы можем создать приложение “внутри” facebook или же реализовать facebook connect на внешнем сайте.

Facebook приложения

Facebook приложения – это веб приложения внутри facebook. Их основной плюс в том, что они напрямую встраиваются в социальную сеть с более чем 300 миллионами пользователей, что позволяет приложению наращивать посещаемость потрясающими темпами с использованием виральных (вирусных) технологий. Последний и наиболее успешный пример – Farmville, с 60 миллионами активных пользователей ежемесячно и с 2 миллионами поклонников, и эти показатели достигнуты всего за несколько месяцев! Эти цифры эквиваленты населению Франции, которое ежемесячно возвращается для того чтобы работать на своей виртуальной ферме. Facebook приложения взаимодействуют с сайтом и социальной сетью различными способами. Ниже приведены различные варианты того, где приложение может появиться:

Канвас (the Canvas)

Канвас переводится с английского как холст, но в среде русскоговорящих разработчиков более принято прямое произношение. Я буду далее использовать именно его. // hudson

Итак, канва, это, как правило, основная часть приложения. Это небольшой вебсайт, встраиваемый внутрь фрейма facebook.

Вкладка профиля (the Profile Tab)

Приложение также может располагаться на вкладке пользовательского профайла или фанатской страницы. Имеются следующие ограничения:

  • Только одна страница. Нет возможности определять ссылки на внутренние страницы приложения на вкладке.
  • Никой динамики в виде flash или JavaScript во время загрузки. Для динамической функциональности приложение должно ожидать пользовательского действия на странице (клика на ссылку или кнопку)

Ящик профиля (the Profile Box)

Эта функциональность осталась от старых версий facebook и фактически мало кем используется. Этот атавизм можно найти на вкладке “Boxes” пользовательского профайла.

Дополнительная информационная вкладка (the Information Tab’s Addendum)

Некоторая статическая информация, привязанная к тому или иному пользователю и приложение могут отображаться на информационной вкладке пользовательского профайла.

Публикация заметок и новостной поток (Publishing Notices and in the News Stream)

Приложение может публиковать новости, ссылки, картинки, видеоролики в потоке новостей (news stream) на стенах друзей или напрямую модифицировать статус пользователя.

Не смотря на то, что нотификациям и стриму (и прочим виральным техникам) уделено несколько слов сейчас и еще несколько слов будет ниже, на самом деле они заслуживают как минимум отдельной статьи. Если вам будет интересно – пишите мне на email или оставьте комментарий. //hudson

Информационная страница (the Information page)

Это аналог профайла, но для приложения. Эта страница создается автоматически. Это место, где создатель приложения может контактировать с пользователями обычным на facebook образом. Данная страница должна больше заботить маркетологов, нежели девелоперов )))

Facebook Connect

Непереводимая игра слов (прим.пер.) ))) Коннект позволяет подключить к любому вебсайту кое-что из немалой функциональности facebook и предоставить ее пользователям сайта. Уже подключенные сайты легко распознать по большой синей кнопке с текстом “connect with facebook”. Наиболее известные из них – digg.com, cnet.com, netvibes.com, yelp.com и т.д. Ниже перечислены четыре основные причины для того чтобы подключить ваш сайт к facebook:

  • Аутентификация одним кликом. Как и OpenID, Facebook Connect предоставляет вашему сайту автоматический логин с использованием facebook сессии. Когда соединение между вебсайтом и facebook установлено пользователем, автоматически создается facebook сессия, тем самым сохраняется время разработчика, которое он потратил бы на свою собственную систему аутентификации.
  • Получение более детальной информации о пользователе.Другой полезной особенностью facebook connect является количество данных о пользователе, которые можно получить. Обычно пользователи оставляют на сайтах немного информации о себе, connect позволяет быстро получить дополнительную информацию о пользователе, например пол, местоположение, возраст, аватарку и т.д. Важно помнить, что facebook запрещает хранить персональную информацию пользователей, если пользователь не дал на то явного согласия. С другой стороны можно пользоваться этими данными, не храня их у себя (просто запрашивать у facebook, когда необходимо).
  • Виральные коммуникации с использованием новостной ленты. Возможность взаимодействия с новостной лентой пользователя, приглашать друзей или публиковать что-либо на стенах друзей позволяет вебсайту использовать весь виральный потенциал facebook. Любой сайт с социальным уклоном может получить реальную выгоду от этих возможностей, до тех пор пока публикуемая информация имеет социальное значение и может заинтересовать друзей и друзей друзей.
  • Преимущества уже существующей социальной сети. Для вебсайтов, которые зависят от социальной сети (типа сети друзей или знакомых), цена построения первого коммьюнити с достаточным числом связей между пользователями, как правило, очень высока. Предоставляя простой доступ к списку друзей пользователя, facebook connect значительно снижает эту цену, устраняя необходимость искать друзей пользователей.

Настройка первого проекта, с использованием sfFacebookConnectPlugin

Создание приложения

Для начала нужен аккаунт на facebook, с установленным приложением Developer. Для того чтобы создать приложение, достаточно указать его имя.

Установка и настройка sfFacebookConnectPlugin

Следующим шагом будет связка пользователей facebook с пользователями sfGuard. Это основное свойство sfFacebookConnectPlugin. После установки плагина его обязательно нужно сконфигурировать, но это не сложно. API key, application secret и application ID должны быть определены в файле app.yml:

# 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: []

Для более ранних версий symfony (<1.3), необходимо установить опцию load_routing в false, так как она используется новой системой маршрутизации.

Настройка facebook приложения

Если ваш проект это facebook приложение (а не connect’ed сайт), тогда есть только один необходимый параметр, который вам нужно указать – этот параметр app_url, который указывает относительный путь к приложению на facebook. Например, для приложения http://apps.facebook.com/my-app, значением app_url будет /my-app

Настройка facebook connect сайта

Если ваш проект – это сайт с facebook connect, следующие конфигурационные параметры могут быть оставлены со значениями по-умолчанию в большинстве случаев:

  • redirect_after_connect – позволяет изменить поведение сайта после клика на кнопку “connect with facebook”. По умолчанию, плагин воспроизводит поведение sfGuardPlugin после регистрации.
  • js_framework – может быть использован для указания js фреймворка, который вы будете использовать. Очень рекомендуем воспользоваться этой возможностью и остановить выбор каком либо из фреймворков типа jQuery, так как JavaScript в Facebook Connect очень тяжел и может вызывать фатальные сбои (!) в IE6 будучи не загруженным в нужный момент.
  • user_permissions – массив прав доступа, которые будут присвоены новому facebook пользователю.

Коннектим sfGuard с Facebook

Связь между пользователем facebook осуществляется благодаря использованию колонки facebook_uid в таблице Profile. Плагин полагает что связь между sfGuardUser и его профайлом с использованием метода getProfile(). Это поведение по-умолчанию для sfPropelGuardPlugin, но для sfDoctrineGuardPlugin потребуется немного танцев с бубном донастроить. Вот возможная схема для Propel:

# 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]

А вот для Doctrine:

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

А что делать если проект использует Doctrine и foreignAlias не Profile? В этом случае плагин работать не будет. Но простой метод getProfile() в классе sfGuardUser.class.php, который ссылается на таблицу Profile – решит эту проблему!

Имейте в виду, что колонка facebook_uid должна быть VARCHAR, так как новые профайлы facebook имеют uids свыше 10^15. Лучше жить спокойно с индексированным VARCHAR, чем пытаться заставить работать BIGINT с различными ORM.

Другие две колонки менее важны: email и email_hash нужны только для случая facebook connect сайта с уже существующими пользователями. В этом случае facebook предоставляет сложный процесс сопоставления существующих аккаунтов с новыми аккаунтами facebook connect с использованием хэша email. Мы постарались упростить этот процесс при помощи CLI таска, который поставляется вместе с sfFacebookConnectPlugin. Этот таск будет описан в конце статьи.

Выбор между FBML и XFBML разрешен в пользу symfony ))

Теперь, когда все настроено, мы можем начать кодировать собственно приложение. Facebook предоставляет много специальных тагов, которые отвечают за рендеринг всего функционала, например форму “invite friends”, или полнофункциональную систему комментирования. Эти таги называются FBML или XFBML тагами. FBML и XFBML таги в целом похожи, но от выбора между ними зависит, будет ли приложение рендериться внутри facebook или нет. Если проект – это сайт с Facebook connect, ваш выбор только XFBML. Если же это Facebook приложение, есть две возможности:

  • Встроить приложение в IFrame внутри страницы facebook-приложения и использовать XFBML внутри IFrame/
  • Встроить приложение “прозрачно” и использовать FBML.

Facebook поощряет разработчиков в использовании их “прозрачного встраивания” или, как его чаще называют, “FBML applicaton”. В самом деле, такие приложения предоставляют ряд интересных особенностей:

  • Никаких IFrame’ов, которые всегда усложняют управление
  • FBML таги автоматически интерпретируются на стороне facebook и позволяют отображать данные пользователя без необходимости заранее получать их от facebook
  • Нет необходимости явно передавать facebook сессию со страницы на страницу

Тем не менее, FBML имеет и недостатки:

  • Любой JavaScript должен быть определен непосредственно внутри страницы, что делает невозможным использование сторонних библиотек типа googlemaps, jquery или системы сбора статистики, отличной от Google Analytics, которая официально поддерживается facebook (к слову сказать, в версии FBJS2 обещают добавить поддержку сторонних JS библиотек в FBML приложения // прим. пер.)
  • Считается, что FBML таги позволяют ускорить работу приложения, по сравнению с тем, если бы вы пользовались вызовами API. Тем не менее, если приложение не сложное, хостинг на своем сервере будет быстрее.
  • FBML приложения сложнее отлаживать, в том числе 500я ошибка перехватывается faceook’ом и заменяется на стандартную.

Согласен с тем что отладка FBML приложения более сложная. Тем не менее, недавно я переводил 30 лучших практик для symfony, там в частности рекомендовалось использовать логгеры вместо привычных многим PHP-разработчикам echo & var_dump. Тут я могу порекомендовать то же самое. Это работает, не зависимо от того, пишите вы на symfony или же на каком-то самописном решении. А вот с JavaScript-отладкой готовьте матюгальник. Без шуток. Даже firebug почти не помогает. Все embedded скрипты парсятся и модифицируются на стороне facebook перед показом. В общем-то вы очень ограничены в выборе клиентских средств (см. FBJS). // hudson

Итак, что же выбрать? Хорошая новость! Symfony и sfFacebookConnectPlugin не требуют от вас подобного выбора. Вы можете писать приложение независимо от его типа и переключаться с IFrame на FBML или на сайт с Facebook Connect имея в основе тот же код. Это возможно, так как технически основное различие между этими типами приложений в их шаблоне (layout), которые очень просто переключать в symfony. Вот вам примеры двух различных шаблонов:

Шаблон для FBML приложения:

<?php sfConfig::set('sf_web_debug', false); ?>
<fb:title><?php echo sfContext::getInstance()->getResponse()->getTitle() ?></fb:title>
<?php echo $sf_content ?>

Шаблон для IFrame/Facebook Connect сайта

<?php use_helper('sfFacebookConnect')?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:fb="http://www.facebook.com/2008/fbml">
  <head>
    <?php include_http_metas() ?>
    <?php include_metas() ?>
    <?php include_title() ?>
    <script type="text/javascript" src="/sfFacebookConnectPlugin/js/animation/animation.js"></script>
  </head>
  <body>
    <?php echo $sf_content ?>
    <?php echo include_facebook_connect_script() ?>
  </body>
</html>

Для автоматического переключения между ними, просто добавьте следующий код в actions.class.php:

public function preExecute()
{
  if (sfFacebook::isInsideFacebook())
  {
    $this->setLayout('layout_fbml');
  }
  else
  {
    $this->setLayout('layout_connect');
  }
}

Есть правда небольшое отличие между FBML и XFBML, которое не решается сменой лэйаута: FBML таги могут быть закрытыми, а XFBML нет. Т.о. всего лишь изменяем

[html]
 <fb:profile-pic uid="12345" size="normal" width="400" />

на

[html]
 <fb:profile-pic uid="12345" size="normal" width="400"></fb:profile-pic>

Конечно, для того чтобы проделывать эти манипуляции, приложение должно быть настроено также и для Facebook Connect в настройках, даже если оно задумывалось лишь как FBML. Но огромная польза от этого заключается в возможности тестировать приложение локально!

Если вы создаете приложение для facebook и планируете использовать FBML таги, которых по большому счету не миновать по тем или иным причинам, елинственный способ протестировать приложение заключается в размещении кода онлайн и тестировать непосредственно на facebook. К счастью, благодаря Facebook Connect, XFBML таги могут рендериться вне facebook.com. И, как уже было сказано, единственное их отличие в layout’ах. Таким образом, это решение позволяет локально рендерить FBML таги, коль сокоро на локальной машине есть выход в интернет (с facebook так или иначе коннектиться придется).

К тому же, если ваша девелоперская среда доступна через интернет, сервер ли это или у вас домашний компьютер с белым IP и вебсервером на 80м порту, система аутентификации будет работать на facebook.com, благодаря Facebook Connect.

Простейшее приложение

При помощи нижеследующего кода в шаблоне мы закончим разработку нашего приложения “Hello you!”:

<?php $sfGuardUser = sfFacebook::getSfGuardUserByFacebookSession(); ?>
Hello <fb:name uid="<?php echo $sfGuardUser?$sfGuardUser->getProfile()->getFacebookUid():'' ?>"></fb:name>

sfFacebookConnectPlugin автоматически конвертирует пользователя facebook в пользователя sfGuard. Это позволяет быструю интеграцию существующего кода, основанного на sfGuardPlugin.

Продолжение следует! ))
P.S. Если вы прочитали статью до этого места – поставьте пожалуйста оценку. Или даже обоснуйте ее в комментарии. Вам не сложно, а мне приятно и наука на будущее ))

Write a Comment

Comment

*

    • А вы вот не только комментарий напишите, но и оцените статью 😉 Это лучшая награда.

  1. Спасибо за перевод, жду продолжения. Ваши комментарии к тесту интересны и представляют даже большую ценность чем сам перевод.

    Единственное замечание, по моему, заголовки в верхнем регистре – не очень хорошая идея, особенно когда в заголовке фигурирует название класса. Можно просто жирным выделить.

  2. Приложение установил, настройки прописал. Как авторизоваться через него, как вывести ктопку при нажатии на которую пользователь будет авторизовываться? Извиняюсь если вам мой вопос покажется глупым, но я уже 2-й день не могу с этим разобраться )).
    Заранее спасибо за ответ!

    • Уууу батенька, у вас большой пробел в знаниях матчасти ) На самом деле за прошедшие 2 года устарела как эта статья, так и сам Symfony 1.x. Да и Facebook за это время серьёзно изменился (OAuth 2.0 к примеру).

      Вы Facebook SDK качали? Там есть пример с кнопками login/logout; Можно также воспользоваться FB.login() методом из JSSDK, а также делать перенаправление пользователя на специальный URL (см. тут https://developers.facebook.com/docs/authentication/)

  3. Спасибо )). Т.е. сейчас нет номальных плакинов для первой симфони которые могли бы обеспечить адекватную авторизацию через фейсбук и надо писать это самому на базе Facebook SDK? Двойка мне пока не нравится – постояно какой-нибудь из бандлов конфликтует с текущей версией ((

    • Мне и тогда тот старый плагин не понравился (хотя по тексту всё очень красиво было). Зато на тот момент я разбирался в том как это работает, да и сейчас что-то под фейсбук намного проще делать, хотя я не слежу постоянно за трендами.