前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【Laravel系列4.4】模型Eloquent ORM的使用(二)

【Laravel系列4.4】模型Eloquent ORM的使用(二)

作者头像
硬核项目经理
发布2023-03-03 13:15:54
2.8K0
发布2023-03-03 13:15:54
举报

模型Eloquent ORM的使用(二)

对于模型的探索我们还将继续。上篇文章中,只是简单地通过模型操作了一下数据库,并且学习了一下关联操作的知识。今天,我们继续学习模型中别的一些好玩的东西,不过,我们不会继续深入地学习模型中别的相关技巧。因为这些东西,都已经写在了官方文档中,而对于这个系列的文章来说,入个门,然后搞清楚原理才是最重要的,对于怎么使用这个事,大家自己好好研究就好了。而且,关于使用的内容,网上也有很多文章以及视频教程了,我也就不走别人的老路咯。

集合操作

其实这个集合操作并不是模型特有的,还记得在 查询构造器 中,我们查询列表的时候,总会在最后加一个 toArray() 吗?这个 toArray() 并不是 Builder 中的方法,如果不加这个 toArray() ,返回的是什么大家有没有注意过?

代码语言:javascript
复制
Route::get('model/test/collection', function () {
    $where = [];
    if(request()->name){
        $where[] = ['name', 'like', '%' . request()->name . '%'];
    }
    if(request()->sex){
        $where[] = ['sex', '=', request()->sex];
    }

    $list = \App\Models\MTest::where($where)
        ->orderBy('id', 'desc')
        ->limit(10)
        ->offset(0)
        ->get();

    dd($list);

// Illuminate\Database\Eloquent\Collection Object
// (
//     [items:protected] => Array
//         (
//             [0] => App\Models\MTest Object
//                 (
//                     [table:protected] => m_test
//                     [timestamps] => 
//                     [connection:protected] => mysql
//                     [primaryKey:protected] => id
//                     [keyType:protected] => int
//                     [incrementing] => 1
//                     [with:protected] => Array
//                         (
//                         )

//                     [withCount:protected] => Array
//                         (
//                         )

//                     [perPage:protected] => 15
//                     [exists] => 1
//                     [wasRecentlyCreated] => 
//                     [attributes:protected] => Array
//                         (
//                             [id] => 20
//                             [name] => Jim
//                             [sex] => 1
//                         )

//                     [original:protected] => Array
//                         (
//                             [id] => 20
//                             [name] => Jim
//                             [sex] => 1
//                         )
//         ………………
//         ………………
//         ………………

});

打印出来,我们会发现,它返回的是一个 laravel/framework/src/Illuminate/Database/Eloquent/Collection.php 对象,然后这个对象里面有个 items 属性,是一个数组。这个对象就是我们的模型组件中的集合对象,它包含很多集合操作的方法,如果以最简单的角度理解的话,其实它就是帮我们封装了很多数组操作函数。

这个集合对象有什么作用呢?其实很明显了,它提供了各种数组操作函数,就是有很多数组操作我们可以以对象的形式提供。比如说我们可以使用类似于 array_map() 的函数把集合中的对象全部转换成数组,还可以用一个类似于 array_column() 的函数只获取数据中的两个字段组成键值对形式的数据。

代码语言:javascript
复制
$list = \App\Models\MTest::where($where)
    ->orderBy('id', 'desc')
    ->limit(10)
    ->offset(0)
    ->get()
    ->pluck('name', 'id')
    ->toArray();
print_r($list);
//    Array
//    (
//        [20] => Jim
//        [19] => Mary
//        [18] => Susan
//        [17] => Tom
//        [16] => Peter
//        [15] => Jim
//        [14] => Mary
//        [13] => Susan
//        [12] => Tom
//        [11] => Peter
//    )

$list = \App\Models\MTest::where($where)
    ->orderBy('id', 'desc')
    ->limit(10)
    ->offset(0)
    ->get()
    ->map(function($item){ return $item->attributesToArray();})
    ->toArray();
