Passed
Push — develop ( 4978b7...ae04bd )
by Andrew
05:55
created

Recipe   F

Complexity

Total Complexity 61

Size/Duplication

Total Lines 543
Duplicated Lines 0 %

Importance

Changes 5
Bugs 0 Features 0
Metric Value
eloc 257
c 5
b 0
f 0
dl 0
loc 543
rs 3.52
wmc 61

9 Methods

Rating   Name   Duplication   Size   Complexity  
A getImageUrl() 0 11 4
A rules() 0 24 1
B renderRecipeJSONLD() 0 77 7
C convertToFractions() 0 69 13
A renderJsonLd() 0 21 3
D getIngredients() 0 110 24
A getAggregateRating() 0 15 4
A getDirections() 0 14 4
A getRatingsCount() 0 3 1

How to fix   Complexity   

Complex Class

Complex classes like Recipe 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 Recipe, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * Recipe plugin for Craft CMS 3.x
4
 *
5
 * A comprehensive recipe FieldType for Craft CMS that includes metric/imperial
6
 * conversion, portion calculation, and JSON-LD microdata support
7
 *
8
 * @link      https://nystudio107.com
0 ignored issues
show
Coding Style introduced by
The tag in position 1 should be the @copyright tag
Loading history...
9
 * @copyright Copyright (c) 2017 nystudio107
0 ignored issues
show
Coding Style introduced by
@copyright tag must contain a year and the name of the copyright holder
Loading history...
10
 */
0 ignored issues
show
Coding Style introduced by
PHP version not specified
Loading history...
Coding Style introduced by
Missing @category tag in file comment
Loading history...
Coding Style introduced by
Missing @package tag in file comment
Loading history...
Coding Style introduced by
Missing @author tag in file comment
Loading history...
Coding Style introduced by
Missing @license tag in file comment
Loading history...
11
12
namespace nystudio107\recipe\models;
13
14
use nystudio107\recipe\helpers\Json;
15
16
use Craft;
17
use craft\base\Model;
18
use craft\helpers\Template;
19
20
/**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
21
 * @author    nystudio107
0 ignored issues
show
Coding Style introduced by
The tag in position 1 should be the @package tag
Loading history...
Coding Style introduced by
Content of the @author tag must be in the form "Display Name <[email protected]>"
Loading history...
Coding Style introduced by
Tag value for @author tag indented incorrectly; expected 2 spaces but found 4
Loading history...
22
 * @package   Recipe
0 ignored issues
show
Coding Style introduced by
Tag value for @package tag indented incorrectly; expected 1 spaces but found 3
Loading history...
23
 * @since     1.0.0
0 ignored issues
show
Coding Style introduced by
The tag in position 3 should be the @author tag
Loading history...
Coding Style introduced by
Tag value for @since tag indented incorrectly; expected 3 spaces but found 5
Loading history...
24
 */
