🦔

PHP StaticメソッドでClassInstanceを返却しつつ、パイプのように実行していく方法(new static)

2024/05/17に公開

概要

PHPのクラスを作成時、new ClassName() をしたり、 new ClassName('引数1') でインスタンスを作ってから処理を実行するのが一般的かと思われます。
ただ、Laravel Eloquent Modelですと以下のような書き方が出来ます。

EloquentModel::query()->get();
EloquentModel::where('column', 'sample')->get();

通常のClassの作り方でも実行が可能です。

$model new EloquentModel();
$model->get();

$model new EloquentModel();
$model->where('column', 'sample')->get();

static なメソッドにもかかわらず、このような方法が採れるのはstaticメソッドで自身のクラスをしている事になります。

今回はそのようなクラスの実装をしてみようかと思います。

クラスを実装してみる


interface SampleClassInterface
{
    public function __construct();
}

class SampleClass implements SampleClassInterface
{
    private string $desert = "";

    public function __construct()
    {
    }

    // ケーキを作る
    public static function makeCake(string $name): self
    {
        return (new static)->newDesert($name . 'Cake');
    }

    // マフィンを作る
    public static function makeMuffin(string $name): self
    {
        return (new static)->newDesert($name . 'Muffin');
    }

    // デザートにする
    private function newDesert(string $name): self
    {
        $this->desert = $name;
        return $this;
    }

    // トッピングする
    public function topping(string $name): self
    {
        $this->desert .= ' and ' .$name;
        return $this;
    }

    // 作ったデザートを取得する
    public function get(): string
    {
        return $this->desert;
    }
}

上記のような実装をすることで、Staticなメソッドからインスタンスを作成した後パイプで処理を追加していけます。

軽く解説

interface SampleClassInterface
{
    public function __construct();
}

class SampleClass implements SampleClassInterface
{
    public function __construct()
    {
    }
}

ここはコンストラクタを定義していますが、 (new static) をしたときにコンストラクタの引数がない事を Interface で縛っています。
記述がない場合はPHP Stanなどで静的解析を使っているとこのあたりでコケるはずなので、記述するようにします。

// ケーキを作る
public static function makeCake(string $name): self
{
    return (new static)->newDesert($name . 'Cake');
}

// マフィンを作る
public static function makeMuffin(string $name): self
{
    return (new static)->newDesert($name . 'Muffin');
}

Staticで呼ばれるメソッド群です。
ここではケーキとマフィンのどちらも作れるようにしておきました。
ケーキもマフィンも同じデザートなので newDesert でデザートを作るメソッドを叩いています。

// デザートにする
private function newDesert(string $name): self
{
    $this->desert = $name;
    return $this;
}

PrivateなメソッドでStaticメソッドの new static 後に呼ばれることを想定しています。
コンストラクタとほぼ同じ扱いになるかと思いますが、好きなStaticメソッドから好きなコンストラクタを呼べる点が普通のコンストラクタとは違う点となります。

// トッピングする
public function topping(string $name): self
{
    $this->desert .= ' and ' .$name;
    return $this;
}

関数をパイプのように実行する為の物です。

SampleClass::makeCake('cake')->topping('chocolate')->get(); のように間に挟むことが出来ます。

// 作ったデザートを取得する
public function get(): string
{
    return $this->desert;
}

ゲッターです。 インスタンス変数を public にしてもいいですが、 private とした場合はこのようにゲッターを作成します。

つかうとどうなる

$chocolateCake = SampleClass::makeCake('cake')
    ->topping('chocolate')
    ->get();

$englishMuffin = SampleClass::makeMuffin('muffin')
    ->topping('cornmeal')
    ->get();

以上。

GitHubで編集を提案

Discussion