1
|
|
|
<?php |
2
|
|
|
declare(strict_types=1); |
3
|
|
|
|
4
|
|
|
namespace Cart\Drivers; |
5
|
|
|
|
6
|
|
|
use Cart\Contracts\CartDriverContract; |
7
|
|
|
use Cart\Contracts\CounterItemContract; |
8
|
|
|
use Cart\Contracts\DiscountContract; |
9
|
|
|
use Cart\Contracts\DiscountDriverContract; |
10
|
|
|
use Cart\CountOperation\AdditionCount; |
11
|
|
|
use Cart\Traits\Validate; |
12
|
|
|
use Predis\Client; |
13
|
|
|
|
14
|
|
|
/** |
15
|
|
|
* Class RedisDriver |
16
|
|
|
* |
17
|
|
|
* @package Cart\Drivers |
18
|
|
|
*/ |
19
|
|
|
class RedisDriver implements CartDriverContract, DiscountDriverContract |
20
|
|
|
{ |
21
|
|
|
/** |
22
|
|
|
* @var string |
23
|
|
|
*/ |
24
|
|
|
protected $prefix = 'cart'; |
25
|
|
|
|
26
|
|
|
use Validate; |
27
|
|
|
|
28
|
|
|
/** |
29
|
|
|
* @var Client |
30
|
|
|
*/ |
31
|
|
|
protected $redis; |
32
|
|
|
|
33
|
15 |
|
public function __construct(Client $client) |
34
|
|
|
{ |
35
|
15 |
|
$this->redis = $client; |
36
|
15 |
|
$this->prefix = config('cart.drivers.redis.prefix'); |
37
|
15 |
|
} |
38
|
|
|
|
39
|
|
|
/** |
40
|
|
|
* Add new item in cart |
41
|
|
|
* |
42
|
|
|
* @param array $item |
43
|
|
|
* @return mixed |
44
|
|
|
*/ |
45
|
4 |
View Code Duplication |
public function add(array $item): bool |
|
|
|
|
46
|
|
|
{ |
47
|
4 |
|
if ($this->validate($item, ['id', 'user_id']) === false) { |
48
|
1 |
|
return false; |
49
|
|
|
} |
50
|
|
|
|
51
|
3 |
|
if ($this->existUser((int)$item['user_id'])) { |
52
|
2 |
|
if ($this->existItem((int)$item['id'], (int)$item['user_id'])) { |
53
|
1 |
|
call_user_func([$this, 'incrementItem'], $item); |
54
|
|
|
} else { |
55
|
1 |
|
call_user_func([$this, 'addItem'], $item); |
56
|
|
|
} |
57
|
|
|
} else { |
58
|
1 |
|
$item['count'] = 1; |
59
|
1 |
|
$this->addRow($item); |
60
|
|
|
} |
61
|
|
|
|
62
|
3 |
|
return true; |
63
|
|
|
} |
64
|
|
|
|
65
|
|
|
/** |
66
|
|
|
* Remove item from cart |
67
|
|
|
* |
68
|
|
|
* @param array $item |
69
|
|
|
* @return bool|mixed |
70
|
|
|
* @internal param array $itemId |
71
|
|
|
*/ |
72
|
3 |
|
public function remove(array $item): bool |
73
|
|
|
{ |
74
|
3 |
|
if ($this->validate($item, ['id', 'user_id']) === false) { |
75
|
1 |
|
return false; |
76
|
|
|
} |
77
|
|
|
|
78
|
2 |
|
$this->redis->hdel($this->normalizeKey((int)$item['user_id']), $item['id']); |
79
|
2 |
|
return true; |
80
|
|
|
} |
81
|
|
|
|
82
|
|
|
/** |
83
|
|
|
* Clear cart for concrete relation entity |
84
|
|
|
* |
85
|
|
|
* @param int $entityId |
86
|
|
|
* @return mixed|void |
87
|
|
|
*/ |
88
|
1 |
|
public function clear(int $entityId): void |
89
|
|
|
{ |
90
|
1 |
|
$this->redis->del($this->normalizeKey($entityId)); |
91
|
1 |
|
} |
92
|
|
|
|
93
|
|
|
/** |
94
|
|
|
* Change item(position) |
95
|
|
|
* |
96
|
|
|
* @param array $item |
97
|
|
|
* @param CounterItemContract $itemContract |
98
|
|
|
* @return bool |
99
|
|
|
*/ |
100
|
3 |
View Code Duplication |
public function change(array $item, CounterItemContract $itemContract): bool |
|
|
|
|
101
|
|
|
{ |
102
|
3 |
|
if ($this->validate($item, ['id', 'user_id', 'count']) === false) { |
103
|
1 |
|
return false; |
104
|
|
|
} |
105
|
|
|
|
106
|
2 |
|
$itemFromRedis = $this->redis->hget($this->normalizeKey((int)$item['user_id']), $item['id']); |
107
|
|
|
|
108
|
2 |
|
if (!is_null($itemFromRedis)) { |
109
|
1 |
|
$itemFromRedis = unserialize($itemFromRedis); |
110
|
|
|
} else { |
111
|
1 |
|
return false; |
112
|
|
|
} |
113
|
|
|
|
114
|
1 |
|
$item['count'] = $itemContract->execute((int)$itemFromRedis['count'], $item['count']); |
115
|
1 |
|
$this->addRow($item); |
116
|
1 |
|
return true; |
117
|
|
|
} |
118
|
|
|
|
119
|
|
|
/** |
120
|
|
|
* Make Discount |
121
|
|
|
* @param DiscountContract $contract |
122
|
|
|
* @param array $item |
123
|
|
|
* @return bool |
124
|
|
|
*/ |
125
|
3 |
View Code Duplication |
public function discount(DiscountContract $contract, array $item): bool |
|
|
|
|
126
|
|
|
{ |
127
|
3 |
|
if ($this->validate($item, ['id', 'user_id', 'price']) === false) { |
128
|
1 |
|
return false; |
129
|
|
|
} |
130
|
|
|
|
131
|
2 |
|
$itemFromRedis = $this->redis->hget($this->normalizeKey((int)$item['user_id']), $item['id']); |
132
|
|
|
|
133
|
2 |
|
if (!is_null($itemFromRedis)) { |
134
|
1 |
|
$itemFromRedis = unserialize($itemFromRedis); |
135
|
1 |
|
$itemFromRedis['discount'] = $contract->make($itemFromRedis['price']); |
136
|
1 |
|
$this->redis->hset($this->normalizeKey((int)$item['user_id']), $item['id'], serialize($itemFromRedis)); |
137
|
1 |
|
return true; |
138
|
|
|
} else { |
139
|
1 |
|
return false; |
140
|
|
|
} |
141
|
|
|
} |
142
|
|
|
|
143
|
|
|
/** |
144
|
|
|
* Get all items to user id |
145
|
|
|
* @param int $userId |
146
|
|
|
* @return array |
147
|
|
|
*/ |
148
|
1 |
|
public function get(int $userId) : array |
149
|
|
|
{ |
150
|
1 |
|
$items = $this->redis->hgetall($this->normalizeKey((int)$userId)); |
151
|
1 |
|
$items = array_map(function (string $value) { |
152
|
1 |
|
return unserialize($value); |
153
|
1 |
|
}, $items); |
154
|
|
|
|
155
|
1 |
|
return $items; |
156
|
|
|
} |
157
|
|
|
|
158
|
|
|
/** |
159
|
|
|
* @param array $item |
160
|
|
|
*/ |
161
|
1 |
|
private function incrementItem(array $item) : void |
162
|
|
|
{ |
163
|
1 |
|
app()->bind(AdditionCount::class, AdditionCount::class); |
164
|
|
|
|
165
|
1 |
|
$itemFromRedis = unserialize($this->redis->hget($this->normalizeKey((int)$item['user_id']), $item['id'])); |
166
|
|
|
|
167
|
1 |
|
$itemFromRedis['count'] = app()->make(AdditionCount::class) |
168
|
1 |
|
->execute(1, (int)$itemFromRedis['count']); |
169
|
|
|
|
170
|
1 |
|
$this->addRow($itemFromRedis); |
171
|
1 |
|
} |
172
|
|
|
|
173
|
1 |
|
private function addItem(array $item) : void |
174
|
|
|
{ |
175
|
1 |
|
$this->addRow($item); |
176
|
1 |
|
} |
177
|
|
|
|
178
|
|
|
/** |
179
|
|
|
* Check exist user in cart |
180
|
|
|
* |
181
|
|
|
* @param int $userId |
182
|
|
|
* @return bool |
183
|
|
|
*/ |
184
|
3 |
|
private function existUser(int $userId): bool |
185
|
|
|
{ |
186
|
3 |
|
return count($this->redis->hgetall($this->normalizeKey($userId))) > 0; |
187
|
|
|
} |
188
|
|
|
|
189
|
|
|
/** |
190
|
|
|
* @param int $itemId |
191
|
|
|
* @param int $userId |
192
|
|
|
* @return bool |
193
|
|
|
*/ |
194
|
2 |
|
private function existItem(int $itemId, int $userId) : bool |
195
|
|
|
{ |
196
|
2 |
|
return !is_null($this->redis->hget($this->normalizeKey($userId), $itemId)); |
197
|
|
|
} |
198
|
|
|
|
199
|
|
|
/** |
200
|
|
|
* Normalize string : key redis |
201
|
|
|
* @param $userId |
202
|
|
|
* @return string |
203
|
|
|
*/ |
204
|
10 |
|
private function normalizeKey(int $userId) : string |
205
|
|
|
{ |
206
|
10 |
|
return $userId . '.' . $this->prefix; |
207
|
|
|
} |
208
|
|
|
|
209
|
|
|
/** |
210
|
|
|
* @param array $item |
211
|
|
|
* @internal param int $userId |
212
|
|
|
* @internal param array $items |
213
|
|
|
*/ |
214
|
4 |
|
private function addRow(array $item) : void |
215
|
|
|
{ |
216
|
4 |
|
if (isset($item['count']) && $item['count'] == 0) { |
217
|
1 |
|
$this->remove($item); |
218
|
|
|
} else { |
219
|
3 |
|
$this->redis->hset($this->normalizeKey((int)$item['user_id']), $item['id'], serialize($item)); |
220
|
|
|
} |
221
|
|
|
} |
222
|
|
|
} |
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.