数据库模型与 Eloquent

复用或克隆 query

通常,我们需要从过滤后的查询中进行更多次查询。因此,大多数时候我们使用 query() 方法, 让我们编写一个查询来获取今天创建的可用和不可用的产品

$query = Product::query();


$today = request()->q_date ?? today();
if($today){
    $query->where('created_at', $today);
}

// 让我们获取可用和不可用的产品
$active_products = $query->where('status', 1)->get(); // 这一行 修改了$query 对象变量
$inactive_products = $query->where('status', 0)->get(); // 所以这里我们将获取不到任何不可用产品

但是,在获得 $active products 后,$query 会被修改。因此 $inactive_products 不会从 $query 中获取到不可用产品,并且每次都返回空集合。因为,它尝试从 $active_products 中查找不可用产品($query 仅返回可用产品)。

为了解决这个问题,我们可以通过重用这个$query对象来查询多次。因此我们在做任何对$query修改操作的时候需要克隆这个$query

$active_products = $query->clone()->where('status', 1)->get(); // it will not modify the $query
$inactive_products = $query->clone()->where('status', 0)->get(); // so we will get inactive products from $query

Eloquent where 日期方法

在 Eloquent 中,使用 whereDay()whereMonth()whereYear()whereDate()whereTime() 函数检查日期。

增量和减量

如果要增加数据库某个表中的某个列的值,只需要使用 increment() 函数。你不仅可以增加 1,还可以增加其他数字,如 50。

禁止 timestamp 列

如果你的数据库表不包含 timestamp 字段 created_atupdated_at,你可以使用 $timestamps = false 属性指定 Eloquent 模型不使用它们。

软删除-多行恢复

使用软删除时,可以在一个句子中恢复多行。

Model all-columns

当调用Eloquent's Model::all()时你可以指定返回哪些列。

To Fail or not to Fail

除了 findOrFail() 之外,还有 Eloquent 方法 firstOrFail(),如果没有找到查询记录,它将返回 404 页面。

列名修改

Eloquent Query Builder 中,您可以像在普通 SQL 查询中一样指定as以返回任何列的别名。

过滤结果集合

Eloquent 查询到结果之后,您可以使用 Collections 中的 map() 函数来修改行数据。

修改默认的 Timestamp 字段

如果您使用的是非 Laravel 数据库并且时间戳列的名称不同怎么办?也许,你有 create_timeupdate_time。 幸运的是,您也可以在模型中指定它们:

按照 created_at 快速排序

不用:

你可以更快的使用排序:

默认情况下 latest() 将按照 created_at排序。

有一个相反的方法 oldest(),按 created_at 升序排序:

您也可以指定另一列进行排序。 例如,如果你想使用 updated_at,你可以这样做:

当创建记录时自动修改某些列的值

如果您想在创建记录时生成一些 DB 列值,请将其添加到模型的 boot() 方法中。 例如,如果您有一个字段 「position」,并且想要赋值下一个可用位置(如 Country::max('position') + 1),请执行以下操作:

数据库原始查询计算运行得更快

使用类似 whereRaw() 方法的 SQL 原始查询,直接在查询中进行一些数据库特定计算,而不是在 Laravel 中,通常情况下结果会更快。 例如,如果您想获得注册后 30 天以上仍处于活跃状态的用户,代码如下:

不止一个范围

您可以在 Eloquent 中组合和链式调用查询范围,在一个 query 查询中使用多个范围。

Model文件内:

控制器内:

无需转换 Carbon

如果你正使用 whereDate() 查询今日的记录,可以直接使用 Carbonnow() 方法,它会自动转换为日期进行查询,而不需要指定 ->toDateString()。

根据首字母分组

你可以用任意自定义条件对 Eloquent 结果进行分组,下面的示例是由用户名的第一个单词进行分组:

永不更新某个字段

如果有一个数据库字段你想只设置一次并不想再次更新,您可以在Eloquent的模型上使用一个修改器设置该限制:

find 查询多条数据

find() 方法可以接受多参数, 传入多个值时会返回所有找到记录的集合,而不是一个模型:

@tahiriqbalnajam 提供

find 多个模型并返回多列

find 方法可接受多参数 使得结果集返回指定列的模型集合,而不是模型的所有列:

@tahiriqbalnajam 提供

按照键查找

