【PHP】提案中のObject Initializerとオブジェクト初期化の小技

著者:杉浦

【PHP】提案中のObject Initializerとオブジェクト初期化の小技

 PHPでObject Initializerという記法が現在提案されています。
 PHP: rfc:object-initializer
 [RFC] Object Initializer – Externals
 [VOTE] Object Initializer – Externals
 その名の通り、オブジェクトの初期化に関する記法です。具体的には次の様な記述をします。

<?php
 
class Customer
{
  public $id;
  public $name;
  private DateTimeImmutable $createdAt;
 
  public function __construct()
  {
    $this->createdAt = new DateTimeImmutable("now");
  }
}
 
class Car
{
  public int $yearOfProduction;
  public string $vin;
}

<?php
 
$customer = new Customer {
  id = 123,
  name = "John Doe",
};
 
$car = new Car {
  yearOfProduction = 2019,
  vin = "1FTFW1CVXAFD54385",
};
// <a href="https://wiki.php.net/rfc/object-initializer">PHP: rfc:object-initializer</a>から引用

 単なる代入によるpublicプロパティの初期化をコンストラクタ外で行おうとする方法です。これによりコンストラクタのコーディング、プロパティの代入忘れ、引数の順番間違いから解放されます。
 これだけだと良いやり方に見えますが、提案の賛成:反対は2019/10/08 17:30(JST)時点で3:17です。投票に理由をコメントする必要はないのでスレを読み取って想像するしかありませんが、コンストラクタと共存すると怪しかったり、__setを介する直接代入でない部分の扱いだったり、今のPHPでも手間をかければ似たようなことができたりで賛成者は少ないです。

 PHPの既にある仕様で多数のプロパティを初期化する方法は多々あります。フレームワークを利用するとさらに増えます。自分がよく使うのは連想配列を用いたパターンです。

<?php
class Pos {
    public function __construct($attributes)
    {
        $this->lat = $attributes['lat'];
        $this->lng = $attributes['lng'];
        $this->x = $attributes['x'] ?? null;
        $this->y = $attributes['y'] ?? null;
    }
}

new Pos([
    'lat' => 35.658577,
    'lng' => 139.745451
]);

 連想配列にまとめることで引数を抑え、連想配列であるため引数の意味を理解しやすい、というものです。このやり方にはIDEの自動補完がまるで働かないという点で問題がありますが、程よく手軽に使える点と他クラスのarray変換ですぐ結び付けられる点で使用しています。他にもstdClassを使ったパターンやインタフェースとDTO(DataTransfarObject)を使うやり方もあります。後者は大がかりなものを扱う時、重用できます。

<?php
class Pos {
    public function __construct($attributes)
    {
        $this->lat = $attributes->lat;
        $this->lng = $attributes->lng;
    }
}

$args = (object)[
    'lat' => 35.658577,
    'lng' => 139.745451
];
new Pos($args);
interface PosArgsContract {
    public $lat;
    public $lng;
}
class Pos {
    public function __construct(PosArgsContract $attributes)
    {
        $this->lat = $attributes->lat;
        $this->lng = $attributes->lng;
    }
}

 初期化とは少し違いますがLaravelのFormRequestとEloquentをつなぐやり方として次のワンライナーがあります。

public function create(UserCreateRequest $request) {
    $user = User::create($request->validated());
}

 validatedメソッドの返り値はバリデーションを通過したリクエストのボディ全ての連想配列です。リクエストのキーをテーブルのキーと一致させることによって上記の記述でブラウザのリクエストからデータベースへのSQL発行まで一気に繋げられます。しかもバリデーション通過済みなので安全な値です。

  • この記事いいね! (1)

著者について

杉浦 administrator