BEAR.SundayでParse.comをラップしてAPIアプリを作る

  • 7
    いいね
  • 8
    コメント
この記事は最終更新日から1年以上が経過しています。

はじめに

最近、以下の構成で趣味プロダクトを作ろうとしています。

  • バックエンドには Parse.com を利用
  • BEAR.Sunday アプリで Parse.com と連携して単純な CRUD 以外の API を実装
  • フロントエンドは AngularJS

Parse.com はデータストアと REST API を提供してくれるクラウドサービス(BaaS)です。
月 100 万リクエストぐらいまで無料らしいので、小規模な趣味プロダクトなら十分無料で使えそうです。

この記事では、BEAR.Sunday アプリで Parse.com をラップして独自の API を提供する方法を紹介したいと思います。

Parse.com は Cloud Code という機能でサーバサイドの処理を拡張できるらしいので、それを使えば BEAR.Sunday アプリを自分でホスティングする必要はなくなりそうですが、今回は BEAR.Sunday の勉強も兼ねているのでこのような構成で作ります。

Parse.com にアプリを準備

  1. Parse.com に サインアップ
  2. アプリを作成
  3. Data Browser の + Add Class ボタンからとりあえず User クラスを追加
  4. Add a row ボタンからユーザーのレコードを 1 つ追加

parse01.png

BEAR.Sunday プロジェクトを作成する

次に、BEAR.Sunday アプリのプロジェクトを作成しましょう。composer で簡単に作成できます。

$ composer create-project bear/skeleton My.App
$ cd My.App
$ composer install

プロジェクト名は [ベンダ名].[アプリ名] と付けると名前空間とかをいい感じにしてくれます。ここでは My.App というプロジェクト名でインストールしています。

プロジェクトが作成できたら、Users アプリケーションリソースを作成しましょう。

src/Resource/App/Users.php
<?php
namespace My\App\Resource\App;

use BEAR\Resource\ResourceObject;

class Users extends ResourceObject
{
    public function onGet()
    {
        $this->body = 'test';

        return $this;
    }
}

仮に、ただ 'test' という文字列を返すだけの API としました。
実行してみましょう。

$ php bootstrap/contexts/api.php get "app://self/users"
200 OK
content-type: ["application\/hal+json; charset=UTF-8"]
cache-control: ["no-cache"]
date: ["Tue, 23 Dec 2014 10:13:28 GMT"]
[BODY]
test

動いてますね。

Parse.com の PHP 用 SDK を組み込む

PHP SDK には、Parse.com 公式の parse/php-sdk を使用します。

$ composer require parse/php-sdk

SDK の初期化に、Parse.com の

  • Application ID
  • REST API Key
  • Master Key

が必要です。ダッシュボードの [Settings] → [Keys] で確認してください。

SDK 初期化

この SDK の初期化処理を BEAR.Sunday のどこに書くべきか迷ったのですが、App クラスに初期化用のメソッドを追加して @PostConstruct アノテートすることでしっくりくる感じに書けました。

src/App.php
<?php

namespace My\App;

use BEAR\Package\Provide\Application\AbstractApp;
use Parse\ParseClient;
use Ray\Di\Di\PostConstruct;

final class App extends AbstractApp
{
    /**
     * @PostConstruct
     */
    public function onInit()
    {
        $appId = 'Application ID';
        $restKey = 'REST API Key';
        $masterKey = 'Master Key';

        ParseClient::initialize($appId, $restKey, $masterKey);
    }
}

Users アプリケーションリソースを修正

Users リソースを修正して実データを取得できるようにしましょう。

src/Resource/App/Users.php
<?php
namespace My\App\Resource\App;

use BEAR\Resource\Code;
use BEAR\Resource\ResourceObject;
use Parse\ParseException;
use Parse\ParseUser;

class Users extends ResourceObject
{
    public function onGet($objectId = null)
    {
        $query = ParseUser::query();

        if (!is_null($objectId)) {
            try {
                $query->get($objectId);
            } catch (ParseException $e) {
                $this['error'] = [
                    'code' => $e->getCode(),
                    'message' => $e->getMessage(),
                ];
                $this->code = Code::NOT_FOUND;

                return $this;
            }
        }

        $users = [];
        foreach ($query->find() as $user) {
            $users[] = json_decode($user->_encode(), true);
        }
        $this->body = $users;

        return $this;
    }
}

実行してみましょう。

$ php bootstrap/contexts/api.php get "app://self/users"
200 OK
content-type: ["application\/hal+json; charset=UTF-8"]
cache-control: ["no-cache"]
date: ["Tue, 23 Dec 2014 11:12:10 GMT"]
[BODY]
0 => array(
  objectId TeDsTS7UxB,
  createdAt => array(
    date 2014-12-23 10:04:40,
    timezone_type 2,
    timezone Z,
  ),
  updatedAt => array(
    date 2014-12-23 10:04:45,
    timezone_type 2,
    timezone Z,
  ),
  username test,
),

