ChatGPT解决这个技术问题 Extra ChatGPT

如何在使用 eloquent 时排除某些列

当我使用 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']);。你看 ?

R
Razor

如果您只需要从模型的数组或 JSON 表示中隐藏属性,您可以使用一种或两种方法:

将 $hidden 属性添加到您的模型类 User extends Model { /** * 应该为数组隐藏的属性。 */ 保护 $hidden = ['密码']; }

使用 makeHidden 函数 $users = $users->makeHidden(['address', 'phone_number']);

有关更多详细信息,请参阅其他答案...但有时您不想将大量数据(地理空间、html、日志...)加载到您的应用程序中,它会很慢并且占用更多内存。 OP 要求 SQL 查询因此我的回答,但大多数时候只隐藏 JSON 响应中的数据更方便。

AFAIK SQL 中没有内置选项来显式排除列,因此 Laravel 不能这样做。但是你可以试试this trick

更新

另一个技巧是指定模型中的所有列(或使用额外查询从 this answer 使用 $this->getTableColumns() 获取所有列,也可以在每次迁移后缓存以避免两次查询)然后添加 local scope 函数

// 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 我已经更新了我对缓存支持的回答
i
iamab.in

在模型中使用 hidden 数组很好,但如果您不想一直隐藏您的列并使用 makeVisible 来访问它们,那么请改为隐藏您的列以防止序列化您需要像这样的 makeHidden 功能:

$res = Model::where('your query')->get();
$res->makeHidden(['column_one','column_two','column_n']);
return response()->json($res);

如果该列很大,这没有用,您仍然会查询它,我们的想法不是查询它。我有一个表格,其中有一列是几何形状,每个值大约为 500KB,我必须调用该模型的 100 个对象,我需要except在我的查询中调用它。
根据使用情况,我需要隐藏created_atupdated_atdeleted_at,这个答案对我来说是最完美的。
例如,如果您想从序列化中隐藏一个字段,则此答案是最好的。
这对我来说也是最好的解决方案。您还可以像 Model::first()->relationship->makeHidden(['field1', 'field2']); 一样对它们进行序列化
调用未定义的方法 Illuminate\Database\Eloquent\Relations\HasMany::makeHidden()
C
Christhofer Natalius

我不知道以前的 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] 数据字段,您通常不想加载它可能应该在单独的模型中。
M
ManojKiran Appathurai

我已经查看了@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 进行一次额外的查询......但是,如果这不是问题(小型应用程序),那没关系
是的,正如您所说,它将进行一个额外的数据库查询,但如果您的应用程序很小,您可以使用我的方法,但对于企业应用程序,您可以将列名存储在缓存中。每当您进行新部署时,您都可以清除缓存。
是的,这是一种更好的方法,您可以通过在每次迁移后缓存结果来避免额外的查询,我将链接到您的答案。
v
vuhung3990

您可以像这样使用 hidden 数组:

class Promotion extends Model
{
    protected $table = 'promotion';
    protected $hidden = array('id');
}

R
Rafael Xavier

我们从包含所有字段的模型中获取 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;

上面的这个例子和第一个例子完全一样,但解释得更多。


这个工作除了你从数据库中获取它开始。我想排除一些员工的主要目的是这样我就不会从数据库中获取大量数据
B
Brad Ahrens

我有一个对我有用的解决方案,它与已经说明的略有不同。

解决方案:

$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 的回答不起作用,因为我收到以下错误:

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

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

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

我希望这可以帮助别人! 😊


M
Mohamed Hany

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

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


a
alisongaleon

您可以利用 Illuminate\Support\Facades\Schema::getColumnListing('table_name');

use Illuminate\Support\Facades\Schema;

$users_table_columns = Schema::getColumnListing('users');

$exclude_columns = [
 'password',
 'token',
 'address',
];
        
$select = array_diff($users_table_columns, (array) $exclude_columns);

$site = User::select($select)
 ->where('gender', 'M')
 ->where('is_active', 1)
 ->first();

d
dastan

您可以使用未设置的 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