chenset
V2EX  ›  PHP

使用 Laravel ORM 时的一个问题?

  •  
  •   chenset · Aug 12, 2016 · 7041 views
    This topic created in 3563 days ago, the information mentioned may be changed or developed.

    我们团队使用 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 replies    2016-08-18 00:51:10 +08:00
    HanSonJ
        1
    HanSonJ  
       Aug 12, 2016
    我会写到一个 service 上而不是 model 上。。。
    chenset
        2
    chenset  
    OP
       Aug 12, 2016
    @HanSonJ 哎呀, 不要歪楼拉. 一个方法也写 server 吗 ? 我们其实也有 repo. 但是只是想讨论这个例子中的问题而已.
    lxrmido
        3
    lxrmido  
       Aug 12, 2016
    其实哪怕不是 ORM ,我也经常遇到这类问题:

    “获取某个用户组下的用户列表” 该写在 User 模块里还是 UserGroup 模块里呢……
    justfindu
        4
    justfindu  
       Aug 12, 2016
    不用 belongs 或者 hasMany 进行关联么~
    hisway
        5
    hisway  
       Aug 12, 2016
    $this->hasMany(Car)
    keikeizhang
        6
    keikeizhang  
       Aug 12, 2016
    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  
       Aug 12, 2016
    第一个查询出 $user 后,再$user->myCars()就能取得数据啦。
    hisway
        8
    hisway  
       Aug 12, 2016
    @hisway User::find(1)->Car
    chenset
        9
    chenset  
    OP
       Aug 12, 2016
    能实现的方式很多拉 . 但是主要想问的还是这个方法应该写在 User 中还是 Car 中.
    chenset
        10
    chenset  
    OP
       Aug 12, 2016
    @lxrmido 对对, 结果呢 ? 谁打赢了 ?
    inmyfree
        11
    inmyfree  
       Aug 12, 2016
    写到 Car 里面,然后在 User 添加一个 carList 方法,里面调用 Car.list,完事
    inmyfree
        12
    inmyfree  
       Aug 12, 2016
    ps : android 写多了看出 OOM 了,哎
    gearh
        13
    gearh  
       Aug 12, 2016
    没有 ORM 时写在 user 中,用 ORM 直接关联啊,关联!
    freefcw
        14
    freefcw  
       Aug 12, 2016
    必须第一种撒。。第二种还自己去折腾,关键代码读起来 shi 一样不连贯
    tabris17
        15
    tabris17  
       Aug 12, 2016
    写在 User 里,这是个 hasMany 的关系
    sujin190
        16
    sujin190  
       Aug 12, 2016
    重点是当和用户相关的数据越来越多的时候用户 model 里岂不是越来越多方法,每个细分功能都会修改 user model ,这样不合适吧,除非所有细分功能共用,否则最好还是写到各自 model 里比较好
    solaya
        17
    solaya  
       Aug 12, 2016   ❤️ 1
    不是用 hsaMany belongsTo ???
    jiujianlu
        18
    jiujianlu  
       Aug 12, 2016
    使用第一种,将来使用关联关系重构的时候,改动比较小。
    chenset
        19
    chenset  
    OP
       Aug 12, 2016
    @sujin190 那么你是支持哪一种?
    freefcw
        20
    freefcw  
       Aug 12, 2016
    说错了。。。 Laravel 自带的 Relation 就是极好的,没必要这么折腾
    sujin190
        21
    sujin190  
       Aug 12, 2016
    @chenset 看你描述的这个场景我觉得还是第二种比较好
    laoyuan
        22
    laoyuan  
       Aug 12, 2016
    你们团队都没有看过文档的 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  
       Aug 12, 2016
    20%的回复提到了 hasMany(),靠谱!
    aggron
        24
    aggron  
       Aug 12, 2016
    赞同 @sujin190 我觉得还是放在 Car 中合适些,系统里与 User 相关的 model 太多了,放里面只会越来越庞大,个人觉得为了“更面向对象”放里面不是个好的设计
    随便选一个算是智者见智吧,可以参考下领域模型中的相关设计 http://www.oschina.net/question/98375_21638
    MrJing1992
        25
    MrJing1992  
       Aug 12, 2016
    按业务逻辑拆分功能。
    当你上 Car 这个业务时,就有了“用户所有的车”这个功能。当你要下线 Car 这个业务时,如果你还有去改 User 业务的代码那是不太合理的。
    所有结论就是:这个功能属于 Car 业务,就放在 Car 业务相关的代码里面。
    jinchun
        26
    jinchun  
       Aug 12, 2016   ❤️ 1
    model 里应该都是 hasMany 和 belongsTO 的关系。。你这代码应该是放在 repositories 里。具体看这个:
    http://slashnode.com/reusable-repository-design-in-laravel/
    lzsadam
        27
    lzsadam  
       Aug 12, 2016
    多表查询的话我是以主表为主的
    这里的主表应该是 car
    话说我也会写在 service 层
    chenset
        28
    chenset  
    OP
       Aug 12, 2016
    @laoyuan 你都没有看帖子吗? "不使用关联关系不手写 SQL 时, 我们团队在争论这两种写法的优劣, 想看看大家的看法."
    SayHaHa
        29
    SayHaHa  
       Aug 12, 2016 via Android
    支持写在 User 中
    Liang
        30
    Liang  
       Aug 12, 2016
    要看是否一辆车是否属于多个人,或一个人是否拥有多辆车。
    看数据表结构,应该是后者。

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

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

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

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

    -----抱歉,我并没有回答你的问题-----
    miaotaizi
        42
    miaotaizi  
       Aug 13, 2016 via iPhone
    @MrJing1992
    如果是我我就把这个方法写在 car 中,并且为一个静态方法.
    首先你取得的是 car ,而不是 user ,这一点就应该在 car 中
    .
    并且你这个方法想必跟 car 实体本身没什么太大关系,那这个方法就应该是一个静态方法.
    至于如何在 user 获取所有的 car ,那就是 user 如何去调用 car 类中的方法的事儿了.
    kzzhr
        43
    kzzhr  
       Aug 14, 2016 via Android
    @assad 用 relation 在将来可以写 whereHas (a.b.c.d ),手写 SQL 不会疯?
    assad
        44
    assad  
       Aug 14, 2016
    @kzzhr 我们的 SQL 都是一个连很多张表的,动不动就半页 SQL ,自然不能 ORM 了
    AbrahamGreyson
        45
    AbrahamGreyson  
       Aug 18, 2016
    个人觉得这和 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 的关联元素, 维护时可能仅仅需要修改特定类的方法,第二种则要修改分散到系统各处的条件,甚至更复杂些,有人用对象表示查询条件。
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Solana   ·   2916 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 140ms · UTC 13:41 · PVG 21:41 · LAX 06:41 · JFK 09:41
    ♥ Do have faith in what you're doing.