Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
1
Help us understand the problem. What is going on with this article?
@nishimura

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

More than 1 year has passed since last update.

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
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
1
Help us understand the problem. What is going on with this article?