Completed
Push — master ( de7ce3...708b8c )
by Younes
01:51
created

Rateable::getRatingQueryBuilder()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 0
dl 0
loc 4
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace Yoeunes\Rateable\Traits;
4
5
use Illuminate\Support\Facades\DB;
6
use Yoeunes\Rateable\RatingBuilder;
7
use Illuminate\Database\Eloquent\Builder;
8
use Illuminate\Database\Query\JoinClause;
9
use Yoeunes\Rateable\Exceptions\InvalidRatingValue;
10
use Illuminate\Database\Eloquent\Relations\Relation;
11
use Yoeunes\Rateable\RatingQueryBuilder;
12
13
trait Rateable
14
{
15
    /**
16
     * This model has many ratings.
17
     *
18
     * @return \Illuminate\Database\Eloquent\Relations\MorphMany
19
     */
20
    public function ratings()
21
    {
22
        return $this->morphMany(config('rateable.rating'), 'rateable');
0 ignored issues
show
Bug introduced by
It seems like morphMany() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
23
    }
24
25
    public function averageRating()
26
    {
27
        return $this->ratings()->avg('value');
28
    }
29
30
    public function countRating()
31
    {
32
        return $this->ratings()->count();
33
    }
34
35
    public function totalRating()
36
    {
37
        return $this->ratings()->sum('value');
38
    }
39
40
    public function averageRatingForUser(int $user_id)
41
    {
42
        return $this->ratings()->where('user_id', $user_id)->avg('value');
43
    }
44
45
    public function totalRatingForUser(int $user_id)
46
    {
47
        return $this->ratings()->where('user_id', $user_id)->sum('value');
48
    }
49
50
    public function countRatingForUser(int $user_id)
51
    {
52
        return $this->ratings()->where('user_id', $user_id)->count();
53
    }
54
55
    public function ratingPercentage()
56
    {
57
        $max = config('rateable.max_rating');
58
59
        $quantity = $this->ratings()->count();
60
61
        $total = $this->totalRating();
62
63
        return ($quantity * $max) > 0 ? $total / (($quantity * $max) / 100) : 0;
64
    }
65
66
    public function positiveRatingCount()
67
    {
68
        return $this->ratings()->where('value', '>=', '0')->count();
69
    }
70
71
    public function positiveRatingTotal()
72
    {
73
        return $this->ratings()->where('value', '>=', '0')->sum('value');
74
    }
75
76
    public function negativeRatingCount()
77
    {
78
        return $this->ratings()->where('value', '<', '0')->count();
79
    }
80
81
    public function negativeRatingTotal()
82
    {
83
        return $this->ratings()->where('value', '<', '0')->sum('value');
84
    }
85
86
    public function isRated()
87
    {
88
        return $this->ratings()->exists();
89
    }
90
91
    public function isRatedBy(int $user_id)
92
    {
93
        return $this->ratings()->where('user_id', $user_id)->exists();
94
    }
95
96
    /**
97
     * to order by average_rating.
98
     *
99
     * add protected $appends = [ 'average_rating' ]; to your model
100
     *
101
     * Lesson::all()->sortBy('average_rating')
102
     * Lesson::with('relatedModel')->get()->sortBy('average_rating')
103
     * Lesson::where('status', 'published')->get()->sortBy('average_rating')
104
     *
105
     * @return mixed
106
     */
107
    public function getAverageRatingAttribute()
108
    {
109
        return $this->averageRating();
110
    }
111
112
    public function scopeOrderByAverageRating(Builder $query, string $direction = 'asc')
113
    {
114
        return $query
115
            ->leftJoin('ratings', function (JoinClause $join) {
116
                $join
117
                    ->on('ratings.rateable_id', $this->getTable() . '.id')
0 ignored issues
show
Bug introduced by
It seems like getTable() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
118
                    ->where('ratings.rateable_type', Relation::getMorphedModel(__CLASS__) ?? __CLASS__);
119
            })
120
            ->addSelect(DB::raw('AVG(ratings.value) as average_rating'))
121
            ->groupBy($this->getTable(). '.id')
0 ignored issues
show
Bug introduced by
It seems like getTable() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
122
            ->orderBy('average_rating', $direction);
123
    }
124
125
    public function deleteRating(int $rating_id)
126
    {
127
        return $this->ratings()->where('id', $rating_id)->delete();
128
    }
129
130
    public function resetRating()
131
    {
132
        return $this->ratings()->delete();
133
    }
134
135
    public function deleteRatingsForUser(int $user_id)
136
    {
137
        return $this->ratings()->where('user_id', $user_id)->delete();
138
    }
139
140
    /**
141
     * @param int $user_id
142
     * @param int $value
143
     *
144
     * @return int
145
     *
146
     * @throws \Throwable
147
     */
148
    public function updateRatingForUser(int $user_id, int $value)
149
    {
150
        throw_if($value < config('rateable.min_rating') || $value > config('rateable.max_rating'), InvalidRatingValue::class, 'Invalid rating value');
151
152
        return $this->ratings()->where('user_id', $user_id)->update(['value' => $value]);
153
    }
154
155
    /**
156
     * @param int $rating_id
157
     * @param int $value
158
     *
159
     * @return int
160
     *
161
     * @throws \Throwable
162
     */
163
    public function updateRating(int $rating_id, int $value)
164
    {
165
        throw_if($value < config('rateable.min_rating') || $value > config('rateable.max_rating'), InvalidRatingValue::class, 'Invalid rating value');
166
167
        return $this->ratings()->where('id', $rating_id)->update(['value' => $value]);
168
    }
169
170
    /**
171
     * @return RatingBuilder
172
     *
173
     * @throws \Throwable
174
     */
175
    public function getRatingBuilder()
176
    {
177
        return (new RatingBuilder())
178
            ->rateable($this);
179
    }
180
181
    public function raters()
182
    {
183
        return $this->morphToMany(config('rateable.user'), 'rateable', 'ratings');
0 ignored issues
show
Bug introduced by
It seems like morphToMany() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
184
    }
185
186
    public function countRatingsByDate($from = null, $to = null)
187
    {
188
        $query = $this->ratings();
189
190
        if (! empty($from) && empty($to)) {
191
            $query->where('created_at', '>=', date_transformer($from));
192
        } elseif (empty($from) && ! empty($to)) {
193
            $query->where('created_at', '<=', date_transformer($to));
194
        } elseif (! empty($from) && ! empty($to)) {
195
            $query->whereBetween('created_at', [date_transformer($from), date_transformer($to)]);
196
        }
197
198
        return $query->sum('value');
199
    }
200
201
    public function getRatingQueryBuilder()
202
    {
203
        return new RatingQueryBuilder($this->ratings());
204
    }
205
}
206