モデルクラスに条件判定メソッドを定義して、仕様をコードにまとめる。

※ コンストラクタは省略しています。

<?php
declare(strict_types=1);

final class Post
{
    const STATUS_PUBLIC = 1;
    const STATUS_CLOSE = 0;

    /** @var int */
    private $status;

    public function status(): int
    {
        return $this->status;
    }
}

ブログ記事を表すPostモデルを例に使います。$post = Post::find(1);とDBから取得できるとします。

statusカラムに記事の公開状態を数値で管理します。

<?php
if ($post->status === Post::STATUS_PUBLIC) {
    // 記事が公開されている時の処理
}

記事が公開されているかを確認する時に、クラスの外で定数を比較していませんか? 条件はメソッドにして分かりやすくしましょう。Postクラスに下記を定義します。 クラス定数をprivateにすることも重要です。クラス外ではisPublicでしか定数が比較されないことを保証します。

<?php
// 一部省略
final class Post
{
    private const STATUS_PUBLIC = 1;
    private const STATUS_CLOSE = 0;

    public function isPublic(): bool
    {
        return $this->status === self::STATUS_PUBLIC;
    }
}

If文はこう変わります。

<?php
if ($post->isPublic()) {
    // ...
}

ここから理由について掘り下げていきます。

メソッドで条件を1行だけに留める

記事はどのような判断で、「公開」と見なすでしょう。アップデートに伴い、postのstatusが増える可能性があります。

  1. 下書き
  2. フォロワーだけに公開
  3. 特定の選択した人にだけ公開

このように増えたとしても、クラス外ではisPublicを使っていれば、変更はありません。もし、直接書いていた場合は、&&||を使いビシネスロジックが読みづらくなってしまうでしょう。

英文のように読みやすいかが良いメソッドの判断基準です。主語と動詞を意識します。上記の追加したステータスによっては、isPublicをisAllPublicなどにリネームする必要があるかもしれません。

考える範囲が狭まり、PRが見やすい

isPublicなど条件判定が複雑なっていたとしても、このメソッドの定義を見る時は「記事が公開の時のみtrueを返すか?」だけを考えればよく、isPublicを呼び出す側のビジネスロジックは考える必要はありません。もちろんグローバル変数など使っておらず、きちんと互いに影響のない独立したisPublicを作ることが条件です。その条件を満たしているかを見れば良いことになります。

そして、一度独立したisPublic作り運用すれば、今後はこのメソッドを使ってもらえれば、判定条件までレビューする必要がなくなります。
※ 「公開する判断」という仕様が変わる時は別です。

もしisPublicがなければ、毎回クラス定数との比較などを見る必要が出てきます。複雑になるほど、! / && / || / ()が多用され、毎回全てが正しいかを確認する必要があります。それなら、誰かが時間をかけて実装したisPublicを使い回しましょう。

データの仕様が分かる・まとまる

Postクラスを見るとステータスがどのように使われているか分かります。もしかしたら、ステータスは公開範囲外でも使われているかもしれません。しかし、データがある場所、つまりPostクラスを見れば分かります。

クラスのデータ、さらに細かく言うと各値の仕様をまとめるためにも、クラスにメソッドをまとめます。別でドキュメントを用意するより、PHPDocなどで書いた方が確実で、小コストで保守できます。

一番良いのは、間違えて使うのが難しいコードです。そのために、メソッドを用意し、引数もできるだけ少なくすると良いでしょう。

プロパティ、1つの値をクラス化してしまう

StatusをPostStatusというクラスにして、PostクラスはPostStatusのインスタンスを保持するようにすると、さらに見やすくなります。

<?php

final class Post
{
    /** @var PostStatus */
    private $status;

    public function status(): PostStatus
    {
        return $this->status;
    }
}

final class PostStatus
{
    const PUBLIC = 1;
    const CLOSE = 0;

    /** @var int */
    private $value;

    public function __construct(int)

    public function value(): int
    {
        return $this->value;
    }

    public function isPublic(): bool
    {
        return $this->value === self::PUBLIC;
    }
}

if ($post->status()->isPublic()) {
}

以前のisPublicを残して、ショートカットとして使うの良いと思います。私はプロキシメソッドと呼んでいます。PostStatusのメソッドが増えるたびに、用意する必要が出るかもしれないため、面倒かもしれません。

<?php
final class Post
{
    /** @var PostStatus */
    private $status;

    public function isPublic(): bool
    {
        return $this->status->isPublic();
    }
}

このようにクラスを分けると、記事の公開状態だけの仕様を考える事ができます。Postには他にも色々なメソッドが増えて、どんなメソッドがあるか分かりづらくなってきます。

インスタンスプロパティが増えるほど、互いのメソッドに影響が出る可能性もあります。「他のメソッドで$statusが変更されていないか?」を考える必要も減らすためにも、PostStatusがあると便利です。少なくともPostStatus内では、Postクラス内のことを考える必要はありません。