Completed
Push — master ( 8827e6...300e34 )
by Nic
15s queued 12s
created

Product::getDefaultVariation()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 1
dl 0
loc 3
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
1
<?php
2
3
namespace Dynamic\Foxy\Page;
4
5
use Dynamic\Foxy\Controller\ProductController;
6
use Dynamic\Foxy\Model\FoxyCategory;
7
use Dynamic\Foxy\Model\Variation;
8
use Dynamic\Foxy\Model\VariationType;
9
use micschk\GroupableGridfield\GridFieldGroupable;
10
use SilverStripe\Forms\CheckboxField;
11
use SilverStripe\Forms\CurrencyField;
12
use SilverStripe\Forms\DropdownField;
13
use SilverStripe\Forms\FieldList;
14
use SilverStripe\Forms\GridField\GridField;
15
use SilverStripe\Forms\GridField\GridFieldAddExistingAutocompleter;
16
use SilverStripe\Forms\GridField\GridFieldConfig_RelationEditor;
17
use SilverStripe\Forms\GridField\GridFieldDeleteAction;
18
use SilverStripe\Forms\GridField\GridFieldPageCount;
19
use SilverStripe\Forms\GridField\GridFieldPaginator;
20
use SilverStripe\Forms\GridField\GridFieldSortableHeader;
21
use SilverStripe\Forms\NumericField;
22
use SilverStripe\Forms\RequiredFields;
23
use SilverStripe\Forms\TextField;
24
use SilverStripe\ORM\DataObject;
25
use SilverStripe\ORM\HasManyList;
26
use SilverStripe\ORM\ValidationException;
27
use SilverStripe\Security\Permission;
28
use SilverStripe\Security\Security;
29
use Symbiote\GridFieldExtensions\GridFieldOrderableRows;
30
use Symbiote\GridFieldExtensions\GridFieldTitleHeader;
31
32
/**
33
 * @property double Price
34
 * @property string Code
35
 * @property string ReceiptTitle
36
 * @property bool Available
37
 * @property int $QuantityMax
38
 *
39
 * @property int FoxyCategoryID
40
 * @method FoxyCategory FoxyCategory()
41
 *
42
 * @method HasManyList Variations()
43
 */