print_r($list);
//    Array
//    (
//        [0] => Array
//        (
//            [id] => 20
//            [name] => Jim
//            [sex] => 1
//        )
//        [1] => Array
//        (
//            [id] => 19
//            [name] => Mary
//            [sex] => 2
//        )
//         ………………
//         ………………
//         ………………
//    )

上面的 plucks() 就是类似于 array_column() 的函数操作,用于获取数组元素指定的列值,这样生成的列表对于一些下拉框的接口非常友好。而另外一个 map() 函数就不用多说了,之前我们说过,Laravel 的 PDO 在默认查询构造器的情况下,走的是 PDO::FETCH_OBJ ,获得的集合结果中的每个数据都是一个 stdClass 对象,而在 Model 下,走的则是 PDO::FETCH_CLASS ,也就是会和我们指定的模型类关联上,获得的结果都是一个 App\Models\MTest Object 对象。而我们在日常的操作中,其实最习惯的是使用数组那种形式的操作,除开我们后面会讲的直接从配置入手来修改 PDO FETCH 属性之外,我们还可以用上面这个 map() 函数配合模型对象的 attributesToArray() 方法来将模型对象转换成数组格式。

当然,这个集合类相关的操作函数还有很多,这里我们只是演示了两个,具体的内容大家自行查阅一下官方手册。而源码呢?我也只给出具体的文件,大家自己去看看,里面的数组各种操作功能都非常经典。laravel/framework/src/Illuminate/Collections/Collection.php 是集合类,里面的方法大部分都调用的是 laravel/framework/src/Illuminate/Collections/Arr.php 里面的方法。

与路由绑定

对于一些获取单个信息的操作来说,模型是可以直接绑定到路由上的,比如下面这样:

代码语言:javascript
复制
Route::get('model/test/bindroute/{mTest}', function(\App\Models\MTest $mTest){
    dump($mTest);
    dump($mTest->name);
});

通过在回调函数中注入模型对象,就可以实现路由与模型的绑定。这里路由的 mTest 参数实际上就是我们查询数据的主键 ID ,然后模型就会自动为我们查询相应的数据并注入到 $mTest 参数中。除了直接绑定路由外,通过控制器实现也是一样的,我们只需要将回调函数变成指定的控制器方法即可。

代码语言:javascript
复制
Route::get('model/test/bindroute/controller/{mTest}', [\App\Http\Controllers\MTestController::class, 'show']);

class MTestController extends Controller
{
    public function show(MTest $mTest){
        dump($mTest);
        dump($mTest->name);
    }
}

快速序列化

对于模型的序列化来说,有两种形式的序列化,一是序列化为数组,二是序列化为 JSON 格式字符串。我们先看看第一种。

代码语言:javascript
复制
Route::get('model/test/ser/array', function(){
    $mTest = \App\Models\MTest::find(1);
    dump($mTest->toArray());
    dump($mTest->attributesToArray());
});

这个其实没有什么多说的,因为 toArray() 和 attributesToArray() 都是我们之前用过的,但是要注意的是,它们两个是不同的概念。toArray() 方法是一个递归方法,它会将所有的属性和关联(包括关联的关联)都转化成数组。而 attributesToArray() 只会将当前模型的属性转化为数组。

对于 JSON 格式,其实也只是调用一个 toJson() 方法就可以方便地实现。

代码语言:javascript
复制
Route::get('model/test/ser/json', function(){
    $mTest = \App\Models\MTest::find(1);
    dump($mTest->toJson());
    dump($mTest->toJson(JSON_PRETTY_PRINT));
});

toJson() 所接收到的参数就是我们日常可以使用的 JSON 系列常量。这个没有什么多说的,大家可以自己尝试一下。

模型调用的是查询构造器?

