#ruby-on-rails #callback #counter-cache #after-save
#ruby-on-rails #обратный вызов #счетчик-кэш #после сохранения
Вопрос:
У меня есть модель, в которой включен counter_cache для ассоциации:
class Post
belongs_to :author, :counter_cache => true
end
class Author
has_many :posts
end
Я также использую фрагмент кэша для каждого ‘автора’, и я хочу, чтобы срок действия этого кэша истекал всякий раз, когда @author.posts_count
обновляется, поскольку это значение отображается в пользовательском интерфейсе. Проблема в том, что внутренние компоненты counter_cache
(increment_counter и decrement_counter), похоже, не вызывают обратные вызовы для Author, поэтому у меня нет способа узнать, когда это произойдет, за исключением истечения срока действия кэша внутри Post observer (или cache sweeper), который просто не кажется таким чистым.
Есть идеи?
Ответ №1:
У меня было аналогичное требование что-то сделать при обновлении счетчика, в моем случае мне нужно было что-то сделать, если счетчик counter_cache превысил определенное значение, моим решением было переопределить update_counters
метод следующим образом:
class Post < ApplicationRecord
belongs_to :author, :counter_cache => true
end
class Author < ApplicationRecord
has_many :posts
def self.update_counters(id, counters)
author = Author.find(id)
author.do_something! if author.posts_count counters['posts_count'] >= some_value
super(id, counters) # continue on with the normal update_counters flow.
end
end
Смотрите документацию update_counters для получения дополнительной информации.
Ответ №2:
Я тоже не смог заставить это работать. В конце концов, я сдался и написал свой собственный метод, подобный cache_counter, и вызываю его из after_save
обратного вызова.
Комментарии:
1. Спасибо, Dex, я также опубликую решение, которое я придумал
Ответ №3:
В итоге я оставил cache_counter таким, каким он был, но затем принудительно увеличил срок действия кэша с помощью обратного вызова Post после создания, вот так:
class Post
belongs_to :author, :counter_cache => true
after_create :force_author_cache_expiry
def force_author_cache_expiry
author.force_cache_expiry!
end
end
class Author
has_many :posts
def force_cache_expiry!
notify :force_expire_cache
end
end
тогда force_expire_cache(author)
есть метод в моем классе AuthorSweeper, срок действия фрагмента кэша которого истекает.
Ответ №4:
Что ж, у меня была такая же проблема, и я попал в ваш пост, но я обнаружил, что, поскольку обратные вызовы «after_» и «before_» являются общедоступными методами, вы можете сделать следующее:
class Author < ActiveRecord::Base
has_many :posts
Post.after_create do
# Do whatever you want, but...
self.class == Post # Beware of this
end
end
Я не знаю, насколько стандартно это делается, но методы являются общедоступными, так что, я думаю, все в порядке.
Если вы хотите, чтобы кэш и модели были разделены, вы можете использовать очистители.
Ответ №5:
У меня также есть требование следить за изменением счетчика. после извлечения исходного кода rails столбец counter_column изменяется с помощью прямого обновления SQL. Другими словами, это не вызовет никакого обратного вызова (в вашем случае, это не вызовет никакого обратного вызова в модели Author при обновлении Post).
в исходном коде rails столбец counter_column также был изменен обратным вызовом after_update.
Мой подход заключается в том, чтобы дать rails путь вверх, обновить counter_column самостоятельно:
class Post
belongs_to :author
after_update :update_author_posts_counter
def update_author_posts_counter
# need to update for both previous author and new author
# find_by will not raise exception if there isn't any record
author_was = Author.find_by(id: author_id_was)
if author_was
author_was.update_posts_count!
end
if author
author.update_posts_count!
end
end
end
class Author
has_many :posts
after_update :expires_cache, if: :posts_count_changed?
def expires_cache
# do whatever you want
end
def update_posts_count!
update(posts_count: posts.count)
end
end