WebAPIの扱いに慣れたらCodeIgniterでAPIサーバも作って、好きなモノを作ろう!

merry-xmas

太平洋時間でメリークリスマス、Frog Advent Calendar 2016 24日目 chan_gamiです。今年はドラゴンのケーキを買いました。

同Advent Calendarの23日目、Hidetoさんが【アメコミ】マーベルAPIでカッコいいサイトを作ろう!という記事を書いてくださいました。

Hidetoさんの記事内容と合わせてAPIアプリケーションごと自分で作ってしまえば自由度がもっともっと広がるので、この機会に挑戦してみましょう。

CodeIgniterとバンクーバーの関係

CodeIgniterはPHPのWeb Frameworkの一つで、オープンソースプロジェクトとして公開されています。

CodeIgniter Web Framework
bcit-ci/CodeIgniter: Open Source PHP Framework (originally from EllisLab)

さて、LICENSEにご注目ください。

Copyright (c) 2014 – 2016, British Columbia Institute of Technology

引用: CodeIgniter/license.txt at master · bcit-ci/CodeIgniter

このBCITというのはカナダのブリティッシュコロンビア州立大学で、バンクーバーの隣りにあるバーナビーに本部をおいています。もちろんバンクーバー・ダウンタウンにもキャンパスがあります。今回Advent Calendarに参加してくださった方の中にもこの学校を卒業されている方が何名かいらっしゃいます。

2ヶ月ほど前からPHPに触れはじめて最近CodeIgniterを触りだしたので、ライセンスを読んだときはびっくりしました。

CodeIgniterプロジェクトの作成

CodeIgniterは上に記述した通りPHPのFrameworkなので、PHPは必須です。XAMPPとかMAMPの環境があれば大丈夫ですが、Composerを使用する場合はPHPバージョン5.3.2以上でないと動作しませんのでご注意ください。

Composerってなに?

Composer
ComposerはPHPのパッケージ管理システムです。他の言語でいうと、Node.jsのnpm、Rubyのgem、Pythonのpipです。便利なので利用して楽しましょう。

macOSユーザであれば、HomebrewからComposerをインストールすることが可能です。

brew install composer composer-completion

昔Homebrewを入れていて、その後にOSアップデートを当てた場合はおそらくエラーが出るかと思います。
ので、エラー解決の手助けになるリンクを置いておきます。mac でbrew がおかしくなった(brew updateができない)のを解決 – Qiita

Composerを使う場合

kenjis/codeigniter-composer-installer: Installs the offical CodeIgniter 3 with secure folder structure via Composer
こちらの非常に便利なインストーラーが利用できます。

api-applicationの部分はプロジェクト名に置き換えてください。

composer create-project kenjis/codeigniter-composer-installer api-application
cd api-application
composer install

そして先のリポジトリのREADME.mdにあるようにapplication/config/config.phpを以下のように書き換えます。

$config['composer_autoload'] = FALSE;
↓
$config['composer_autoload'] = realpath(APPPATH . '../vendor/autoload.php');

$config['index_page'] = 'index.php';
↓
$config['index_page'] = '';

正常にインストールされているか確認してみましょう。

bin/server.sh
open http://localhost:8000

ci-running

無事表示されればComposerを利用した開発の準備は終わりです。

Composerを使わない場合

公式からダウンロードします。

ci-download

解凍してファイルを展開したあとフォルダ名をお好きなプロジェクト名に変更し、コマンドラインで解凍したフォルダに移動して

php -S 127.0.0.1:8000

エラーなく動き出したようであればブラウザでhttp://localhost:8000にアクセスします。

ci-running

無事表示されれば開発の準備は終わりです。

アプリケーションの動きを把握する

まずはアプリケーションの動きを把握しましょう。

先ほどhttp://localhost:8000にアクセスした際に表示されていたファイルはapplication/views/welcome_message.phpです。

次に、これを表示するようにしているのがapplication/controllers/Welcome.phpの以下の部分です。

    public function index()
    {
        $this->load->view('welcome_message');
    }

そして、このWelcome.phpを呼び出しているのはapplication/config/routes.phpの以下の部分です。

$route['default_controller'] = 'welcome';

つまり、アプリケーションの動作を新たに作りたいときはまずこのroutes.phpで定義する必要があるということだけ覚えておいてください。

モデルを実装する