[VIEW]
{
    "0": {
        "objectId": "TeDsTS7UxB",
        "createdAt": {
            "date": "2014-12-23 10:04:40",
            "timezone_type": 2,
            "timezone": "Z"
        },
        "updatedAt": {
            "date": "2014-12-23 10:04:45",
            "timezone_type": 2,
            "timezone": "Z"
        },
        "username": "test"
    },
    "_links": {
        "self": {
            "href": "http://localhost/app/users/"
        }
    }
}

ちゃんと取得できました!

ユーザの作成も出来るようにする

Users リソースに onPost() を追加して、ユーザの作成も出来るようにしてみましょう。

    public function onPost($username, $password, $email)
    {
        $user = new ParseUser();
        $user->set('username', $username);
        $user->set('password', $password);
        $user->set('email', $email);

        try {
            $user->signUp();
            $this->body = json_decode($user->_encode(), true);

        } catch (ParseException $e) {
            $this['error'] = [
                'code' => $e->getCode(),
                'message' => $e->getMessage(),
            ];
            $this->code = Code::BAD_REQUEST;
        }

        return $this;
    }

作成してみる。

$ php bootstrap/contexts/api.php post "app://self/users?username=name&password=pass&email=user@test.com"
200 OK
content-type: ["application\/hal+json; charset=UTF-8"]
cache-control: ["no-cache"]
date: ["Tue, 23 Dec 2014 11:23:56 GMT"]
[BODY]
objectId NhOZYYKoBC,
createdAt => array(
  date 2014-12-23 11:23:56,
  timezone_type 2,
  timezone Z,
),
updatedAt => array(
  date 2014-12-23 11:23:56,
  timezone_type 2,
  timezone Z,
),
username name,
email user@test.com,

[VIEW]
{
    "objectId": "NhOZYYKoBC",
    "createdAt": {
        "date": "2014-12-23 11:23:56",
        "timezone_type": 2,
        "timezone": "Z"
    },
    "updatedAt": {
        "date": "2014-12-23 11:23:56",
        "timezone_type": 2,
        "timezone": "Z"
    },
    "username": "name",
    "email": "user@test.com",
    "_links": {
        "self": {
            "href": "http://localhost/app/users/?username=name&password=pass&email=user%40test.com"
        }
    }
}

再度取得してみる。

$ php bootstrap/contexts/api.php get "app://self/users"
200 OK
content-type: ["application\/hal+json; charset=UTF-8"]
cache-control: ["no-cache"]
date: ["Tue, 23 Dec 2014 11:25:41 GMT"]
[BODY]
0 => array(
  objectId TeDsTS7UxB,
  createdAt => array(
    date 2014-12-23 10:04:40,
    timezone_type 2,
    timezone Z,
  ),
  updatedAt => array(
    date 2014-12-23 10:04:45,
    timezone_type 2,
    timezone Z,
  ),
  username test,
),
1 => array(
  objectId NhOZYYKoBC,
  createdAt => array(
    date 2014-12-23 11:23:56,
    timezone_type 2,
    timezone Z,
  ),
  updatedAt => array(
    date 2014-12-23 11:23:56,
    timezone_type 2,
    timezone Z,
  ),
  email user@test.com,
  username name,
),

[VIEW]
{
    "0": {
        "objectId": "TeDsTS7UxB",
        "createdAt": {
            "date": "2014-12-23 10:04:40",
            "timezone_type": 2,
            "timezone": "Z"
        },
        "updatedAt": {
            "date": "2014-12-23 10:04:45",
            "timezone_type": 2,
            "timezone": "Z"
        },
        "username": "test"
    },
    "1": {
        "objectId": "NhOZYYKoBC",
        "createdAt": {
            "date": "2014-12-23 11:23:56",
            "timezone_type": 2,
            "timezone": "Z"
        },
        "updatedAt": {
            "date": "2014-12-23 11:23:56",
            "timezone_type": 2,
            "timezone": "Z"
        },
        "email": "user@test.com",
        "username": "name"
    },
    "_links": {
        "self": {
            "href": "http://localhost/app/users/"
        }
    }
}

ちゃんと作成されてますね!

一応ダッシュボードでも確認。ユーザがちゃんと増えてます。

parse02.png

まとめ

ここまで出来たらあとは BEAR.Sunday 側で Parse.com のデータを加工して返してくれる API とか、関連するデータを一括で変更する API とか、いろいろ簡単に作れそうですね!

BaaS が提供してくれる API では基本的な CRUD しかできないので、基本的な処理を BEAR.Sunday 側で実装しておけば、クライアント側はほぼビューを用意するだけで良くなるんじゃないかと画策しています。(複数のクライアントアプリを簡単に作れる)

BEAR.Sunday も初心者だし Parse.com の SDK もまだ使い方をちゃんと理解できてないので、この記事で紹介したサンプルコードは完成度が低いと思いますが、少しでもお役に立てば幸いです。