• teruchiです。

    今日はFacebookのAPIで遊びましょう。

    今回はCakePHPを使ってサクっと作ってしまおうと思います。

    ポイントは、

    ・CakePHPを使う
    ・FacebookのPHP sdkを使う
    ・Facebookアプリの登録、設定

    のあたりでしょうか。

    完成形はこちら
    http://genieframelamp.com/

    (URLは今後変更になると思います)

    「Log In」をクリックして、Facebookでアプリの許可をすると
    リストのページへ飛びます。
    (自動で飛ばない場合は再読み込みしてください)

    検証環境はこんな感じ

    ・Mac OS X Lion (10.7.2)
    ・Apache 2.2.21 (MacPorts)
    ・PHP 5.3.8 (MacPorts)
    (こちらは無くても動くはず)
    ・MySQL 5.1.57 (MacPorts)

    さて、どんどん行きましょう。

    PHPのSDKがFacebookから出てますので、それを使います。

    ドキュメントはこちら
    https://developers.facebook.com/docs/reference/php/

    sdkのダウンロードはこちら
    https://github.com/facebook/php-sdk

    ダウンロードして解凍したフォルダの中にある src フォルダを、
    facebook とリネームして、app/vendors フォルダにコピーします。

    あとはCakePHPでControllerとViewを作成します。

    ControllerはRankingControllerとしておきましょう。

    <?php
    class RankingController extends AppController {
    	var $uses = array();
    	
    	function index() {
    		App::import('Vendor', 'facebook');
    
    		$facebook = new Facebook(array(
    			'appId'  => '261137913930180',
    			'secret' => 'df6442de267eb23f2d8aa0a26a061544',
    		));
    		
    		try {
    			if($facebook->api('/me')) {
    				$this->redirect(array('action' => 'view'));
    			}
    		} catch(FacebookApiException $e) {
    		}
    		
    		$this->set(compact('facebook'));
    	}
    	
    	function view($page=0) {
    		App::import('Vendor', 'facebook');
    
    		$facebook = new Facebook(array(
    			'appId'  => '261137913930180',
    			'secret' => 'df6442de267eb23f2d8aa0a26a061544',
    		));
    
    		if(!($facebook->getUser())) {
    			$this->redirect(array('action' => 'index'));
    		}
    
    		try {
    			$me = $facebook->api('/me');
    			
    			$offset = $page ? $page * 20 : 0;
    			
    			$likes = $facebook->api('/me/likes?limit=20&offset='.$offset.'&fields=id,name,picture,link,likes,category,website');
    		} catch(FacebookApiException $e) {
    			$this->redirect(array('action' => 'index'));
    		}
    		
    		$this->set(compact('page', 'me', 'likes'));
    	}
    }
    ?>
    

    ここでポイントはviewアクションでFacebook::api()を叩いてるところ。

    ドキュメントをみてもなかなか見つからないのですが、
    こことか
    http://developers.facebook.com/docs/reference/php/facebook-api/

    ここ
    http://developers.facebook.com/docs/reference/api/

    のあたりを読み込んで、いろいろ試してみたらこんな使い方が出来ることが判明。

    あとはViewです

    まずindex.ctp

    <div id="fb-root"></div>
    
    <script>
      window.fbAsyncInit = function() {
        FB.init({
          appId      : '<?php echo $facebook->getAppId(); ?>', // App ID
          status     : true, // check login status
          cookie     : true, // enable cookies to allow the server to access the session
          oauth      : true, // enable OAuth 2.0
          xfbml      : true  // parse XFBML
        });
    
    	FB.Event.subscribe('auth.login', function() {
    		window.location.reload();
    	});
      };
    
      (function(d){
         var js, id = 'facebook-jssdk'; if (d.getElementById(id)) {return;}
         js = d.createElement('script'); js.id = id; js.async = true;
         js.src = "//connect.facebook.net/en_US/all.js";
         d.getElementsByTagName('head')[0].appendChild(js);
       }(document));
    </script>
    
    <fb:login-button scope="user_likes"></fb:login-button>
    
    

    FacebookのJavascript APIを使って、初期化とログイン後にリロードするように設定しています。

    最後に一覧用のView。view.ctp

    <h2>Facebookでいいね!したリスト</h2>
    
    <h3>me</h3>
    
    <p>名前:<?= $me['name'] ?></p>
    
    <br>
    
    <h3>Likes</h3>
    
    <table>
    <tr>
    <th>category</th>
    <th>name</th>
    <th>image</th>
    <th>Facebook Page</th>
    <th>url</th>
    </tr>
    <? foreach($likes['data'] as $data) : ?>
    <tr>
    <td><?= $data['category'] ?></td>
    <td><?= $data['name'] ?></td>
    <td><img src="<?= $data['picture'] ?>"></td>
    <td><?= $data['link'] ?></td>
    <td><?= @$data['website'] ?></td>
    </tr>
    <? endforeach; ?>
    </table>
    
    <? if($page > 0) : ?>
    <?= $this->Html->link('前の20件', array($page-1)) ?> | 
    <? endif; ?>
    <?= $this->Html->link('次の20件', array($page+1)) ?>
    

    最大件数とかを取ってくる方法が分からなかったので、
    固定で20件づつ表示してます。

    今日はここまで

    Top

  • CakePHPでcondition付きのpaginate

    投稿 6 2011年7月 by teruchi in 開発日誌 with 0 comments


    Mac OS X Lion 早くこないかなー。
    teruchiです。

    (下記内容はCakePHP 1.3.7で確認)

    CakePHPのPginatorはModelが組み込まれてる状態なので、
    簡単に扱える反面、カスタマイズが難しい。

    なんかのリストをステータスごとに絞り込みたい時、
    社内用の管理画面であればこんな感じで書いてます。

    まずViewではPaginator::link()を使って

    Paginator->link('全て', array('status' => ''))?>
    Paginator->link('公開中', array('status' => 1))?>
    Paginator->link('公開停止中', array('status' => 2))?>
    Paginator->link('削除済み', array('status' => 9))?>

    としてあげます。
    これでページやソート条件が勝手にリンクにつくようになります。

    あとはコントローラー側で、

    $this->paginate = array('order' => array('created' => 'DESC'));

    // 検索条件
    if(!empty($this->params['named']['status'])) {
    $this->paginate['conditions']['status'] = $this->params['named']['status'];
    }

    $this->set('books', $this->paginate());

    な感じで書いてあげれば条件付きで検索されます。

    では今月も良いコードを。

    Top


  • 今度のガンダムは低年齢層向けらしいので、ガンダムUCの第4話を待ち望んでワクワクしとこうと思ってるteruchiです。

    今回もCakePHPネタです。

    前回、paginateの時のbindModel()について書きましたが、

    http://blog.genieframe.com/?p=721

    今回はModel::save()時のunbindModel()です。

    bindしてるModelをsaveしようとすると、カラム名にモデル名を付加しないとSQLエラーになることがよくあります。

    SQLをデバッグするとわかりますが、UPDATE文に関連モデルをjoinしていることがわかります。

    saveAll()とかで、これがうまい具合に効いてくるのだと思いますが、
    単純に1レコードだけ更新(もしくは追加)したいのに邪魔です。

    contain()を使いたくなりますが、save()の時には無関係のようです。
    (ソースを追っかけてないので想像)

    で、unbindModelを使えばこれがサクっと解決します。


    Book belongsTo Shop

    の状態であれば、

    $this->Book->unbindModel(array('belongsTo' => array('Shop')), false); // falseはいらない?

    $this->Book->save(array(
    'name' => 'ユニコーンの日(上) 機動戦士ガンダムUC(1)’,
    'auther' => '福井 晴敏',
    'publisher' => '角川文庫'));

    で行けます。

    ガンダムUCの第4話ではどうやらあのブライト艦長が出てくるようです。

    それでは、また来週もエンターキーで弾幕張りましょう。
    よい週末を。

    Top


  • ガンダムのアニメ新作が準備中ということでわくわくしながらキーを叩くteruchiです。

    CakePHPで、モデルをbelongsToとかで関連付けしてて、
    2階層目のモデルも一緒に引っ張ってきたい時ってありません?

    たとえば、

    Book belongsTo Shop
    Shop belongsTo City

    みたいにつながってる状態で、
    本(Book)を検索すると本屋さん(Shop)がある街の名前(City.name)も引っ張ってきたいなんてパターン。

    私はこんな感じでやってます。


    // 本モデルに本屋さんモデルを関連付け
    $this->Book->bindModel(array('belongsTo' => array('Shop')), false);

    // 本屋さんモデルに街モデルと店種別モデルを関連付け
    $this->Book->Shop->bindModel(array('belongsTo' => array('City', 'Type')), false);

    // paginateのオプションにcontainを使って取ってくる情報を絞る
    $this->paginate = array(
    'order' => 'Book.updated DESC',
    'contain' => array('Shop.id' => array('City.name', 'Type.name')));

    $this->set('books', $this->paginate());

    ちょっとよけいなのも付いてますが(w

    ポイントはbindModelの第2パラメーターにfalseを指定すること。
    これでpaginate()に処理が移っても関連付けが外れなくなります。

    あとcontainを使ってShop.idを渡すとこ。
    DBをフェッチする時の情報を減らします。(プチ減量)

    SQLをデバッグするとわかりますが、idの一覧を取ってきて、
    関連付け先は個別にSELECTしてくれます。(それも問題だけど。。。)

    一覧で使う場合はあまりおすすめできないかな。。。

    ちなみにapp_model.phpで

    var $actsAs = array('Containable');

    としてるのが前提です。(これ大事)

    それでは今日も良いコードを。

    Top