リモート開発メインのソフトウェア開発企業のエンジニアブログです

PHPの静的メソッド呼び出しと利用について

PHP, Laravelの話。

とあるプロジェクトで静的メソッド(staticメソッド)について学ぶ機会がありました。

本記事はPHP初学者、オブジェクト指向を学習中の方、staticメソッドを存じない方向けの内容です。

きっかけ

あるとき開発メンバーからLaravelのコードについて質問されました。

内容は特定のModelでDBからレコードを取得した後、そのModel内に書かれているメソッド呼び出す方法について。

先にModelからレコードを取得しているのでそのレコードの入った変数から直接メソッドを呼び出したいが出来なかったとのこと。(他の箇所で同じような取り方が出来ているので真似したいとのこと)

コード

Controllerには以下の処理が書かれている。(以下、コードは全て簡略化して書いてます)

$hoge_members = Hoge::where('type', 'member')->get();
$hoge = new Hoge;
$lang = 'jp';
$hoge_lang = $hoge->getLang($lang);

そのModelのメソッドは以下のようなシンプルなもの。

public function getLang ($lang=NULL) {
    if ($lang == 'jp') {
        return '日本語';
    } else if ($lang == 'en') {
        return '英語';
    }
    return 'その他';
}

これをControllerで以下のよう呼び出したかったがエラーになった。

$hoge_members = Hoge::where('type', 'member')->get();
$lang = 'jp';
$hoge_lang = $hoge_members->getLang($lang);

※ちなみに他の箇所で動いているコードはこのように書かれていた。

$hoge_member = Hoge::where('type', 'member')->first();
$lang = 'jp';
$hoge_lang = $hoge_member->getLang($lang);

動かす方法は分かっていたので説明しようと思いましたが、getで取ったオブジェクトとfirstで取ったオブジェクトの動かす方法を知っているだけで適した利用方法を説明できるほど理解できておらず、曖昧な返答をしてしまいました。

良い機会だったのでこのケースでの適したコードを調べてみました。

動かない理由

まずレコードを呼ぶ際にgetメソッドを使っているかfirstメソッドを使っているかの違いがあります。

1つのレコードを取得するfirstやfindのメソッドはオブジェクトを返します。

複数のレコードをまとめて取得するgetやallはコレクションを返します。

今回のケースはコレクションを返しているのでそこから直接インスタンスメソッドを呼び出すことはできません。

とりあえず動く書き方にする

※あくまで元のコードを動く形にしただけであり、ベターな書き方ではありません。

コレクションの1つ1つはModelのオブジェクトなのでその中身を取り出せば呼び出すことが出来ます。

例えば以下のように呼び出すことが出来ます。

例1. コレクションをループで分けて呼び出す。

$hoge_members = Hoge::where('type', 'member')->get();
$lang = 'jp';
foreach($hoge_members as $hoge) {
    $hoge_lang = $hoge->getLang($lang);
}

レコードをループで回すついでにメソッドを呼び出します。(無駄に何度も呼び出すので少し制御が必要)

例2. コレクションの先頭を取り出してからメソッドを呼び出す。

$hoge_members = Hoge::where('type', 'member')->get();

$lang = 'jp';
$hoge_lang = $hoge_members->first()->getLang($lang);

メソッドの呼び出しだけを1行で書く場合です。

しかしそもそも今回のケースはメソッドの書き方そのものが適していませんでした。

staticメソッドを使う

もっと奇麗にコードを書く方法がないかググったり他のメンバーに聞いたりしてみました。

結果、このインスタンスメソッドをstaticメソッドにしてModelをインスタンス化せずに呼び出すという方法になりました。

まずfunction getLangをstaticメソッドにします。

public static function getLang ($lang=NULL) {
    if ($lang == 'jp') {
        return '日本語';
    } else if ($lang == 'en') {
        return '英語';
    }
    return 'その他';
}

次にControllerでModelから直接メソッドを呼び出します。

$lang = 'jp';
$hoge_lang = Hoge::getLang($lang);

こうすることで無駄なコードが減った上にメソッドの意味も理解しやすくなりました。

staticメソッドについて

staticメソッドとはメソッドが書かれているClassをインスタンス化せずに静的に呼び出すことが出来るメソッドです。

呼び出すときは「::(スコープ定義演算子)」を使います。「->(オブジェクト演算子) 」は使えません。(※詳しくはこちらのPHPマニュアル参照)

今回のメソッドはModel内に書かれていますが、テーブルのレコードなど一切使っていないのでstaticメソッドが適しています。

メソッドをModelではなく別のオブジェクトに書くという方法もありますが、staticは正しく利用することでメソッドの目的を簡単に読み解いてもらえることも期待できそうです。

感想

今回の件で適切なコードの書き方も知れましたが、オブジェクト指向のプログラムの理解も深まりました。

staticというもの自体はいろいろなコードに埋め込まれていたので知らなかったわけではありませんが、使うときはコードの流用が多かったため正しく理解していませんでした。

こういった理解が深まることでコードを書く能力だけでなく他人の書いたコードを理解する力も上がったと思いました。

Tags

← 前の投稿

JMeter gRPC Request で gRPC リクエストを送る方法とハマった点

次の投稿 →

Docker による AWS Lambda 関数コンテナイメージ化のすすめ

コメントを残す