Passed
Push — master ( 4f783c...449b2c )
by Paul
07:14
created

Review   B

Complexity

Total Complexity 43

Size/Duplication

Total Lines 315
Duplicated Lines 0 %

Test Coverage

Coverage 49.58%

Importance

Changes 7
Bugs 0 Features 2
Metric Value
wmc 43
eloc 104
c 7
b 0
f 2
dl 0
loc 315
ccs 59
cts 119
cp 0.4958
rs 8.96

24 Methods

Rating   Name   Duplication   Size   Complexity  
A __toString() 0 3 1
A meta() 0 12 2
A rating() 0 3 1
A offsetSet() 0 9 2
A render() 0 3 1
A isEditable() 0 5 2
A assignedPosts() 0 9 2
A assignedUsers() 0 8 2
A offsetExists() 0 3 2
A custom() 0 9 1
A offsetUnset() 0 2 1
A __call() 0 6 2
A __construct() 0 11 2
A post() 0 6 2
A build() 0 3 1
A type() 0 5 1
A date() 0 14 4
A assignedTerms() 0 10 3
A author() 0 3 1
A offsetGet() 0 20 4
A isValid() 0 3 2
A avatar() 0 3 1
A isReview() 0 3 1
A hasRevisions() 0 8 2

How to fix   Complexity   

Complex Class

Complex classes like Review often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Review, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace GeminiLabs\SiteReviews;
4
5
use GeminiLabs\SiteReviews\Database\OptionManager;
6
use GeminiLabs\SiteReviews\Database\Query;
7
use GeminiLabs\SiteReviews\Defaults\CustomFieldsDefaults;
8
use GeminiLabs\SiteReviews\Defaults\ReviewDefaults;
9
use GeminiLabs\SiteReviews\Helpers\Arr;
10
use GeminiLabs\SiteReviews\Helpers\Cast;
11
use GeminiLabs\SiteReviews\Helpers\Str;
12
use GeminiLabs\SiteReviews\Helpers\Text;
13
use GeminiLabs\SiteReviews\Modules\Avatar;
14
use GeminiLabs\SiteReviews\Modules\Date;
15
use GeminiLabs\SiteReviews\Modules\Html\ReviewHtml;
16
17
/**
18
 * @property bool $approved  This property is mapped to $is_approved
19
 * @property array $assigned_posts
20
 * @property array $assigned_terms
21
 * @property array $assigned_users
22
 * @property string $author
23
 * @property int $author_id
24
 * @property string $avatar
25
 * @property string $content
26
 * @property Arguments $custom
27
 * @property string $date
28
 * @property string $date_gmt
29
 * @property string $name  This property is mapped to $author
30
 * @property string $email
31
 * @property bool $has_revisions  This property is mapped to $is_modified
32
 * @property int $ID
33
 * @property string $ip_address
34
 * @property bool $is_approved
35
 * @property bool $is_modified
36
 * @property bool $is_pinned
37
 * @property bool $modified  This property is mapped to $is_modified
38
 * @property bool $pinned  This property is mapped to $is_pinned
39
 * @property int $rating
40
 * @property int $rating_id
41
 * @property string $response
42
 * @property string $status
43
 * @property bool $terms
44
 * @property string $title
45
 * @property string $type
46
 * @property string $url
47
 * @property int $user_id  This property is mapped to $author_id
48
 */
