Passed
Push — master ( e15236...3150f9 )
by Sébastien
04:08
created

ContredanseProductAccess::getProductOrders()   A

Complexity

Conditions 5
Paths 6

Size

Total Lines 65
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 16.9828

Importance

Changes 0
Metric Value
eloc 23
dl 0
loc 65
ccs 5
cts 23
cp 0.2174
rs 9.2408
c 0
b 0
f 0
cc 5
nc 6
nop 2
crap 16.9828

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
declare(strict_types=1);
4
5
namespace App\Security;
6
7
use App\Security\Exception\MissingProductConfigException;
8
use App\Security\Exception\NoProductAccessException;
9
use App\Security\Exception\ProductAccessExpiredException;
10
use App\Security\Exception\ProductPaymentIssueException;
11
use App\Security\Exception\QueryErrorException;
12
use App\Security\Exception\UnsupportedProductException;
13
14
class ContredanseProductAccess
15
{
16
    public const PAXTON_PRODUCT = 'product:paxton';
17
18
    public const SUPPORTED_PRODUCTS = [
19
        self::PAXTON_PRODUCT
20
    ];
21
22
    /**
23
     * @var \PDO
24
     */
25
    private $adapter;
26
27
    /**
28
     * @var array<string,string[]>
29
     */
30
    private $productAccess;
31
32
    /**
33
     * @param array<string,string[]> $productAccess
34
     */
35
    public function __construct(\PDO $adapter, array $productAccess)
36
    {
37
        $this->adapter       = $adapter;
38
        $this->productAccess = $productAccess;
39
    }
40
41
    /**
42
     * @param string $productName see constants self::PAXTON_PRODUCT
43
     *
44
     * Those exceptions can be considered as system/config errors
45
     *
46
     * @throws MissingProductConfigException
47
     * @throws UnsupportedProductException
48
     * @throws QueryErrorException
49
     *
50
     * Those exceptions emplements ProductAccessExceptionInterface
51
     * and can be used to determine is the user have access to
52
     * the product
53
     * @throws NoProductAccessException
54
     * @throws ProductPaymentIssueException
55
     * @throws ProductPaymentIssueException
56
     */
57 3
    public function ensureAccess(string $productName, string $email): void
58
    {
59 3
        $orders = $this->getProductOrders($productName, $email);
60
61 2
        if (count($orders) === 0) {
62
            // Cool, he never bought anything
63 1
            throw new NoProductAccessException(sprintf(
64 1
                sprintf('No access, product "%s" is present in orders', $productName)
65
            ));
66
        }
67
68
        // Pick the most recent order
69
70 1
        $order = $orders[0];
71
72
        // Is there a payment issue ?
73
74
        // o.pay_status = 9; so all is cool ;)
75
76 1
        if ($order['pay_status'] !== 9) {
77 1
            throw new ProductPaymentIssueException(sprintf(
78 1
                sprintf(
79 1
                    'Look we have a payment issue, pay_status code in order detail %s is %s',
80 1
                    $order['detail_id'],
81 1
                    $order['pay_status']
82
                )
83
            ));
84
        }
85
86
        // Is there a validity issue ?
87
88
        $expires_at = $order['expires_at'];
89
90
        throw new ProductAccessExpiredException(sprintf(
91
            sprintf(
92
                'Product access have expired on %s (see order detail_id %s)',
93
                $expires_at,
94
                $order['detail_id']
95
            )
96
        ));
97
    }
98
99
    /**
100
     * Get user orders relative to a certain product.
101
     *
102
     * @param string $productName see constants self::PAXTON_PRODUCT
103
     * @param string $email       identity of the user to check for product access
104
     *
105
     * @return array<int, mixed[]>
106
     *
107
     * @throws MissingProductConfigException
108
     * @throws UnsupportedProductException
109
     * @throws QueryErrorException
110
     */
111 1
    public function getProductOrders(string $productName, string $email): array
112
    {
113 1
        if (!in_array($productName, self::SUPPORTED_PRODUCTS, true)) {
114 1
            throw new UnsupportedProductException(sprintf(
115 1
                'Product name %s is not supported',
116 1
                $productName
117
            ));
118
        }
119
120
        if (!array_key_exists($productName, $this->productAccess)) {
121
            throw new MissingProductConfigException(sprintf(
122
                'Missing configuration: product %s does not have associated ids',
123
                $productName
124
            ));
125
        }
126
127
        $productIds = $this->productAccess[$productName];
128
129
        $holderValues = [];
130
        foreach ($productIds as $idx => $productId) {
131
            $holderValues[":product_id_$idx"] = (int) $productId;
132
        }
133
        $inParams = implode(',', array_keys($holderValues));
134
135
        $sql = "		
136
			SELECT 
137
				s.suj_id AS subject_id,
138
				l.Login AS email,
139
				o.order_id,
140
				o.total_value,
141
				o.total_pay,
142
				o.pay_status,
143
				DATE_FORMAT(FROM_UNIXTIME(o.cre_dt),
144
						'%Y-%m-%d %H:%i:%s') AS order_created_at,
145
			    d.detail_id,  
146
				d.product_id,
147
				d.quantity,
148
				DATE_FORMAT(FROM_UNIXTIME(d.cre_dt),
149
						'%Y-%m-%d %H:%i:%s') AS line_created_at,
150
				DATE_FORMAT(FROM_UNIXTIME(d.expiry_dt),
151
						'%Y-%m-%d %H:%i:%s') AS expires_at
152
			FROM
153
				shop_order o
154
					INNER JOIN
155
				shop_order_detail d ON d.order_id = o.order_id
156
					INNER JOIN
157
				sujet s ON s.suj_id = o.suj_id
158
					INNER JOIN
159
				usr_login l ON l.suj_id = s.suj_id
160
			WHERE 
161
			          l.Login = :email
162
				  AND d.product_id in (${inParams})	
163
			ORDER BY d.expiry_dt desc
164
		";
165
166
        $stmt = $this->adapter->prepare($sql);
167
        $stmt->execute(array_merge([
168
            ':email' => $email,
169
        ], $holderValues));
170
        $rows = $stmt->fetchAll(\PDO::FETCH_ASSOC);
171
        if ($rows === false) {
172
            throw new QueryErrorException('Cannot get users');
173
        }
174
175
        return $rows;
176
    }
177
}
178