如何在使用 eloquent 时排除某些列
Posted
技术标签:
【中文标题】如何在使用 eloquent 时排除某些列【英文标题】:How to exclude certains columns while using eloquent 【发布时间】:2014-06-30 00:05:35 【问题描述】:当我使用 eloquent 时,我可以使用“where”方法,然后使用“get”方法来填充包含我在数据库中选择的对象的对象。 我的意思是:
$users = User::where('gender', 'M')->where('is_active', 1)->get(['pseudo', 'email', 'age', 'created_at'])->toArray();
在这里我可以选择我想要的列,例如“伪”、“电子邮件”等。 但是我在 laravel doc 中错过的是相反的方法。 可能是这样的:
$users = User::where('gender', 'M')->where('is_active', 1)->notGet(['pseudo', 'email', 'age', 'created_at'])->toArray();
感谢您对未来的回答,祝您有美好的一天。
【问题讨论】:
问题是,你为什么要这么做?使用 ORM 你宁愿不这样做,如果你只是不想显示某些列,还有其他方法可以实现。 我问它是因为当您有 15 列并且想要 13 列时,执行类似 ->notGet(['column14', 'column15']); 之类的操作可能会更快。而不是 ->get(['column1', 'column2', [...], 'column13']);。你看到了吗? 【参考方案1】:如果您只需要从模型的数组或 JSON 表示中隐藏属性,您可以使用一种或两种方法:
添加$hidden
属性到您的模型
class User extends Model
/**
* The attributes that should be hidden for arrays.
*/
protected $hidden = ['password'];
使用
makeHidden
功能
$users = $users->makeHidden(['address', 'phone_number']);
查看其他答案以获取更多详细信息... 但是有时您不想将大量数据(地理空间、html、日志...)加载到您的应用程序中,这会很慢并且需要更多的记忆。 OP 要求 SQL 查询因此我的回答,但大多数时候只隐藏 JSON 响应中的数据更方便。
AFAIK 在 SQL 中没有内置选项来显式排除列,因此 Laravel 不能这样做。不过你可以试试this trick
更新
另一个技巧是指定模型中的所有列(或使用额外的查询从this answer 获取使用$this->getTableColumns()
的所有列,也可以在每次迁移后缓存以避免两个查询)然后添加@987654325 @函数
// The below code requires you to define all columns in $columns.
// A better approach is to query the schema of the table and cache it after each
// migration, for more details: https://***.com/a/56425794/3192276
protected $columns = ['id','pseudo','email'];
public function scopeExclude($query, $value = [])
return $query->select(array_diff($this->columns, (array) $value));
那么你可以这样做:
$users = User::where('gender', 'M')
->where('is_active', 1)
->exclude(['pseudo', 'email', 'age', 'created_at'])
->toArray();
【讨论】:
->排除?这将导致方法不被允许。 @Leon 上面的模型函数scopeExclude()
就是这样调用的。在 laravel.com/docs/5.3/eloquent#local-scopes 上阅读有关 laravel 范围的信息
此方法在与eager relationship loading 链接时不起作用:模型本身返回正确的列而没有排除的列,但无法检索关系。
@ChristosLytras 我不知道任何其他解决方法,因为我说过这是一个 SQL 限制,所以 Laravel 不能用一个查询来做到这一点。所有 DBMS 中的总体共识是查询模式,因此您可以定义一个 sql 函数或过程或视图...您还可以缓存 Schema::getColumnListing('table')
的结果并在每次迁移时将其清除,即避免额外的 SQL 查询的方式。
嘿@Razor 我已经更新了我对缓存支持的回答【参考方案2】:
你可以像这样使用hidden
数组:
class Promotion extends Model
protected $table = 'promotion';
protected $hidden = array('id');
【讨论】:
【参考方案3】:我不知道以前的 Laravel 版本,但是在 5.4 中你可以将这一行放在 User 模型中
protected $hidden = ['pseudo', 'email', 'age', 'created_at'];
然后User::find(1);
将返回除pseudo
、email
、age
和created_at
之外的所有字段。
但您仍然可以使用以下方法检索这些隐藏字段:
$user = User::find(1);
$email = $user['email']; // or $user->email;
【讨论】:
也可以在 Laravel 5.1 中使用 它从输出中隐藏它(toArray()、toJSON())但仍然从数据库中加载它,所以当你不需要加载某些数据时这种方法是无用的 @Stalinko 如果模型中有某些数据,默认情况下根本不想加载,听起来你应该拆分资源并使用关系。 @kb,我不同意。拆分资源和使用关系是一个非常困难的解决方案,仅适用于大多数复杂的情况。在现实生活中,仅加载当前需要的列是很常见的任务。例如,可能有很重的description
列,仅在加载单个模型时才需要,而在加载所有模型时则不需要。跳过它可以节省大量内存。
@Stalinko 我只是部分同意这一点。我明白你的意思,但我认为这是一个集合的责任,它限制你需要处理大量数据的数据集/只有使用特定字段才能保存计算资源。模型应该是可预测的和原子的,对于仍然需要它的特殊情况,有很多方法可以通过直接查询/构建器过滤对象/加载它。如果您的模型中有 [esp large] 数据字段,您通常不想加载它应该在一个单独的模型中。【参考方案4】:
我们从包含所有字段的模型中获取 eloquent 对象,将其转换为数组,然后将其放入集合中。除了数组 $fields 中指定的所有字段之外,我们得到所有字段。
$fields = ['a', 'b', 'c', 'N'];
$object = Model::find($id);
return collect($object->toArray())->except($fields);
更清楚,我们举个例子:
// Array of fields you want to remove
$fields_to_remove = ['age', 'birthday', 'address'];
// Get the result of database
$user = User::find($id);
// Transform user object to array
$user = $user->toArray();
// Create a collection with the user inside
$collection = collect($user);
// Get all fields of our collection except these fields we don't want
$result = $collection->except($fields_to_remove);
// Return
return $result;
上面的这个例子和第一个例子完全一样,但解释得更清楚了。
【讨论】:
这个工作除了你从数据库中获取它开始。我想排除一些员工的主要目的是避免从数据库中获取大量数据【参考方案5】:在模型中使用hidden
数组很好,但是如果你不想一直隐藏你的列并使用makeVisible
来访问它们,那么隐藏你的您需要使用 makeHidden
函数的序列化列,如下所示:
$res = Model::where('your query')->get();
$res->makeHidden(['column_one','column_two','column_n']);
return response()->json($res);
【讨论】:
如果该列很大,这没有用,您仍然会查询它,想法不是查询它。我有一个表格,其中有一列是形状几何,每个值都像 500KB,我必须调用该模型的 100 个对象,我需要在我的查询中except
it。
根据使用情况,我需要隐藏created_at
,updated_at
,deleted_at
,这个答案对我来说是最完美的。
如果你想隐藏一个字段以防止序列化,这个答案是最好的。
这对我来说也是最好的解决方案。您也可以像Model::first()->relationship->makeHidden(['field1', 'field2']);
一样对它们进行序列化
调用未定义的方法 Illuminate\Database\Eloquent\Relations\HasMany::makeHidden()【参考方案6】:
我已经查看了@Razor 的答案
但是通过跳过 $columns 属性有一种非常方便的方式
/**
* Scope a query to only exclude specific Columns.
*
* @author Manojkiran.A <manojkiran10031998@gmail.com>
* @param \Illuminate\Database\Eloquent\Builder $query
* @return \Illuminate\Database\Eloquent\Builder
*/
public function scopeExclude($query, ...$columns)
if ($columns !== [])
if (count($columns) !== count($columns, COUNT_RECURSIVE))
$columns = iterator_to_array(new \RecursiveIteratorIterator(new \RecursiveArrayIterator($columns)));
return $query->select(array_diff($this->getTableColumns(), $columns));
return $query;
/**
* Shows All the columns of the Corresponding Table of Model
*
* @author Manojkiran.A <manojkiran10031998@gmail.com>
* If You need to get all the Columns of the Model Table.
* Useful while including the columns in search
* @return array
**/
public function getTableColumns()
return \Illuminate\Support\Facades\Cache::rememberForever('MigrMod:' . filemtime(database_path('migrations')), function ()
return $this->getConnection()->getSchemaBuilder()->getColumnListing($this->getTable());
);
getTableColumns
函数将获取表的所有列,因此您无需定义$column
属性
注意:表的列名将被缓存,直到添加或删除迁移目录的内容。
修改迁移目录中文件的内容将 不重新缓存列
要手动清除缓存,您可以运行php artisan cache:clear
【讨论】:
我喜欢你的方法,因为它可以在任何模型中重复使用。唯一的缺点是 getTableColumns() 方法会导致对 DB 进行一次额外的查询......但是,如果这不是问题(小应用程序),那没关系 是的,正如您所说,它会进行一个额外的数据库查询,但如果您的应用程序很小,您可以使用我的方法,但对于企业应用程序,您可以将列名存储在缓存中。每当您进行新部署时,您都可以清除缓存。 是的,这是一种更好的方法,您可以通过在每次迁移后缓存结果来避免额外的查询,我将链接到您的答案。【参考方案7】:您可以使用未设置的unset($category->created_at,$category->updated_at);
$fcategory = array();
$kCategory = KCategory::where("enabled", true)->get();
foreach ($kCategory as $category)
$subkCategory = PostCategory::select("id", "name", "desc")
->where("id_kcategory", $category->id)
->where("enabled", true)
->get();
unset($category->created_at, $category->updated_at);
$fcategory[] = $category;
【讨论】:
它在"laravel/lumen-framework": "5.7.8"
为我工作
这是在 mysql/DB 端的 php 端完成的。您仍在从数据库中获取所有列。这只是从已获取数据的集合中删除(取消设置)数据。
是的,正确,从数据库中获取所有数据,但使用 unset 从集合中删除一个字段,但我不知道为什么它对其他人不起作用,并且它被否决了。它不包括归档。
这是因为我们已经在内存中获得了不需要的列数据。对于小型应用程序或者如果列的数据量很小,那么这将不是问题。如果您可以在数据库端进行优化,那么这应该始终是优先事项。
是的,但如果它在一个 API 中需要,但在另一个 API 中不需要【参考方案8】:
我有一个对我有用的解决方案,它与已经说明的略有不同。
解决办法:
$all_columns = Schema::getColumnListing('TABLE_NAME');
$exclude_columns = ['COLUMN_TO_EXCLUDE_1', 'COLUMN_TO_EXCLUDE_2'];
$get_columns = array_diff($all_columns, $exclude_columns);
return User::select($get_columns)->get();
推理:
对我来说:
-
Razor's answer 不起作用,因为我收到以下错误:
BadMethodCallException with message 'Call to undefined method App/CaseStudy::exclude()'
-
然后,其余答案试图隐藏模型中的列。不幸的是,这会为我班级中的每个方法隐藏它们,这不是我想要的。
因此,最后,我修改了 Razor 的解决方案,使其无需隐藏每种方法的任何列即可工作。
我希望这对某人有所帮助! ?
【讨论】:
【参考方案9】:您可以像这样使用makeHidden
数组:(在get() 或all() 之后)
$users = User::where('gender', 'M')->where('is_active', 1)->get()->makeHidden(['pseudo', 'email', 'age' , 'created_at'])->toArray();
【讨论】:
以上是关于如何在使用 eloquent 时排除某些列的主要内容,如果未能解决你的问题,请参考以下文章