49
class Review extends Arguments
50
{
51
    /**
52
     * @var Arguments
53
     */
54
    protected $_meta;
55
56
    /**
57
     * @var \WP_Post
58
     */
59
    protected $_post;
60
61
    /**
62
     * @var bool
63
     */
64
    protected $has_checked_revisions;
65
66
    /**
67
     * @var int
68
     */
69
    protected $id;
70
71
    /**
72
     * @param array|object $values
73
     * @param bool $init
74
     */
75 15
    public function __construct($values, $init = true)
76
    {
77 15
        $values = glsr()->args($values);
78 15
        $this->id = Cast::toInt($values->review_id);
79 15
        $args = glsr(ReviewDefaults::class)->restrict($values->toArray());
80 15
        $args['ID'] = $this->id;
81 15
        parent::__construct($args);
82 15
        if ($init) {
83 14
            $this->set('avatar', glsr(Avatar::class)->url($this));
84 14
            $this->set('custom', $this->custom());
85 14
            $this->set('response', $this->meta()->_response);
86
        }
87 15
    }
88
89
    /**
90
     * @return mixed
91
     */
92
    public function __call($method, $args)
93
    {
94
        array_unshift($args, $this);
95
        $result = apply_filters_ref_array(glsr()->id.'/review/call/'.$method, $args);
96
        if (!is_a($result, get_class($this))) {
97
            return $result;
98
        }
99
    }
100
101
    /**
102
     * @return string
103
     */
104 6
    public function __toString()
105
    {
106 6
        return (string) $this->build();
107
    }
108
109
    /**
110
     * @return array
111
     */
112
    public function assignedPosts()
113
    {
114
        if (empty($this->assigned_posts)) {
115
            return $this->assigned_posts;
116
        }
117
        return get_posts([
118
            'post__in' => $this->assigned_posts,
119
            'post_type' => 'any',
120
            'posts_per_page' => -1,
121
        ]);
122
    }
123
124
    /**
125
     * @return array
126
     */
127 6
    public function assignedTerms()
128
    {
129 6
        if (empty($this->assigned_terms)) {
130 6
            return $this->assigned_terms;
131
        }
132
        $terms = get_terms(glsr()->taxonomy, ['include' => $this->assigned_terms]);
133
        if (is_wp_error($terms)) {
134
            return $this->assigned_terms;
135
        }
136
        return $terms;
137
    }
138
139
    /**
140
     * @return array
141
     */
142 6
    public function assignedUsers()
143
    {
144 6
        if (empty($this->assigned_users)) {
145 6
            return $this->assigned_users;
146
        }
147
        return get_users([
148
            'fields' => ['display_name', 'ID', 'user_email', 'user_nicename', 'user_url'],
149
            'include' => $this->assigned_users,
150
        ]);
151
    }
152
153
    /**
154
     * @return string
155
     */
156 6
    public function author()
157
    {
158 6
        return Text::name($this->get('author'));
159
    }
160
161
    /**
162
     * @param int $size
163
     * @return string
164
     */
165
    public function avatar($size = 0)
166
    {
167
        return glsr(Avatar::class)->img($this, $size);
168
    }
169
170
    /**
171
     * @return ReviewHtml
172
     */
173 6
    public function build(array $args = [])
174
    {
175 6
        return new ReviewHtml($this, $args);
176
    }
177
178
    /**
179
     * @return Arguments
180
     */
181 14
    public function custom()
182
    {
183
        $custom = array_filter($this->meta()->toArray(), function ($key) {
184 14
            return Str::startsWith('_custom', $key);
185 14
        }, ARRAY_FILTER_USE_KEY);
186 14
        $custom = Arr::unprefixKeys($custom, '_custom_');
187 14
        $custom = Arr::unprefixKeys($custom, '_');
188 14
        $custom = glsr(CustomFieldsDefaults::class)->merge($custom);
189 14
        return glsr()->args($custom);
190
    }
191
192
    /**
193
     * @return string
194
     */
195 6
    public function date($format = 'F j, Y')
196
    {
197 6
        $value = $this->get('date_gmt');
198 6
        if (!empty(func_get_args())) {
199
            return date_i18n($format, strtotime($value));
200
        }
201 6
        $dateFormat = glsr_get_option('reviews.date.format', 'default');
202 6
        if ('relative' == $dateFormat) {
203
            return glsr(Date::class)->relative($value);
204
        }
205 6
        $format = 'custom' == $dateFormat
206
            ? glsr_get_option('reviews.date.custom', 'M j, Y')
207 6
            : glsr(OptionManager::class)->getWP('date_format', 'F j, Y');
208 6
        return date_i18n($format, strtotime($value));
209
    }
210
211
    /**
212
     * @param int|\WP_Post $post
213
     * @return bool
214
     */
215
    public static function isEditable($post)
216
    {
217
        $postId = Helper::getPostId($post);
218
        return static::isReview($postId)
219
            && in_array(glsr(Query::class)->review($postId)->type, ['', 'local']);
220
    }
221
222
    /**
223
     * @param \WP_Post|int|false $post
224
     * @return bool
225
     */
226 5
    public static function isReview($post)
227
    {
228 5
        return glsr()->post_type === get_post_type($post);
229
    }
230
231
    /**
232
     * @return bool
233
     */
234 14
    public function isValid()
235
    {
236 14
        return !empty($this->id) && !empty($this->get('rating_id'));
237
    }
238
239
    /**
240
     * @return Arguments
241
     */
242 14
    public function meta()
243
    {
244 14
        if (!$this->_meta instanceof Arguments) {
245 14
            $meta = Arr::consolidate(get_post_meta($this->id));
246
            $meta = array_map(function ($item) {
247 14
                return array_shift($item);
248 14
            }, array_filter($meta));
249 14
            $meta = array_filter($meta, 'strlen');
250 14
            $meta = array_map('maybe_unserialize', $meta);
251 14
            $this->_meta = glsr()->args($meta);
252
        }
253 14
        return $this->_meta;
254
    }
255
256
    /**
257
     * @param mixed $key
258
     * @return bool
259
     */
260 6
    public function offsetExists($key)
261
    {
262 6
        return parent::offsetExists($key) || !is_null($this->custom()->$key);
263
    }
264
265
    /**
266
     * @param mixed $key
267
     * @return mixed
268
     */
269 15
    public function offsetGet($key)
270
    {
271
        $alternateKeys = [
272 15
            'approved' => 'is_approved',
273
            'has_revisions' => 'is_modified',
274
            'modified' => 'is_modified',
275
            'name' => 'author',
276
            'pinned' => 'is_pinned',
277
            'user_id' => 'author_id',
278
        ];
279 15
        if (array_key_exists($key, $alternateKeys)) {
280
            return $this->offsetGet($alternateKeys[$key]);
281
        }
282 15
        if ('is_modified' === $key) {
283
            return $this->hasRevisions();
284
        }
285 15
        if (is_null($value = parent::offsetGet($key))) {
286
            return $this->custom()->$key;
287
        }
288 15
        return $value;
289
    }
290
291
    /**
292
     * @param mixed $key
293
     * @return void
294
     */
295
    public function offsetSet($key, $value)
296
    {
297
        // This class is read-only, except for custom fields
298
        if ('custom' === $key) {
299
            $value = Arr::consolidate($value);
300
            $value = Arr::prefixKeys($value, '_custom_');
301
            $meta = wp_parse_args($this->_meta->toArray(), $value);
302
            $this->_meta = glsr()->args($meta);
303
            parent::offsetSet($key, $this->custom());
304
        }
305
    }
306
307
    /**
308
     * @param mixed $key
309
     * @return void
310
     */
311
    public function offsetUnset($key)
312
    {
313
        // This class is read-only
314
    }
315
316
    /**
317
     * @return \WP_Post|null
318
     */
319
    public function post()
320
    {
321
        if (!$this->_post instanceof \WP_Post) {
322
            $this->_post = get_post($this->id);
323
        }
324
        return $this->_post;
325
    }
326
327
    /**
328
     * @return void
329
     */
330
    public function render()
331
    {
332
        echo $this->build();
333
    }
334
335
    /**
336
     * @return string
337
     */
338
    public function rating()
339
    {
340
        return glsr_star_rating($this->get('rating'));
341
    }
342
343
    /**
344
     * @return string
345
     */
346
    public function type()
347
    {
348
        $type = $this->get('type');
349
        $reviewTypes = glsr()->retrieveAs('array', 'review_types');
350
        return Arr::get($reviewTypes, $type, _x('Unknown', 'admin-text', 'site-reviews'));
351
    }
352
353
    /**
354
     * @return bool
355
     */
356
    protected function hasRevisions()
357
    {
358
        if (!$this->has_checked_revisions) {
359
            $modified = glsr(Query::class)->hasRevisions($this->ID);
360
            $this->set('is_modified', $modified);
361
            $this->has_checked_revisions = true;
362
        }
363
        return $this->get('is_modified');
364
    }
365
}
366