44
class Product extends \Page
45
{
46
    /**
47
     * @var string
48
     */
49
    private static $table_name = 'FoxyProduct';
50
51
    /**
52
     * @var array
53
     */
54
    private static $db = [
55
        'Price' => 'Currency',
56
        'Code' => 'Varchar(100)',
57
        'ReceiptTitle' => 'HTMLVarchar(255)',
58
        'Available' => 'Boolean',
59
        'QuantityMax' => 'Int',
60
    ];
61
62
    /**
63
     * @var array
64
     */
65
    private static $has_one = [
66
        'FoxyCategory' => FoxyCategory::class,
67
    ];
68
69
    /**
70
     * @var string[]
71
     */
72
    private static $has_many = [
73
        'Variations' => Variation::class,
74
    ];
75
76
    /**
77
     * @var array
78
     */
79
    private static $indexes = [
80
        'Code' => [
81
            'type' => 'unique',
82
            'columns' => ['Code'],
83
        ],
84
    ];
85
86
    /**
87
     * @var array
88
     */
89
    private static $defaults = [
90
        'ShowInMenus' => false,
91
        'Available' => true,
92
    ];
93
94
    /**
95
     * @var array
96
     */
97
    private static $summary_fields = [
98
        //'Image.CMSThumbnail',
99
        'Title',
100
        'Code',
101
        'Price.Nice',
102
    ];
103
104
    /**
105
     * @var array
106
     */
107
    private static $searchable_fields = [
108
        'Title',
109
        'Code',
110
        'Available',
111
    ];
112
113
    /**
114
     * @param $includeRelations
115
     * @return array
116
     */
117
    public function fieldLabels($includeRelations = true)
118
    {
119
        $labels = parent::fieldLabels($includeRelations);
120
121
        $labels['Title'] = _t(__CLASS__ . '.TitleLabel', 'Product Name');
122
        $labels['Code'] = _t(__CLASS__ . '.CodeLabel', 'Code');
123
        $labels['Price'] = _t(__CLASS__ . '.PriceLabel', 'Price');
124
        $labels['Price.Nice'] = _t(__CLASS__ . '.PriceLabel', 'Price');
125
        $labels['Available'] = _t(__CLASS__ . '.AvailableLabel', 'Available for purchase');
126
        $labels['Available.Nice'] = _t(__CLASS__ . '.AvailableLabelNice', 'Available');
127
        //$labels['Image.CMSThumbnail'] = _t(__CLASS__ . '.ImageLabel', 'Image');
128
        $labels['ReceiptTitle'] = _t(__CLASS__ . '.ReceiptTitleLabel', 'Product title for receipt');
129
        $labels['FoxyCategoryID'] = _t(__CLASS__ . '.FoxyCategoryLabel', 'Foxy Category');
130
131
        return $labels;
132
    }
133
134
    /**
135
     * @return FieldList
136
     */
137
    public function getCMSFields()
138
    {
139
        $this->beforeUpdateCMSFields(function (FieldList $fields) {
140
            $fields->removeByName([
141
                'SKU',
142
            ]);
143
144
            $fields->addFieldsToTab(
145
                'Root.Ecommerce',
146
                [
147
                    CurrencyField::create('Price')
148
                        ->setDescription(_t(
149
                            __CLASS__ . '.PriceDescription',
150
                            'Base price for this product. Can be modified using Product variations'
151
                        )),
152
                    TextField::create('Code')
153
                        ->setDescription(_t(
154
                            __CLASS__ . '.CodeDescription',
155
                            'Required, must be unique. Product identifier used by FoxyCart in transactions. All leading and trailing spaces are removed on save.'
156
                        )),
157
                    DropdownField::create('FoxyCategoryID')
158
                        ->setTitle($this->fieldLabel('FoxyCategoryID'))
159
                        ->setSource(FoxyCategory::get()->map())
160
                        ->setDescription(_t(
161
                            __CLASS__ . '.FoxyCategoryDescription',
162
                            'Required. Must also exist in
163
                        <a href="https://admin.foxycart.com/admin.php?ThisAction=ManageProductCategories"
164
                            target="_blank">
165
                            Foxy Categories
166
                        </a>.
167
                        Used to set category specific options like shipping and taxes. Managed in Foxy > Categories'
168
                        ))
169
                        ->setEmptyString(''),
170
                    TextField::create('ReceiptTitle')
171
                        ->setDescription(_t(
172
                            __CLASS__ . '.ReceiptTitleDescription',
173
                            'Optional. Alternate title to display on order receipt'
174
                        )),
175
                ],
176
                'Content'
177
            );
178
179
            if ($this->exists()) {
180
                $variationsConfig = GridFieldConfig_RelationEditor::create()
181
                    ->removeComponentsByType([
182
                        GridFieldAddExistingAutocompleter::class,
183
                        GridFieldPaginator::class,
184
                        GridFieldPageCount::class,
185
                        GridFieldSortableHeader::class,
186
                        GridFieldDeleteAction::class,
187
                    ])
188
                    ->addComponents([
189
                        new GridFieldOrderableRows('SortOrder'),
190
                        new GridFieldTitleHeader(),
191
                        new GridFieldGroupable(
192
                            'VariationTypeID',    // The fieldname to set the Group
193
                            'Variation Type',   // A description of the function of the group
194
                            'none',         // A title/header for items without a group/unassigned
195
                            VariationType::get()->sort('SortOrder')->map()->toArray()
196
                        ),
197
                        new GridFieldDeleteAction(),
198
                    ]);
199
200
                $fields->addFieldToTab(
201
                    'Root.Variations',
202
                    GridField::create(
203
                        'Variations',
204
                        'Variations',
205
                        $this->Variations(),
206
                        $variationsConfig
207
                    )
208
                );
209
            }
210
211
            $fields->addFieldsToTab(
212
                'Root.Inventory',
213
                [
214
                    NumericField::create('QuantityMax')
215
                        ->setTitle('Maximum quantity allowed in the cart')
216
                        ->setDescription('For unlimited enter 0')
217
                        ->addExtraClass('stacked'),
218
                    CheckboxField::create('Available')
219
                        ->setDescription(_t(
220
                            __CLASS__ . '.AvailableDescription',
221
                            'If unchecked, will remove "Add to Cart" form and instead display "Currently unavailable"'
222
                        )),
223
                ]
224
            );
225
        });
226
227
        return parent::getCMSFields();
228
    }
229
230
    /**
231
     * @return RequiredFields
232
     */
233
    public function getCMSValidator()
234
    {
235
        return new RequiredFields([
236
            "Price",
237
            "Code",
238
            "FoxyCategoryID",
239
        ]);
240
    }
241
242
    /**
243
     * @return bool
244
     */
245
    public function getIsAvailable()
246
    {
247
        $available = true;
248
249
        if (!$this->Available) {
250
            $available = false;
251
        }
252
253
        if ($available && $this->Variations()->count()) {
254
            $available = false;
255
            foreach ($this->Variations() as $variation) {
256
                if ($variation->Available) {
257
                    $available = true;
258
                }
259
            }
260
        }
261
262
        $this->extend('updateGetIsAvailable', $available);
263
264
        return $available;
265
    }
266
267
    /**
268
     * @return array
269
     */
270
    public function providePermissions()
271
    {
272
        return [
273
            'MANAGE_FOXY_PRODUCTS' => [
274
                'name' => _t(
275
                    __CLASS__ . '.PERMISSION_MANAGE_PRODUCTS_DESCRIPTION',
276
                    'Manage products'
277
                ),
278
                'category' => _t(
279
                    __CLASS__ . '.PERMISSIONS_CATEGORY',
280
                    'Foxy'
281
                ),
282
                'help' => _t(
283
                    __CLASS__ . '.PERMISSION_MANAGE_PRODUCTS_HELP',
284
                    'Manage products and related settings'
285
                ),
286
                'sort' => 400,
287
            ],
288
        ];
289
    }
290
291
    /**
292
     * @param $member
293
     * @param $context
294
     * @return bool|int
295
     */
296
    public function canCreate($member = null, $context = [])
297
    {
298
        if (!$member) {
299
            $member = Security::getCurrentUser();
300
        }
301
302
        return Permission::checkMember($member, 'MANAGE_FOXY_PRODUCTS');
303
    }
304
305
    /**
306
     * @param $member
307
     * @return bool|int
308
     */
309
    public function canEdit($member = null)
310
    {
311
        if (!$member) {
312
            $member = Security::getCurrentUser();
313
        }
314
315
        return Permission::checkMember($member, 'MANAGE_FOXY_PRODUCTS');
316
    }
317
318
    /**
319
     * @param $member
320
     * @return bool|int
321
     */
322
    public function canDelete($member = null)
323
    {
324
        if (!$member) {
325
            $member = Security::getCurrentUser();
326
        }
327
328
        return Permission::checkMember($member, 'MANAGE_FOXY_PRODUCTS');
329
    }
330
331
    /**
332
     * @param $member
333
     * @return bool|int|mixed|null
334
     */
335
    public function canUnpublish($member = null)
336
    {
337
        if (!$member) {
338
            $member = Security::getCurrentUser();
339
        }
340
341
        return Permission::checkMember($member, 'MANAGE_FOXY_PRODUCTS');
342
    }
343
344
    /**
345
     * @param $member
346
     * @return bool|int
347
     */
348
    public function canArchive($member = null)
349
    {
350
        if (!$member) {
351
            $member = Security::getCurrentUser();
352
        }
353
354
        return Permission::checkMember($member, 'MANAGE_FOXY_PRODUCTS');
355
    }
356
357
    /**
358
     * @return void
359
     */
360
    public function onBeforeWrite()
361
    {
362
        parent::onBeforeWrite();
363
364
        // trim spaces and replace duplicate spaces
365
        $trimmed = trim($this->Code);
366
        $this->Code = preg_replace('/\s+/', ' ', $trimmed);
367
    }
368
369
    /**
370
     * @return void
371
     * @throws ValidationException
372
     */
373
    protected function onAfterWrite()
374
    {
375
        parent::onAfterWrite();
376
377
        if (!$this->getDefaultVariation() || $this->baseProductChange()) {
378
            $this->resetDefaultVariant();
379
        }
380
    }
381
382
    /**
383
     * @return string
384
     */
385
    public function getControllerName()
386
    {
387
        return ProductController::class;
388
    }
389
390
    /**
391
     * @return bool
392
     */
393
    protected function baseProductChange()
394
    {
395
        return $this->isChanged('Code');
396
    }
397
398
    /**
399
     * @return DataObject|null
400
     */
401
    public function getDefaultVariation()
402
    {
403
        return $this->Variations()->filter('IsDefault', true)->first();
404
    }
405
406
    /**
407
     * @return void
408
     * @throws ValidationException
409
     */
410
    protected function resetDefaultVariant()
411
    {
412
        if (!$variant = $this->getDefaultVariation()) {
413
            $variant = Variation::create();
414
415
            foreach (Variation::singleton()->config()->get('default_variation_mapping') as $productField => $variationField) {
416
                $variant->$variationField = $this->$productField;
417
            }
418
419
            $variant->ProductID = $this->ID;
420
            $variant->IsDefault = true;
421
422
            $variant->write();
423
        }
424
425
        $variant->Title = $this->Title;
426
        $variant->CodeModifier = $this->Code;
427
        $variant->CodeModifierAction = 'Set';
428
429
        $variant->write();
430
    }
431
}
432