1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

型がある時代のPHP、テンプレートエンジンでも型チェックしたい

Posted at

DBからデータを取得して表示するページがあるとする。

src/Page/Product.php
<?php
class Product
{
    public static function index($db)
    {
        // ...
    }

    public static function detail($db, $productId)
    {
        // ...
    }
}

こんなページ専用のクラス(アクションクラス、コントローラークラス)があったとして、

src/Model/Product.php
<?php

namespace MyProject\Model;

use MyProject\Dto;

class Product
{
    private $db;
    public function __construct($db)
    {
        $this->db = $db;
    }

    /**
     * @return array<Dto\Product>
     */
    public function getList($limit, $offset): array
    {
        // $db->find($this->conditions(), $limit, $offset)
        return [];
    }

    public function get($productId): Dto\Product
    {
        // return $db->get($productId);
        return new Dto\Product();
    }
}

こういうモデルを呼び出して、

src\Dto\Product.php
<?php

namespace MyProject\Dto;

use DateTimeInterface;
use DateTimeImmutable;

class Product
{
    /** @var int */
    public $product_id;
    /** @var string */
    public $product_name;
    /** @var string */
    public $product_code;

    /** @var int */
    public $price;

    /** @var ?int */
    public $discount;

    /** @var DateTimeInterface */
    public $created_at;
    /** @var ?DateTimeInterface */
    public $updated_at;

    public $db_created_at;
    public $db_updated_at;

    public function __construct()
    {
        // ...
        $this->created_at = new DateTimeImmutable($this->db_created_at);
        if ($this->db_updated_at &&
            $this->db_updated_at != $this->db_created_at)
            $this->updated_at = new DateTimeImmutable($this->db_updated_at);
    }
}

こういうオブジェクトを取得する。
そうするとページ用クラスは

src/Page/Product.php
<?php
namespace MyProject\Page;

use MyProject\Model;
use MyProject\template;

class Product
{
    public static function index($db)
    {
        $model = new Model\Product($db);
        $products = $model->getList(10, 0);

        template\product\index::exec($products);
    }

    public static function detail($db, $productId)
    {
        $model = new Model\Product($db);
        $product = $model->get($productId);

        return template\product\detail::exec($product);
    }
}

こういう感じになる。$productDto\Product だと分かっているので、テンプレート用クラスは

template\product\detail.php
<?php

namespace MyProject\template\product;

use MyProject\Dto;

class detail
{
    public static function exec(Dto\Product $product)
    {
        // ...
    }
}

こうなる。

template.png

nullable がエラーになっていることに気付く。

template2.png

修正して、めでたしめでたし。

なんか今までは色々動的生成して一周回ってベタ書きしたくなってる。

phpstanを使えば簡単なif文までチェックしてくれるのに、テンプレートエンジンで @if とか書いちゃって {{ $product->updated_at->format('Ymd') }} とか書くと静的解析が効かない。
昔は型を書いても意味なかったし、文字でも数字でもnullでも取り敢えず全部テンプレートエンジンに突っ込んで何らかのキーで値を引っ張ってくるのが手っ取り早かった。

今は、というかPHP7.4から先は、上のコードで言うDtoのプロパティにがっつり型を書けるわけだし、そうすると素のPHPでHTML出力した方が型チェック出来て幸せなのでは?とか思ったりしている。

タグジャンプを設定しているなら、テンプレートの変数からコードへの行き来もすぐ出来るので楽ちん。(テンプレートエンジンを使っていてもキーワードで飛べることがあるけど、飛びたい箇所から外れることも多い)

型を書く派の人達は、HTMLと変数を結合する箇所での型チェックはどうしているんだろう。
IDE使わないから知らないだけで、例えばPhpStormとBladeならテンプレートエンジンも標準で型チェックされる、とかになっているのだろうか。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?