【问题标题】:One to many relationship count - difference in accessing relationship一对多关系计数 - 访问关系的差异
【发布时间】:2015-01-06 19:56:23
【问题描述】:

我有一对多关系 - Entry 可以有多个 Visits

在我的Entry 模型中,我有以下方法:

public function visits() {
    return $this->hasMany ('Visit', 'entry_id','id');
}

public function visitsCount() {
    return $this->hasMany('Visit', 'entry_id','id')
        ->selectRaw('SUM(number) as count')
        ->groupBy('entry_id');
}

在 Blade 中,我可以使用以下方法获取我的条目的访问次数:

{{$entry->visits()->count() }}

{{ $entry->visitsCount()->first()->count }}

如果我想创建访问器来获取访问次数,我可以定义:

public function getNrVisitsAttribute() 
{
    $related = $this->visitsCount()->first();
    return ($related) ? $related->count : 0;
}

现在我可以使用了:

{{ $entry->nr_visits }}

问题

  1. 在一些示例中,我看到这样定义这种关系:

    public function getNrVisitsAttribute()
    {    
        if (!array_key_exists('visitsCount', $this->relations)) {
            $this->load('visitsCount');
        }
        $related = $this->getRelation('visitsCount')->first();
        return ($related) ? $related->count : 0;
    }
    

    问题是:这和我一开始展示的“简单方法”有什么区别?是更快/使用更少的资源还是...?

  2. 为什么这种方法在这种情况下不起作用? $relatednull 所以访问器返回 0 而使用“简单方法”它返回正确的访问次数

我也尝试将visitsCount 方法关系从hasMany 更改为hasOne,但这并没有改变任何东西。

【问题讨论】:

    标签: laravel laravel-4 orm eloquent relationship


    【解决方案1】:

    假设您的架构在您的visits 表中反映每次访问的一条记录/模型,最好的方法是摆脱visitsCount() 关系并仅使用$entry->visits->count() 来检索对该条目的访问次数。

    这样做的原因是,一旦加载了这个关系,它就会简单地计算集合中的模型,而不是重新查询它们(如果使用单独的关系)

    如果您担心的是开销和不必要的查询:我的建议是将这些模型作为用户对象的子级急切加载到某个基本控制器中并缓存它,因此您真正需要重新查询任何其中之一是发生了变化。

    基础控制器:

    public function __construct(){
    
        if(!Cache::has('user-'.Auth::user()->id)){
            $this->user = User::with('entries.visits')->find(Auth::user()->id);
            Cache::put('user-'.Auth::user()->id, $this->user, 60);
        } else {
            $this->user = Cache::get('user-'.Auth::user()->id);
        }
    
    }
    

    然后在您的 Entry 模型上设置一个观察者,以在保存时刷新用户缓存。如果您使用MemcachedReddis,另一种可能性是使用缓存标签,这样您就不必在每次添加或修改Entry 模型时刷新整个用户的缓存。

    当然,这也假设每个 Entry 都与一个用户相关,但是,如果不是,并且您需要单独使用 Entry 作为父级,则可以应用相同的逻辑,通过移动 @ 987654331@ 类调用你的EntryController

    【讨论】:

    • 但问题是我通常并不真正需要和使用这些访问。我只想获取他们的号码,这样就不会启动任何额外的查询。
    【解决方案2】:

    1 你的关系不起作用,因为你没有select 外键:

    public function visitsCount() {
        // also use hasOne here
        return $this->hasOne('Visit', 'entry_id','id')
            ->selectRaw('entry_id, SUM(number) as count')
            ->groupBy('entry_id');
    }
    

    2 您的访问器应该与关系具有相同的名称以便有意义(这就是我首先创建这些访问器的原因):

    public function getVisitsCountAttribute() 
    {
       if ( ! array_key_exists('visitsCount', $this->relations)) $this->load('visitsCount');
    
       $related = $this->getRelation('visitsCount');
    
       return ($related) ? $related->count : 0;
    }
    

    此访问器只是一种以这种方式调用计数的便捷方式:

    $entry->visitsCount;
    

    而不是

    $entry->visitsCount->count;
    // or in your case with hasMany
    $entry->visitsCount->first()->count;
    

    所以它与性能无关。

    另外请注意,它并没有以不同的方式定义关系,它需要像上面那样定义关系。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-05-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多