您还可以使用 whereKey() 方法根据您指定的主键查找多条记录。(默认 id 但是你可以在 Eloquent 模型中覆盖掉)

使用 UUID 替换 auto-increment

您不想在模型中使用自动递增 ID?

迁移:

模型:

Laravel 中的子查询

从 Laravel 6 开始,您可以在 Eloquent 语句中使用 addSelect()方法,对列进行一些计算。

隐藏某些列

在进行 Eloquent 查询时,如果您想在返回中隐藏特定字段,最快捷的方法之一是在集合结果上添加 ->makeHidden()

确定 DB 报错

如果您想捕获 Eloquent Query 异常,请使用特定的 QueryException 代替默认的 Exception 类,您将能够获得SQL确切的错误代码。

软删除与查询构造器

注意 当你用到 Eloquent 时 软删除将会起作用,但是查询构造器不行。

SQL 声明

如果你需要执行一个简单的 SQL 查询,但没有方案 —— 比如改变数据库模式中的某些东西,只需执行 DB::statement()。

数据库事务

如果您执行了两个数据库操作,第二个可能会出错,那么您应该回滚第一个,对吗? 为此,我建议使用 DB Transactions,它在 Laravel 中非常简单:

更新或创建

如果你需要检查记录是否存在,然后更新它,或者创建一个新记录,你可以用一句话来完成 - 使用 Eloquent updateOrCreate() 方法:

保存时移除缓存

如果您缓存了一个键存储了 posts 这个集合,想在新增或更新时移除缓存键,可以在您的模型上调用静态的 saved 函数:

@pratiksh404 提供

修改 Created_at 和 Updated_at 的格式

想要改变 created_at 的格式,您可以在模型中添加一个方法,如下所示:

你可以在需要改变时间格式时使用 $entry->created_at_formatted ,它会返回 created_at 的属性如同 04:19 23, Aug 2020

你也可以用同样的方法更改 updated_at

在有需要的时候使用 $entry->updated_at_formatted。它会返回 updated_at 的属性如同: 04:19 23, Aug 2020

@syofyanzuhad 提供

数组类型存储到 JSON 中

如果你的输入字段有一个数组需要存储为 JSON 格式,你可以在模型中使用 $casts 属性。 这里的 images 是 JSON 属性

这样你可以以 JSON 格式存储它,但当你从 DB 中读取时,它会以数组方式使用。

@pratiksh404 提供

复制一个模型

如果你有两个非常相似的模型(比如送货地址和账单地址),而且你想要复制其中一个作为另一个,你可以使用 replicate() 方法并更改一部分属性。

官方文档的示例

降低内存占用

有时我们需要将大量的数据加载到内存中,比如:

但如果我们有非常庞大的数据库,这可能会很慢,因为 Laravel 会准备好模型类的对象。在这种情况下,Laravel 有一个很方便的函数 toBase()

通过调用这个方法,它将从数据库中获取数据,但它不会准备模型类。同时,向 get() 方法传递一个字段数组通常是个好主意,这样可以防止从数据库中获取所有字段。

忽略 $fillable / $guarded 并强制执行

如果你为其他开发者创建了一个 Laravel 模板, 然后你不能控制他们以后会在模型的 $fillable / $guarded 中填写什么,你可以使用 forceFill()

如果 name 不在team模型的 $fillable 中,怎么办?或者如果根本就没有 $fillable/$guarded, 怎么办?

这将忽略该查询的 $fillable 并强制执行。

3 层父子级结构

If you have a 3-level structure of parent-children, like categories in an e-shop, and you want to show the number of products on the third level, you can use with('yyy.yyy') and then add withCount() as a condition

使用 find 来搜索更多的记录

你不仅可以用 find() 来搜索单条记录,还可以用 IDs 的集合来搜索更多的记录,方法如下:

这么做:

如果是整数使用 "whereIntegerInRaw" 比 "whereIn "快。

@sachinkiranti 提供

失败时执行任何操作

当查询一条记录时,如果没有找到,你可能想执行一些操作。除了用 ->firstOrFail() 会抛出 404 之外,你可以在失败时执行任何操作,只需要使用

->firstOr(function() { ... })

检查记录是否存在否则显示 404

不要使用 find() ,然后再检查记录是否存在,使用 findOrFail()

更简单的方法:

条件语句为否时中止

可以使用 abort_if() 作为判断条件和抛出错误页面的快捷方式。

