【问题标题】:How to exclude certains columns while using eloquent如何在使用 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']);。你看到了吗?

标签: php laravel eloquent


【解决方案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://stackoverflow.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();
    

    推理:

    对我来说:

    1. Razor's answer 不起作用,因为我收到以下错误:

    BadMethodCallException with message 'Call to undefined method App/CaseStudy::exclude()'

    1. 然后,其余答案试图隐藏模型中的列。不幸的是,这会为我班级中的每个方法隐藏它们,这不是我想要的。

    因此,最后,我修改了 Razor 的解决方案,使其无需隐藏每种方法的任何列即可工作。

    我希望这对某人有所帮助! ?

    【讨论】:

      【解决方案9】:

      您可以像这样使用makeHidden 数组:(在get() 或all() 之后)

      $users = User::where('gender', 'M')->where('is_active', 1)->get()->makeHidden(['pseudo', 'email', 'age' , 'created_at'])->toArray();

      【讨论】:

        猜你喜欢
        • 2021-06-22
        • 2020-02-04
        • 2011-06-01
        • 1970-01-01
        • 2018-03-03
        • 1970-01-01
        • 2020-07-17
        • 1970-01-01
        相关资源
        最近更新 更多