Laravel:使用Eloquent实现抽象父类Animal获取所有子类实例
需求完全可以实现!
根据你的场景,有两种常见的实现方案,取决于你是用单表继承还是多表存储来区分猫和狗:
方案一:单表继承(推荐,更符合Eloquent设计)
如果猫和狗的属性大部分重合,只需要一个animals表,用type字段(比如值为cat/dog)来区分类型。这样实现起来最简洁,也能完美适配你的需求:
1. 抽象类 Animal
abstract class Animal extends Illuminate\Database\Eloquent\Model { protected $table = 'animals'; // 包含所有动物共有的字段,以及子类特有的字段 protected $fillable = ['name', 'type', 'cat_specific_field', 'dog_specific_field']; }
因为是抽象类,它本身无法被实例化,完全符合你“不存在Animal实际实例”的要求。
2. 子类 Cat 和 Dog
在子类中添加全局作用域,自动过滤对应类型的记录:
class Cat extends Animal { protected static function boot() { parent::boot(); // 全局作用域:只查询type为cat的记录 static::addGlobalScope('cat_type', function ($query) { $query->where('type', 'cat'); }); } }
class Dog extends Animal { protected static function boot() { parent::boot(); // 全局作用域:只查询type为dog的记录 static::addGlobalScope('dog_type', function ($query) { $query->where('type', 'dog'); }); } }
效果验证
Cat::all():自动带上WHERE type = 'cat',返回所有猫实例Dog::all():自动带上WHERE type = 'dog',返回所有狗实例Animal::all():因为抽象类没有添加任何全局作用域,会直接返回animals表的所有记录(包含猫和狗)
方案二:多表存储(适合属性差异极大的场景)
如果猫和狗的属性差异很大,需要分开存到cats和dogs两张表,那可以通过重写Animal的all()方法来合并结果:
1. 抽象类 Animal
abstract class Animal extends Illuminate\Database\Eloquent\Model { protected $name; // 重写all方法,合并猫和狗的结果 public static function all($columns = ['*']) { $cats = Cat::all($columns); $dogs = Dog::all($columns); // 合并两个Eloquent集合 return $cats->merge($dogs); } }
2. 子类 Cat 和 Dog
各自指定对应的表:
class Cat extends Animal { protected $table = 'cats'; // 猫特有的属性和配置 }
class Dog extends Animal { protected $table = 'dogs'; // 狗特有的属性和配置 }
注意事项
这种方案下,Animal::all()返回的是两个集合的合并结果,无法直接链式调用Eloquent的查询方法(比如where)。如果需要对所有动物做复杂查询,单表继承的方案会更灵活。
内容的提问来源于stack exchange,提问作者Barret Wallace
相关产品推荐
相关产品推荐

