使用 Laravel ORM 时的一个问题? - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
chenset
V2EX    PHP

使用 Laravel ORM 时的一个问题?

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

    我们团队使用 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;
    $icture = 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     Solana     3298 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 28ms UTC 11:57 PVG 19:57 LAX 04:57 JFK 07:57
    Do have faith in what you're doing.
    ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86