Passed
Push — master ( d6ccbc...d5a1e0 )
by Carlos
57s queued 11s
created

src/Follow.php (9 issues)

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
The trait Illuminate\Database\Eloquent\SoftDeletes requires the property $timestamps which is not provided by Overtrue\LaravelFollow\Follow.
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
The condition false === event(new Over...ion, $targets, $class)) is always false.
Loading history...
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 ignore-type  annotation

102
        if (false === \event(new RelationAttached($model, $relation, /** @scrutinizer ignore-type */ $targets, $class))) {
Loading history...
The condition false === event(new Over...ion, $targets, $class)) is always false.
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
The condition false === event(new Over...ion, $targets, $class)) is always false.
Loading history...
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 ignore-type  annotation

125
        if (false === \event(new RelationDetached($model, $relation, /** @scrutinizer ignore-type */ $targets, $class))) {
Loading history...
The condition false === event(new Over...ion, $targets, $class)) is always false.
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
The condition false === event(new Over...ion, $targets, $class)) is always false.
Loading history...
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
The condition false === event(new Over...ets, $class, $results)) is always false.
Loading history...
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