Passed
Push — master ( 53e130...58b4b7 )
by Jason
02:39
created

Variation::getGeneratedTitle()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 12
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
eloc 9
c 1
b 0
f 0
nc 4
nop 0
dl 0
loc 12
rs 9.9666
1
<?php
2
3
namespace Dynamic\Foxy\Model;
4
5
use Bummzack\SortableFile\Forms\SortableUploadField;
6
use SilverStripe\Assets\File;
7
use SilverStripe\Forms\CheckboxField;
8
use SilverStripe\Forms\CurrencyField;
9
use SilverStripe\Forms\DropdownField;
10
use SilverStripe\Forms\FieldGroup;
11
use SilverStripe\Forms\FieldList;
12
use SilverStripe\Forms\NumericField;
13
use SilverStripe\Forms\ReadonlyField;
14
use SilverStripe\Forms\TextField;
15
use SilverStripe\ORM\DataObject;
16
use SilverStripe\ORM\ManyManyList;
17
use SilverStripe\ORM\ValidationResult;
18
use SilverStripe\Security\Permission;
19
use SilverStripe\Security\Security;
20
21
/**
22
 * Class Variation
23
 * @package Dynamic\Foxy\Model
24
 *
25
 * @property string $Title
26
 * @property bool $UseProductContent
27
 * @property string Content
28
 * @property float $WeightModifier
29
 * @property string $CodeModifier
30
 * @property float $PriceModifier
31
 * @property string $WeightModifierAction
32
 * @property string $CodeModifierAction
33
 * @property string $PriceModifierAction
34
 * @property bool $Available
35
 * @property int $Type
36
 * @property string $OptionModifierKey
37
 * @property int $SortOrder
38
 * @property float $FinalPrice
39
 * @property float $FinalWeight
40
 * @property string $FinalCode
41
 * @property int $VariationTypeID
42
 *
43
 * @method VariationType VariationType()
44
 *
45
 * @method ManyManyList Images()
46
 */
