Laravel如何检索继承自父模型的所有子模型实例?
如何在Eloquent中检索所有继承自同一父模型的子模型记录?
看起来你想实现的是把多个独立数据表的子模型记录,统一以父模型的视角拉取出来——就像调用LivingBeing::all()一样,对吧?Eloquent本身并没有原生支持这种跨表的父模型查询(毕竟每个子模型都绑定了自己的表),不过我们可以通过几种方式来实现你的需求:
方法1:手动合并子模型集合(最简单直接)
既然每个子模型都能独立查询,我们可以分别获取每个子模型的所有记录,再合并成一个Eloquent集合:
use App\Models\Human; use App\Models\Cat; use App\Models\Squid; use Illuminate\Database\Eloquent\Collection; // 初始化空集合 $allLivingBeings = new Collection(); // 逐个合并子模型的查询结果 $allLivingBeings = $allLivingBeings->merge(Human::all()); $allLivingBeings = $allLivingBeings->merge(Cat::all()); $allLivingBeings = $allLivingBeings->merge(Squid::all());
这样得到的集合里包含了所有子模型的实例,而且因为它们都继承自LivingBeing,所以你可以直接调用父模型的方法(比如isAlive()),完全符合你的需求。
方法2:在父模型封装静态方法(复用性更强)
为了避免每次都重复写合并逻辑,我们可以在LivingBeing模型里添加一个静态方法,把合并的逻辑封装起来:
class LivingBeing extends Model{ public function isAlive(){ return $this->is_alive; //boolean } public static function allLivingBeings() { $collection = new Collection(); // 列出所有继承自当前父模型的子模型 $childModels = [Human::class, Cat::class, Squid::class]; foreach ($childModels as $model) { $collection = $collection->merge($model::all()); } return $collection; } }
之后你只需要调用LivingBeing::allLivingBeings()就能拿到所有记录了,非常方便。
如果需要支持自定义查询条件(比如筛选存活的生物),还可以扩展这个方法,支持传入查询回调:
public static function queryAll($callback = null) { $collection = new Collection(); $childModels = [Human::class, Cat::class, Squid::class]; foreach ($childModels as $model) { $query = $model::query(); // 如果传入了查询回调,就应用到每个子模型的查询上 if (is_callable($callback)) { $callback($query); } $collection = $collection->merge($query->get()); } return $collection; }
使用示例:
// 获取所有存活的生物 $aliveBeings = LivingBeing::queryAll(function($query) { $query->where('is_alive', true); });
方法3:数据库UNION查询(大数据量场景)
如果你的子模型数据表数据量很大,多次查询再合并的性能可能不够理想,这时候可以用数据库的UNION操作来合并查询结果:
use Illuminate\Support\Facades\DB; class LivingBeing extends Model{ // ... 其他代码 public static function allLivingBeings() { // 为每个子模型的查询添加一个type字段,方便区分来源 $humanQuery = Human::select('*', DB::raw('"human" as source_type')); $catQuery = Cat::select('*', DB::raw('"cat" as source_type')); $squidQuery = Squid::select('*', DB::raw('"squid" as source_type')); // 合并三个查询 $unionResults = $humanQuery->union($catQuery)->union($squidQuery)->get(); // 把结果转换成LivingBeing实例(如果不需要子模型的特有方法) return self::hydrate($unionResults->toArray()); // 如果需要保留子模型的实例,直接返回unionResults即可 // return $unionResults; } }
不过要注意:用hydrate生成的是LivingBeing纯父模型实例,会丢失子模型的特有属性和Trait方法(比如hasSpine、hasTentacles里的逻辑)。如果你的业务需要用到子模型的特有功能,还是用前两种方法更合适。
内容的提问来源于stack exchange,提问作者Gabriel
相关产品推荐
相关产品推荐

