ShoppingCartController   B
last analyzed

Complexity

Total Complexity 51

Size/Duplication

Total Lines 297
Duplicated Lines 0 %

Importance

Changes 2
Bugs 0 Features 1
Metric Value
eloc 108
c 2
b 0
f 1
dl 0
loc 297
rs 7.92
wmc 51

16 Methods

Rating   Name   Duplication   Size   Complexity  
A index() 0 8 3
A debug() 0 10 4
B buyableFromRequest() 0 39 10
A clear() 0 6 2
A add_item_link() 0 3 1
A remove_all_item_link() 0 3 1
A build_url() 0 22 6
A init() 0 4 1
A removeall() 0 9 3
A remove_item_link() 0 3 1
A remove() 0 9 3
A setquantity() 0 11 3
A add() 0 15 4
A direct() 0 9 4
A updateLocale() 0 5 4
A set_quantity_item_link() 0 3 1

How to fix   Complexity   

Complex Class

Complex classes like ShoppingCartController often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use ShoppingCartController, and based on these observations, apply Extract Interface, too.

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
Bug introduced by
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
introduced by
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
introduced by
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 = [])
73
    {
74
        return self::build_url('add', $buyable, $parameters);
75
    }
76
77
    public static function remove_item_link(Buyable $buyable, $parameters = [])
78
    {
79
        return self::build_url('remove', $buyable, $parameters);
80
    }
81
82
    public static function remove_all_item_link(Buyable $buyable, $parameters = [])
83
    {
84
        return self::build_url('removeall', $buyable, $parameters);
85
    }
86
87
    public static function set_quantity_item_link(Buyable $buyable, $parameters = [])
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)) {
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 string|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();
212
    }
213
214
    /**
215
     * Action: remove a certain number of items from the cart
216
     *
217
     * @param HTTPRequest $request
218
     *
219
     * @return string|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();
231
    }
232
233
    /**
234
     * Action: remove all of an item from the cart
235
     *
236
     * @param HTTPRequest $request
237
     *
238
     * @return string|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();
250
    }
251
252
    /**
253
     * Action: update the quantity of an item in the cart
254
     *
255
     * @param HTTPRequest $request
256
     *
257
     * @return string|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();
271
    }
272
273
    /**
274
     * Action: clear the cart
275
     *
276
     * @param HTTPRequest $request
277
     *
278
     * @return string|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();
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
Bug introduced by
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
introduced by
$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