Completed
Push — master ( 0af618...962883 )
by
unknown
8s
created

Ratings::validateRating()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 6
rs 9.4285
cc 3
eloc 3
nc 2
nop 1
1
<?php
2
3
namespace MovingImage\Client\VMPro\Services;
4
5
use MovingImage\Client\VMPro\Entity\Video;
6
use MovingImage\Client\VMPro\Entity\VideoRequestParameters;
7
use MovingImage\Client\VMPro\Interfaces\ApiClientInterface;
8
9
/**
10
 * Class Ratings.
11
 *
12
 * @author Robert Szeker <[email protected]>
13
 */
14
class Ratings
15
{
16
    const MINIMUM_RATING = 1;
17
    const MAXIMUM_RATING = 5;
18
19
    /** @var ApiClientInterface */
20
    private $client;
21
22
    /** @var Video [] */
23
    private $videos = [];
24
25
    /** @var int */
26
    private $vmId;
27
28
    /** @var string */
29
    private $metadataFieldAverage;
30
31
    /** @var string */
32
    private $metadataFieldCount;
33
34
    public function __construct(
35
        ApiClientInterface $client,
36
        $vmId,
37
        $metadataFieldAverage,
38
        $metadataFieldCount
39
    ) {
40
        $this->client = $client;
41
        $this->metadataFieldAverage = $metadataFieldAverage;
42
        $this->metadataFieldCount = $metadataFieldCount;
43
        $this->vmId = $vmId;
44
    }
45
46
    /**
47
     * Increases the count of all ratings by one and calculates a new average rating value.
48
     *
49
     * @param string $videoId
50
     * @param int    $rating
51
     *
52
     * @throws \InvalidArgumentException
53
     */
54
    public function addRating($videoId, $rating)
55
    {
56
        $this->validateRating($rating);
57
        $customMetaData = $this->getVideo($videoId)->getCustomMetadata();
58
59
        $average = $this->getRatingAverage($videoId);
60
        $count = $this->getRatingCount($videoId);
61
        $newCount = $count + 1;
62
63
        $customMetaData[$this->metadataFieldCount] = $newCount;
64
        $customMetaData[$this->metadataFieldAverage] = (($average * $count) + $rating) / $newCount;
65
66
        $this->storeCustomMetaData($customMetaData, $videoId);
67
    }
68
69
    /**
70
     * Modifies the average rating value. Count of all ratings stays the same (will not be increased).
71
     * The use case of this function is, when someone wants to change its rating (video was already rated by that person).
72
     *
73
     * @param string $videoId
74
     * @param int    $rating
75
     * @param int    $oldRating
76
     *
77
     * @throws \InvalidArgumentException
78
     */
79
    public function modifyRating($videoId, $rating, $oldRating)
80
    {
81
        $this->validateRating($rating);
82
        $customMetaData = $this->getVideo($videoId)->getCustomMetadata();
83
84
        $average = $this->getRatingAverage($videoId);
85
        $count = $this->getRatingCount($videoId);
86
87
        $customMetaData[$this->metadataFieldAverage] = (($average * $count) - $oldRating + $rating) / $count;
88
89
        $this->storeCustomMetaData($customMetaData, $videoId);
90
    }
91
92
    /**
93
     * Returns the average rating value from the custom meta data fields from a given video.
94
     *
95
     * @param string $videoId
96
     *
97
     * @return float|int
98
     */
99
    public function getRatingAverage($videoId)
100
    {
101
        return $this->getCustomMetaDataField($videoId, $this->metadataFieldAverage);
102
    }
103
104
    /**
105
     * Returns the count of all ratings from the custom meta data fields from a given video.
106
     *
107
     * @param string $videoId
108
     *
109
     * @return float|int
110
     */
111
    private function getRatingCount($videoId)
112
    {
113
        return $this->getCustomMetaDataField($videoId, $this->metadataFieldCount);
114
    }
115
116
    /**
117
     * Returns a meta data field of a video always as a number.
118
     *
119
     * @param $videoId
120
     * @param $customMetaDataField
121
     *
122
     * @return float|int
123
     */
124
    private function getCustomMetaDataField($videoId, $customMetaDataField)
125
    {
126
        $customMetaData = $this->getVideo($videoId)->getCustomMetadata();
127
128
        return array_key_exists($customMetaDataField, $customMetaData)
129
            ? (float) $customMetaData[$customMetaDataField]
130
            : 0;
131
    }
132
133
    /**
134
     * Stores the custom meta data fields with the api client.
135
     *
136
     * @param array  $customMetaData
137
     * @param string $videoId
138
     */
139
    private function storeCustomMetaData($customMetaData, $videoId)
140
    {
141
        // only update custom meta data fields related to rating
142
        $this->client->setCustomMetaData(
143
            $this->vmId,
144
            $videoId,
145
            $this->filterCustomMetaData($customMetaData)
146
        );
147
148
        // also store custom meta data fields locally, if video is fetched again by function $this->getVideo($videoId)
149
        $this->getVideo($videoId)->setCustomMetadata($customMetaData);
150
    }
151
152
    /**
153
     * Fetches and returns video from api client and stores it locally.
154
     * This way api client will be requested only once for every video.
155
     *
156
     * @param string $videoId
157
     *
158
     * @return Video
159
     */
160
    private function getVideo($videoId)
161
    {
162
        if (!array_key_exists($videoId, $this->videos)) {
163
            $options = new VideoRequestParameters();
164
            $options->setIncludeCustomMetadata(true);
165
            $this->videos[$videoId] = $this->client->getVideo($this->vmId, $videoId, $options);
166
        }
167
168
        return $this->videos[$videoId];
169
    }
170
171
    /**
172
     * Returns custom meta data fields which are related to rating.
173
     *
174
     * @param array $customMetaData
175
     *
176
     * @return array
177
     */
178
    private function filterCustomMetaData($customMetaData)
179
    {
180
        foreach ($customMetaData as $key => $data) {
181
            if (!in_array($key, [$this->metadataFieldCount, $this->metadataFieldAverage])) {
182
                unset($customMetaData[$key]);
183
            }
184
        }
185
186
        return $customMetaData;
187
    }
188
189
    /**
190
     * Checks the rating value if it is in range from 1 to 5.
191
     * Throws an exception if not.
192
     *
193
     * @param int $rating
194
     *
195
     * @throws \InvalidArgumentException
196
     */
197
    private function validateRating($rating)
198
    {
199
        if ($rating < self::MINIMUM_RATING || $rating > self::MAXIMUM_RATING) {
200
            throw new \InvalidArgumentException('rating value is not in expected range');
201
        }
202
    }
203
}
204