Help us understand the problem. What is going on with this article?

インスパイヤされて掲示板を作りたくなった(4)

More than 3 years have passed since last update.

せっかくだから俺は形から入るぜ!

スレ 内容
インスパイヤされて掲示板を作りたくなった(1) PHPとComposer、あとべんりなライブラリ
インスパイヤされて掲示板を作りたくなった(2) SQLite3のスキーマ設計とTwigテンプレート
インスパイヤされて掲示板を作りたくなった(3) ルーティングとモデルの説明
インスパイヤされて掲示板を作りたくなった(4) ← イマココ

せっかくだけどドキュメントを生成したくなった

毎度のことながらライブラリを入れました。今回はApiGen | Smart and Readable Documentation for your PHP projectです。

例によってコマンドラインからコマンドを実行しよう。

composer require --dev apigen/apigen

次に設定ファイルを用意します。

apigen.neon
source: src
destination: docs
main: InspireBBS
title: いんすぱいやーBBS
download: no
php: yes
tree: yes

で、次はcomposer.jsonにちょっと細工をしてみます。

composer.json
{
    "autoload": {
        "files": ["src/functions.php"],
        "psr-4": {
            "InspireBBS\\": "src/"
        }
    },
    "require": {
        "vlucas/phpdotenv": "^2.2",
        "twig/twig": "^1.24",
        "twig/extensions": "^1.3",
        "zonuexe/simple-routing": "^0.5.0",
        "zonuexe/objectsystem": "^0.5.3",
        "zonuexe/tetosql": "^0.0.1",
        "apigen/apigen": "^4.1"
    },
    "require-dev": {
        "filp/whoops": "^2.0"
    },
    "scripts": {
        "doc": "apigen generate"
    }
}

一番下の"scripts""doc"のあたりがポイント。

で、コマンドラインでcomposer docを実行してみるんじゃ。

> apigen generate
Scanning sources and parsing
Found 1 classes, 0 constants and 1 functions
Generating API documentation
  0 %
 11 %
 22 %
 33 %
 44 %
 55 %
 66 %
 77 %
 88 %
100 % - Finished!

するとどうでしょう、docsディレクトリの中に、なにやらファイルが増えてるじゃないですか。

スクリーンショット 2016-02-23 1.24.38.png

スクリーンショット 2016-02-23 1.24.55.png

カッコイイドキュメントが生成されたぞー。

前回用意したBoard.phpの上のコメント

    /**
     * @param  string $id
     * @return Board|false
     */
    public static function find($id)
    {

↑ これをいい感じに読み取って綺麗なドキュメントを生成してくれるスゴイやつだよ

これをPHPDocって呼ぶんだけど、詳細はWEB+DB PRESS Vol.87に書いたから近所の本屋かAmazonで取り寄せて読んでくれよな! (宣伝)

自分が書いたコードがわかりやすいドキュメントとして形になると仕事した気持ちになるので、形から入るのも案外ばかにしたもんじゃないよ ヾ(〃><)ノ゙

ModelとTypedProperty

さて、前回の記事では唐突に実装されたBoard.phpを投下していったので、これを順に説明していきます。

final class Board
{
    // ↓ これはトレイト
    use \Teto\Object\TypedProperty;

    // プロパティと型の対応表
    private static $property_types = [
        'id'    => 'string',
        'name'  => 'string',
        'text'  => 'string',
    ];

Teto\Object\TypedProperty@tadsan氏謹製のライブラリzonuexe/objectsystemに含まれるトレイトで、クラスの$property_typesにプロパティ名と型名orクラス名を書いてやると型検査を強制することができます。謹製は謙譲語。

TypedPropertyのちょっと詳しい仕様はpoem.ja.mdに書きました。どのような理窟で実現されてるのかはprivate/protectedなプロパティを外部から読み込み可能にするの本文とコメントのを読んでいただけるとよろしいかとー。

さておき、これで$board->idとか$board->nameみたいな感じでオブジェクトにアクセスできるようになったわけです。

SQL

これな。

    /**
     * @return Board[]
     */
    public static function findAll()
    {
        $data = SQL\Query::execute(db(), self::findAll_query, [])
            ->fetchAll(\PDO::FETCH_ASSOC) ?: [];

        $boards = [];
        foreach ($data as $b) {
            $boards[] = new Board($b);
        }

        return $boards;
    }
    const findAll_query = '
        SELECT `id`, `name`, `text` FROM `boards`
    ';
    // ↑ SQL文を定数として持つ

順に説明すると、static functionこれは$obj->hoge()みたいな形式じゃなくてBoard::hoge()みたいな形式でメソッド呼び出しできるようにします。このようなメソッドをPHPでは「静的メソッド」と呼びます。Rubyの「クラスメソッド」とだいたい同じようなものです。

次にSQL\Query::execute、これはzonuexe/tetosqlのメソッドです。

このライブラリの特徴は以下の通りです。

  • PDOのラッパーで、同じような感覚で利用できる
  • SQL文をクラス定数(const)で持つスタイルを推奨し、実行時に不意に加工されることを防ぐ
  • SQLインジェクションの耐性を持つ
    • 変数の型をクエリ内部に明示的に持つ
  • PDOが苦手とする可変個のIN句に対応する
    • :values@string[]のように記述することで「文字列の配列」を意味する
  • 特定のSQLに依存しない
    • もともとはMySQLで利用するために書かれたが、SQLiteでも動く
  • 現在のところ対応してないもの
    • 64ビット符号付き整数以外の数値型
  • アノテーションやメタプログラミングに依存しない
    • 特に必要ない

そんな感じで、現在PDOに依存してる箇所なら割と簡単に移行できて、安全性が高められるんじゃないかなー、と。もちろん制約もあるんですけど。

もう一個メソッドを定義してみる

現在あるのはレコードを全部取得するfindAll()なので、特定のIDの板だけ取得するfind($id)を実装してみよう。

    /**
     * @param  string $id
     * @return Board|false
     */
    public static function find($id)
    {
        $data = SQL\Query::execute(db(), self::find_query, [
        // 連想配列でクエリに当て嵌める値を代入
            ':id' => $id
        ])->fetch(\PDO::FETCH_ASSOC);

        if ($data === false) {
            return false;
        }

        return new BoardModel($data);
    }
    const find_query = '
        SELECT `id`, `name`, `text` FROM `boards` WHERE `id` = :id@string
    ';

こんな感じっすかね。

本日のまとめ

こんな感じって、どうでもいい周り道と前回までの説明しかしてない気がしますね>< こんなことでは完成はいつになることやら…

たぶんこの記事、読むだけじゃ得られることあんまりないので、自分で手を動かしてみるのが良いのではー

tadsan
僕に警備する自宅をください。Emacs初心者。Rubyist。 全ての投稿された記事は別段の表記がない限りはCC 3.0 BY-SA https://creativecommons.org/licenses/by-sa/3.0/deed.ja で二次利用できます。 記事中に含まれる全てのコードスニペットの著作権は抛棄するので、煮るなり焼くなりお好きにどうぞ。
https://tadsan.github.io/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした