PluginProbe
FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses / 2.10.0
FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses v2.10.0
2.10.0 2.10.01 2.9.1 2.9.0 2.8.1 2.8.0 2.7.7 2.7.5 2.7.0 2.6.01 2.6.0 2.5.0 2.4.01 trunk 1.0.90 1.0.91 1.0.92 1.0.93 1.0.94 1.0.95 1.0.96 1.0.97 1.0.98 1.0.99 1.1.0 All 77 releases
← All changes | app/Http/Controllers/ReactionController.php +34 -11 2.8.02.10.0 View file →
@@ -4,9 +4,11 @@
4 4
5 5 use FluentCommunity\App\Models\Comment;
6 6 use FluentCommunity\App\Models\Feed;
7 7 use FluentCommunity\App\Models\Reaction;
8 +use FluentCommunity\App\Models\XProfile;
8 9 use FluentCommunity\App\Services\FeedsHelper;
10 +use FluentCommunity\App\Services\Helper;
9 11 use FluentCommunity\App\Services\ProfileHelper;
10 12 use FluentCommunity\Framework\Http\Request\Request;
11 13 use FluentCommunity\Framework\Support\Arr;
12 14
@@ -81,9 +83,9 @@
81 83 $type = $request->get('react_type', 'like');
82 84 $type = in_array($type, ['like', 'bookmark'], true) ? $type : 'like';
83 85 $willRemove = $request->get('remove');
84 86
85 - if ($feed->status != 'published') {
87 + if (!in_array($feed->status, FeedsHelper::getViewableByLinkStatuses(), true)) {
86 88 return $this->sendError([
87 89 'message' => __('This post is not published yet', 'fluent-community')
88 90 ]);
89 91 }
@@ -123,20 +125,41 @@
123 125 'new_count' => $feed->reactions_count
124 126 ];
125 127 }
126 128
127 - $react = Reaction::create([
128 - 'user_id' => $currentUser->ID,
129 - 'object_id' => $feed->id,
130 - 'type' => $type,
131 - 'object_type' => 'feed'
132 - ]);
129 + // Serialize a user's concurrent reactions by locking their profile row,
130 + // so parallel add requests cannot each insert a duplicate reaction. The
131 + // like counter is updated atomically, so unrelated users never contend.
132 + $react = Helper::dbTransaction(function () use ($feed, $currentUser, $type) {
133 + XProfile::where('user_id', $currentUser->ID)->lockForUpdate()->first();
133 134
134 - if ($type == 'like') {
135 - $feed->reactions_count = $feed->reactions_count + 1;
136 - $feed->timestamps = false; // Don't update the updated_at timestamp
137 - $feed->save();
135 + $react = Reaction::where('user_id', $currentUser->ID)
136 + ->where('object_id', $feed->id)
137 + ->where('type', $type)
138 + ->objectType('feed')
139 + ->first();
138 140
141 + if ($react) {
142 + return $react;
143 + }
144 +
145 + $react = Reaction::create([
146 + 'user_id' => $currentUser->ID,
147 + 'object_id' => $feed->id,
148 + 'type' => $type,
149 + 'object_type' => 'feed'
150 + ]);
151 +
152 + if ($type == 'like') {
153 + // getQuery() so the atomic increment does not touch updated_at
154 + Feed::withoutGlobalScopes()->where('id', $feed->id)->getQuery()->increment('reactions_count');
155 + $feed->reactions_count = $feed->reactions_count + 1;
156 + }
157 +
158 + return $react;
159 + });
160 +
161 + if ($react->wasRecentlyCreated && $type == 'like') {
139 162 $react->load('xprofile');
140 163 do_action('fluent_community/feed/react_added', $react, $feed);
141 164 }
142 165