Issues (464)

src/Checkout/Component/AddressBook.php (1 issue)

Severity
1
<?php
2
3
namespace SilverShop\Checkout\Component;
4
5
use SilverShop\Model\Order;
6
use SilverStripe\Core\Config\Config;
7
use SilverStripe\Forms\CompositeField;
8
use SilverStripe\Forms\DropdownField;
9
use SilverStripe\Forms\FieldList;
10
use SilverStripe\Forms\OptionsetField;
11
use SilverStripe\i18n\i18nEntityProvider;
12
use SilverStripe\ORM\ValidationException;
13
use SilverStripe\ORM\ValidationResult;
14
use SilverStripe\Security\Security;
15
use SilverStripe\View\Requirements;
16
17
/**
18
 * Adds the ability to use the member's address book for choosing addresses
19
 */
20
abstract class AddressBook extends Address implements i18nEntityProvider
21
{
22
    private static $jquery_file = 'https://code.jquery.com/jquery-3.7.0.min.js';
23
    /**
24
     * The composite field tag to use
25
     *
26
     * @config
27
     * @var    string
28
     */
29
    private static $composite_field_tag = 'div';
30
31
    protected $addtoaddressbook = true;
32
33
    public function getFormFields(Order $order)
34
    {
35
        $fields = parent::getFormFields($order);
36
37
        if ($existingaddressfields = $this->getExistingAddressFields()) {
38
            if ($jquery = $this->config()->get('jquery_file')) {
39
                Requirements::javascript($jquery);
40
                Requirements::javascript('silvershop/core:client/dist/javascript/CheckoutPage.js');
41
            } else {
42
                Requirements::javascript('silvershop/core:client/dist/javascript/CheckoutPage.nojquery.js');
43
            }
44
45
            // add the fields for a new address after the dropdown field
46
            $existingaddressfields->merge($fields);
47
            // group under a composite field (invisible by default) so we
48
            // easily know which fields to show/hide
49
            $label = _t(
50
                "SilverShop\Model\Address.{$this->addresstype}Address",
51
                "{$this->addresstype} Address"
52
            );
53
54
            return FieldList::create(
55
                CompositeField::create($existingaddressfields)
56
                    ->addExtraClass('hasExistingValues')
57
                    ->setLegend($label)
58
                    ->setTag(Config::inst()->get(self::class, 'composite_field_tag'))
59
            );
60
        }
61
62
        return $fields;
63
    }
64
65
    /**
66
     * Allow choosing from an existing address
67
     *
68
     * @return FieldList|null fields for
69
     */
70
    public function getExistingAddressFields()
71
    {
72
        $member = Security::getCurrentUser();
73
        if ($member && $member->AddressBook()->exists()) {
74
            $addressoptions = $member->AddressBook()->sort('Created', 'DESC')->map('ID', 'toString')->toArray();
75
            $addressoptions['newaddress'] = _t('SilverShop\Model\Address.CreateNewAddress', 'Create new address');
76
            $fieldtype = count($addressoptions) > 3 ? DropdownField::class : OptionsetField::class;
77
78
            $label = _t("SilverShop\Model\Address.Existing{$this->addresstype}Address", "Existing {$this->addresstype} Address");
79
80
            return FieldList::create(
81
                $fieldtype::create(
82
                    $this->addresstype . 'AddressID',
83
                    $label,
84
                    $addressoptions,
85
                    $member->{'Default' . $this->addresstype . 'AddressID'}
86
                )->addExtraClass('existingValues')
87
            );
88
        }
89
90
        return null;
91
    }
92
93
    /**
94
     * We don't know at the front end which fields are required so we defer to validateData
95
     *
96
     * @param Order $order
97
     *
98
     * @return array
99
     */
100
    public function getRequiredFields(Order $order)
101
    {
102
        return [];
103
    }
104
105
    /**
106
     * @param Order $order
107
     * @param array $data
108
     *
109
     * @throws ValidationException
110
     */
111
    public function validateData(Order $order, array $data)
112
    {
113
        $result = ValidationResult::create();
114
        $existingID =
115
            !empty($data[$this->addresstype . 'AddressID']) ? (int)$data[$this->addresstype . 'AddressID'] : 0;
116
117
        if ($existingID) {
118
            $member = Security::getCurrentUser();
119
            // If existing address selected, check that it exists in $member->AddressBook
120
            if (!$member || !$member->AddressBook()->byID($existingID)) {
0 ignored issues
show
$member is of type SilverStripe\Security\Member, thus it always evaluated to true.
Loading history...
121
                $result->addError('Invalid address supplied', $this->addresstype . 'AddressID');
122
                throw new ValidationException($result);
123
            }
124
        } else {
125
            // Otherwise, require the normal address fields
126
            $required = parent::getRequiredFields($order);
127
            $addressLabels = singleton(\SilverShop\Model\Address::class)->fieldLabels(false);
128
129
            foreach ($required as $fieldName) {
130
                if (empty($data[$fieldName])) {
131
                    // attempt to get the translated field name
132
                    $fieldLabel = isset($addressLabels[$fieldName]) ? $addressLabels[$fieldName] : $fieldName;
133
                    $errorMessage = _t(
134
                        'SilverShop\Forms.FIELDISREQUIRED',
135
                        '{name} is required',
136
                        ['name' => $fieldLabel]
137
                    );
138
139
                    $result->addError($errorMessage, $fieldName);
140
                    throw new ValidationException($result);
141
                }
142
            }
143
        }
144
    }
145
146
    /**
147
     * Create a new address if the existing address has changed, or is not yet
148
     * created.
149
     *
150
     * @param  Order $order order to get addresses from
151
     * @param  array $data  data to set
152
     * @throws ValidationException
153
     */
154
    public function setData(Order $order, array $data)
155
    {
156
        $existingID =
157
            !empty($data[$this->addresstype . 'AddressID']) ? (int)$data[$this->addresstype . 'AddressID'] : 0;
158
        if ($existingID > 0) {
159
            $order->{$this->addresstype . 'AddressID'} = $existingID;
160
            $order->write();
161
            $order->extend('onSet' . $this->addresstype . 'Address', $address);
162
        } else {
163
            parent::setData($order, $data);
164
        }
165
    }
166
167
    /**
168
     * Provide translatable entities for this class
169
     *
170
     * @return array
171
     */
172
    public function provideI18nEntities()
173
    {
174
        if ($this->addresstype) {
175
            return [
176
177
                "SilverShop\Model\Address.{$this->addresstype}Address" => [
178
                    "{$this->addresstype} Address",
179
                    "Label for the {$this->addresstype} address",
180
                ],
181
                "SilverShop\Model\Address.Existing{$this->addresstype}Address" => [
182
                    "Existing {$this->addresstype} Address",
183
                    "Label to select an existing {$this->addresstype} Address",
184
                ],
185
            ];
186
        }
187
188
        return [];
189
    }
190
}
191