V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
chenset
V2EX  ›  PHP

使用 Laravel ORM 时的一个问题?

  •  
  •   chenset · 2016-08-12 08:45:13 +08:00 · 6218 次点击
    这是一个创建于 3030 天前的主题,其中的信息可能已经有所发展或是发生改变。

    我们团队使用 ORM 写了两个例子, 争论这两个例子的优缺点. 麻烦帮忙分析一下.

    问题: 需要取得用户的所有车辆, 这个方法应该写在 User 中还是 Car 中.

    表结构: user: [ id, ]

    car: [ id, user_id, car_status, ]

    Laravel ORM 代码:

    <?php
    class User extends \Illuminate\Database\Eloquent\Model
    {
        // 例子 1: 将取用户所有车辆的方法写在 User 中, user ID 参数使用$this->id
        // 例子 1 的理由是: 因为要取得用户的所属车辆, 出发点是 User 对象所以方法应该写在 User 中, 这样会更加面向对象.
        public function myCars()
        {
            return Car::where(['user_id' => $this->id, 'car_status' => 1])->get();
        }
    }
    
    class Car extends \Illuminate\Database\Eloquent\Model
    {
        // 例子 2: 将取用户所有车辆的方法写在 Car 中, user ID 参数使用通过方法传递进来
        // 例子 2 的理由是: 因为所查询的是 Car 表, user ID 应该作为 Car 的一个属性传递给 Car.list 方法,而且$moreCondition 可以日后增加更多筛选条件复用性好.
        public function list(int $userID, $moreCondition...)
        {
            return $this->where(['user_id' => $userID, $moreCondition...])->get();
        }
    }
    

    不使用关联关系不手写 SQL 时, 我们团队在争论这两种写法的优劣, 想看看大家的看法.

    例子 1: 将取用户所有车辆的方法写在 User 中, user ID 参数使用$this->id 例子 1 的理由是: 因为要取得用户的所属车辆, 出发点是 User 对象所以方法应该写在 User 中, 这样会更加面向对象.

    例子 2: 将取用户所有车辆的方法写在 Car 中, user ID 参数使用通过方法传递进来 例子 2 的理由是: 因为所查询的是 Car 表, user ID 应该作为 Car 的一个属性传递给 Car.list 方法,而且$moreCondition 可以日后增加更多筛选条件复用性好.

    主要想问的是这个方法应该写在 User 中还是 Car 中.

    45 条回复    2016-08-18 00:51:10 +08:00
    HanSonJ
        1
    HanSonJ  
       2016-08-12 08:54:19 +08:00
    我会写到一个 service 上而不是 model 上。。。
    chenset
        2
    chenset  
    OP
       2016-08-12 08:57:01 +08:00
    @HanSonJ 哎呀, 不要歪楼拉. 一个方法也写 server 吗 ? 我们其实也有 repo. 但是只是想讨论这个例子中的问题而已.
    lxrmido
        3
    lxrmido  
       2016-08-12 08:58:30 +08:00
    其实哪怕不是 ORM ,我也经常遇到这类问题:

    “获取某个用户组下的用户列表” 该写在 User 模块里还是 UserGroup 模块里呢……
    justfindu
        4
    justfindu  
       2016-08-12 09:00:07 +08:00
    不用 belongs 或者 hasMany 进行关联么~
    hisway
        5
    hisway  
       2016-08-12 09:00:20 +08:00
    $this->hasMany(Car)
    keikeizhang
        6
    keikeizhang  
       2016-08-12 09:02:50 +08:00
    class PictureController extends Controller
    {
    /**
    * Display a listing of the resource.
    *
    * @return \Illuminate\Http\Response
    */
    public function index(Request $req)
    {
    $user = $req->user();
    $dealer = $user->dealer;
    $picture = Picture::orderBy('id','desc')->where('dealer','=',$dealer)->get();
    return view('picture.index', compact('picture'));
    }
    }

    取出一个用户拥有的所有图片...
    phpcxy
        7
    phpcxy  
       2016-08-12 09:03:01 +08:00
    第一个查询出 $user 后,再$user->myCars()就能取得数据啦。
    hisway
        8
    hisway  
       2016-08-12 09:03:02 +08:00
    @hisway User::find(1)->Car
    chenset
        9
    chenset  
    OP
       2016-08-12 09:09:03 +08:00
    能实现的方式很多拉 . 但是主要想问的还是这个方法应该写在 User 中还是 Car 中.
    chenset
        10
    chenset  
    OP
       2016-08-12 09:09:25 +08:00
    @lxrmido 对对, 结果呢 ? 谁打赢了 ?
    inmyfree
        11
    inmyfree  
       2016-08-12 09:10:04 +08:00
    写到 Car 里面,然后在 User 添加一个 carList 方法,里面调用 Car.list,完事
    inmyfree
        12
    inmyfree  
       2016-08-12 09:10:33 +08:00
    ps : android 写多了看出 OOM 了,哎
    gearh
        13
    gearh  
       2016-08-12 09:11:58 +08:00
    没有 ORM 时写在 user 中,用 ORM 直接关联啊,关联!
    freefcw
        14
    freefcw  
       2016-08-12 09:14:20 +08:00
    必须第一种撒。。第二种还自己去折腾,关键代码读起来 shi 一样不连贯
    tabris17
        15
    tabris17  
       2016-08-12 09:16:17 +08:00
    写在 User 里,这是个 hasMany 的关系
    sujin190
        16
    sujin190  
       2016-08-12 09:16:24 +08:00
    重点是当和用户相关的数据越来越多的时候用户 model 里岂不是越来越多方法,每个细分功能都会修改 user model ,这样不合适吧,除非所有细分功能共用,否则最好还是写到各自 model 里比较好
    solaya
        17
    solaya  
       2016-08-12 09:22:16 +08:00   ❤️ 1
    不是用 hsaMany belongsTo ???
    jiujianlu
        18
    jiujianlu  
       2016-08-12 09:23:19 +08:00
    使用第一种,将来使用关联关系重构的时候,改动比较小。
    chenset
        19
    chenset  
    OP
       2016-08-12 09:25:29 +08:00
    @sujin190 那么你是支持哪一种?
    freefcw
        20
    freefcw  
       2016-08-12 09:26:22 +08:00
    说错了。。。 Laravel 自带的 Relation 就是极好的,没必要这么折腾
    sujin190
        21
    sujin190  
       2016-08-12 09:43:53 +08:00
    @chenset 看你描述的这个场景我觉得还是第二种比较好
    laoyuan
        22
    laoyuan  
       2016-08-12 09:52:39 +08:00
    你们团队都没有看过文档的 Tutorials 么
    https://laravel.com/docs/5.2/quickstart-intermediate#eloquent-relationships

    class User extends Authenticatable
    {
    // Other Eloquent Properties...

    /**
    * Get all of the tasks for the user.
    */
    public function tasks()
    {
    return $this->hasMany(Task::class);
    }
    laoyuan
        23
    laoyuan  
       2016-08-12 09:53:55 +08:00
    20%的回复提到了 hasMany(),靠谱!
    aggron
        24
    aggron  
       2016-08-12 10:02:48 +08:00
    赞同 @sujin190 我觉得还是放在 Car 中合适些,系统里与 User 相关的 model 太多了,放里面只会越来越庞大,个人觉得为了“更面向对象”放里面不是个好的设计
    随便选一个算是智者见智吧,可以参考下领域模型中的相关设计 http://www.oschina.net/question/98375_21638
    MrJing1992
        25
    MrJing1992  
       2016-08-12 10:09:15 +08:00
    按业务逻辑拆分功能。
    当你上 Car 这个业务时,就有了“用户所有的车”这个功能。当你要下线 Car 这个业务时,如果你还有去改 User 业务的代码那是不太合理的。
    所有结论就是:这个功能属于 Car 业务,就放在 Car 业务相关的代码里面。
    jinchun
        26
    jinchun  
       2016-08-12 10:55:27 +08:00   ❤️ 1
    model 里应该都是 hasMany 和 belongsTO 的关系。。你这代码应该是放在 repositories 里。具体看这个:
    http://slashnode.com/reusable-repository-design-in-laravel/
    lzsadam
        27
    lzsadam  
       2016-08-12 11:10:15 +08:00
    多表查询的话我是以主表为主的
    这里的主表应该是 car
    话说我也会写在 service 层
    chenset
        28
    chenset  
    OP
       2016-08-12 11:23:28 +08:00
    @laoyuan 你都没有看帖子吗? "不使用关联关系不手写 SQL 时, 我们团队在争论这两种写法的优劣, 想看看大家的看法."
    SayHaHa
        29
    SayHaHa  
       2016-08-12 11:28:33 +08:00 via Android
    支持写在 User 中
    Liang
        30
    Liang  
       2016-08-12 11:43:52 +08:00
    要看是否一辆车是否属于多个人,或一个人是否拥有多辆车。
    看数据表结构,应该是后者。

    我习惯会把"->"当成"的"。$user->cars()会更好, User 中使用 hasMany()。

    个人习惯~不代表权威
    mahone3297
        31
    mahone3297  
       2016-08-12 11:49:00 +08:00
    写在 User 中?那一个用户,如果有多辆车,那是 User 表中多条数据?那还叫 User 表? User 表不是一个 user 一条数据?
    shuson
        32
    shuson  
       2016-08-12 11:50:47 +08:00
    hasMany 是正解,不要 anti pattern
    wanghanlin
        33
    wanghanlin  
       2016-08-12 12:00:11 +08:00
    还是推荐使用 relationships ,没必要这么搞,不过回到这个问题,还是放 User 里合适
    assad
        34
    assad  
       2016-08-12 13:47:42 +08:00
    一个破 SQL ,费用要什么 HasMany ,遇到复杂设计的,奇葩需求的,统统不好使。真心不喜欢框架这种 ORM ,简单的业务还行,一遇到复杂的需求,还是直接写 SQL ,建立一个 model ,自己处理的好!
    justfindu
        35
    justfindu  
       2016-08-12 13:51:38 +08:00
    按你这个设计 那就应该放第二种, 并且 user_id 不作为必要条件 这样更能复用
    silov
        36
    silov  
       2016-08-12 13:53:09 +08:00
    hasMany 和 belongsTo 什么的可以很好的完成你的需求,尤其是配合预加载的时候。

    顺便吐槽一下第一种,你在一个 Model 里面写另一个 model 的 where 条件查询,不觉得怪么?
    zhangchaozheng
        37
    zhangchaozheng  
       2016-08-12 16:28:10 +08:00
    孩纸们,忘掉 ORM 这种东西吧!
    pein
        38
    pein  
       2016-08-12 16:58:29 +08:00
    @inmyfree 哈,你这把 android 黑得毫无破绽啊,高!
    cxbig
        39
    cxbig  
       2016-08-12 17:59:15 +08:00
    1. 肯定会定义关联
    2. User->myCars()
    klgd
        40
    klgd  
       2016-08-12 22:45:20 +08:00
    如果在不使用关联关系的情况下,我选第二种,各自操作各自的表,互不干扰
    当然要是用关联关系的话,那就是 User 里使用 hasMany

    ps :我觉得比较规范点儿的表结构是 users , cars , user_car 三个表
    caola
        41
    caola  
       2016-08-13 01:22:55 +08:00
    表的名称应该为复数,多数情况下是加上个“ s ”, user 表名应该为 users ,这也是 laravel 默认的命名方式。
    ORM 中如果不是这样复数的表,你可能要多写几个代码

    -----抱歉,我并没有回答你的问题-----
    miaotaizi
        42
    miaotaizi  
       2016-08-13 11:33:01 +08:00 via iPhone
    @MrJing1992
    如果是我我就把这个方法写在 car 中,并且为一个静态方法.
    首先你取得的是 car ,而不是 user ,这一点就应该在 car 中
    .
    并且你这个方法想必跟 car 实体本身没什么太大关系,那这个方法就应该是一个静态方法.
    至于如何在 user 获取所有的 car ,那就是 user 如何去调用 car 类中的方法的事儿了.
    kzzhr
        43
    kzzhr  
       2016-08-14 09:04:11 +08:00 via Android
    @assad 用 relation 在将来可以写 whereHas (a.b.c.d ),手写 SQL 不会疯?
    assad
        44
    assad  
       2016-08-14 11:46:59 +08:00
    @kzzhr 我们的 SQL 都是一个连很多张表的,动不动就半页 SQL ,自然不能 ORM 了
    AbrahamGreyson
        45
    AbrahamGreyson  
       2016-08-18 00:51:10 +08:00
    个人觉得这和 ORM 与否没什么关系,不是 Eloquent 才会出现的疑惑,更和面向对象没关系,因为我们的开发,早已经假设一切问题都建立在 oop 之上。并不是说 this.id 就是 oop , oop 更多的是一种思考问题的方式。

    我们从思考问题的方式出发。

    首先还得看业务需求,如果 User 是一个模块 /领域模型 /聚集的根(主入口,唯一标识),那么它内部所维护的各个值对象理应通过 User 来拿,我们姑且以模块来称呼这个 User 的功能,如果 User 这个模块的一个重要功能是按照特定的 User 实体去获得属于他的 /关联的其它对象,我想不出为什么不直接写在 User 数据库类中,因为,特定的用户不存在的时候,与他相关的一切其它值都将失去意义。 Laravel ORM 把对象关系映射、实体、模块聚集浓缩到了一层中,直接去在 User 对象中访问模块内部的其它元素便是了。然后你可能因为测试 /关注分离 /业务和基础设施分离等原因,可能会在客户端代码和 User 对象中增加类似 Repository 、 Factory 之类的东西,这个时候 Factory 返回的将是一个 User 模块的聚集,它的根是 User 对象,它的其它元素,就是你自己维护的与 User 对象相关的其它实体。

    尽管这样我也见过有人用另一种方式,也就是使用动态的数据库查询去返回对象(楼主的方法 2 ),这么做的目的是给 User 模块瘦身,但是我个人偏好第一种。第一种方式拿 User 的关联元素, 维护时可能仅仅需要修改特定类的方法,第二种则要修改分散到系统各处的条件,甚至更复杂些,有人用对象表示查询条件。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1175 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 23ms · UTC 18:47 · PVG 02:47 · LAX 10:47 · JFK 13:47
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.