Passed
Push — master ( 300e34...a36352 )
by Nic
10:09 queued 06:34
created

AddToCartForm::getProductActions()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 16
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 2
nop 1
dl 0
loc 16
rs 9.9666
1
<?php
2
3
namespace Dynamic\Foxy\Form;
4
5
use Dynamic\Foxy\Model\FoxyHelper;
6
use Dynamic\Foxy\Model\Variation;
7
use Dynamic\Foxy\Model\VariationType;
8
use Dynamic\Foxy\Page\Product;
9
use Dynamic\Foxy\Page\ShippableProduct;
10
use SilverStripe\CMS\Model\VirtualPage;
11
use SilverStripe\Forms\CompositeField;
12
use SilverStripe\Forms\DropdownField;
13
use SilverStripe\Forms\FieldList;
14
use SilverStripe\Forms\Form;
15
use SilverStripe\Forms\FormAction;
16
use SilverStripe\Forms\FormField;
17
use SilverStripe\Forms\HeaderField;
18
use SilverStripe\Forms\HiddenField;
19
use SilverStripe\Forms\RequiredFields;
20
use SilverStripe\ORM\HasManyList;
21
use SilverStripe\ORM\ValidationException;
22
23
/**
24
 * Class AddToCartForm
25
 * @package Dynamic\Foxy\Form
26
 */