今回モデル・ビュー・コントローラーなどの説明はしませんが、ご存じない方はモデルというものはデータベースのすぐ近くの存在でデータを司る者、それ以外の処理は行わない者、というくらいの認識で大丈夫です。

ではアプリケーションの実装を行っていきます。

application/models配下にFrog_advent_calendar.phpを作成して、以下を記述します。

<?php

//Frog_advent_calendar.php

class Frog_advent_calendar_model extends CI_Model
{
    private $data = [
        ["id" => 1, "date" => "2016-12-01", "author" => "chan_gami", "url" => "http://www.changami.com/2016/12/frog-advent-calendar-greeting/"],
        ["id" => 2, "date" => "2016-12-02", "author" => "Senna", "url" => "https://frogagent.com/job-placement/how-to-pick-a-good-school/"],
        ["id" => 3, "date" => "2016-12-03", "author" => "chan_gami", "url" => "http://www.changami.com/2016/12/board-game-for-raincouver/"],
        ["id" => 4, "date" => "2016-12-04", "author" => "ellekasai", "url" => "https://note.mu/ellekasai/n/n1d6a6c77b8be"],
        ["id" => 5, "date" => "2016-12-05", "author" => "Kay", "url" => "http://blog.kaffeekantate.xyz/enjoy-cafe-and-coffee-in-vancouver/"],
        ["id" => 6, "date" => "2016-12-06", "author" => "ellekasai", "url" => "https://note.mu/ellekasai/n/nc361452b245e"],
        ["id" => 7, "date" => "2016-12-07", "author" => "chan_gami", "url" => ""],
        ["id" => 8, "date" => "2016-12-08", "author" => "Toru", "url" => "http://loudandproud.me/nikkei/"],
        ["id" => 9, "date" => "2016-12-09", "author" => "Julia", "url" => "http://lilyfolio.com/lilypress/2016/12/loss-in-vancouver/"],
        ["id" => 10, "date" => "2016-12-10", "author" => "Leo", "url" => "http://unique-experience.xyz/?p=2722"],
        ["id" => 11, "date" => "2016-12-11", "author" => "ellekasai", "url" => "https://note.mu/ellekasai/n/n62e262e44499"],
        ["id" => 12, "date" => "2016-12-12", "author" => "MAIMAI", "url" => "https://note.mu/mm72862444/n/n1a6805bc2ef7"],
        ["id" => 13, "date" => "2016-12-13", "author" => "Hideto", "url" => "http://webdev-bodymake.com/bodymake-in-vancouver/"],
        ["id" => 14, "date" => "2016-12-14", "author" => "Mao", "url" => "https://frogagent.com/schoolinfo/tips_for_a_better_school_life_in_vancouver/"],
        ["id" => 15, "date" => "2016-12-15", "author" => "sjntn", "url" => "http://blog.sjntn.com/Entry/119/"],
        ["id" => 16, "date" => "2016-12-16", "author" => "ginpei", "url" => "http://ginpen.com/2016/12/16/slack-transportation-bot/"],
        ["id" => 17, "date" => "2016-12-17", "author" => "chan_gami", "url" => "http://www.changami.com/2016/12/introduction-of-apache-cordova/"],
        ["id" => 18, "date" => "2016-12-18", "author" => "Saaya", "url" => "http://catchdesign.tumblr.com/post/154625302290/%E3%83%90%E3%83%B3%E3%82%AF%E3%83%BC%E3%83%90%E3%83%BC%E3%81%A7%E3%83%95%E3%82%A1%E3%82%B9%E3%83%88%E3%83%97%E3%83%AD%E3%83%88%E3%82%BF%E3%82%A4%E3%83%94%E3%83%B3%E3%82%B0"],
        ["id" => 19, "date" => "2016-12-19", "author" => "Leo", "url" => "http://unique-experience.xyz/?p=2702"],
        ["id" => 20, "date" => "2016-12-20", "author" => "Hitomi", "url" => "https://frogagent.com/vancouver/20161220van_design/"],
        ["id" => 21, "date" => "2016-12-21", "author" => "suige", "url" => "https://suige.github.io/2016/12/wordpress-timezone/"],
        ["id" => 22, "date" => "2016-12-22", "author" => "Kay", "url" => "https://blog.kaffeekantate.xyz/design-agency-workflow-vancouver/"],
        ["id" => 23, "date" => "2016-12-23", "author" => "Hideto", "url" => "http://webdev-bodymake.com/marvel-api/"],
        ["id" => 24, "date" => "2016-12-24", "author" => "chan_gami", "url" => ""],
        ["id" => 25, "date" => "2016-12-25", "author" => "Senna", "url" => ""],
    ];

