Completed
Push — master ( 3c661d...2a57f9 )
by Will
26s queued 12s
created

src/Cart/ShoppingCartController.php (11 issues)

1
<?php
2
3
namespace SilverShop\Cart;
4
5
use SilverShop\Extension\ViewableCartExtension;
6
use SilverShop\Model\Buyable;
7
use SilverShop\Model\Variation\Variation;
8
use SilverShop\Page\CartPage;
9
use SilverShop\Page\Product;
10
use SilverShop\ShopTools;
11
use SilverStripe\Control\Controller;
12
use SilverStripe\Control\Director;
13
use SilverStripe\Control\HTTPRequest;
14
use SilverStripe\Control\HTTPResponse;
15
use SilverStripe\Core\ClassInfo;
16
use SilverStripe\Dev\Debug;
17
use SilverStripe\ErrorPage\ErrorPage;
0 ignored issues
show
The type SilverStripe\ErrorPage\ErrorPage was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
18
use SilverStripe\ORM\DataObject;
19
use SilverStripe\Security\Permission;
20
use SilverStripe\Security\SecurityToken;
21
use SilverStripe\Versioned\Versioned;
22
use SilverStripe\View\Requirements;
23
24
/**
25
 * Manipulate the cart via urls.
26
 *
27
 * @mixin  ViewableCartExtension
28
 * @method ShoppingCart Cart()
29
 */
