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

ShowAvailablePaymentMethodsController   A

Complexity

Total Complexity 9

Size/Duplication

Total Lines 116
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 12

Importance

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

5 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 11 1
A showAction() 0 19 3
A getCartOr404() 0 10 2
A isCheckoutTransitionPossible() 0 4 1
A getPaymentMethods() 0 17 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\PaymentInterface;
19
use Sylius\Component\Core\OrderCheckoutTransitions;
20
use Sylius\Component\Core\Repository\OrderRepositoryInterface;
21
use Sylius\Component\Payment\Resolver\PaymentMethodsResolverInterface;
22
use Symfony\Component\HttpFoundation\Request;
23
use Symfony\Component\HttpFoundation\Response;
24
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
25
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
26
27
/**
28
 * @author Łukasz Chruściel <[email protected]>
29
 */
30
final class ShowAvailablePaymentMethodsController
31
{
32
    /**
33
     * @var FactoryInterface
34
     */
35
    private $stateMachineFactory;
36
37
    /**
38
     * @var OrderRepositoryInterface
39
     */
40
    private $orderRepository;
41
42
    /**
43
     * @var PaymentMethodsResolverInterface
44
     */
45
    private $paymentMethodResolver;
46
47
    /**
48
     * @var ViewHandlerInterface
49
     */
50
    private $restViewHandler;
51
52
    /**
53
     * @param FactoryInterface $stateMachineFactory
54
     * @param OrderRepositoryInterface $orderRepository
55
     * @param PaymentMethodsResolverInterface $paymentMethodResolver
56
     * @param ViewHandlerInterface $restViewHandler
57
     */
58
    public function __construct(
59
        FactoryInterface $stateMachineFactory,
60
        OrderRepositoryInterface $orderRepository,
61
        PaymentMethodsResolverInterface $paymentMethodResolver,
0 ignored issues
show
Comprehensibility Naming introduced by
The variable name $paymentMethodResolver 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...
62
        ViewHandlerInterface $restViewHandler
63
    ) {
64
        $this->stateMachineFactory = $stateMachineFactory;
65
        $this->orderRepository = $orderRepository;
66
        $this->paymentMethodResolver = $paymentMethodResolver;
67
        $this->restViewHandler = $restViewHandler;
68
    }
69
70
    /**
71
     * @param Request $request
72
     *
73
     * @return Response
74
     */
75
    public function showAction(Request $request)
76
    {
77
        /** @var OrderInterface $cart */
78
        $cart = $this->getCartOr404($request->attributes->get('orderId'));
79
80
        if (!$this->isCheckoutTransitionPossible($cart, OrderCheckoutTransitions::TRANSITION_SELECT_PAYMENT)) {
81
            throw new BadRequestHttpException('The payment methods cannot be resolved in the current state of cart!');
82
        }
83
84
        $payments = [];
85
86
        foreach ($cart->getPayments() as $payment) {
87
            $payments['payments'][] = [
88
                'methods' => $this->getPaymentMethods($payment, $cart->getLocaleCode()),
89
            ];
90
        }
91
92
        return $this->restViewHandler->handle(View::create($payments));
93
    }
94
95
    /**
96
     * @param mixed $cartId
97
     *
98
     * @return OrderInterface
99
     */
100
    private function getCartOr404($cartId)
101
    {
102
        $cart = $this->orderRepository->findCartById($cartId);
103
104
        if (null === $cart) {
105
            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...
106
        }
107
108
        return $cart;
109
    }
110
111
    /**
112
     * @param OrderInterface $cart
113
     * @param string $transition
114
     *
115
     * @return bool
116
     */
117
    private function isCheckoutTransitionPossible(OrderInterface $cart, $transition)
118
    {
119
        return $this->stateMachineFactory->get($cart, OrderCheckoutTransitions::GRAPH)->can($transition);
120
    }
121
122
    /**
123
     * @param PaymentInterface $payment
124
     * @param string $locale
125
     *
126
     * @return array
127
     */
128
    private function getPaymentMethods(PaymentInterface $payment, $locale)
129
    {
130
        $paymentMethods =  $this->paymentMethodResolver->getSupportedMethods($payment);
131
132
        $rawPaymentMethods = [];
133
134
        foreach ($paymentMethods as $paymentMethod) {
135
            $rawPaymentMethods[] = [
136
                'id' => $paymentMethod->getId(),
137
                'code' => $paymentMethod->getCode(),
138
                'name' => $paymentMethod->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...
139
                'description' => $paymentMethod->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...
140
            ];
141
        }
142
143
        return $rawPaymentMethods;
144
    }
145
}
146