如何在无实例情况下检查Eloquent模型是否存在指定键?
无需实例化Eloquent模型即可检查字段是否存在的解决方案
问题描述
我想创建一个辅助函数,用来检查Eloquent模型中是否存在特定字段,以此生成仅验证合法字段的动态查询。我写了两个辅助函数,但问题在于不实例化模型的话无法获取类的属性,而我现在只能拿到类名(比如MyCoolModel::class)。请问有没有无需实例化就能检查Eloquent模型是否包含指定字段的最佳方法?
我原本编写的辅助函数代码:
if(!function_exists('entityHasKey')){ function entityHasKey(Model $eloquentModel, string $key): bool { return array_key_exists($key, $eloquentModel->getAttributes()); } } if(!function_exists('hasEntityKeys')){ /** * @param string $eloquentModel * @param string[] $keys * @return bool */ function entityHasKeys(string $eloquentModel, array $keys): bool { $hasKey = false; foreach($keys as $key){ if(entityHasKey($eloquentModel, $key)){ $hasKey = true; break; } } return $hasKey; } }
期望的使用示例:
// 希望能直接传入类名调用 (*其他条件* and entityHasKeys(MyCoolModel::class, $filters['columns']))
解决方案
方法1:直接查询数据库表结构(推荐)
通过Laravel的Schema类直接获取模型对应表的字段列表,结果完全同步数据库结构,无需完整实例化模型:
优化后的辅助函数
use Illuminate\Database\Eloquent\Model; use Illuminate\Support\Facades\Schema; if(!function_exists('entityHasKey')){ function entityHasKey(string $eloquentModelClass, string $key): bool { // 通过容器解析模型类(仅创建空实例,无数据库查询),获取对应表名 $tableName = app($eloquentModelClass)->getTable(); // 获取表的所有字段 $columns = Schema::getColumnListing($tableName); return in_array($key, $columns); } } if(!function_exists('hasEntityKeys')){ /** * @param string $eloquentModelClass * @param string[] $keys * @return bool */ function hasEntityKeys(string $eloquentModelClass, array $keys): bool { $tableName = app($eloquentModelClass)->getTable(); $columns = Schema::getColumnListing($tableName); // 检查是否有至少一个目标键存在于字段列表中 return !empty(array_intersect($keys, $columns)); } }
说明
app($eloquentModelClass)仅创建一个空的模型实例,不会触发任何数据库查询,性能开销极低Schema::getColumnListing()直接读取数据库元数据,结果最准确,不受模型代码配置影响
方法2:利用模型的$fillable/$guarded属性
如果你的模型严格维护了$fillable(允许批量赋值的字段)或$guarded(禁止批量赋值的字段),可以直接读取这些静态属性判断:
use Illuminate\Database\Eloquent\Model; if(!function_exists('entityHasKey')){ function entityHasKey(string $eloquentModelClass, string $key): bool { // 检查是否在允许填充的字段列表中 return in_array($key, $eloquentModelClass::$fillable); // 若使用黑名单模式($guarded != ['*']),可改用下面的判断 // return !in_array($key, $eloquentModelClass::$guarded); } }
注意事项
- 此方法依赖模型的代码配置,若
$fillable/$guarded维护不及时,会导致判断结果不准确 - 如果模型使用默认的
$guarded = ['*'],则此方法无法使用
方法3:通过反射创建空模型实例
使用PHP反射创建不执行构造函数的模型空实例,避免数据库查询的同时获取模型属性:
use Illuminate\Database\Eloquent\Model; use ReflectionClass; if(!function_exists('entityHasKey')){ function entityHasKey(string $eloquentModelClass, string $key): bool { $reflection = new ReflectionClass($eloquentModelClass); // 创建不执行构造函数的空模型实例 $model = $reflection->newInstanceWithoutConstructor(); return array_key_exists($key, $model->getAttributes()); } }
说明
- 此方法不会触发任何数据库操作,但依赖模型内部的属性初始化逻辑,可靠性不如直接查询数据库表结构
内容的提问来源于stack exchange,提问作者Kristoffer Tølbøll
相关产品推荐
相关产品推荐