更简单的方法:

在删除模型之前执行任何额外的操作

我们可以使用 Model::delete() 执行额外的操作来覆盖原本的删除方法

@back2Lobby 提供

当你需要在保存数据到数据库时自动填充一个字段

当你需要在保存数据到数据库时自动填充一个字段 (例如: slug),使用模型观察者来代替重复编写代码

@sky_0xs 提供

获取查询语句的额外信息

你可以使用 explain() 方法来获取查询语句的额外信息

@amit_merchant 提供

在 Laravel 中使用 doesntExist() 方法

@ShawnHooper 提供

在一些模型的 boot () 方法中自动调用一个特性

如果你有一个特性,你想把它添加到几个模型中,自动调用它们的 boot() 方法,你可以把特性的方法作为 boot (特性名称)来调用

Laravel 的 find 方法,比只传一个 ID 更多的选择

在 Laravel 中有两种常见的方法来确定一个表是否为空表

在 Laravel 中,有两种常见的方法来确定一个表是否为空表。 直接在模型上使用 exists() 或者 count() 不等于一个返回严格的布尔值,另一个返回一个整数,你都可以在条件语句中使用。

@aschmelyun 提供

如何避免 property of non-object 错误

@coderahuljat 提供

Eloquent 数据改变后获取原始数据

Eloquent 模型数据改变后,你可以使用 getOriginal () 方法来获取原始数据

@devThaer 提供

一种更简单创建数据库的方法

Laravel 还可以使用 .sql 文件来更简单的创建数据库

@w3Nicolas 提供

Query构造器的crossJoinSub方法

使用CROSS JOIN交叉连接

@PascalBaljet 提供

belongsToMany 的中间表命名

为了决定 关系表的中间表, Eloquent 将按字母顺序连接两个相关的型号名称。

这意味着可以这样添加 “Post” 和 “Tag” 之间的连接:

但是,您可以自由重写此约定,并且需要在第二个参数中指定联接表。

如果希望明确说明主键,还可以将其作为第三个和第四个参数提供。

@iammikek 提供

根据 Pivot 字段排序

BelongsToMany::orderByPivot() 允许你直接对BelongsToMany 关系查询的结果集进行排序。

@PascalBaljet 提供

从数据库中查询一条记录

sole() 方法将会只返回一条匹配标准的记录。如果没找到,将会抛出NoRecordsFoundException 异常。如果发现了多条记录,抛出MultipleRecordsFoundException 异常

@PascalBaljet 提供

记录自动分块

each() 相同,但是更简单使用。chunks 自动将记录分成多块。

@PascalBaljet 提供

定时清理过期记录中的模型

定期清理过时记录的模型。有了这个特性,Laravel 将自动完成这项工作,只需调整内核类中 model:prune 命令的频率

此外,在修剪方法中,可以设置删除模型之前必须执行的操作:

@PascalBaljet 提供

不变的日期和对它们的强制转换

Laravel 8.53 介绍了 immutable_dateimmutable_datetime 将日期转换为 Immutable.

转换成 CarbonImmutable,而不是常规的 Carbon 实例。

@PascalBaljet 提供

findorfail方法也接收ids数组

findorfail 方法也接收 ids 数组。若无 ids 被找到 则失败。

若你想拿到一个模型的集合 并不想检测返回数量为你想得到的数量时很好用。

@timacdonald87 提供

从你的数据库中自动移除模型 prunableTrait

Laravel 8.50新特性:

你可以使用 prunable trait 从你的数据库中自动移除模型。举例:你可以在几天后永久移除软删除的模型。

@Philo01 提供

日期转换

当标记改变时 原来用布尔值来控制模型的可见性,现在可以使用something_at 替换。比如 一个产品变成可见:

@alexjgarrett 提供

多模型更新插入

upsert() 方法将插入/更新多个记录。

  • 第一个参数数组:要更新/插入的值

  • 第二个:查询表达式中使用的唯一标识列

  • 第三个:若记录存在 你想要更新的列

@mmartin_joo 提供

过滤结果集之后获取查询构造器

你可以使用 toQuery() 在过滤结果集之后获取查询构造器。

该方法在内部使用集合的第一个模型 并使用集合模型上的 “whereKey” 比较器。(此处翻译拗口 存疑。但是使用方法很明确。)

@RBilloir 提供