47
class Variation extends DataObject
48
{
49
    /**
50
     * @var string
51
     */
52
    private static $table_name = 'Variation';
53
54
    /**
55
     * @var string
56
     */
57
    private static $singular_name = 'Variation';
58
59
    /**
60
     * @var string
61
     */
62
    private static $plural_name = 'Variations';
63
64
    /**
65
     * @var string[]
66
     */
67
    private static $db = [
68
        'Title' => 'Varchar(255)',
69
        'UseProductContent' => 'Boolean',
70
        'Content' => 'HTMLText',
71
        'WeightModifier' => 'Decimal(9,3)',
72
        'CodeModifier' => 'Text',
73
        'PriceModifier' => 'Currency',
74
        'WeightModifierAction' => "Enum('Add,Subtract,Set', null)",
75
        'CodeModifierAction' => "Enum('Add,Subtract,Set', null)",
76
        'PriceModifierAction' => "Enum('Add,Subtract,Set', null)",
77
        'Available' => 'Boolean',
78
        'Type' => 'Int',
79
        'OptionModifierKey' => 'Varchar(255)',
80
        'SortOrder' => 'Int',
81
        'FinalPrice' => 'Currency',
82
        'FinalWeight' => 'Decimal(9,3)',
83
        'FinalCode' => 'Varchar(255)',
84
    ];
85
86
    /**
87
     * @var string[]
88
     */
89
    private static $indexes = [
90
        'FinalPrice' => true,
91
        'FinalWeight' => true,
92
        'FinalCode' => true,
93
    ];
94
95
    /**
96
     * @var string[]
97
     */
98
    private static $has_one = [
99
        'VariationType' => VariationType::class,
100
    ];
101
102
    /**
103
     * @var array
104
     */
105
    private static $many_many = [
106
        'Images' => File::class,
107
    ];
108
109
    /**
110
     * @var \string[][]
111
     */
112
    private static $many_many_extraFields = [
113
        'Images' => [
114
            'SortOrder' => 'Int',
115
        ],
116
    ];
117
118
    /**
119
     * @var string[]
120
     */
121
    private static $owns = [
122
        'Images',
123
    ];
124
125
    /**
126
     * @var string[]
127
     */
128
    private static $default_sort = 'SortOrder';
129
130
    /**
131
     * The relation name was established before requests for videos.
132
     * The relation has subsequently been updated from Image::class to File::class
133
     * to allow for additional file types such as mp4
134
     *
135
     * @var array
136
     */
137
    private static $allowed_images_extensions = [
138
        'gif',
139
        'jpeg',
140
        'jpg',
141
        'png',
142
        'bmp',
143
        'ico',
144
        'mp4',
145
    ];
146
147
    /**
148
     * @return FieldList
149
     */
150
    public function getCMSFields()
151
    {
152
        $this->beforeUpdateCMSFields(function (FieldList $fields) {
153
            $fields->removeByName([
154
                'Images',
155
                'WeightModifier',
156
                'CodeModifier',
157
                'PriceModifier',
158
                'WeightModifierAction',
159
                'CodeModifierAction',
160
                'PriceModifierAction',
161
                'Available',
162
                'Type',
163
                'OptionModifierKey',
164
                'SortOrder',
165
                'ProductID',
166
                'FinalPrice',
167
                'FinalWeight',
168
                'FinalCode',
169
            ]);
170
171
            $fields->insertBefore(
172
                'Content',
173
                CheckboxField::create('Available')
174
                    ->setTitle('Available for purchase')
175
            );
176
177
            $fields->insertBefore(
178
                'Content',
179
                $fields->dataFieldByName('VariationTypeID')
180
            );
181
182
            $fields->insertBefore(
183
                'Content',
184
                $fields->dataFieldByName('UseProductContent')
185
            );
186
187
            $content = $fields->dataFieldByName('Content');
188
189
            $content->hideUnless('UseProductContent')->isNotChecked()->end();
190
191
            if ($this->exists()) {
192
                $fields->addFieldToTab(
193
                    'Root.ProductModifications',
194
                    ReadonlyField::create('OptionModifierKey')
195
                        ->setTitle(_t('Variation.ModifierKey', 'Modifier Key'))
196
                );
197
            }
198
199
            if ($this->Product()->hasDatabaseField('Weight')) {
0 ignored issues
show
Bug introduced by
The method Product() does not exist on Dynamic\Foxy\Model\Variation. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

199
            if ($this->/** @scrutinizer ignore-call */ Product()->hasDatabaseField('Weight')) {
Loading history...
200
                $fields->addFieldtoTab(
201
                    'Root.ProductModifications',
202
                    FieldGroup::create(
203
                        DropdownField::create(
204
                            'WeightModifierAction',
205
                            _t('Variation.WeightModifierAction', 'Weight Modification Type'),
206
                            [
207
                                'Add' => _t(
208
                                    'Variation.WeightAdd',
209
                                    'Add to Base Weight',
210
                                    'Add to weight'
211
                                ),
212
                                'Subtract' => _t(
213
                                    'Variation.WeightSubtract',
214
                                    'Subtract from Base Weight',
215
                                    'Subtract from weight'
216
                                ),
217
                                'Set' => _t('Variation.WeightSet', 'Set as a new Weight'),
218
                            ]
219
                        )
220
                            ->setEmptyString('')
221
                            ->setDescription(_t(
222
                                'Variation.WeightDescription',
223
                                'Does weight modify or replace base weight?'
224
                            )),
225
                        NumericField::create("WeightModifier")
226
                            ->setTitle(_t('Variation.WeightModifier', 'Weight'))
227
                            ->setScale(3)
228
                            ->setDescription(_t(
229
                                'Variation.WeightDescription',
230
                                'Only supports up to 3 decimal places'
231
                            ))->displayIf('WeightModifierAction')->isNotEmpty()->end(),
232
                        NumericField::create('FinalWeight')
233
                            ->setTitle('Final Modified Weight')
234
                            ->setDescription("Product's weight is {$this->Product()->Weight}")
235
                            ->performDisabledTransformation()
236
                    )->setTitle('Weight Modification')
237
                );
238
            }
239
240
            $fields->addFieldsToTab(
241
                'Root.ProductModifications',
242
                [
243
                    // Price Modifier Fields
244
                    //HeaderField::create('PriceHD', _t('Variation.PriceHD', 'Modify Price'), 4),
245
                    FieldGroup::create(
246
                        DropdownField::create(
247
                            'PriceModifierAction',
248
                            _t('Variation.PriceModifierAction', 'Price Modification Type'),
249
                            [
250
                                'Add' => _t(
251
                                    'Variation.PriceAdd',
252
                                    'Add to Base Price',
253
                                    'Add to price'
254
                                ),
255
                                'Subtract' => _t(
256
                                    'Variation.PriceSubtract',
257
                                    'Subtract from Base Price',
258
                                    'Subtract from price'
259
                                ),
260
                                'Set' => _t('Variation.PriceSet', 'Set as a new Price'),
261
                            ]
262
                        )
263
                            ->setEmptyString('')
264
                            ->setDescription(_t('Variation.PriceDescription', 'Does price modify or replace base price?')),
265
                        CurrencyField::create('PriceModifier')
266
                            ->setTitle(_t('Variation.PriceModifier', 'Price'))
267
                            ->displayIf('PriceModifierAction')->isNotEmpty()->end(),
268
                        CurrencyField::create('FinalPrice')
269
                            ->setTitle('Final Modified Price')
270
                            ->setDescription("Product's price is {$this->Product()->Price}")
271
                            ->performDisabledTransformation()
272
                    )->setTitle('Price Modifications'),
273
274
                    // Code Modifier Fields
275
                    //HeaderField::create('CodeHD', _t('Variation.CodeHD', 'Modify Code'), 4),
276
                    FieldGroup::create(
277
                        DropdownField::create(
278
                            'CodeModifierAction',
279
                            _t('Variation.CodeModifierAction', 'Code Modification Type'),
280
                            [
281
                                'Add' => _t(
282
                                    'Variation.CodeAdd',
283
                                    'Add to Base Code',
284
                                    'Add to code'
285
                                ),
286
                                'Subtract' => _t(
287
                                    'Variation.CodeSubtract',
288
                                    'Subtract from Base Code',
289
                                    'Subtract from code'
290
                                ),
291
                                'Set' => _t('Variation.CodeSet', 'Set as a new Code'),
292
                            ]
293
                        )
294
                            ->setEmptyString('')
295
                            ->setDescription(_t('Variation.CodeDescription', 'Does code modify or replace base code?')),
296
                        TextField::create('CodeModifier')
297
                            ->setTitle(_t('Variation.CodeModifier', 'Code'))
298
                            ->displayIf('CodeModifierAction')->isNotEmpty()->end(),
299
                        TextField::create('FinalCode')
300
                            ->setTitle('Final Modified Code')
301
                            ->setDescription("Product's code is {$this->Product()->Code}")
302
                            ->performDisabledTransformation()
303
                    )->setTitle('Code Modification'),
304
                ]
305
            );
306
307
            // Images tab
308
            $images = SortableUploadField::create('Images')
309
                ->setSortColumn('SortOrder')
310
                ->setIsMultiUpload(true)
311
                ->setAllowedExtensions($this->config()->get('allowed_images_extensions'))
312
                ->setFolderName('Uploads/Products/Images');
313
314
            $fields->addFieldsToTab('Root.Images', [
315
                $images,
316
            ]);
317
        });
318
319
        return parent::getCMSFields();
320
    }
321
322
    /**
323
     *
324
     */
325
    public function onBeforeWrite()
326
    {
327
        parent::onBeforeWrite();
328
329
        $modifierKeyField = 'OptionModifierKey';
330
        $this->{$modifierKeyField} = $this->getGeneratedValue();
331
332
        $codeModifierField = 'CodeModifier';
333
        switch ($this->CodeModifierAction) {
334
            case 'Subtract':
335
            case 'Add':
336
                if ($this->config()->get('trimAllWhitespace') == false) {
337
                    // trim the right of the code - some companies use spaces to denote options
338
                    $trimmed = rtrim($this->{$codeModifierField});
339
                    // replace duplicate spaces
340
                    $this->{$codeModifierField} = preg_replace('/\s+/', ' ', $trimmed);
341
                    break;
342
                }
343
            /* falls through */
344
            case 'Set':
345
                $trimmed = trim($this->{$codeModifierField});
346
                $this->{$codeModifierField} = preg_replace('/\s+/', ' ', $trimmed);
347
                break;
348
        }
349
350
        $this->FinalPrice = $this->calculateFinalPrice();
351
        $this->FinalCode = $this->calculateFinalCode();
352
353
        if ($this->Product()->hasDatabaseField('Weight')) {
354
            $this->FinalWeight = $this->calculateFinalWeight();
355
        }
356
    }
357
358
    /**
359
     * @return ValidationResult
360
     */
361
    public function validate()
362
    {
363
        $validate = parent::validate();
364
        $product = $this->Product();
365
366
        if (!$this->Title) {
367
            $validate->addFieldError('Title', 'A title is required');
368
        }
369
370
        /*if (!$this->VariationTypeID) {
371
            $validate->addFieldError('VariationTypeID', 'A variation type is required');
372
        }//*/
373
374
        if ($this->PriceModifierAction == 'Subtract' && $this->PriceModifier > $product->Price) {
375
            $validate->addFieldError('PriceModifier', "You can't subtract more than the price of the product ({$product->Price})");
376
        }
377
378
        if ($this->WeightModifierAction == 'Subtract' && $this->WeightModifier > $product->Weight) {
379
            $validate->addFieldError('WeightModifier', "You can't subtract more than the weight of the product ({$product->Weight})");
380
        }
381
382
        return $validate;
383
    }
384
385
    /**
386
     * @return string
387
     */
388
    public function getGeneratedValue()
389
    {
390
        $modPrice = ($this->PriceModifier) ? (string)$this->PriceModifier : '0';
391
        $modPriceWithSymbol = self::getOptionModifierActionSymbol($this->PriceModifierAction) . $modPrice;
392
        $modWeight = ($this->WeightModifier) ? (string)$this->WeightModifier : '0';
393
        $modWeight = self::getOptionModifierActionSymbol($this->WeightModifierAction) . $modWeight;
394
        $modCode = self::getOptionModifierActionSymbol($this->CodeModifierAction) . $this->CodeModifier;
395
396
        return $this->Title . '{p' . $modPriceWithSymbol . '|w' . $modWeight . '|c' . $modCode . '}';
397
    }
398
399
    /**
400
     * @param $oma
401
     * @param bool $returnWithOnlyPlusMinus
402
     *
403
     * @return string
404
     */
405
    public static function getOptionModifierActionSymbol($oma, $returnWithOnlyPlusMinus = false)
406
    {
407
        switch ($oma) {
408
            case 'Subtract':
409
                $symbol = '-';
410
                break;
411
            case 'Set':
412
                $symbol = ($returnWithOnlyPlusMinus) ? '' : ':';
413
                break;
414
            default:
415
                $symbol = '+';
416
        }
417
418
        return $symbol;
419
    }
420
421
    /**
422
     * @return string
423
     */
424
    protected function getWeightModifierWithSymbol()
425
    {
426
        return $this->getOptionModifierActionSymbol($this->WeightModifierAction) . $this->WeightModifier;
427
    }
428
429
    /**
430
     * @return string
431
     */
432
    protected function getPriceModifierWithSymbol()
433
    {
434
        return $this->getOptionModifierActionSymbol($this->PriceModifierAction) . $this->PriceModifier;
435
    }
436
437
    /**
438
     * @return string
439
     */
440
    protected function getCodeModifierWithSymbol()
441
    {
442
        return $this->getOptionModifierActionSymbol($this->CodeModifierAction) . $this->CodeModifier;
443
    }
444
445
    /**
446
     * @return float
447
     */
448
    protected function calculateFinalPrice()
449
    {
450
        $product = $this->Product();// this relation is set by a developer's data extension
451
452
        if ($this->PriceModifierAction == 'Add') {
453
            return $this->PriceModifier + $product->Price;
454
        } elseif ($this->PriceModifierAction == 'Subtract') {
455
            return $product->Price - $this->PriceModifier;
456
        } elseif ($this->PriceModifierAction == 'Set') {
457
            return $this->PriceModifier;
458
        }
459
460
        return $product->Price;
461
    }
462
463
    /**
464
     * @return float
465
     */
466
    protected function calculateFinalWeight()
467
    {
468
        $product = $this->Product();// this relation is set by a developer's data extension
469
470
        if ($this->WeightModifierAction == 'Add') {
471
            return $this->WeightModifier + $product->Weight;
472
        } elseif ($this->WeightModifierAction == 'Subtract') {
473
            return $product->Weight - $this->WeightModifier;
474
        } elseif ($this->WeightModifierAction == 'Set') {
475
            return $this->WeightModifier;
476
        }
477
478
        return $product->Weight;
479
    }
480
481
    /**
482
     * @return string
483
     */
484
    protected function calculateFinalCode()
485
    {
486
        $product = $this->Product();// this relation is set by a developer's data extension
487
488
        if ($this->CodeModifierAction == 'Add') {
489
            return "{$product->Code}{$this->CodeModifier}";
490
        } elseif ($this->CodeModifierAction == 'Subtract') {
491
            return rtrim($product->Code, $this->CodeModifier);
492
        } elseif ($this->CodeModifierAction == 'Set') {
493
            return $this->CodeModifier;
494
        }
495
496
        return $product->Code;
497
    }
498
499
    /**
500
     * @return mixed|string
501
     */
502
    public function getGeneratedTitle()
503
    {
504
        $modPrice = ($this->PriceModifier) ? (string)$this->PriceModifier : '0';
505
        $title = $this->Title;
506
        $title .= ($this->PriceModifier != 0) ?
507
            ': (' . self::getOptionModifierActionSymbol(
508
                $this->PriceModifierAction,
509
                $returnWithOnlyPlusMinus = true
510
            ) . '$' . $modPrice . ')' :
511
            '';
512
513
        return $title;
514
    }
515
516
    /**
517
     * @return bool
518
     */
519
    public function getAvailability()
520
    {
521
        $available = $this->Available;
522
523
        $this->extend('updateAvailability', $available);
524
525
        return $available;
526
    }
527
528
    /**
529
     * @param $member
530
     * @return bool|int|void
531
     */
532
    public function canCreate($member = null, $context = [])
533
    {
534
        if (!$member) {
535
            $member = Security::getCurrentUser();
536
        }
537
538
        return Permission::checkMember($member, 'MANAGE_FOXY_PRODUCTS');
539
    }
540
541
    /**
542
     * @param $member
543
     * @return bool|int|void|null
544
     */
545
    public function canEdit($member = null, $context = [])
0 ignored issues
show
Unused Code introduced by
The parameter $context is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

545
    public function canEdit($member = null, /** @scrutinizer ignore-unused */ $context = [])

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
546
    {
547
        if (!$member) {
548
            $member = Security::getCurrentUser();
549
        }
550
551
        return Permission::checkMember($member, 'MANAGE_FOXY_PRODUCTS');
552
    }
553
554
    /**
555
     * @param $member
556
     * @return bool|int|void
557
     */
558
    public function canDelete($member = null, $context = [])
0 ignored issues
show
Unused Code introduced by
The parameter $context is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

558
    public function canDelete($member = null, /** @scrutinizer ignore-unused */ $context = [])

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
559
    {
560
        if (!$member) {
561
            $member = Security::getCurrentUser();
562
        }
563
564
        return Permission::checkMember($member, 'MANAGE_FOXY_PRODUCTS');
565
    }
566
}
567