2012-02-22 4 views
1

그래서 Folder 및 FolderItem 모델이 있습니다.레일 : counter_cache가 after_update 콜백을 트리거하지 않음

나는 하나의 폴더의 itens의 수를 유지하기 위해 counter_cache을 사용하고

# == Schema Information 
# 
# Table name: folders 
# 
# id     :integer   not null, primary key 
# name    :string(255)  not null 
# parent_folder_id :integer 
# user_id   :integer   not null 
# folder_itens_count :integer   default(0) 
# created_at   :datetime 
# updated_at   :datetime 
# 

class Folder < ActiveRecord::Base 
... 

    belongs_to :parent_folder, :class_name => 'Folder' 
    has_many :child_folders, :class_name => 'Folder', :foreign_key => :parent_folder_id 
    has_many :folder_itens, :order => 'created_at DESC' 

    after_update { 
    update_parent_folder_itens_count 
    } 

    def update_parent_folder_itens_count 
    parent_folder = self.parent_folder 
    if self.folder_itens_count_changed? && parent_folder 
     quant_changed = self.folder_itens_count - self.folder_itens_count_was 
     parent_folder.increment(:folder_itens_count, quant_changed) 
    end 
    end 
end 

class FolderItem < ActiveRecord::Base 
... 
    belongs_to :folder, :counter_cache => :folder_itens_count 
end 

UPDATE. 그러나 폴더가 다른 폴더의 부모가 될 수 있으며 부모 폴더에 모든 자식의 counter_cache 합계를 더한 값과 함께 counter_cache 값을 갖기를 원합니다.

이렇게하려면 counter_cache 열에서 변경 한 내용을 캐싱하는 after_update 메서드를 넣으려고했지만 새로운 FolderItem이 만들어 질 때이 메서드는 호출되지 않습니다.

+0

실제 코드를 호출하지 않는 이유를 알 수있는 실제 코드는 훌륭합니다. FolderItem 클래스와 마찬가지로? counter_cache는 Folder.has_many : items 이외의 항목으로 만들어지지 않았기 때문에 완전한 사용자 정의 캐싱 코드를 작성해야하는 것처럼 보입니다. 당신이하고있는 일은 Folder.has_many : 폴더 그 자체에 많은 폴더가있는 폴더입니다. – tombruijn

+0

코드를 추가했습니다. 나는 여전히 가능하다고 생각하지만 counter_cache가 처리되는 방식 때문에 작동하지 않는다고 생각합니다. 중첩 된 중첩 SQL과 같아서 레일 코드가 무시됩니다. 그러나 나는 단지 확신하고 싶다. –

답변

1

이렇게하면됩니다.

폴더 테이블

$ rails g migration add_cache_counters_to_folders child_folders_count:integer \ 
                folder_items_count:integer \ 
                total_items_count:integer \ 
                sum_of_children_count:integer 

그리고 루비 코드에 일부 캐시 카운터 필드를 추가

class Folder < ActiveRecord::Base 
    belongs_to :parent_folder, class_name: 'Folder', counter_cache: :child_folders_count 
    has_many :child_folders, class_name: 'Folder', foreign_key: :parent_folder_id 
    has_many :folder_items 

    before_save :cache_counters 

    # Direct descendants - files and items within this folder 
    def total_items 
    child_folders_count + folder_items_count 
    end 

    # All descendants - files and items within all the folders in this folder 
    def sum_of_children 
    folder_items_count + child_folders.map(&:sum_of_children).inject(:+) 
    end 

private 
    def cache_counters 
    self.total_items_count = total_items 
    self.sum_of_children_count = sum_of_children 
    end 
end 

class FolderItem < ActiveRecord::Base 
    belongs_to :folder, counter_cache: true # folder_items_count 
end 

대규모 데이터 세트를 위해 응용 프로그램을 느려질 수 있도록 Folder#sum_of_children -method이, 재귀 있습니다. 좀 더 SQL-magic을 해보고 싶을 지 모르지만, 순수한 Ruby는 이것이 얻을 수있는 좋은 해결책에 가깝습니다. 나는 당신이 그것을 다른 방향으로 한 것을 보았습니다, 그것은 당신이 상향식에서부터 업데이트 할 때까지 마찬가지로 느릴 것입니다. (위에서 아래입니다.)

원하는지 모르겠지만 폴더 내의 항목 수를 캐싱 할 때 읽을 수있는 솔루션입니다.