选择聚合计算相关模型

选择聚合计算相关模型。

需要指出的是在一组相关模型上使用 count 方法要慢一点。

@alexjgarrett 提供

自定义强制转换

你可以自定义强制转换来让 Laravel 自动格式化你的模型数据。

下面是一个在检索或更改用户名时将其大写的示例。

@mattkingshott 提供

保存中不要触发事件

若你不想触发模型事件 使用 saveQuietly() 方法

@mmartin_joo 提供

基于相关模型的平均值或总数排序

你是否曾需要基于关系模型的平均值或总数来排序?

这很简单

@mmartin_joo 提供

返回事务结果

若你有一个 DB 事务 并且你想返回它的结果 至少有两种方法:

从 query 中移除多个公共 scope

当使用 Global Scopes 时 你不仅可以使用多个 scope 而且可以在不需要的时候通过提供的 withoutGlobalScopes 方法移除他们

Link to docs

JSON 列属性排序

你可以使用 JSON 列属性排序

@brbcoding 提供

从第一个结果中获取单列的值

你可以使用 value 方法从第一个结果中获取单列的值。

@justsanjit 提供

检测模型属性是否被修改

想知道您对模型所做的更改是否改变了键的值吗?没问题,只需originalIsEquivalent 方法即可。

@mattkingshott 提供

定义访问器与修改器的新方法

Laravel 8.77:定义访问器与修改器的新方法

@Teacoders 提供

另外一种定义访问器与修改器的方法

在一些模型中想用同样的修改器 访问器 可以自定义转换。

只需要创建一个类 实现 CastsAttributes 实现两个方法

  • get 标识模型应当从数据库如何拿到

  • set 标识数据应当如何存储到数据库

然后你可以在模型中实现这个转换

@AhmedRezk 提供

在搜索第一条记录时,你可以执行一些操作

当搜索第一条记录时,你想执行一些操作,当你没有找到它时。 firstOrFail() 抛出一个 404 异常。

你可以用 firstOr(function() {}) 代替。 Laravel帮你解决了这个问题

@bhaidar 提供

直接将 created_at 日期转换为人性化格式

你知道吗,你可以使用 diffForHumans() 函数直接将 created_at 日期转换成人性化格式,如 1 分钟前,1 个月前。Laravel eloquent 默认在 created_at 字段上启用 Carbon 实例。

@vishal__2931 提供

通过获取器排序

我们不是按数据库级别排序,而是按返回的集合的获取器排序。

sortByDescsortBy 方法在集合中。

@bhaidar 提供

创建或发现特定模型的检查

如果你想检查特定模型是否被创建或找到,使用 wasRecentlyCreated 模型属性。

@sky_0xs 提供

带有数据库驱动的 Laravel Scout

使用 laravel v9,你可以使用 Laravel Scout(搜索)与数据库驱动程序。

@magarrent 提供

使用查询生成器的 value 方法

当你只需要检索一个列时,利用查询生成器上的 value 方法来执行一个更有效的查询。

@mattkingshott 提供

将数组传给 where 方法

你可以传递一个数组给 where 方法。

@cosmeescobedo 提供

从模型集合中返回主键

你知道 eloquent 中的 modelsKeys() 集合方法吗?它从模型集合中返回主键。

@iamharis010 提供

永久关闭懒加载

如果你想在你的应用程序中阻止懒加载,你只需要在你的 "AppServiceProvider" 的 "boot() " 方法中添加以下一行

但是,如果你想只在你的本地开发中启用这个功能,你可以改变上述代码。

@CatS0up 提供

使你所有的模型的字段批量分配

出于安全原因,这不是一个推荐的方法,但它是可能的。

当你想这样做时,你不需要为每个模型设置一个空的 $guarded 数组,像这样:

只要在你的 "AppServiceProvider" 中的 "boot() " 方法中添加以下一行:

现在,你的所有模型都是可以自动分配的。

@CatS0up 提供

在查询语句中隐藏列

如果你使用 Laravel v8.78 和 MySQL 8.0.23 及以上版本, 你可以将选择的列定义为 "invisible"。被定义为 "invisible" 的列将被隐藏在 "select *" 语句中。

然而,要做到这一点,我们必须在迁移中使用一个 invisible() 方法。

这将使选择的列从 "select *" 语句中隐藏

@CatS0up 提供

最后更新于

这有帮助吗?