1 Jan 2014

has_and_belongs_to_many associationで counter_cacheを有効にする

has_and_belongs_to_many associationで counter_cacheを有効にするサンプル。

以下は Category has_and_belongs_to_many Website な関係が成立しています。HABTMでcounter_cacheを実施するには has_and_belongs_to_manyに:after_add, :after_removeなどのオプションを渡してフックするメソッドをしてあげれば良いです。

そうすることで website.categries = [category_foo, category_bar] としたときに以前のcategoriesとの差分についてcounter_cacheをincrement/decrementしてくれる。

Website自身のフック :after_save, :after_create, :after_destroyは関連を更新したときにはトリガーされないです。

*. #make!はmachinistからデータを作成しています
> Rails.env
# => "test"
> c1,c2,c3 = Category.make!,Category.make!,Category.make!
[deprecated] I18n.enforce_available_locales will default to true in the future. If you really want to skip validation of your locale you can set I18n.enforce_available_locales = false to avoid this message.
(0.4ms) BEGIN
test
SQL (0.5ms) INSERT INTO `categories` (`created_at`, `description`, `name`, `updated_at`) VALUES ('2014-01-01 04:08:14', 'syndicate strategic web-readiness', 'Books', '2014-01-01 04:08:14')
(1.9ms) COMMIT
Category Load (0.3ms) SELECT `categories`.* FROM `categories` WHERE `categories`.`id` = 16 LIMIT 1
(0.1ms) BEGIN
test
SQL (0.2ms) INSERT INTO `categories` (`created_at`, `description`, `name`, `updated_at`) VALUES ('2014-01-01 04:08:14', 'enhance clicks-and-mortar bandwidth', 'Electronics & Computers', '2014-01-01 04:08:14')
(0.8ms) COMMIT
Category Load (0.2ms) SELECT `categories`.* FROM `categories` WHERE `categories`.`id` = 17 LIMIT 1
(0.1ms) BEGIN
test
SQL (0.1ms) INSERT INTO `categories` (`created_at`, `description`, `name`, `updated_at`) VALUES ('2014-01-01 04:08:14', 'leverage front-end synergies', 'Home, Garden & Tools', '2014-01-01 04:08:14')
(0.3ms) COMMIT
Category Load (0.1ms) SELECT `categories`.* FROM `categories` WHERE `categories`.`id` = 18 LIMIT 1
> website = Website.make!(categories: [c1, c2, c3])
SQL (0.7ms) UPDATE `categories` SET `websites_count` = COALESCE(`websites_count`, 0) + 1 WHERE `categories`.`id` = 16
SQL (0.4ms) UPDATE `categories` SET `websites_count` = COALESCE(`websites_count`, 0) + 1 WHERE `categories`.`id` = 17
SQL (0.5ms) UPDATE `categories` SET `websites_count` = COALESCE(`websites_count`, 0) + 1 WHERE `categories`.`id` = 18
(0.2ms) BEGIN
SQL (0.3ms) INSERT INTO `websites` (`created_at`, `description`, `likes_count`, `name`, `updated_at`, `url`) VALUES ('2014-01-01 04:08:17', 'engage back-end metrics', 48486642, 'Elmer Pfeffer', '2014-01-01 04:08:17', 'http://runolfonprohaska.name')
(0.1ms) INSERT INTO `categories_websites` (`website_id`, `category_id`) VALUES (37, 16)
(0.1ms) INSERT INTO `categories_websites` (`website_id`, `category_id`) VALUES (37, 17)
(0.1ms) INSERT INTO `categories_websites` (`website_id`, `category_id`) VALUES (37, 18)
(0.3ms) COMMIT
Website Load (0.2ms) SELECT `websites`.* FROM `websites` WHERE `websites`.`id` = 37 LIMIT 1
=> #<Website id: 37, name: "Elmer Pfeffer", description: "engage back-end metrics", url: "http://runolfonprohaska.name", likes_count: 48486642, comments_count: 0, created_at: "2014-01-01 04:08:17", updated_at: "2014-01-01 04:08:17", logo_file_name: nil, logo_content_type: nil, logo_file_size: nil, logo_updated_at: nil>
> website.categories = [c1, c2]
Category Load (0.4ms) SELECT `categories`.* FROM `categories` INNER JOIN `categories_websites` ON `categories`.`id` = `categories_websites`.`category_id` WHERE `categories_websites`.`website_id` = 37
(0.1ms) BEGIN
(0.2ms) DELETE FROM `categories_websites` WHERE `categories_websites`.`website_id` = 37 AND `categories_websites`.`category_id` IN (18)
SQL (0.1ms) UPDATE `categories` SET `websites_count` = COALESCE(`websites_count`, 0) - 1 WHERE `categories`.`id` = 18
(1.5ms) COMMIT
=> [#<Category id: 16, name: "Books", description: "syndicate strategic web-readiness", created_at: "2014-01-01 04:08:14", updated_at: "2014-01-01 04:08:14", websites_count: nil>,
#<Category id: 17, name: "Electronics & Computers", description: "enhance clicks-and-mortar bandwidth", created_at: "2014-01-01 04:08:14", updated_at: "2014-01-01 04:08:14", websites_count: nil>]
view raw on_pry.rb hosted with ❤ by GitHub
class Website < ActiveRecord::Base
has_and_belongs_to_many :categories, after_add: :increment_categories_counter_cache, after_remove: :decrement_categories_counter_cache
private
def increment_categories_counter_cache(category)
Category.increment_counter(:websites_count, category.id )
end
def decrement_categories_counter_cache(category)
Category.decrement_counter(:websites_count, category.id )
end
end
view raw Website.rb hosted with ❤ by GitHub