    public function find_all()
    {
        return $this->data;
    }

    public function find_by_id($id)
    {
        return $id > 0 && $id <= count($this->data) ? $this->data[$id - 1] : new stdClass();
    }
}

今回はデータベースとの接続を想定していないのでdataの値を直書きしています。本来は$this->db->...といった形でデータベースの値を取得して返すような動きが期待されます。

これでモデルの実装は終わりです。

REST APIを実装する

chriskacerguis/codeigniter-restserver: A fully RESTful server implementation for CodeIgniter using one library, one config file and one controller.

codeigniter-restserverという便利なものがあるのでそれを利用します。

ci-restserver-download

ダウンロードしたファイルの中から、これらのファイルをプロジェクトフォルダに入れてください。

  • application/config/rest.php
  • application/language/english/rest_controller_lang.php
  • application/libraries/Format.php
  • application/libraries/REST_Controller.php

そして今回のAPIを実装するapplication/controllers/Rest_frog_advent_calendar.phpを作成して、以下を記述します。

<?php
//Rest_frog_advent_calendar.php

require APPPATH . '/libraries/REST_Controller.php';

class Rest_frog_advent_calendar extends REST_Controller
{
    function __construct()
    {
        parent::__construct();
    }

    public function list_get()
    {
        $this->load->model('Frog_advent_calendar_model');
        $data = $this->Frog_advent_calendar_model->find_all();
        if (!empty($data)) {
            $this->set_response([
                'status' => REST_Controller::HTTP_OK,
                'message' => 'success',
                'data' => $data,
            ], REST_Controller::HTTP_OK);
        } else {
            $this->set_response([
                'status' => REST_Controller::HTTP_INTERNAL_SERVER_ERROR,
                'message' => 'error',
                'data' => new stdClass(),
            ], REST_Controller::HTTP_INTERNAL_SERVER_ERROR);
        }
    }

    public function find_by_get($id)
    {
        $this->load->model('Frog_advent_calendar_model');
        $data = $this->Frog_advent_calendar_model->find_by_id($id);
        if (!empty($data)) {
            $this->set_response([
                'status' => REST_Controller::HTTP_OK,
                'message' => 'success',
                'data' => $data,
            ], REST_Controller::HTTP_OK);
        } else {
            $this->set_response([
                'status' => REST_Controller::HTTP_INTERNAL_SERVER_ERROR,
                'message' => 'error',
                'data' => new stdClass(),
            ], REST_Controller::HTTP_INTERNAL_SERVER_ERROR);
        }
    }
}

そして最後に、application/config/routes.phpに以下を追記します。

//routes.php
$route['v1/frog_advent_calendar']['GET'] = 'rest_frog_advent_calendar/list';
$route['v1/frog_advent_calendar/(:num)']['GET'] = 'rest_frog_advent_calendar/find_by/$1';

これでアプリケーションの一連の動きの実装が終わりました。

動かしてみる

実装を終えたら動作を確認してみます。

CodeIgniterプロジェクトの作成で行った動作確認と同じ方法でアプリケーションを起動してください。そして、

curl -i -X GET http://localhost:8000/v1/frog_advent_calendar

またはブラウザからhttp://localhost:8000/v1/frog_advent_calendarへアクセスします。

