前回の型情報付きでテーブルオブジェクトを取得するの続き
課題
通常CakePHPではエンティティオブジェクトをQuery/ResultSetからfirst()等で取得する。
ResultSetからforeachで取るような場合は@var
書くしかないして、fisrt()のときくらいはEntityInterfaceではなく具体的な型情報がほしい。
解決策
ジェネリクスを使用した回避策がこれ。
例としてByIdにしてあるけど条件は好みで適当に調整。
use Cake\Utility\Inflector;
/**
* @template T
* @param class-string<T> $fullEntityName 完全修飾エンティティ名
* @param int $id
* @return T of \Cake\ORM\Entity
*/
public static function getEntityById(string $fullEntityName, int $id, array $contain = [])
{
if (!preg_match('#(\w+)$#', $fullEntityName, $matches)) {
throw new \DomainException();
}
$tableName = Inflector::pluralize($matches[1]);
return TableRegistry::getTableLocator()->get($tableName)->findById($id)->contain($contain)->first();
}
使用例
$article = getEntityById('\App\Model\Entity\Article', 3);
または
$article = getEntityById(\App\Model\Entity\Article::class, 3);
または
use \App\Model\Entity\Article;
$article = getEntityById(Article::class, 3);
いずれも\App\Model\Entity\Article型としてIDEに認識される
留意事項
テーブルモデルと同様、ジェネリクスを使う都合上、単にgetEntityById('Articles', ~~)と書くことはできず、完全修飾名で指定する必要がある。
使用例の真ん中の書き方であればIDE補完付きで書くことができるのでそれほど手間ではない。
また配列形式であれば以下のように書けるはずだが、現時点ではPhpStormは対応しておらず、今後対応予定とのこと。(公式ブログのWI-60894 Support parameter wrapping)
use Cake\Utility\Inflector;
/**
* @template T
* @param class-string<T> $fullEntityName 完全修飾エンティティ名
* @param string $named
* @return array<int, T>
*/
public static function getEntitiesByName(string $fullEntityName, string $name, array $contain = [])
{
if (!preg_match('#(\w+)$#', $fullEntityName, $matches)) {
throw new \DomainException();
}
$tableName = Inflector::pluralize($matches[1]);
return TableRegistry::getTableLocator()->get($tableName)->findByName($name)->contain($contain)->toArray();
}