Passed
Pull Request — master (#99)
by Nic
07:01 queued 04:16
created

Variation::getGeneratedValue()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 9
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

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

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