【Laravel】空のリレーションを手軽に設定する方法
こんにちは、スズキです。
今回は空のリレーションを手軽に設定する方法を紹介します。
以前、このような記事を書いたのを覚えている方はいますでしょうか。
上記記事では、「逆方向のリレーションを動的に設定する方法」を紹介していますが、
そもそもの目的は「ぬるぽエラーを回避するために、空のリレーションを設定したい」というものです。
つまり、以下のようなアクセスを行った際に、
このままでは「$parent->child」がNULLの場合、「$parent->child->name」にアクセスしようとするとエラーが発生してしまいます。
そこで、
というのが前回記事の本来の目的だったわけです。
また、先日開発していたときの話なのですが、
(上記例ではHasOne系のリレーションの話をしましたが、)
HasMany系のリレーションに対し、
上記の場合、「$parent->children->first()->name;」とアクセスすると、childrenが0件の場合にエラーになってしまいます。
そのため、HasOne系のリレーションのときと同様に、
「childrenが0件の場合は空のChildモデルを$parent->childrenに追加する」
という処理が必要だな〜となったわけです。
で、このあたりの空のリレーションを設定する処理を簡単に1行で記述できるようなメソッドを作ったのですが、
今回はこのメソッドを紹介します。
はい、というわけで、作成した空リレーション設定用の処理は以下になります。
●空リレーション設定用のTrait
上記処理では
そのため、利用する際は以下のように、実にシンプルな形で空リレーションの設定を実現することができます。
●空リレーション設定用メソッドを利用するモデルクラス
●利用方法
今回は空のリレーションを手軽に設定する方法を紹介します。
前振り
以前、このような記事を書いたのを覚えている方はいますでしょうか。
上記記事では、「逆方向のリレーションを動的に設定する方法」を紹介していますが、
そもそもの目的は「ぬるぽエラーを回避するために、空のリレーションを設定したい」というものです。
つまり、以下のようなアクセスを行った際に、
このままでは「$parent->child」がNULLの場合、「$parent->child->name」にアクセスしようとするとエラーが発生してしまいます。
$parent = Parent::query()->first(); $childName = $parent->child->name;
そこで、
// Parent情報取得
$parent = Parent::query()->first();
// Childのレコード情報が登録されていない場合
if(!$parent->child)
{
// 空のChild情報を設定
$emptyChild = new Child();
$parent->setRelation('child', $emptyChild);
}
// レコード情報が無い場合には空のリレーションを設定しているので、
// 本来は「$parent->child === null」だったとしても、
// 以下の処理の際にエラーにならない
$childName = $parent->child->name;
のように空のリレーションを設定する処理を行うことで、「$parent->child」がNULLの場合でも、「$parent->child->name」にアクセスしてもエラーにならないようにする、というのが前回記事の本来の目的だったわけです。
また、先日開発していたときの話なのですが、
(上記例ではHasOne系のリレーションの話をしましたが、)
HasMany系のリレーションに対し、
$childId = 999;
$query = Parent::query();
$query->with([
'children' => function ($query) use ($childId) {
$query->where('children.id', '=', $childId);
},
]);
$parent = $query->first();
$childName = $parent->children->first()->name;
というように、with句を設定してchildrenを1件のみに絞った後に、
$parent->children->first()->name;という形で最初の1件のみ利用する、というアクセスの仕方をすることになりました。
上記の場合、「$parent->children->first()->name;」とアクセスすると、childrenが0件の場合にエラーになってしまいます。
そのため、HasOne系のリレーションのときと同様に、
「childrenが0件の場合は空のChildモデルを$parent->childrenに追加する」
という処理が必要だな〜となったわけです。
で、このあたりの空のリレーションを設定する処理を簡単に1行で記述できるようなメソッドを作ったのですが、
今回はこのメソッドを紹介します。
本題
はい、というわけで、作成した空リレーション設定用の処理は以下になります。
●空リレーション設定用のTrait
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Model;
trait SetRelationTrait
{
/**
* 空のリレーションを設定します。(HasOne系リレーション用)
* @param $relationName
* @param null $reverseRelationName
*/
public function setHasOneDummyRelation($relationName, $reverseRelationName = null)
{
/**
* @var Model $emptyModel
*/
/**
* @var Model $this
*/
// 空モデル取得
$emptyModel = $this->$relationName()->getQuery()->getModel()->replicate();
// 逆リレーションを設定する場合
if ($reverseRelationName) {
// 逆リーレション設定
$emptyModel->setRelation($reverseRelationName, $this);
}
// リレーション設定
$this->setRelation($relationName, $emptyModel);
}
/**
* 空のリレーションを設定します。(HasMany系リレーション用)
* @param $relationName
* @param null $reverseRelationName
*/
public function setHasManyDummyRelation($relationName, $reverseRelationName = null)
{
/**
* @var Model $emptyModel
*/
/**
* @var Model $this
*/
$emptyModels = new Collection();
// 空モデル取得
$emptyModel = $this->$relationName()->getQuery()->getModel()->replicate();
$emptyModels->add($emptyModel);
// 逆リレーションを設定する場合
if ($reverseRelationName) {
foreach ($emptyModels as $key => $value) {
// 逆リーレション設定
$emptyModels[$key]->setRelation($reverseRelationName, $this);
}
}
// リレーション設定
$this->setRelation($relationName, $emptyModels);
}
}
上記処理では
$emptyModel = $this->$relationName()->getQuery()->getModel()->replicate();
の部分で目的のモデルクラスのインスタンスをリレーション名から自動で取得できるようにしています。そのため、利用する際は以下のように、実にシンプルな形で空リレーションの設定を実現することができます。
●空リレーション設定用メソッドを利用するモデルクラス
class Parent extends Model
{
use SetRelationTrait;
public function grandparent()
{
return $this->belongsTo(Grandparent::class);
}
public function children()
{
return $this->hasMany(Child::class);
}
}
●利用方法
$childId = 999;
$query = Parent::query();
$query->with([
'grandparent',
'children' => function ($query) use ($childId) {
$query->where('children.id', '=', $childId);
},
]);
$parent = $query->first();
// 祖父情報がNULLの場合
if(!$parent->grandparent) {
$parent->setHasOneDummyRelation('grandparent', 'parent');
}
// 子情報が0件の場合
if(!count($parent->children)) {
$parent->setHasManyDummyRelation('children', 'parent');
}
// grandparentがNULLの場合も以下の処理でエラーにならない
$grandparentName = $parent->grandparent->name;
// chldrenが0件の場合も以下の処理でエラーにならない
$childName = $parent->children->first()->name;