0 ignored issues
show
Coding Style introduced by
Missing @category tag in class comment
Loading history...
Coding Style introduced by
Missing @license tag in class comment
Loading history...
Coding Style introduced by
Missing @link tag in class comment
Loading history...
25
class Recipe extends Model
26
{
27
    // Public Properties
28
    // =========================================================================
29
30
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
31
     * @var string
32
     */
33
    public $name;
34
35
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
36
     * @var string
37
     */
38
    public $description;
39
40
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
41
     * @var string
42
     */
43
    public $skill = 'intermediate';
44
45
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
46
     * @var int
47
     */
48
    public $serves = 1;
49
50
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
51
     * @var array
52
     */
53
    public $ingredients = [];
54
55
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
56
     * @var array
57
     */
58
    public $directions = [];
59
60
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
61
     * @var int
62
     */
63
    public $imageId = 0;
64
65
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
66
     * @var int
67
     */
68
    public $prepTime;
69
70
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
71
     * @var int
72
     */
73
    public $cookTime;
74
75
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
76
     * @var int
77
     */
78
    public $totalTime;
79
80
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
81
     * @var array
82
     */
83
    public $ratings = [];
84
85
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
86
     * @var string
87
     */
88
    public $servingSize;
89
90
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
91
     * @var int
92
     */
93
    public $calories;
94
95
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
96
     * @var int
97
     */
98
    public $carbohydrateContent;
99
100
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
101
     * @var int
102
     */
103
    public $cholesterolContent;
104
105
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
106
     * @var int
107
     */
108
    public $fatContent;
109
110
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
111
     * @var int
112
     */
113
    public $fiberContent;
114
115
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
116
     * @var int
117
     */
118
    public $proteinContent;
119
120
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
121
     * @var int
122
     */
123
    public $saturatedFatContent;
124
125
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
126
     * @var int
127
     */
128
    public $sodiumContent;
129
130
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
131
     * @var int
132
     */
133
    public $sugarContent;
134
135
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
136
     * @var int
137
     */
138
    public $transFatContent;
139
140
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
141
     * @var int
142
     */
143
    public $unsaturatedFatContent;
144
145
    // Public Methods
146
    // =========================================================================
147
148
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
149
     * @inheritdoc
150
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
151
    public function rules()
152
    {
153
        return [
154
            ['name', 'string'],
155
            ['name', 'default', 'value' => ''],
156
            ['description', 'string'],
157
            ['skill', 'string'],
158
            ['serves', 'integer'],
159
            ['imageId', 'integer'],
160
            ['prepTime', 'integer'],
161
            ['cookTime', 'integer'],
162
            ['totalTime', 'integer'],
163
            ['servingSize', 'string'],
164
            ['calories', 'integer'],
165
            ['carbohydrateContent', 'integer'],
166
            ['cholesterolContent', 'integer'],
167
            ['fatContent', 'integer'],
168
            ['fiberContent', 'integer'],
169
            ['proteinContent', 'integer'],
170
            ['saturatedFatContent', 'integer'],
171
            ['sodiumContent', 'integer'],
172
            ['sugarContent', 'integer'],
173
            ['transFatContent', 'integer'],
174
            ['unsaturatedFatContent', 'integer'],
175
        ];
176
    }
177
178
    /**
179
     * Render the JSON-LD Structured Data for this recipe
180
     *
181
     * @param bool $raw
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
182
     *
183
     * @return string|\Twig_Markup
184
     */
185
    public function renderRecipeJSONLD($raw = true)
186
    {
187
        $recipeJSONLD = [
188
            "context" => "http://schema.org",
189
            "type" => "Recipe",
190
            "name" => $this->name,
191
            "image" => $this->getImageUrl(),
192
            "description" => $this->description,
193
            "recipeYield" => $this->serves,
194
            "recipeIngredient" => $this->getIngredients("imperial", 0, false),
195
            "recipeInstructions" => $this->getDirections(false),
196
        ];
197
        $recipeJSONLD = array_filter($recipeJSONLD);
198
199
        $nutrition = [
200
            "type" => "NutritionInformation",
201
            'servingSize' => $this->servingSize,
202
            'calories' => $this->calories,
203
            'carbohydrateContent' => $this->carbohydrateContent,
204
            'cholesterolContent' => $this->cholesterolContent,
205
            'fatContent' => $this->fatContent,
206
            'fiberContent' => $this->fiberContent,
207
            'proteinContent' => $this->proteinContent,
208
            'saturatedFatContent' => $this->saturatedFatContent,
209
            'sodiumContent' => $this->sodiumContent,
210
            'sugarContent' => $this->sugarContent,
211
            'transFatContent' => $this->transFatContent,
212
            'unsaturatedFatContent' => $this->unsaturatedFatContent,
213
        ];
214
        $nutrition = array_filter($nutrition);
215
        $recipeJSONLD['nutrition'] = $nutrition;
216
        if (count($recipeJSONLD['nutrition']) == 1) {
217
            unset($recipeJSONLD['nutrition']);
218
        }
219
        $aggregateRating = $this->getAggregateRating();
220
        if ($aggregateRating) {
221
            $aggregateRatings = [
222
                "type" => "AggregateRating",
223
                'ratingCount' => $this->getRatingsCount(),
224
                'bestRating' => '5',
225
                'worstRating' => '1',
226
                'ratingValue' => $aggregateRating,
227
            ];
228
            $aggregateRatings = array_filter($aggregateRatings);
229
            $recipeJSONLD['aggregateRating'] = $aggregateRatings;
230
231
            $reviews = [];
232
            foreach ($this->ratings as $rating) {
233
                $review = [
234
                    "type" => "Review",
235
                    'author' => $rating['author'],
236
                    'name' => $this->name." ".Craft::t("recipe", "Review"),
237
                    'description' => $rating['review'],
238
                    'reviewRating' => [
239
                        "type" => "Rating",
240
                        'bestRating' => '5',
241
                        'worstRating' => '1',
242
                        'ratingValue' => $rating['rating'],
243
                    ],
244
                ];
245
                array_push($reviews, $review);
246
            }
247
            $reviews = array_filter($reviews);
248
            $recipeJSONLD['review'] = $reviews;
249
        }
250
251
        if ($this->prepTime) {
252
            $recipeJSONLD['prepTime'] = "PT".$this->prepTime."M";
253
        }
254
        if ($this->cookTime) {
255
            $recipeJSONLD['cookTime'] = "PT".$this->cookTime."M";
256
        }
257
        if ($this->totalTime) {
258
            $recipeJSONLD['totalTime'] = "PT".$this->totalTime."M";
259
        }
260
261
        return $this->renderJsonLd($recipeJSONLD, $raw);
262
    }
263
264
    /**
265
     * Get the URL to the recipe's image
266
     *
267
     * @return null|string
268
     */
269
    public function getImageUrl()
270
    {
271
        $result = "";
272
        if (isset($this->imageId) && $this->imageId) {
273
            $image = Craft::$app->getAssets()->getAssetById($this->imageId[0]);
274
            if ($image) {
0 ignored issues
show
introduced by
$image is of type craft\elements\Asset, thus it always evaluated to true.
Loading history...
275
                $result = $image->url;
276
            }
277
        }
278
279
        return $result;
280
    }
281
282
    /**
283
     * Get all of the ingredients for this recipe
284
     *
285
     * @param string $outputUnits
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
286
     * @param int    $serving
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
287
     * @param bool   $raw
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
288
     *
289
     * @return array
290
     */
291
    public function getIngredients($outputUnits = "imperial", $serving = 0, $raw = true)
292
    {
293
        $result = [];
294
295
        if ($this->ingredients != '') {
0 ignored issues
show
introduced by
The condition $this->ingredients != '' is always true.
Loading history...
296
            foreach ($this->ingredients as $row) {
297
                $convertedUnits = "";
298
                $ingredient = "";
299
                if ($row['quantity']) {
300
                    // Multiply the quantity by how many servings we want
301
                    $multiplier = 1;
302
                    if ($serving > 0) {
303
                        $multiplier = $serving / $this->serves;
304
                    }
305
                    $quantity = $row['quantity'] * $multiplier;
306
                    $originalQuantity = $quantity;
307
308
                    // Do the units conversion
309
310
                    if ($outputUnits == 'imperial') {
311
                        if ($row['units'] == "mls") {
312
                            $convertedUnits = "tsps";
313
                            $quantity = $quantity * 0.2;
314
                        }
315
316
                        if ($row['units'] == "ls") {
317
                            $convertedUnits = "cups";
318
                            $quantity = $quantity * 4.2;
319
                        }
320
321
                        if ($row['units'] == "mgs") {
322
                            $convertedUnits = "ozs";
323
                            $quantity = $quantity * 0.000035274;
324
                        }
325
326
                        if ($row['units'] == "gs") {
327
                            $convertedUnits = "ozs";
328
                            $quantity = $quantity * 0.035274;
329
                        }
330
331
                        if ($row['units'] == "kg") {
332
                            $convertedUnits = "lbs";
333
                            $quantity = $quantity * 2.2046226218;
334
                        }
335
                    }
336
337
                    if ($outputUnits == 'metric') {
338
                        if ($row['units'] == "tsps") {
339
                            $convertedUnits = "mls";
340
                            $quantity = $quantity * 4.929;
341
                        }
342
343
                        if ($row['units'] == "tbsps") {
344
                            $convertedUnits = "mls";
345
                            $quantity = $quantity * 14.787;
346
                        }
347
348
                        if ($row['units'] == "flozs") {
349
                            $convertedUnits = "mls";
350
                            $quantity = $quantity * 29.574;
351
                        }
352
353
                        if ($row['units'] == "cups") {
354
                            $convertedUnits = "ls";
355
                            $quantity = $quantity * 0.236588;
356
                        }
357
358
                        if ($row['units'] == "ozs") {
359
                            $convertedUnits = "gs";
360
                            $quantity = $quantity * 28.3495;
361
                        }
362
363
                        if ($row['units'] == "lbs") {
364
                            $convertedUnits = "kg";
365
                            $quantity = $quantity * 0.45359237;
366
                        }
367
368
                        $quantity = round($quantity, 1);
369
                    }
370
371
                    // Convert imperial units to nice fractions
372
373
                    if ($outputUnits == 'imperial') {
374
                        $quantity = $this->convertToFractions($quantity);
375
                    }
376
                    $ingredient .= $quantity;
377
378
                    if ($row['units']) {
379
                        $units = $row['units'];
380
                        if ($convertedUnits) {
381
                            $units = $convertedUnits;
382
                        }
383
                        if ($originalQuantity <= 1) {
384
                            $units = rtrim($units);
385
                            $units = rtrim($units, 's');
386
                        }
387
                        $ingredient .= " ".$units;
388
                    }
389
                }
390
                if ($row['ingredient']) {
391
                    $ingredient .= " ".$row['ingredient'];
392
                }
393
                if ($raw) {
394
                    $ingredient = Template::raw($ingredient);
395
                }
396
                array_push($result, $ingredient);
397
            }
398
        }
399
400
        return $result;
401
    }
402
403
    /**
404
     * Convert decimal numbers into fractions
405
     *
406
     * @param $quantity
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
407
     *
408
     * @return string
409
     */
410
    private function convertToFractions($quantity)
0 ignored issues
show
Coding Style introduced by
Private method name "Recipe::convertToFractions" must be prefixed with an underscore
Loading history...
411
    {
412
        $whole = floor($quantity);
413
        // Round the mantissa so we can do a floating point comparison without
414
        // weirdness, per: https://www.php.net/manual/en/language.types.float.php#113703
415
        $fraction = round($quantity - $whole, 3);
416
        switch ($fraction) {
417
            case 0:
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 8 spaces, found 12
Loading history...
418
                $fraction = "";
419
                break;
420
421
            case 0.25:
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 8 spaces, found 12
Loading history...
422
                $fraction = " &frac14;";
423
                break;
424
425
            case 0.33:
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 8 spaces, found 12
Loading history...
426
                $fraction = " &frac13;";
427
                break;
428
429
            case 0.66:
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 8 spaces, found 12
Loading history...
430
                $fraction = " &frac23;";
431
                break;
432
433
            case 0.165:
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 8 spaces, found 12
Loading history...
434
                $fraction = " &frac16;";
435
                break;
436
437
            case 0.5:
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 8 spaces, found 12
Loading history...
438
                $fraction = " &frac12;";
439
                break;
440
441
            case 0.75:
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 8 spaces, found 12
Loading history...
442
                $fraction = " &frac34;";
443
                break;
444
445
            case 0.125:
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 8 spaces, found 12
Loading history...
446
                $fraction = " &#x215B;";
447
                break;
448
449
            case 0.375:
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 8 spaces, found 12
Loading history...
450
                $fraction = " &#x215C;";
451
                break;
452
453
            case 0.625:
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 8 spaces, found 12
Loading history...
454
                $fraction = " &#x215D;";
455
                break;
456
457
            case 0.875:
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 8 spaces, found 12
Loading history...
458
                $fraction = " &#x215E;";
459
                break;
460
461
            default:
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 8 spaces, found 12
Loading history...
462
                $precision = 5;
463
                $pnum = round($fraction, $precision);
464
                $denominator = pow(10, $precision);
465
                $numerator = $pnum * $denominator;
466
                $fraction = "<sup>"
467
                    .$numerator
468
                    ."</sup>&frasl;<sub>"
469
                    .$denominator
470
                    ."</sub>";
471
                break;
472
        }
473
        if ($whole == 0) {
474
            $whole = "";
475
        }
476
        $result = $whole.$fraction;
477
478
        return $result;
479
    }
480
481
    /**
482
     * Get all of the directions for this recipe
483
     *
484
     * @param bool $raw
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
485
     *
486
     * @return array
487
     */
488
    public function getDirections($raw = true)
489
    {
490
        $result = [];
491
        if ($this->directions != '') {
0 ignored issues
show
introduced by
The condition $this->directions != '' is always true.
Loading history...
492
            foreach ($this->directions as $row) {
493
                $direction = $row['direction'];
494
                if ($raw) {
495
                    $direction = Template::raw($direction);
496
                }
497
                array_push($result, $direction);
498
            }
499
        }
500
501
        return $result;
502
    }
503
504
    /**
505
     * Get the aggregate rating from all of the ratings
506
     *
507
     * @return float|int|string
508
     */
509
    public function getAggregateRating()
510
    {
511
        $result = 0;
512
        $total = 0;
513
        if (isset($this->ratings) && !empty($this->ratings)) {
514
            foreach ($this->ratings as $row) {
515
                $result += $row['rating'];
516
                $total++;
517
            }
518
            $result = $result / $total;
519
        } else {
520
            $result = "";
521
        }
522
523
        return $result;
524
    }
525
526
    // Private Methods
527
    // =========================================================================
528
529
    /**
530
     * Get the total number of ratings
531
     *
532
     * @return int
533
     */
534
    public function getRatingsCount()
535
    {
536
        return count($this->ratings);
537
    }
538
539
    /**
540
     * Renders a JSON-LD representation of the schema
541
     *
542
     * @param      $json
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 1 spaces but found 6
Loading history...
543
     * @param bool $raw
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
544
     *
545
     * @return string|\Twig_Markup
546
     */
547
    private function renderJsonLd($json, $raw = true)
0 ignored issues
show
Coding Style introduced by
Private method name "Recipe::renderJsonLd" must be prefixed with an underscore
Loading history...
548
    {
549
        $linebreak = "";
550
551
        // If `devMode` is enabled, make the JSON-LD human-readable
552
        if (Craft::$app->getConfig()->getGeneral()->devMode) {
553
            $linebreak = PHP_EOL;
554
        }
555
556
        // Render the resulting JSON-LD
557
        $result = '<script type="application/ld+json">'
558
            .$linebreak
559
            .Json::encode($json)
560
            .$linebreak
561
            .'</script>';
562
563
        if ($raw === true) {
564
            $result = Template::raw($result);
565
        }
566
567
        return $result;
568
    }
569
}
570