30
class ShoppingCartController extends Controller
31
{
32
    private static $url_segment = 'shoppingcart';
33
34
    /**
35
     * Whether or not this controller redirects to the cart-page whenever an item was added
36
     *
37
     * @config
38
     * @var    bool
39
     */
40
    private static $direct_to_cart_page = false;
41
42
    /**
43
     * @var ShoppingCart
44
     */
45
    protected $cart;
46
47
    /**
48
     * @config
49
     * @var array
50
     */
51
    private static $url_handlers = [
0 ignored issues
show
The private property $url_handlers is not used, and could be removed.
Loading history...
52
        '$Action/$Buyable/$ID' => 'handleAction',
53
    ];
54
55
    /**
56
     * @config
57
     * @var array
58
     */
59
    private static $allowed_actions = [
0 ignored issues
show
The private property $allowed_actions is not used, and could be removed.
Loading history...
60
        'add',
61
        'additem',
62
        'remove',
63
        'removeitem',
64
        'removeall',
65
        'removeallitem',
66
        'setquantity',
67
        'setquantityitem',
68
        'clear',
69
        'debug',
70
    ];
71
72
    public static function add_item_link(Buyable $buyable, $parameters = array())
73
    {
74
        return self::build_url('add', $buyable, $parameters);
75
    }
76
77
    public static function remove_item_link(Buyable $buyable, $parameters = array())
78
    {
79
        return self::build_url('remove', $buyable, $parameters);
80
    }
81
82
    public static function remove_all_item_link(Buyable $buyable, $parameters = array())
83
    {
84
        return self::build_url('removeall', $buyable, $parameters);
85
    }
86
87
    public static function set_quantity_item_link(Buyable $buyable, $parameters = array())
88
    {
89
        return self::build_url('setquantity', $buyable, $parameters);
90
    }
91
92
    /**
93
     * Helper for creating a url
94
     */
95
    protected static function build_url($action, $buyable, $params = [])
96
    {
97
        if (!$action || !$buyable) {
98
            return false;
99
        }
100
101
        if (SecurityToken::is_enabled() && !self::config()->disable_security_token) {
102
            $params[SecurityToken::inst()->getName()] = SecurityToken::inst()->getValue();
103
        }
104
105
        $className = get_class($buyable);
106
107
        $link = Controller::join_links(
108
            [
109
            self::config()->url_segment,
110
            $action,
111
            ShopTools::sanitiseClassName($className),
112
            $buyable->ID
113
            ]
114
        );
115
116
        return empty($params) ? $link : $link . '?' . http_build_query($params);
117
    }
118
119
    /**
120
     * This is used here and in VariationForm and AddProductForm
121
     *
122
     * @param bool|string $status
123
     *
124
     * @return string|HTTPResponse
125
     */
126
    public static function direct($status = true)
127
    {
128
        if (Director::is_ajax()) {
129
            return (string)$status;
130
        }
131
        if (self::config()->direct_to_cart_page && ($cartlink = CartPage::find_link())) {
132
            return Controller::curr()->redirect($cartlink);
133
        } else {
134
            return Controller::curr()->redirectBack();
135
        }
136
    }
137
138
    public function init()
139
    {
140
        parent::init();
141
        $this->cart = ShoppingCart::singleton();
142
    }
143
144
    /**
145
     * @return Product|Variation|Buyable
146
     * @throws \SilverStripe\Control\HTTPResponse_Exception
147
     */
148
    protected function buyableFromRequest()
149
    {
150
        $request = $this->getRequest();
151
        if (SecurityToken::is_enabled()
152
            && !self::config()->disable_security_token
153
            && !SecurityToken::inst()->checkRequest($request)
154
        ) {
155
            return $this->httpError(
156
                400,
157
                _t(
158
                    'SilverShop\Cart\ShoppingCart.InvalidSecurityToken',
159
                    'Invalid security token, possible CSRF attack.'
160
                )
161
            );
162
        }
163
        $id = (int)$request->param('ID');
164
        if (empty($id)) {
165
            //TODO: store error message
166
            return null;
167
        }
168
        $buyableclass = Product::class;
169
        if ($class = $request->param('Buyable')) {
170
            $buyableclass = ShopTools::unsanitiseClassName($class);
171
        }
172
        if (!ClassInfo::exists($buyableclass)) {
173
            //TODO: store error message
174
            return null;
175
        }
176
        //ensure only live products are returned, if they are versioned
177
        $buyable = $buyableclass::has_extension(Versioned::class)
178
            ? Versioned::get_by_stage($buyableclass, 'Live')->byID($id)
179
            : DataObject::get($buyableclass)->byID($id);
180
181
        if (!$buyable || !($buyable instanceof Buyable)) {
0 ignored issues
show
$buyable is of type SilverStripe\ORM\DataObject, thus it always evaluated to true.
Loading history...
182
            //TODO: store error message
183
            return null;
184
        }
185
186
        return $this->cart->getCorrectBuyable($buyable);
187
    }
188
189
    /**
190
     * Action: add item to cart
191
     *
192
     * @param HTTPRequest $request
193
     *
194
     * @return HTTPResponse
195
     * @throws \SilverStripe\Control\HTTPResponse_Exception
196
     */
197
    public function add($request)
198
    {
199
        $result = false;
200
201
        if ($product = $this->buyableFromRequest()) {
202
            $quantity = (int)$request->getVar('quantity');
203
            if (!$quantity) {
204
                $quantity = 1;
205
            }
206
            $result = $this->cart->add($product, $quantity, $request->getVars());
207
        }
208
209
        $this->updateLocale($request);
210
        $this->extend('updateAddResponse', $request, $response, $product, $quantity, $result);
211
        return $response ? $response : self::direct();
0 ignored issues
show
Bug Best Practice introduced by
The expression return $response ? $response : self::direct() also could return the type string which is incompatible with the documented return type SilverStripe\Control\HTTPResponse.
Loading history...
212
    }
213
214
    /**
215
     * Action: remove a certain number of items from the cart
216
     *
217
     * @param HTTPRequest $request
218
     *
219
     * @return HTTPResponse
220
     * @throws \SilverStripe\Control\HTTPResponse_Exception
221
     */
222
    public function remove($request)
223
    {
224
        if ($product = $this->buyableFromRequest()) {
225
            $this->cart->remove($product, $quantity = 1, $request->getVars());
226
        }
227
228
        $this->updateLocale($request);
229
        $this->extend('updateRemoveResponse', $request, $response, $product, $quantity);
230
        return $response ? $response : self::direct();
0 ignored issues
show
Bug Best Practice introduced by
The expression return $response ? $response : self::direct() also could return the type string which is incompatible with the documented return type SilverStripe\Control\HTTPResponse.
Loading history...
231
    }
232
233
    /**
234
     * Action: remove all of an item from the cart
235
     *
236
     * @param HTTPRequest $request
237
     *
238
     * @return HTTPResponse
239
     * @throws \SilverStripe\Control\HTTPResponse_Exception
240
     */
241
    public function removeall($request)
242
    {
243
        if ($product = $this->buyableFromRequest()) {
244
            $this->cart->remove($product, null, $request->getVars());
245
        }
246
247
        $this->updateLocale($request);
248
        $this->extend('updateRemoveAllResponse', $request, $response, $product);
249
        return $response ? $response : self::direct();
0 ignored issues
show
Bug Best Practice introduced by
The expression return $response ? $response : self::direct() also could return the type string which is incompatible with the documented return type SilverStripe\Control\HTTPResponse.
Loading history...
250
    }
251
252
    /**
253
     * Action: update the quantity of an item in the cart
254
     *
255
     * @param HTTPRequest $request
256
     *
257
     * @return HTTPResponse
258
     * @throws \SilverStripe\Control\HTTPResponse_Exception
259
     */
260
    public function setquantity($request)
261
    {
262
        $product = $this->buyableFromRequest();
263
        $quantity = (int)$request->getVar('quantity');
264
        if ($product) {
265
            $this->cart->setQuantity($product, $quantity, $request->getVars());
266
        }
267
268
        $this->updateLocale($request);
269
        $this->extend('updateSetQuantityResponse', $request, $response, $product, $quantity);
270
        return $response ? $response : self::direct();
0 ignored issues
show
Bug Best Practice introduced by
The expression return $response ? $response : self::direct() also could return the type string which is incompatible with the documented return type SilverStripe\Control\HTTPResponse.
Loading history...
271
    }
272
273
    /**
274
     * Action: clear the cart
275
     *
276
     * @param HTTPRequest $request
277
     *
278
     * @return HTTPResponse
279
     */
280
    public function clear($request)
281
    {
282
        $this->updateLocale($request);
283
        $this->cart->clear();
284
        $this->extend('updateClearResponse', $request, $response);
285
        return $response ? $response : self::direct();
0 ignored issues
show
Bug Best Practice introduced by
The expression return $response ? $response : self::direct() also could return the type string which is incompatible with the documented return type SilverStripe\Control\HTTPResponse.
Loading history...
286
    }
287
288
    /**
289
     * Handle index requests
290
     *
291
     * @throws \SilverStripe\Control\HTTPResponse_Exception
292
     */
293
    public function index()
294
    {
295
        if ($cart = $this->Cart()) {
296
            return $this->redirect($cart->CartLink);
0 ignored issues
show
The property CartLink does not seem to exist on SilverShop\Cart\ShoppingCart.
Loading history...
297
        } elseif ($response = ErrorPage::response_for(404)) {
298
            return $response;
299
        }
300
        return $this->httpError(404, _t('SilverShop\Cart\ShoppingCart.NoCartInitialised', 'no cart initialised'));
301
    }
302
303
    /**
304
     * Displays order info and cart contents.
305
     */
306
    public function debug()
307
    {
308
        if (Director::isDev() || Permission::check('ADMIN')) {
309
            //TODO: allow specifying a particular id to debug
310
            Requirements::css('silvershop/core: client/dist/css/cartdebug.css');
311
            $order = ShoppingCart::curr();
312
            $content = ($order)
0 ignored issues
show
$order is of type SilverShop\Model\Order, thus it always evaluated to true.
Loading history...
313
                ? Debug::text($order)
314
                : 'Cart has not been created yet. Add a product.';
315
            return ['Content' => $content];
316
        }
317
    }
318
319
    /**
320
     * @param HTTPRequest $request
321
     */
322
    protected function updateLocale($request)
323
    {
324
        $order = $this->cart->current();
325
        if ($request && $request->isAjax() && $order) {
326
            ShopTools::install_locale($order->Locale);
327
        }
328
    }
329
}
330