{"status":200,"message":"success","data":[{"id":1,"date":"2016-12-01","author":"chan_gami","url":"http:\/\/www.changami.com\/2016\/12\/frog-advent-calendar-greeting\/"},{"id":2,"date":"2016-12-02","author":"Senna","url":"https:\/\/frogagent.com\/job-placement\/how-to-pick-a-good-school\/"},{"id":3,"date":"2016-12-03","author":"chan_gami","url":"http:\/\/www.changami.com\/2016\/12\/board-game-for-raincouver\/"},{"id":4,"date":"2016-12-04","author":"ellekasai","url":"https:\/\/note.mu\/ellekasai\/n\/n1d6a6c77b8be"},{"id":5,"date":"2016-12-05","author":"Kay","url":"http:\/\/blog.kaffeekantate.xyz\/enjoy-cafe-and-coffee-in-vancouver\/"},{"id":6,"date":"2016-12-06","author":"ellekasai","url":"https:\/\/note.mu\/ellekasai\/n\/nc361452b245e"},{"id":7,"date":"2016-12-07","author":"chan_gami","url":""},{"id":8,"date":"2016-12-08","author":"Toru","url":"http:\/\/loudandproud.me\/nikkei\/"},{"id":9,"date":"2016-12-09","author":"Julia","url":"http:\/\/lilyfolio.com\/lilypress\/2016\/12\/loss-in-vancouver\/"},{"id":10,"date":"2016-12-10","author":"Leo","url":"http:\/\/unique-experience.xyz\/?p=2722"},{"id":11,"date":"2016-12-11","author":"ellekasai","url":"https:\/\/note.mu\/ellekasai\/n\/n62e262e44499"},{"id":12,"date":"2016-12-12","author":"MAIMAI","url":"https:\/\/note.mu\/mm72862444\/n\/n1a6805bc2ef7"},{"id":13,"date":"2016-12-13","author":"Hideto","url":"http:\/\/webdev-bodymake.com\/bodymake-in-vancouver\/"},{"id":14,"date":"2016-12-14","author":"Mao","url":"https:\/\/frogagent.com\/schoolinfo\/tips_for_a_better_school_life_in_vancouver\/"},{"id":15,"date":"2016-12-15","author":"sjntn","url":"http:\/\/blog.sjntn.com\/Entry\/119\/"},{"id":16,"date":"2016-12-16","author":"ginpei","url":"http:\/\/ginpen.com\/2016\/12\/16\/slack-transportation-bot\/"},{"id":17,"date":"2016-12-17","author":"chan_gami","url":"http:\/\/www.changami.com\/2016\/12\/introduction-of-apache-cordova\/"},{"id":18,"date":"2016-12-18","author":"Saaya","url":"http:\/\/catchdesign.tumblr.com\/post\/154625302290\/%E3%83%90%E3%83%B3%E3%82%AF%E3%83%BC%E3%83%90%E3%83%BC%E3%81%A7%E3%83%95%E3%82%A1%E3%82%B9%E3%83%88%E3%83%97%E3%83%AD%E3%83%88%E3%82%BF%E3%82%A4%E3%83%94%E3%83%B3%E3%82%B0"},{"id":19,"date":"2016-12-19","author":"Leo","url":"http:\/\/unique-experience.xyz\/?p=2702"},{"id":20,"date":"2016-12-20","author":"Hitomi","url":"https:\/\/frogagent.com\/vancouver\/20161220van_design\/"},{"id":21,"date":"2016-12-21","author":"suige","url":"https:\/\/suige.github.io\/2016\/12\/wordpress-timezone\/"},{"id":22,"date":"2016-12-22","author":"Kay","url":"https:\/\/blog.kaffeekantate.xyz\/design-agency-workflow-vancouver\/"},{"id":23,"date":"2016-12-23","author":"Hideto","url":"http:\/\/webdev-bodymake.com\/marvel-api\/"},{"id":24,"date":"2016-12-24","author":"chan_gami","url":""},{"id":25,"date":"2016-12-25","author":"Senna","url":""}]}

このような結果が得られていれば成功です。また、routes.phpで指定しているようにidごとに取得も可能です。

curl -i -X GET http://localhost:8000/v1/frog_advent_calendar/1

またはブラウザからhttp://localhost:8000/v1/frog_advent_calendar/1へアクセスします。

{"status":200,"message":"success","data":{"id":1,"date":"2016-12-01","author":"chan_gami","url":"http:\/\/www.changami.com\/2016\/12\/frog-advent-calendar-greeting\/"}}

今後の実装について

今回はAPIアプリケーションの最低限の動きだけ実装しました。これをきっかけに面白さを感じた方は今後も開発を続けていただきたいと思います。

作りたいものはできたけど、今後の実装はどういうところから始めればいいのかわからない。そんな方はまずはこの辺りを考えてみていただければと思います。

  • データベースの利用
  • APIのURL
  • APIのメッセージ設計
  • データの管理
  • ログイン・ログアウトの管理
  • モデルの抽象化(ベースモデルを作る、など)

では皆様、良いお年をお迎えください!
ありがとうございました。