27
class AddToCartForm extends Form
28
{
29
    /**
30
     * @var FoxyHelper
31
     */
32
    protected FoxyHelper $helper;
33
34
    /**
35
     * @var Product
36
     */
37
    private Product $product;
38
39
    /**
40
     * @param $helper
41
     *
42
     * @return $this
43
     */
44
    public function setFoxyHelper($helper): self
45
    {
46
        $helper = $helper === null ? FoxyHelper::create() : $helper;
47
        if ($helper instanceof FoxyHelper) {
48
            $this->helper = $helper;
49
50
            return $this;
51
        }
52
        throw new \InvalidArgumentException('$helper needs to be an instance of FoxyHelper.');
53
    }
54
55
    /**
56
     * @return FoxyHelper
57
     */
58
    public function getFoxyHelper(): FoxyHelper
59
    {
60
        if (!$this->helper) {
61
            $this->setFoxyHelper(FoxyHelper::create());
62
        }
63
64
        return $this->helper;
65
    }
66
67
    /**
68
     * @param $product
69
     *
70
     * @return $this
71
     */
72
    public function setProduct($product): self
73
    {
74
        if ($product instanceof VirtualPage) {
75
            if (!$product = Product::get_by_id(Product::class, $product->CopyContentFromID)) {
76
                throw new \InvalidArgumentException(sprintf('$product needs to be a descendant of %s, or a Virtual Page copied from a %s descendant.', Product::class, Product::class));
77
            }
78
        }
79
80
        $this->product = $product;
81
82
        return $this;
83
    }
84
85
    /**
86
     * @return Product
87
     */
88
    public function getProduct(): Product
89
    {
90
        return $this->product;
91
    }
92
93
    /**
94
     * AddToCartForm constructor.
95
     * @param $controller
96
     * @param $name
97
     * @param FieldList|null $fields
98
     * @param FieldList|null $actions
99
     * @param null $validator
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $validator is correct as it would always require null to be passed?
Loading history...
100
     * @param null $product
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $product is correct as it would always require null to be passed?
Loading history...
101
     * @param null $helper
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $helper is correct as it would always require null to be passed?
Loading history...
102
     * @throws ValidationException
103
     */
104
    public function __construct(
105
        $controller,
106
        $name,
107
        FieldList $fields = null,
108
        FieldList $actions = null,
109
        $validator = null,
110
        $product = null,
111
        $helper = null
112
    )
113
    {
114
        $this->setProduct($product);
115
        $this->setFoxyHelper($helper);
116
117
        $fields = ($fields != null && $fields->exists()) ?
118
            $this->getProductFields($fields) :
119
            $this->getProductFields(FieldList::create());
120
121
        $actions = ($actions != null && $actions->exists()) ?
122
            $this->getProductActions($actions) :
123
            $this->getProductActions(FieldList::create());
124
125
        $validator = (!empty($validator) || $validator != null) ? $validator : RequiredFields::create();
126
127
        parent::__construct($controller, $name, $fields, $actions, $validator);
128
129
        //have to call after parent::__construct()
130
        $this->setAttribute('action', FoxyHelper::FormActionURL());
131
        $this->disableSecurityToken();
132
        $this->setHTMLID($this->getTemplateHelper()->generateFormID($this) . "_{$product->ID}");
133
    }
134
135
    /**
136
     * @param FieldList $fields
137
     *
138
     * @return FieldList
139
     */
140
    protected function getProductFields(FieldList $fields): FieldList
141
    {
142
        $hiddenTitle = ($this->product->ReceiptTitle) ?
143
            htmlspecialchars($this->product->ReceiptTitle) :
144
            htmlspecialchars($this->product->Title);
145
        $code = $this->product->Code;
146
147
        if ($this->product->getIsAvailable()) {
148
            $fields->push(
149
                HiddenField::create('name')
150
                    ->setValue(
151
                        self::getGeneratedValue($code, 'name', $hiddenTitle, 'value')
152
                    )
153
            );
154
155
            $fields->push(
156
                HiddenField::create('category')
157
                    ->setValue(
158
                        self::getGeneratedValue($code, 'category', $this->product->FoxyCategory()->Code, 'value')
159
                    )
160
            );
161
162
            $fields->push(
163
                HiddenField::create('code')
164
                    ->setValue(
165
                        self::getGeneratedValue($code, 'code', $this->product->Code, 'value')
166
                    )
167
            );
168
169
            $fields->push(
170
                HiddenField::create('product_id')
171
                    ->setValue(
172
                        self::getGeneratedValue($code, 'product_id', $this->product->ID, 'value')
173
                    )
174
                    ->setName('h:product_id')
175
            );
176
177
            $fields->push(
178
                HiddenField::create('price')
179
                    ->setValue(
180
                        self::getGeneratedValue($code, 'price', $this->product->Price, 'value')
181
                    )
182
            );
183
184
            if ($this->product->hasMethod('AbsoluteLink')) {
185
                $fields->push(
186
                    HiddenField::create('url')
187
                        ->setValue(
188
                            self::getGeneratedValue($code, 'url', $this->product->AbsoluteLink(), 'value')
189
                        )
190
                );
191
            }
192
193
            if ($this->product instanceof ShippableProduct) {
194
                if ($this->product->Weight > 0) {
0 ignored issues
show
Bug Best Practice introduced by
The property Weight does not exist on Dynamic\Foxy\Page\Product. Since you implemented __get, consider adding a @property annotation.
Loading history...
195
                    $fields->push(
196
                        HiddenField::create('weight')
197
                            ->setValue(
198
                                self::getGeneratedValue($code, 'weight', $this->product->Weight, 'value')
199
                            )
200
                    );
201
                }
202
            }
203
204
            $image = null;
205
            if ($this->product->hasMethod('getImage')) {
206
                if ($this->product->getImage()) {
0 ignored issues
show
Bug introduced by
The method getImage() does not exist on Dynamic\Foxy\Page\Product. 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

206
                if ($this->product->/** @scrutinizer ignore-call */ getImage()) {
Loading history...
207
                    $image = $this->product->getImage()->CMSThumbnail()->absoluteURL;
208
                }
209
                if ($image) {
210
                    $fields->push(
211
                        HiddenField::create('image')
212
                            ->setValue(
213
                                self::getGeneratedValue($code, 'image', $image, 'value')
214
                            )
215
                    );
216
                }
217
            }
218
219
            if ($this->product->QuantityMax > 0) {
220
                $fields->push(
221
                    HiddenField::create('quantity_max')
222
                        ->setValue(self::getGeneratedValue($code, 'quantity_max', $this->product->QuantityMax, 'value'))
223
                );
224
            }
225
226
            if ($variationsField = $this->getProductVariations()) {
227
                $fields->push($variationsField);
0 ignored issues
show
Bug introduced by
It seems like $variationsField can also be of type true; however, parameter $item of SilverStripe\Forms\FieldList::push() does only seem to accept SilverStripe\Forms\FormField, maybe add an additional type check? ( Ignorable by Annotation )

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

227
                $fields->push(/** @scrutinizer ignore-type */ $variationsField);
Loading history...
228
            }
229
230
            if ($this->product->QuantityMax != 1) {
231
                $fields->push(QuantityField::create('x:visibleQuantity')->setTitle('Quantity')->setValue(1));
232
            }
233
            $fields->push(
234
                HiddenField::create('quantity')
235
                    ->setValue(
236
                        self::getGeneratedValue($code, 'quantity', 1, 'value')
237
                    )
238
            );
239
240
            $fields->push(
241
                HeaderField::create('submitPrice', '$' . $this->product->Price, 4)
242
                    ->addExtraClass('submit-price')
243
            );
244
        }
245
246
        $this->extend('updateProductFields', $fields);
247
248
        return $fields;
249
    }
250
251
    /**
252
     * @param FieldList $actions
253
     *
254
     * @return FieldList
255
     */
256
    protected function getProductActions(FieldList $actions): FieldList
257
    {
258
        if (!empty(trim($this->helper->getStoreCartURL())) && $this->product->getIsAvailable()) {
259
            $actions->push(
260
                FormAction::create(
261
                    'x:submit',
262
                    _t(__CLASS__ . '.AddToCart', 'Add to Cart')
263
                )
264
                    ->addExtraClass('fs-add-to-cart-button')
265
                    ->setName('x:submit')
266
            );
267
        }
268
269
        $this->extend('updateProductActions', $actions);
270
271
        return $actions;
272
    }
273
274
    /**
275
     * @param null $productCode
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $productCode is correct as it would always require null to be passed?
Loading history...
276
     * @param null $optionName
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $optionName is correct as it would always require null to be passed?
Loading history...
277
     * @param null $optionValue
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $optionValue is correct as it would always require null to be passed?
Loading history...
278
     * @param string $method
279
     * @param bool $output
280
     * @param bool $urlEncode
281
     *
282
     * @return null|string
283
     */
284
    public static function getGeneratedValue(
285
        $productCode = null,
286
        $optionName = null,
287
        $optionValue = null,
288
        $method = 'name',
289
        $output = false,
290
        $urlEncode = false
291
    ): string
292
    {
293
        $optionName = ($optionName !== null) ? preg_replace('/\s/', '_', $optionName) : $optionName;
0 ignored issues
show
introduced by
The condition $optionName !== null is always false.
Loading history...
294
        $helper = FoxyHelper::create();
295
296
        return $helper::fc_hash_value($productCode, $optionName, $optionValue, $method, $output, $urlEncode);
297
    }
298
299
    /**
300
     * @return CompositeField|FormField|DropdownField|bool
301
     */
302
    protected function getProductVariations()
303
    {
304
        // we have 1 variant (the default) so we don't need to do anything
305
        if ($this->getProduct()->Variations()->count() == 1) {
306
            return false;
307
        }
308
309
        if (($types = VariationType::get()) && $types->count()) {
310
            $variationsField = CompositeField::create();
311
312
            foreach ($types as $type) {
313
                if (($variations = $type->Variations()->filter('ProductID', $this->product->ID)) && $variations->count()) {
314
                    $variationsField->push($this->createVariationField($variations, $type));
315
                }
316
            }
317
        } else {
318
            $variationsField = $this->createVariationField($this->getProduct()->Variations());
319
        }
320
321
        return $variationsField;
322
    }
323
324
    /**
325
     * @param HasManyList $variations
326
     * @param VariationType|null $type
327
     * @return DropdownField
328
     */
329
    protected function createVariationField(HasManyList $variations, VariationType $type = null): DropdownField
330
    {
331
        $disabled = [];
332
        $list = [];
333
        $variationField = DropdownField::create(preg_replace('/\s/', '_', $type->Title));
334
335
        /** @var Variation $variation */
336
        foreach ($variations as $variation) {
337
            $name = self::getGeneratedValue(
338
                $this->product->Code,
339
                $type->Title,
340
                $variation->getGeneratedValue(),
341
                'value'
342
            );
343
344
            $list[$name] = $variation->getGeneratedTitle();
345
346
            if (!$variation->getIsAvailable()) {
347
                array_push($disabled, $name);
348
            }
349
        }
350
351
        $variationField->setSource($list)
352
            ->setTitle($type->Title)
353
            ->addExtraClass("product-options");
354
355
        if (!empty($disabled)) {
356
            $variationField->setDisabledItems($disabled);
357
        }
358
359
        return $variationField;
360
    }
361
}
362