@@ -17,7 +17,7 @@ module ClassMethods
17
17
#
18
18
# ==== Parameters
19
19
#
20
- # * +id+ - The id of the object you wish to reset a counter on.
20
+ # * +id+ - The id of the object you wish to reset a counter on or an array of ids .
21
21
# * +counters+ - One or more association counters to reset. Association name or counter name can be given.
22
22
# * <tt>:touch</tt> - Touch timestamp columns when updating.
23
23
# Pass +true+ to touch +updated_at+ and/or +updated_on+. Pass a symbol to
@@ -28,13 +28,25 @@ module ClassMethods
28
28
# # For the Post with id #1, reset the comments_count
29
29
# Post.reset_counters(1, :comments)
30
30
#
31
+ # # For posts with ids #1 and #2, reset the comments_count
32
+ # Post.reset_counters([1, 2], :comments)
33
+ #
31
34
# # Like above, but also touch the +updated_at+ and/or +updated_on+
32
35
# # attributes.
33
36
# Post.reset_counters(1, :comments, touch: true)
34
37
def reset_counters ( id , *counters , touch : nil )
35
- object = find ( id )
38
+ ids = if composite_primary_key?
39
+ if id . first . is_a? ( Array )
40
+ id
41
+ else
42
+ [ id ]
43
+ end
44
+ else
45
+ Array ( id )
46
+ end
47
+
48
+ updates = Hash . new { |h , k | h [ k ] = { } }
36
49
37
- updates = { }
38
50
counters . each do |counter_association |
39
51
has_many_association = _reflect_on_association ( counter_association )
40
52
unless has_many_association
@@ -48,25 +60,38 @@ def reset_counters(id, *counters, touch: nil)
48
60
has_many_association = has_many_association . through_reflection
49
61
end
50
62
63
+ counter_association = counter_association . to_sym
51
64
foreign_key = has_many_association . foreign_key . to_s
52
65
child_class = has_many_association . klass
53
66
reflection = child_class . _reflections . values . find { |e | e . belongs_to? && e . foreign_key . to_s == foreign_key && e . options [ :counter_cache ] . present? }
54
67
counter_name = reflection . counter_cache_column
55
68
56
- count_was = object . send ( counter_name )
57
- count = object . send ( counter_association ) . count ( :all )
58
- updates [ counter_name ] = count if count != count_was
69
+ counts =
70
+ unscoped
71
+ . joins ( counter_association )
72
+ . where ( primary_key => ids )
73
+ . group ( primary_key )
74
+ . count ( :all )
75
+
76
+ ids . each do |id |
77
+ updates [ id ] . merge! ( counter_name => counts [ id ] || 0 )
78
+ end
59
79
end
60
80
61
81
if touch
62
82
names = touch if touch != true
63
83
names = Array . wrap ( names )
64
84
options = names . extract_options!
65
85
touch_updates = touch_attributes_with_time ( *names , **options )
66
- updates . merge! ( touch_updates )
86
+
87
+ updates . each_value do |record_updates |
88
+ record_updates . merge! ( touch_updates )
89
+ end
67
90
end
68
91
69
- unscoped . where ( primary_key => [ object . id ] ) . update_all ( updates ) if updates . any?
92
+ updates . each do |id , record_updates |
93
+ unscoped . where ( primary_key => [ id ] ) . update_all ( record_updates )
94
+ end
70
95
71
96
true
72
97
end
0 commit comments