【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;