之前我们就一直在强调,原生查询 操作封装成 查询构造器 ,然后 查询构造器 进一步面向对象化的封装变成了 ORM 类型的 模型 。这是一个连续递进的关系,之前在 查询构造器 的文章中,我们已经看到了它的底层就是调用的 原生查询 操作。那么这回,我们再来看一下 Model 中的方法,在底层是不是调用的是 查询构造器 。

在所有模型都要继承的 laravel/framework/src/Illuminate/Database/Eloquent/Model.php 类中,我们很快就能发现一个 query() 静态方法。一路向下追踪,你马上就会发现它最后会调用到一个 newBaseQueryBuilder() 方法。

代码语言:javascript
复制
protected function newBaseQueryBuilder()
{
    return $this->getConnection()->query();
}

这个似乎就已经非常明显了。getConnection() 会返回一个之前讲过的工厂方法创建的 Connection 对象,而 query() 方法则会根据 Connection 创建一个 QueryBuilder 对象。剩下的还需要我们细讲吗?我觉得到这里真的已经非常清晰了。

然后我们来看一下这个 Model 基类中的其它方法,貌似没有发现 get() 、find() 之类的方法呀?这是怎么回事。别急,get() 、find() 不都是在 查询构造器 中的方法嘛。我们来看看 Model 中的 __call() 这个方法。

代码语言:javascript
复制
public function __call($method, $parameters)
{
    if (in_array($method, ['increment', 'decrement'])) {
        return $this->$method(...$parameters);
    }

    if ($resolver = (static::$relationResolvers[get_class($this)][$method] ?? null)) {
        return $resolver($this);
    }

    return $this->forwardCallTo($this->newQuery(), $method, $parameters);
}

当前类中找不到的方法就会进入 __call() 魔术方法中,在这里,我们看到它调用了 forwardCallTo() 方法,然后传递进去的是一个新的 查询构造器 对象和方法名以及参数。不过这里需要注意的是,模型默认生成的 QueryBuilder 是 llaravel/framework/src/Illuminate/Database/Eloquent/Builder.php 对象,而不是我们之前 查询构造器 中的 laravel/framework/src/Illuminate/Database/Query/Builder.php 对象。但 Eloquent\Builder 的内部持有的一个query 属性依然是 Query\Builder 对象,也就是说在底层,它依然是调用的我们熟悉的那个 查询构造器 来进行工作的。但是,这里划重点了,Eloquent\Builder 中有些方法是没有的,比如说 insert()、insertGetId() ,在模型中,使用 save() 就可以代替这两个方法的操作。说白了,直接 mTest->insert() 是会报错的,不过也有方法解决,只不过那样就完全像是使用一个 查询构造器 了,大家自己找找解决方案哦。

关于模型的内容还有很多,在这里我们就不一一讲解了。相关的源码也都在上面的源码文件路径中都给出了,其它有意思功能的源码大家可以自己尝试去分析一下,毕竟我们也学习了一段时间了,相信很多东西大家自己也能找到了。最主要的还是那句话,看框架真的就是在考验你的基础水平,找不到方法了怎么办?找 __call() 或者 __callStatic() ;找不到属性了怎么办?找 __set()、__get() ;来回调用看着好晕怎么办?Debug工具与编辑器的配置一定要配好,设计模式一定要理解透。相信有了这些,后面的内容你也可以写出来了,期待大家的分享哦!

参考文档:

https://learnku.com/docs/laravel/8.x/eloquent/9406

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-03-31,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 码农老张 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 模型Eloquent ORM的使用(二)
    • 集合操作
      • 与路由绑定
        • 快速序列化
          • 模型调用的是查询构造器?
            相关产品与服务
            文件存储
            文件存储(Cloud File Storage,CFS)为您提供安全可靠、可扩展的共享文件存储服务。文件存储可与腾讯云服务器、容器服务、批量计算等服务搭配使用,为多个计算节点提供容量和性能可弹性扩展的高性能共享存储。腾讯云文件存储的管理界面简单、易使用,可实现对现有应用的无缝集成;按实际用量付费,为您节约成本,简化 IT 运维工作。
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档