如何在使用 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); 将返回除pseudoemailagecreated_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 个对象,我需要在我的查询中 exceptit。 根据使用情况,我需要隐藏created_atupdated_atdeleted_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-&gt;created_at,$category-&gt;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 时排除某些列的主要内容,如果未能解决你的问题,请参考以下文章

如何通过排除特定格式的某些波段来排除空格?

如何在 R 中加入多个数据框但排除某些列?

使用 eloquent 和 vuejs 时如何检索数据库列

导出数据表时排除/隐藏某些列

如何使用 Eloquent 模型获取多个表列名

CXF:通过 SOAP 发送对象时如何排除某些属性?