【问题标题】:Distant HasManyThrough遥远的有很多通过
【发布时间】:2014-11-14 07:03:41
【问题描述】:

我有四个模型:

  1. 用户
  2. 客户
  3. 商店
  4. 机会

关系是这样定义的:

  • 用户有很多客户
  • Client hasMany Store
  • 商店有很多机会
  • 用户 hasManyThrough Store,客户端(此工作)

问题是我试图通过内置的 Laravel 关系访问 User->Opportunity 关系,但如果没有自定义查询或机会上的附加 user_id 列,我似乎无法做到这一点表以允许直接访问(即使可以从 Store->Client 关系中推断出一个)。如果可以避免嵌套 foreach 循环,我也不喜欢它们。

我的问题:

在这种情况下,有没有办法更深入并直接访问用户的机会?实际Model代码及所有相关关系如下:

用户

class User extends Eloquent{
    public function clients(){
        return $this->hasMany('Client');
    }
    public function stores(){
        return $this->hasManyThrough('Store', 'Client');
    }
    public function proposals(){
        return $this->hasMany('Proposal');
    }
    public function opportunities(){ //This does the job, but I feel like it could be better
        return Opportunity::join('stores', 'stores.id', '=', 'opportunities.store_id')->
                            join('clients', 'clients.id', '=', 'stores.client_id')->
                            join('users', 'users.id', '=', 'clients.user_id')->
                            select('opportunities.*')->
                            where('users.id', $this->id);
    }
    public function getOpportunitiesAttribute(){ //This just helps mimic the hasManyThrough shorthand
        return $this->opportunities()->get();       
    }
}

客户

class Client extends Eloquent{
    public function stores(){
        return $this->hasMany('Store');
    }
    public function user(){
        return $this->belongsTo('User');
    }
    public function opportunities(){
        return $this->hasManyThrough('Opportunity', 'Store');
    }
}

商店

class Store extends Eloquent {
    public function client(){
        return $this->belongsTo('Client');
    }
    public function opportunities(){
        return $this->hasMany('Opportunity');
    }
}

机会

class Opportunity extends Eloquent {
    public function store(){
        return $this->belongsTo('Store');
    }
}

【问题讨论】:

  • 我不了解 Laravell,但根据我的 Java Persistance API 经验,我可以说您需要针对此类问题的自定义查询,或者,正如您所提到的,用户和机会之间的直接关系。
  • @ITroubs 是的,这就是它目前的样子。不过,如果我能把蛋糕也吃掉,那就太棒了。
  • 您使用joins 的解决方案几乎是您能做的最好的。否则,您可以为这种情况创建自定义关系,这将像 hasManyThorugh 一样工作,但嵌套级别多了 1 个。
  • 像你一样使用连接也是我处理远距离关系的方式。还没有找到另一种方法,但应该可以通过以类似于hasManyThrough() 的方式创建关系来实现。这个包做belongsToThrough() 并管理多个级别,所以可以作为灵感:github.com/znck/belongs-to-through

标签: php mysql laravel eloquent has-many-through


【解决方案1】:

我创建了一个无限级别的HasManyThrough 关系:Repository on GitHub

安装后可以这样使用:

class User extends Model {
    use \Staudenmeir\EloquentHasManyDeep\HasRelationships;

    public function opportunities() {
        return $this->hasManyDeep(Opportunity::class, [Client::class, Store::class]);
    }
}

【讨论】:

    【解决方案2】:

    我认为 Laravel 中没有这种方法。您必须创建自定义查询。此自定义查询可能非常昂贵,因为将执行多个查询。因此,据我说,对此的最佳解决方案是将 User 和 Opportunity 与外键相关联。

    但是,如果您不希望将用户和机会与外键链接起来,那么您可以创建一个自定义查询来处理这个问题。只需在 Opportunity 和 Client 模型之间添加一个“hasManyThrough”关系,例如,

        <?php
        class Client extends Eloquent{
            public function store(){
                return $this->hasMany('Store');
            }
            public function user(){
                return $this->belongsTo('User');
            }
    
            public function opportunity(){
                return $this->hasManyThrough('Opportunity', 'Store');
            }
        }
    

    然后在 User 模型中创建一个静态函数。

        <?php
    
        class User extends Eloquent implements UserInterface, RemindableInterface {
    
            use UserTrait, RemindableTrait;
    
            public function client(){
                return $this->hasMany('Client');
            }
            public function store(){
                return $this->hasManyThrough('Store', 'Client');
            }
    
            public static function getOpportunityOfUser($userId)
            {
                 $clients = User::find($userId)->client;
    
                foreach ($clients as $client) {
                    $opportunities[] = Client::find($client->id)->opportunity;
                }
    
                return $opportunities;
            }
        }
    

    现在您可以一次性访问已分配给用户的商机,

        Route::get('/', function()
        {   
             return $usersOpportunities = User::getOpportunityOfUser(1);
        });
    

    这将返回与 ID 为“1”的用户相关的所有客户的所有机会。

    【讨论】:

    • 不知道为什么你被否决了。这不是一个糟糕的处理方式,但是分离出这样的模型查询会导致对数据库的多个查询,这可能会变得很昂贵。
    • 另外,这也行不通。用户有很多客户。客户通过商店有很多机会(如我最初的示例所示)。这仅检索第一个客户端,而不是所有客户端。无论客户或商店如何,我都希望为给定用户返回所有机会。我认为您是对的,因为自定义查询是进行此操作的唯一方法。
    • 如果您想要所有客户的机会,那么您可以遍历 $client 数组并获取与该客户关联的机会。同样,需要将获取的机会数据推送到一个数组中,该数组将在循环完成后返回。通过这种方式,您将获得与具有相同用户 ID 的许多客户相关联的一系列机会。是的,它相当昂贵。您必须做很多事情,并且将运行多个查询。因此,最佳解决方案是使用外键链接 User 和 Opportunity。通过这种方式,您可以在一个查询中获得所需的数据。
    • 我刚刚编辑了我的答案。现在,它会检索与同一用户 ID 相关的所有客户及其机会。
    • 是一种不好的处理方式。计算您使用此代码运行的查询,然后您就会知道原因。同样创建人工关系(fk user-opportunities)也不是最好的解决方案。 OP joins 是实现这一目标的更有效和更好的方法。误导性的关系名称是反对这个答案的另一件事。这就是为什么 -1。
    猜你喜欢
    • 2012-04-15
    • 1970-01-01
    • 2020-03-10
    • 2012-03-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-09-22
    相关资源
    最近更新 更多