※ これは CakePHP 1.3 時代の記事です。2.0以降だとこのままでは動かないと思います。
OGP (Open Graph Protocol) という約束事がありますね。
例えばウェブページで Facebook の「いいね!」(Like) ボタンが押されたとき
どんな画像や説明文を表示さるかを指定するのに使われるやつ。
このブログの <head />
内にも埋め込んであります。
本来は誰か(何か)と誰か(何か)の関係の集まりであるソーシャルグラフの中で
それは誰(何)なのかを表現するための決め事と理解してますけど、
まあ実質「いいね!」のためにウェブサイトで使われることが多いので。
実際には何も書いておかなくても
Facebook が「これでしょ」と適当に画像や文字列を拾ってくれるんだけど
結果を見たら「それじゃない!」と思うことが多いので
こっちから指定しておいた方がいいですね。
ブログだけじゃなくてあらゆるウェブサイトに使えるから
CakePHP のサイトにも埋め込んでおきたい。
最近作ったサービスにはだいたい入れてあります。
ただ、OGP のタグ自体は <meta />
内に情報を書いていくだけで
特に複雑なものではないんだけど
毎日書くようなものじゃないからお作法をすぐ忘れちゃう。
というわけで、
CakePHP で作られたサイトに自動で OGP タグを埋め込んで
Facebook の「いいね!」やシェアに対応させる
OgpHelper を作りました。
コードは長くなっちゃうから後で。
環境
- ここでは CakePHP 1.3 を前提としています。
- 1.2 で使う場合は View からの呼び出しを
$this->Ogp->func()
ではなく$ogp->func()
にしてください。 - 2.0 対応はこれからやります。あるいは誰かやってください。
設置
OgpHelper が書かれた ogp.php
をapp/view/helpers/
に放り込んで、
あとは AppController
かどっかで読み込み。
public $helpers = array ( 'Ogp' , 'Html' , 'Form' ); |
とりあえず何も設定しなくても <head />
内にこういうものが追加されます。
< meta property = "og:url" content = "[現在の URL]" /> < meta property = "og:type" content = "website" /> < meta property = "og:title" content = "[ビュー変数の $title_for_layout]" /> < meta property = "og:locale" content = "ja_JP(言語設定による)" /> < meta property = "og:site_name" content = "[レイアウトの title]" /> |
基本的な設定
上記だけでは情報が足りないので、
次のどこかの場所に初期設定を配列で書きます。
まあヘルパーを直接いじるより Configure
した方がいいと思います。
- OgpHelper::$defaults
- Configure::write(‘OGP’, array(…));
- View 変数の $ogp_params
(Controller から $this->set() で指定したもの)
設定できる内容はこんな感じ。
もっといろいろ設定できるんだけど、
「いいね!」ボタンやコメントボックスを設置するときに必要か、
あるいは設定してないと Facebook の Debugger でおこられるのは以下。
array ( 'fb:admins' => '[FB のユーザーID]' , 'fb:app_id' => '[FB のアプリケーション ID' ], 'og:url' => '[設定したい URL]' , 'og:type' => '[ページのタイプ]' , 'og:title' => '[ページのタイトル]' , 'og:locale' => '[言語_地域コード]' , 'og:site_name' => '[サイトの名前]' , 'og:image' => '[ページ内容を表す画像]' , 'og:description' => '[ページの説明]' , ) |
設定すれば自動で埋め込まれるので、
テンプレートファイルを変更する必要はありません。
false に設定すると、そのタグは埋め込まれません。
ただし後述の自動取得設定が true
になっていたら
それらしい値を自動で挿入します。
ヒント
fb:admins
- https://graph.facebook.com/(ユーザー名) で調べられます。
fb:app_id
- https://developers.facebook.com/apps で新規作成/確認。
og:type
- 使えるタイプ名一覧は https://developers.facebook.com/docs/opengraph/#types
og:locale
- 日本語なら ja_JP, 米語なら en_US.
一覧は XML で提供されてます。 https://www.facebook.com/translations/FacebookLocales.xml
なお、Configure
のキー名と view 変数の名前は変更することができます。
手っ取り早いのは view から
$this ->Ogp->configName = '[Configure のキー名]' ; $this ->Ogp->varName = '[ビュー変数の名前]' ; |
プロパティ名の自動修正
プロパティ名は og:url
とか og:site_name
とか決まってるんだけどappid
だったか app_id
だったか、siteName
だったか site_name
だったかわからなくなるとか
いちいち og:
って書くのがめんどくさいとかありそうなので
別名で書いても正しく修正されるようにしてあります。
'admin' => 'fb:admins' , 'admins' => 'fb:admins' , 'app_id' => 'fb:app_id' , 'appid' => 'fb:app_id' , 'appId' => 'fb:app_id' , 'appID' => 'fb:app_id' , 'url' => 'og:url' , 'URL' => 'og:url' , 'type' => 'og:type' , 'title' => 'og:title' , 'locale' => 'og:locale' , 'site_name' => 'og:site_name' , 'sitename' => 'og:site_name' , 'siteName' => 'og:site_name' , 'image' => 'og:image' , 'img' => 'og:image' , 'description' => 'og:description' , |
その他のメソッド
set($property, $content = false, $id = false)
View のテンプレートファイル内からタグの設定を書き換えることができます。
$this ->Ogp->set( 'image' , 'http://www.msng.info/wp-content/themes/msng/img/oboete.png' ); |
また set()
は与えられた文字列をそのまま返すので、
HTML に文字列を出力しつつ、同時に OGP の値を設定することができます。
< p ><? php echo $this->Ogp->set('description', 'ほげほげについて説明します。');" /></ p > |
既に設定したプロパティを持つタグを set()
した場合、og:image
は追記され、それ以外のプロパティは上書きされます。
ただし $id
を指定すると、同じプロパティ名を持つ別のタグを追加します。
image 以外のものを複数入れることってなさそうな気もしますけど一応。
$this ->Ogp->set( 'description' , 'これも説明文ですお見逃しなく!' , 2); |
$content
に false
を指定すると、
既に設定したプロパティ名を持つタグを削除することができます。
$this ->Ogp->set( 'title' , false); |
$id
を指定して削除することもできます。
$this ->Ogp->set( 'image' , false, 'logo' ); |
ただし削除には次の delete()
を使った方が自然だと思います。
delete($property, $id = 0)
指定したプロパティ名を持つタグを削除します。set()
で設定した $id
を指定して削除することもできます。
set()
で $id
が無指定の場合は 0
になるので、$id
無指定で set()
したものは $id
無指定で削除できます。
ns()
OgpHelper
自体はレイアウトやテンプレートを変更しなくても動くんだけど<html />
要素に OGP と FB の namespace
属性を指定するのが本当なので
それを出力するためのメソッドです。
< html <?php echo $this->Ogp->ns(); ?> /> |
で、次のように出力されます。
< html xmlns:og = "http://ogp.me/ns#" xmlns:fb = "http://www.facebook.com/2008/fbml" > |
その他の設定
$autoGet
各値の自動取得を行うかどうかの設定です。
public $autoGetUrl = true; public $autoGetTitle = true; public $autoGetSiteName = true; public $autoGetLocale = true; |
いずれも、その値がどこにも設定されていない場合に作動します。
$autoGetUrl
- 現在リクエストされている URL を出力します。
$autoGetTitle
- 現在の View レイアウトでの
$title_for_layout
を出力します。 $autoGetSiteName
- 現在の View レイアウトの
<title />
内の文字列を出力します。__()
やグローバル変数などの出力も評価します。ただし少々強引に切り取っていますし、サイト名なんてアプリケーション全体で変わらないものだから素直にConfigure
した方が賢いと思います。 $autoGetLocale
- アプリケーションでの現在の設定から言語を拾い、
og:locale
の値にマッピングできる場合は自動で出力します。次項を参照してください。
$locales
CakePHP で使われる言語コードとog:locale
コードをマッピングします。
主に $autoGetLocale
の設定でロケールを自動取得する場合に使用されます。
これらは完全に1対1で対応させることができないので、
初期設定では次のものだけが指定してあります。
array ( 'ja' => 'ja_JP' , 'en' => 'en_US' , ) |
これ以外のロケール値を指定する場合は
アプリケーションのどこか (bootstrap.php
など) で
次のように指定してください。
Configure::write( 'OGPlocales' , array ( 'fr' => 'fr_FR' , 'it' => 'it_IT' , )); |
ここでの設定と初期設定はマージされ、Configure
されたものが優先されます。
また Configure
のキー名 OGPlocales
は
次項の $configLocaleName
で変更できます。
$configLocaleName
前項の $locales
にマージされる Configure
のキー名を指定します。
初期設定は ‘OGPlocales
‘ です。
コード
以下に貼りますけど、直接 gist で見た方がはやいかも。
<?php | |
/** | |
* Ogp Helper class file | |
* | |
* Adds OGP elements in <head /> | |
* | |
* Licensed under The MIT License | |
* | |
* @author Masunaga Ray (http://www.msng.info/) | |
* @link http://www.msng.info/archives/2011/12/cakephp-ogp-helper.php | |
* @license MIT License (http://www.opensource.org/licenses/mit-license.php) | |
*/ | |
class OgpHelper extends AppHelper | |
{ | |
public $helpers = array('Html'); | |
/** | |
* Decides whether or not each property should be taken automatically | |
* | |
* Works only when the property is not configured anywhere. | |
**/ | |
public $autoGetUrl = true; | |
public $autoGetTitle = true; | |
public $autoGetSiteName = true; | |
public $autoGetLocale = true; | |
/** | |
* Names for configurations | |
**/ | |
public $configName = 'OGP'; | |
public $configLocaleName = 'OGPlocales'; | |
public $varName = 'ogp_params'; | |
/** | |
* Default values for OGP are read from this property, | |
* Configure::read['OGP'] and View variable $ogp_params (by default) | |
* and the former is overwritten by the latter. | |
* Set to false to skip adding the property. | |
**/ | |
public $defaults = array( | |
'fb:admins' => false, | |
'fb:app_id' => false, | |
'og:url' => false, | |
'og:type' => 'website', | |
'og:title' => false, | |
'og:locale' => false, | |
'og:site_name' => false, | |
'og:image' => false, | |
'og:description' => false, | |
); | |
/** | |
* Map for CakePHP's config.language with Facebook's locale code | |
* used by _getLocale method. | |
* | |
* Since they can not be associated one to one, | |
* these values are supposed to be overwritten by Configure::write('OGPlocales'). | |
**/ | |
public $locales = array( | |
'ja' => 'ja_JP', | |
'en' => 'en_US', | |
); | |
/** | |
* Namespace attributes for OGP and Facebook | |
**/ | |
protected $_ns = ' xmlns:og="http://ogp.me/ns#" xmlns:fb="http://www.facebook.com/2008/fbml"'; | |
/** | |
* Map used to correct irregular property names | |
**/ | |
protected $_propertyNames = array( | |
'admin' => 'fb:admins', | |
'admins' => 'fb:admins', | |
'app_id' => 'fb:app_id', | |
'appid' => 'fb:app_id', | |
'appId' => 'fb:app_id', | |
'appID' => 'fb:app_id', | |
'url' => 'og:url', | |
'URL' => 'og:url', | |
'type' => 'og:type', | |
'title' => 'og:title', | |
'locale' => 'og:locale', | |
'site_name' => 'og:site_name', | |
'sitename' => 'og:site_name', | |
'siteName' => 'og:site_name', | |
'image' => 'og:image', | |
'img' => 'og:image', | |
'description' => 'og:description', | |
); | |
protected $_properties = array(); | |
public function __construct() { | |
$this->view =& ClassRegistry::getObject('view'); | |
} | |
/** | |
* Sets default values | |
**/ | |
public function beforeRender() { | |
$this->defaults = $this->_normalize($this->defaults); | |
$conf = $this->_normalize(Configure::read($this->configName)); | |
$viewVar = $this->_normalize($this->view->getVar($this->varName)); | |
$ogp = array_merge($this->defaults, $conf, $viewVar); | |
if ($this->autoGetUrl && empty($ogp['og:url'])) { | |
$ogp['og:url'] = $this->url(null, true); | |
} | |
if ($this->autoGetTitle && empty($ogp['og:title'])) { | |
$ogp['og:title'] = $this->_getTitle(); | |
} | |
if ($this->autoGetSiteName && empty($ogp['og:site_name'])) { | |
$ogp['og:site_name'] = $this->_getSiteName(); | |
} | |
if ($this->autoGetLocale && empty($ogp['og:locale'])) { | |
$ogp['og:locale'] = $this->_getLocale(); | |
} | |
foreach ($ogp as $property => $content) { | |
$this->set($property, $content); | |
} | |
} | |
/** | |
* Sets a meta tag in the view layout's <head /> | |
**/ | |
public function set($property, $content = false, $id = false) { | |
if ($property) { | |
$property = $this->_getPropertyName($property); | |
if (empty($this->_properties[$property]) || !in_array($content, $this->_properties[$property])) { | |
if ($id === false) { | |
if ($property == 'og:image' && isset($this->_properties['og:image'])) { | |
$id = count($this->_properties['og:image']); | |
} else { | |
$id = 0; | |
} | |
} | |
if ($content === false) { | |
return $this->delete($property, $id); | |
} else { | |
$propertyId = $this->_getPropertyId($property, $id); | |
$meta = $this->Html->meta(null, null, array( | |
'property' => $property, | |
'content' => $content, | |
'inline' => true, | |
)); | |
$this->view->addScript($propertyId, $meta); | |
$this->_properties[$property][$id] = $meta; | |
} | |
} | |
} | |
return $content; | |
} | |
/** | |
* Deletes a tag associated with property name and the ID. | |
**/ | |
public function delete($property, $id = 0) { | |
if ($property) { | |
$property = $this->_getPropertyName($property); | |
$propertyId = $this->_getPropertyId($property, $id); | |
if (isset($this->view->__scripts[$propertyId], $this->_properties[$property][$id])) { | |
unset($this->view->__scripts[$propertyId]); | |
unset($this->_properties[$property][$id]); | |
return true; | |
} | |
} | |
return false; | |
} | |
/** | |
* Returns namespace attributes for OGP and Facebook. | |
**/ | |
public function ns() { | |
return $this->_ns; | |
} | |
/** | |
* Normalizes property names set as keys of an array | |
**/ | |
protected function _normalize($ogp) { | |
if (is_array($ogp)) { | |
foreach ($ogp as $property => $content) { | |
$propertyName = $this->_getPropertyName($property); | |
if ($propertyName != $property) { | |
unset($ogp[$property]); | |
$ogp[$propertyName] = $content; | |
} | |
} | |
} else { | |
$ogp = array(); | |
} | |
return $ogp; | |
} | |
/** | |
* Returns a proper property name for an OGP tag | |
**/ | |
protected function _getPropertyName($property) { | |
if (isset($this->_propertyNames[$property])) { | |
$property = $this->_propertyNames[$property]; | |
} | |
return $property; | |
} | |
/** | |
* Returns the $title_for_layout for the current view | |
**/ | |
protected function _getTitle() { | |
if (!$title = $this->view->getVar('title_for_layout')) { | |
$title = Inflector::humanize($this->view->viewPath); | |
} | |
if ($title) { | |
return __($title, true); | |
} else { | |
return false; | |
} | |
} | |
/** | |
* Returns the <title /> in the layout | |
**/ | |
protected function _getSiteName() { | |
$layoutFileName = $this->view->_getLayoutFileName(); | |
if ($layoutFileName && file_exists($layoutFileName)) { | |
if ($layout = file_get_contents($layoutFileName)) { | |
$layout = str_replace(array("\r", "\n"), ' ', $layout); | |
$pattern = '#<title.*?>(.+?)</title.*?>#i'; | |
preg_match($pattern, $layout, $matches); | |
$title = $matches[1]; | |
$pattern = '/<\?(?:php|=)(.*?)\?>/i'; | |
preg_match_all($pattern, $title, $codes); | |
if (is_array($codes[1]) && $codes[1]) { | |
$debug = Configure::read('debug'); | |
Configure::write('debug', 0); | |
foreach ($codes[1] as $key => $code) { | |
ob_start(); | |
eval($code); | |
$result = ob_get_contents(); | |
ob_end_clean(); | |
$title = str_replace($codes[0][$key], $result, $title); | |
} | |
Configure::write('debug', $debug); | |
} | |
$title = trim($title); | |
if ($title) { | |
return $title; | |
} | |
} | |
} | |
return false; | |
} | |
/** | |
* Returns a Facebook locale code associated with Config.language | |
**/ | |
protected function _getLocale() { | |
if (!class_exists('L10n')) { | |
App::import('Core', 'l10n'); | |
} | |
$l10n = new L10n; | |
$l10n->get(); | |
$language = Configure::read('Config.language'); | |
if (!empty($_SESSION['Config']['language'])) { | |
$language = $_SESSION['Config']['language']; | |
} | |
if ($language) { | |
$this->locales = array_merge($this->locales, (array)Configure::read($this->configLocaleName)); | |
if (!empty($this->locales[$language])) { | |
return $this->locales[$language]; | |
} | |
} | |
return false; | |
} | |
/** | |
* Creates a property ID string | |
**/ | |
protected function _getPropertyId($property, $id) { | |
return $property . '-' . $id; | |
} | |
} |
利用しているサービス
OGP Helper は先日公開したこのサービスで使っています。
もう終了したけどな。
次の人
CakePHP Advent Calendar 2011
明日は @papettoTV さんですお楽しみに!