Completed
Push — currency-cookie ( 8ef560...28b724 )
by Kamil
24:47
created

ShowAvailableShippingMethodsController   A

Complexity

Total Complexity 9

Size/Duplication

Total Lines 127
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 13

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 9
c 1
b 0
f 0
lcom 1
cbo 13
dl 0
loc 127
rs 10

5 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 13 1
A showAction() 0 19 3
A getCartOr404() 0 10 2
A isCheckoutTransitionPossible() 0 4 1
A getCalculatedShippingMethods() 0 20 2
1
<?php
2
3
/*
4
 * This file is part of the Sylius package.
5
 *
6
 * (c) Paweł Jędrzejewski
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Sylius\Bundle\ApiBundle\Controller;
13
14
use FOS\RestBundle\View\View;
15
use FOS\RestBundle\View\ViewHandlerInterface;
16
use SM\Factory\FactoryInterface;
17
use Sylius\Component\Core\Model\OrderInterface;
18
use Sylius\Component\Core\Model\ShipmentInterface;
19
use Sylius\Component\Core\OrderCheckoutTransitions;
20
use Sylius\Component\Core\Repository\OrderRepositoryInterface;
21
use Sylius\Component\Registry\ServiceRegistryInterface;
22
use Sylius\Component\Shipping\Resolver\ShippingMethodsResolverInterface;
23
use Symfony\Component\HttpFoundation\Request;
24
use Symfony\Component\HttpFoundation\Response;
25
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
26
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
27
28
/**
29
 * @author Łukasz Chruściel <[email protected]>
30
 */
31
final class ShowAvailableShippingMethodsController
32
{
33
    /**
34
     * @var FactoryInterface
35
     */
36
    private $stateMachineFactory;
37
38
    /**
39
     * @var OrderRepositoryInterface
40
     */
41
    private $orderRepository;
42
43
    /**
44
     * @var ShippingMethodsResolverInterface
45
     */
46
    private $shippingMethodsResolver;
47
48
    /**
49
     * @var ViewHandlerInterface
50
     */
51
    private $restViewHandler;
52
53
    /**
54
     * @var ServiceRegistryInterface
55
     */
56
    private $calculators;
57
58
    /**
59
     * @param FactoryInterface $stateMachineFactory
60
     * @param OrderRepositoryInterface $orderRepository
61
     * @param ShippingMethodsResolverInterface $shippingMethodsResolver
62
     * @param ViewHandlerInterface $restViewHandler
63
     * @param ServiceRegistryInterface $calculators
64
     */
65
    public function __construct(
66
        FactoryInterface $stateMachineFactory,
67
        OrderRepositoryInterface $orderRepository,
68
        ShippingMethodsResolverInterface $shippingMethodsResolver,
0 ignored issues
show
Comprehensibility Naming introduced by
The variable name $shippingMethodsResolver exceeds the maximum configured length of 20.

Very long variable names usually make code harder to read. It is therefore recommended not to make variable names too verbose.

Loading history...
69
        ViewHandlerInterface $restViewHandler,
70
        ServiceRegistryInterface $calculators
71
    ) {
72
        $this->stateMachineFactory = $stateMachineFactory;
73
        $this->orderRepository = $orderRepository;
74
        $this->shippingMethodsResolver = $shippingMethodsResolver;
75
        $this->restViewHandler = $restViewHandler;
76
        $this->calculators = $calculators;
77
    }
78
79
    /**
80
     * @param Request $request
81
     *
82
     * @return Response
83
     */
84
    public function showAction(Request $request)
85
    {
86
        /** @var OrderInterface $cart */
87
        $cart = $this->getCartOr404($request->attributes->get('orderId'));
88
89
        if (!$this->isCheckoutTransitionPossible($cart, OrderCheckoutTransitions::TRANSITION_SELECT_SHIPPING)) {
90
            throw new BadRequestHttpException('The shipment methods cannot be resolved in the current state of cart!');
91
        }
92
93
        $shipments = [];
94
95
        foreach ($cart->getShipments() as $shipment) {
96
            $shipments['shipments'][] = [
97
                'methods' => $this->getCalculatedShippingMethods($shipment, $cart->getLocaleCode()),
98
            ];
99
        }
100
101
        return $this->restViewHandler->handle(View::create($shipments));
102
    }
103
104
    /**
105
     * @param mixed $cartId
106
     *
107
     * @return OrderInterface
108
     */
109
    private function getCartOr404($cartId)
110
    {
111
        $cart = $this->orderRepository->findCartById($cartId);
112
113
        if (null === $cart) {
114
            throw new NotFoundHttpException(sprintf("The cart with %s id could not be found!", $cartId));
0 ignored issues
show
Coding Style Comprehensibility introduced by
The string literal The cart with %s id could not be found! does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
115
        }
116
117
        return $cart;
118
    }
119
120
    /**
121
     * @param OrderInterface $cart
122
     * @param string $transition
123
     *
124
     * @return bool
125
     */
126
    private function isCheckoutTransitionPossible(OrderInterface $cart, $transition)
127
    {
128
        return $this->stateMachineFactory->get($cart, OrderCheckoutTransitions::GRAPH)->can($transition);
129
    }
130
131
    /**
132
     * @param ShipmentInterface $shipment
133
     * @param string $locale
134
     *
135
     * @return array
136
     */
137
    private function getCalculatedShippingMethods(ShipmentInterface $shipment, $locale)
138
    {
139
        $shippingMethods =  $this->shippingMethodsResolver->getSupportedMethods($shipment);
140
141
        $rawShippingMethods = [];
142
143
        foreach ($shippingMethods as $shippingMethod) {
144
            $calculator = $this->calculators->get($shippingMethod->getCalculator());
145
146
            $rawShippingMethods[] = [
147
                'id' => $shippingMethod->getId(),
148
                'code' => $shippingMethod->getCode(),
149
                'name' => $shippingMethod->getTranslation($locale)->getName(),
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Sylius\Component\Resourc...el\TranslationInterface as the method getName() does only exist in the following implementations of said interface: Sylius\Component\Attribu...el\AttributeTranslation, Sylius\Component\Core\Model\ProductTranslation, Sylius\Component\Payment...aymentMethodTranslation, Sylius\Component\Product...ociationTypeTranslation, Sylius\Component\Product...uctAttributeTranslation, Sylius\Component\Product...roductOptionTranslation, Sylius\Component\Product\Model\ProductTranslation, Sylius\Component\Product...oductVariantTranslation, Sylius\Component\Shippin...ippingMethodTranslation, Sylius\Component\Taxonomy\Model\TaxonTranslation.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
150
                'description' => $shippingMethod->getTranslation($locale)->getDescription(),
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Sylius\Component\Resourc...el\TranslationInterface as the method getDescription() does only exist in the following implementations of said interface: Sylius\Component\Core\Model\ProductTranslation, Sylius\Component\Payment...aymentMethodTranslation, Sylius\Component\Product\Model\ProductTranslation, Sylius\Component\Shippin...ippingMethodTranslation, Sylius\Component\Taxonomy\Model\TaxonTranslation.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
151
                'price' => $calculator->calculate($shipment, $shippingMethod->getConfiguration()),
152
            ];
153
        }
154
155
        return $rawShippingMethods;
156
    }
157
}
158