1 | <?php |
||||
2 | |||||
3 | /* |
||||
4 | * This file is part of the overtrue/laravel-follow |
||||
5 | * |
||||
6 | * (c) overtrue <[email protected]> |
||||
7 | * |
||||
8 | * This source file is subject to the MIT license that is bundled |
||||
9 | * with this source code in the file LICENSE. |
||||
10 | */ |
||||
11 | |||||
12 | namespace Overtrue\LaravelFollow; |
||||
13 | |||||
14 | use Carbon\Carbon; |
||||
15 | use Illuminate\Database\Eloquent\Model; |
||||
16 | use Illuminate\Database\Eloquent\Relations\MorphToMany; |
||||
17 | use Illuminate\Database\Eloquent\SoftDeletes; |
||||
18 | use Overtrue\LaravelFollow\Events\RelationAttached; |
||||
19 | use Overtrue\LaravelFollow\Events\RelationAttaching; |
||||
20 | use Overtrue\LaravelFollow\Events\RelationDetached; |
||||
21 | use Overtrue\LaravelFollow\Events\RelationDetaching; |
||||
22 | use Overtrue\LaravelFollow\Events\RelationToggled; |
||||
23 | use Overtrue\LaravelFollow\Events\RelationToggling; |
||||
24 | use stdClass; |
||||
25 | |||||
26 | /** |
||||
27 | * Class Follow. |
||||
28 | */ |
||||
29 | class Follow |
||||
30 | { |
||||
31 | use SoftDeletes; |
||||
0 ignored issues
–
show
Bug
introduced
by
Loading history...
|
|||||
32 | |||||
33 | const RELATION_LIKE = 'like'; |
||||
34 | |||||
35 | const RELATION_FOLLOW = 'follow'; |
||||
36 | |||||
37 | const RELATION_BOOKMARK = 'bookmark'; |
||||
38 | |||||
39 | const RELATION_SUBSCRIBE = 'subscribe'; |
||||
40 | |||||
41 | const RELATION_FAVORITE = 'favorite'; |
||||
42 | |||||
43 | const RELATION_UPVOTE = 'upvote'; |
||||
44 | |||||
45 | const RELATION_DOWNVOTE = 'downvote'; |
||||
46 | |||||
47 | const RELATION_TYPES = [ |
||||
48 | 'likes' => 'like', |
||||
49 | 'likers' => 'like', |
||||
50 | 'fans' => 'like', |
||||
51 | 'followings' => 'follow', |
||||
52 | 'followers' => 'follow', |
||||
53 | 'favoriters' => 'favorite', |
||||
54 | 'favorites' => 'favorite', |
||||
55 | 'bookmarkers' => 'bookmark', |
||||
56 | 'bookmarks' => 'bookmark', |
||||
57 | 'subscriptions' => 'subscribe', |
||||
58 | 'subscribers' => 'subscribe', |
||||
59 | 'upvotes' => 'upvote', |
||||
60 | 'upvoters' => 'upvote', |
||||
61 | 'downvotes' => 'downvote', |
||||
62 | 'downvoters' => 'downvote', |
||||
63 | ]; |
||||
64 | |||||
65 | /** |
||||
66 | * @param \Illuminate\Database\Eloquent\Model $model |
||||
67 | * @param string $relation |
||||
68 | * @param array|string|\Illuminate\Database\Eloquent\Model $target |
||||
69 | * @param string $class |
||||
70 | * |
||||
71 | * @return bool |
||||
72 | */ |
||||
73 | public static function isRelationExists(Model $model, $relation, $target, $class = null) |
||||
74 | { |
||||
75 | $target = self::formatTargets($target, $class ?: config('follow.user_model')); |
||||
76 | |||||
77 | if ($model->relationLoaded($relation)) { |
||||
78 | return $model->{$relation}->where('id', head($target->ids))->isNotEmpty(); |
||||
79 | } |
||||
80 | |||||
81 | return $model->{$relation}($target->classname)->where('id', head($target->ids))->exists(); |
||||
82 | } |
||||
83 | |||||
84 | /** |
||||
85 | * @param \Illuminate\Database\Eloquent\Model $model |
||||
86 | * @param string $relation |
||||
87 | * @param array|string|\Illuminate\Database\Eloquent\Model $targets |
||||
88 | * @param string $class |
||||
89 | * |
||||
90 | * @throws \Exception |
||||
91 | * |
||||
92 | * @return array |
||||
93 | */ |
||||
94 | public static function attachRelations(Model $model, $relation, $targets, $class) |
||||
95 | { |
||||
96 | if (false === \event(new RelationAttaching($model, $relation, $targets, $class))) { |
||||
0 ignored issues
–
show
|
|||||
97 | return false; |
||||
98 | } |
||||
99 | |||||
100 | $targets = self::attachPivotsFromRelation($model->{$relation}(), $targets, $class); |
||||
101 | |||||
102 | if (false === \event(new RelationAttached($model, $relation, $targets, $class))) { |
||||
0 ignored issues
–
show
$targets of type stdClass is incompatible with the type array|integer expected by parameter $targets of Overtrue\LaravelFollow\E...Attached::__construct() .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
|
|||||
103 | return false; |
||||
104 | } |
||||
105 | |||||
106 | return $model->{$relation}($targets->classname)->sync($targets->targets, false); |
||||
107 | } |
||||
108 | |||||
109 | /** |
||||
110 | * @param \Illuminate\Database\Eloquent\Model $model |
||||
111 | * @param string $relation |
||||
112 | * @param array|string|\Illuminate\Database\Eloquent\Model $targets |
||||
113 | * @param string $class |
||||
114 | * |
||||
115 | * @return array |
||||
116 | */ |
||||
117 | public static function detachRelations(Model $model, $relation, $targets, $class) |
||||
118 | { |
||||
119 | if (false === \event(new RelationDetaching($model, $relation, $targets, $class))) { |
||||
0 ignored issues
–
show
|
|||||
120 | return false; |
||||
121 | } |
||||
122 | |||||
123 | $targets = self::formatTargets($targets, $class); |
||||
124 | |||||
125 | if (false === \event(new RelationDetached($model, $relation, $targets, $class))) { |
||||
0 ignored issues
–
show
$targets of type stdClass is incompatible with the type array|integer expected by parameter $targets of Overtrue\LaravelFollow\E...Detached::__construct() .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
|
|||||
126 | return false; |
||||
127 | } |
||||
128 | |||||
129 | return $model->{$relation}($targets->classname)->detach($targets->ids); |
||||
130 | } |
||||
131 | |||||
132 | /** |
||||
133 | * @param \Illuminate\Database\Eloquent\Model $model |
||||
134 | * @param string $relation |
||||
135 | * @param array|string|\Illuminate\Database\Eloquent\Model $targets |
||||
136 | * @param string $class |
||||
137 | * |
||||
138 | * @throws \Exception |
||||
139 | * |
||||
140 | * @return array |
||||
141 | */ |
||||
142 | public static function toggleRelations(Model $model, $relation, $targets, $class) |
||||
143 | { |
||||
144 | if (false === \event(new RelationToggling($model, $relation, $targets, $class))) { |
||||
0 ignored issues
–
show
|
|||||
145 | return false; |
||||
146 | } |
||||
147 | |||||
148 | $targets = self::attachPivotsFromRelation($model->{$relation}(), $targets, $class); |
||||
149 | |||||
150 | $results = $model->{$relation}($targets->classname)->toggle($targets->targets); |
||||
151 | |||||
152 | if (false === \event(new RelationToggled($model, $relation, $targets, $class, $results))) { |
||||
0 ignored issues
–
show
|
|||||
153 | return false; |
||||
154 | } |
||||
155 | |||||
156 | return $results; |
||||
157 | } |
||||
158 | |||||
159 | /** |
||||
160 | * @param \Illuminate\Database\Eloquent\Relations\MorphToMany $morph |
||||
161 | * @param array|string|\Illuminate\Database\Eloquent\Model $targets |
||||
162 | * @param string $class |
||||
163 | * |
||||
164 | * @throws \Exception |
||||
165 | * |
||||
166 | * @return \stdClass |
||||
167 | */ |
||||
168 | public static function attachPivotsFromRelation(MorphToMany $morph, $targets, $class) |
||||
169 | { |
||||
170 | return self::formatTargets($targets, $class, [ |
||||
171 | 'relation' => self::getRelationTypeFromRelation($morph), |
||||
172 | 'created_at' => Carbon::now()->format(config('follow.date_format', 'Y-m-d H:i:s')), |
||||
173 | ]); |
||||
174 | } |
||||
175 | |||||
176 | /** |
||||
177 | * @param array|string|\Illuminate\Database\Eloquent\Model $targets |
||||
178 | * @param string $classname |
||||
179 | * @param array $update |
||||
180 | * |
||||
181 | * @return \stdClass |
||||
182 | */ |
||||
183 | public static function formatTargets($targets, $classname, array $update = []) |
||||
184 | { |
||||
185 | $result = new stdClass(); |
||||
186 | $result->classname = $classname; |
||||
187 | |||||
188 | if (!is_array($targets)) { |
||||
189 | $targets = [$targets]; |
||||
190 | } |
||||
191 | |||||
192 | $result->ids = array_map(function ($target) use ($result) { |
||||
193 | if ($target instanceof Model) { |
||||
194 | $result->classname = get_class($target); |
||||
195 | |||||
196 | return $target->getKey(); |
||||
197 | } |
||||
198 | |||||
199 | return intval($target); |
||||
200 | }, $targets); |
||||
201 | |||||
202 | $result->targets = empty($update) ? $result->ids : array_combine($result->ids, array_pad([], count($result->ids), $update)); |
||||
203 | |||||
204 | return $result; |
||||
205 | } |
||||
206 | |||||
207 | /** |
||||
208 | * @param \Illuminate\Database\Eloquent\Relations\MorphToMany $relation |
||||
209 | * |
||||
210 | * @throws \Exception |
||||
211 | * |
||||
212 | * @return array |
||||
213 | */ |
||||
214 | protected static function getRelationTypeFromRelation(MorphToMany $relation) |
||||
215 | { |
||||
216 | if (!\array_key_exists($relation->getRelationName(), self::RELATION_TYPES)) { |
||||
217 | throw new \Exception('Invalid relation definition.'); |
||||
218 | } |
||||
219 | |||||
220 | return self::RELATION_TYPES[$relation->getRelationName()]; |
||||
221 | } |
||||
222 | |||||
223 | /** |
||||
224 | * @param string $field |
||||
225 | * |
||||
226 | * @return string |
||||
227 | */ |
||||
228 | protected static function tablePrefixedField($field) |
||||
229 | { |
||||
230 | return \sprintf('%s.%s', config('follow.followable_table'), $field); |
||||
231 | } |
||||
232 | } |
||||
233 |