Completed
Push — master ( 510e22...2830aa )
by Carlos
05:38
created

Follow::detachRelations()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 13
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 6
dl 0
loc 13
rs 10
c 0
b 0
f 0
cc 3
nc 3
nop 4
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\SoftDeletes;
17
use Illuminate\Database\Eloquent\Relations\MorphToMany;
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
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
        $field = self::tablePrefixedField($class ? 'followable_id' : config('follow.users_table_foreign_key', 'user_id'));
77
78
        return $model->{$relation}($target->classname)
79
                        ->where($field, head($target->ids))->exists();
80
    }
81
82
    /**
83
     * @param \Illuminate\Database\Eloquent\Model              $model
84
     * @param string                                           $relation
85
     * @param array|string|\Illuminate\Database\Eloquent\Model $targets
86
     * @param string                                           $class
87
     *
88
     * @throws \Exception
89
     *
90
     * @return array
91
     */
92
    public static function attachRelations(Model $model, $relation, $targets, $class)
93
    {
94
        if (false === \event(new RelationAttaching($model, $relation, $targets, $class))) {
0 ignored issues
show
introduced by
The condition false === event(new Over...ion, $targets, $class)) is always false.
Loading history...
95
            return false;
96
        }
97
98
        $targets = self::attachPivotsFromRelation($model->{$relation}(), $targets, $class);
99
100
        if (false === \event(new RelationAttached($model, $relation, $targets, $class))) {
0 ignored issues
show
introduced by
The condition false === event(new Over...ion, $targets, $class)) is always false.
Loading history...
Bug introduced by
$targets of type stdClass is incompatible with the type integer|array 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

100
        if (false === \event(new RelationAttached($model, $relation, /** @scrutinizer ignore-type */ $targets, $class))) {
Loading history...
101
            return false;
102
        }
103
104
        return $model->{$relation}($targets->classname)->sync($targets->targets, false);
105
    }
106
107
    /**
108
     * @param \Illuminate\Database\Eloquent\Model              $model
109
     * @param string                                           $relation
110
     * @param array|string|\Illuminate\Database\Eloquent\Model $targets
111
     * @param string                                           $class
112
     *
113
     * @return array
114
     */
115
    public static function detachRelations(Model $model, $relation, $targets, $class)
116
    {
117
        if (false === \event(new RelationDetaching($model, $relation, $targets, $class))) {
0 ignored issues
show
introduced by
The condition false === event(new Over...ion, $targets, $class)) is always false.
Loading history...
118
            return false;
119
        }
120
121
        $targets = self::formatTargets($targets, $class);
122
123
        if (false === \event(new RelationDetached($model, $relation, $targets, $class))) {
0 ignored issues
show
introduced by
The condition false === event(new Over...ion, $targets, $class)) is always false.
Loading history...
Bug introduced by
$targets of type stdClass is incompatible with the type integer|array 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

123
        if (false === \event(new RelationDetached($model, $relation, /** @scrutinizer ignore-type */ $targets, $class))) {
Loading history...
124
            return false;
125
        }
126
127
        return $model->{$relation}($targets->classname)->detach($targets->ids);
128
    }
129
130
    /**
131
     * @param \Illuminate\Database\Eloquent\Model              $model
132
     * @param string                                           $relation
133
     * @param array|string|\Illuminate\Database\Eloquent\Model $targets
134
     * @param string                                           $class
135
     *
136
     * @throws \Exception
137
     *
138
     * @return array
139
     */
140
    public static function toggleRelations(Model $model, $relation, $targets, $class)
141
    {
142
        if (false === \event(new RelationToggling($model, $relation, $targets, $class))) {
0 ignored issues
show
introduced by
The condition false === event(new Over...ion, $targets, $class)) is always false.
Loading history...
143
            return false;
144
        }
145
146
        $targets = self::attachPivotsFromRelation($model->{$relation}(), $targets, $class);
147
148
        $results = $model->{$relation}($targets->classname)->toggle($targets->targets);
149
150
        if (false === \event(new RelationToggled($model, $relation, $targets, $class, $results))) {
0 ignored issues
show
introduced by
The condition false === event(new Over...ets, $class, $results)) is always false.
Loading history...
151
            return false;
152
        }
153
154
        return $results;
155
    }
156
157
    /**
158
     * @param \Illuminate\Database\Eloquent\Relations\MorphToMany $morph
159
     * @param array|string|\Illuminate\Database\Eloquent\Model    $targets
160
     * @param string                                              $class
161
     *
162
     * @throws \Exception
163
     *
164
     * @return \stdClass
165
     */
166
    public static function attachPivotsFromRelation(MorphToMany $morph, $targets, $class)
167
    {
168
        return self::formatTargets($targets, $class, [
169
            'relation' => self::getRelationTypeFromRelation($morph),
170
            'created_at' => Carbon::now()->format(config('follow.date_format', 'Y-m-d H:i:s')),
171
        ]);
172
    }
173
174
    /**
175
     * @param array|string|\Illuminate\Database\Eloquent\Model $targets
176
     * @param string                                           $classname
177
     * @param array                                            $update
178
     *
179
     * @return \stdClass
180
     */
181
    public static function formatTargets($targets, $classname, array $update = [])
182
    {
183
        $result = new stdClass();
184
        $result->classname = $classname;
185
186
        if (!is_array($targets)) {
187
            $targets = [$targets];
188
        }
189
190
        $result->ids = array_map(function ($target) use ($result) {
191
            if ($target instanceof Model) {
192
                $result->classname = get_class($target);
193
194
                return $target->getKey();
195
            }
196
197
            return intval($target);
198
        }, $targets);
199
200
        $result->targets = empty($update) ? $result->ids : array_combine($result->ids, array_pad([], count($result->ids), $update));
201
202
        return $result;
203
    }
204
205
    /**
206
     * @param \Illuminate\Database\Eloquent\Relations\MorphToMany $relation
207
     *
208
     * @throws \Exception
209
     *
210
     * @return array
211
     */
212
    protected static function getRelationTypeFromRelation(MorphToMany $relation)
213
    {
214
        if (!\array_key_exists($relation->getRelationName(), self::RELATION_TYPES)) {
215
            throw new \Exception('Invalid relation definition.');
216
        }
217
218
        return self::RELATION_TYPES[$relation->getRelationName()];
219
    }
220
221
    /**
222
     * @param string $field
223
     *
224
     * @return string
225
     */
226
    protected static function tablePrefixedField($field)
227
    {
228
        return \sprintf('%s.%s', config('follow.followable_table'), $field);
229
    }
230
}
231