Packer::packOrder()   B
last analyzed

Complexity

Conditions 6
Paths 6

Size

Total Lines 30
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 30
rs 8.439
c 0
b 0
f 0
cc 6
eloc 16
nc 6
nop 2
1
<?php
2
3
/**
4
 * @package Packer
5
 * @author Iurii Makukh <[email protected]>
6
 * @copyright Copyright (c) 2018, Iurii Makukh <[email protected]>
7
 * @license https://www.gnu.org/licenses/gpl-3.0.en.html GPL-3.0-or-later
8
 */
9
10
namespace gplcart\modules\packer\models;
11
12
use DVDoug\BoxPacker\ItemList;
13
use DVDoug\BoxPacker\VolumePacker;
14
use gplcart\core\Library;
15
use gplcart\core\models\Convertor;
16
use gplcart\modules\packer\helpers\Box as BoxHelper;
17
use gplcart\modules\packer\helpers\Item as ItemHelper;
18
use LogicException;
19
use OutOfRangeException;
20
use UnexpectedValueException;
21
22
/**
23
 * Manages basic behaviors and data related to Packer module
24
 */
25
class Packer
26
{
27
28
    /**
29
     * Library class instance
30
     * @var \gplcart\core\Library $library
31
     */
32
    protected $library;
33
34
    /**
35
     * Convertor model class instance
36
     * @var \gplcart\core\models\Convertor $convertor
37
     */
38
    protected $convertor;
39
40
    /**
41
     * Box model class instanceS
42
     * @var \gplcart\modules\packer\models\Box $box
43
     */
44
    protected $box;
45
46
    /**
47
     * Packer constructor.
48
     * @param Library $library
49
     * @param Convertor $convertor
50
     * @param Box $box
51
     */
52
    public function __construct(Library $library, Convertor $convertor, Box $box)
53
    {
54
        $this->library = $library;
55
        $this->convertor = $convertor;
56
        $this->box = $box;
57
    }
58
59
    /**
60
     * Returns library class instance
61
     * @return \DVDoug\BoxPacker\Packer
62
     * @throws LogicException
63
     */
64
    public function getPacker()
65
    {
66
        $this->library->load('packer');
67
68
        if (!class_exists('DVDoug\BoxPacker\Packer')) {
69
            throw new \LogicException('Class \DVDoug\BoxPacker\Packer not found');
70
        }
71
72
        return new \DVDoug\BoxPacker\Packer;
73
    }
74
75
    /**
76
     * Pack a set of items into a given set of box types
77
     * @param array $items
78
     * @param array $boxes
79
     * @return \DVDoug\BoxPacker\PackedBoxList
80
     */
81
    public function pack(array $items, array $boxes)
82
    {
83
        $packer = $this->getPacker();
84
85
        $this->prepareBoxes($boxes, $items);
86
87
        foreach ($boxes as $box) {
88
            $packer->addBox(new BoxHelper($box));
89
        }
90
91
        foreach ($items as $item) {
92
            $packer->addItem(new ItemHelper($item));
93
        }
94
95
        return $packer->pack();
96
    }
97
98
    /**
99
     * Returns an array of boxes needed to fit all the order items
100
     * @param array $order
101
     * @param array $cart
102
     * @return array
103
     * @throws OutOfRangeException
104
     * @throws UnexpectedValueException
105
     */
106
    public function packOrder(array $order, array $cart)
107
    {
108
        if (!isset($order['shipping'])) {
109
            throw new OutOfRangeException('"shipping" key is not set in the order data');
110
        }
111
112
        if (!isset($order['volume'])) {
113
            throw new OutOfRangeException('"volume" key is not set in the order data');
114
        }
115
116
        if (!isset($cart['items'])) {
117
            throw new OutOfRangeException('"items" key is not set in the cart data');
118
        }
119
120
        $boxes = $this->box->getListByShippingMethod($order['shipping']);
121
122
        if (empty($boxes)) {
123
            throw new UnexpectedValueException('No enabled boxes found in the database for the shipping method');
124
        }
125
126
        $items = array();
127
128
        foreach ($cart['items'] as $item) {
129
            $product = $item['product'];
130
            $product['volume'] = $order['volume'];
131
            $items[] = $product;
132
        }
133
134
        return $this->getPackedBoxes($items, $boxes);
135
    }
136
137
    /**
138
     * Returns an array of packed boxes
139
     * @param array $items
140
     * @param array $boxes
141
     * @return array
142
     */
143
    public function getPackedBoxes(array $items, array $boxes)
144
    {
145
        $packed = array();
146
147
        foreach ($this->pack($items, $boxes) as $pack) {
148
            /** @var \gplcart\modules\packer\helpers\Box $box */
149
            $box = $pack->getBox();
150
            $data = $box->getBoxData();
151
            $packed[$data['box_id']] = $data;
152
        }
153
154
        return $packed;
155
    }
156
157
    /**
158
     * Does an array of items fit into the box
159
     * @param array $items
160
     * @param array $box
161
     * @return \DVDoug\BoxPacker\PackedBox
162
     */
163
    public function fit(array $items, array $box)
164
    {
165
        $this->getPacker();
166
        $this->prepareItems($items, $box);
167
168
        $box_object = new BoxHelper($box);
169
        $items_object = new ItemList();
170
171
        foreach ($items as $item) {
172
            $items_object->insert(new ItemHelper($item));
173
        }
174
175
        $packer = new VolumePacker($box_object, $items_object);
176
        return $packer->pack();
177
    }
178
179
    /**
180
     * Returns an array of items that fit into the box
181
     * @param array $items
182
     * @param array $box
183
     * @return array
184
     */
185
    public function getFitItems(array $items, array $box)
186
    {
187
        $fit = array();
188
189
        /** @var \gplcart\modules\packer\helpers\Item $item */
190
        foreach ($this->fit($items, $box)->getItems() as $item) {
191
            $fit[] = $item->getItemData();
192
        }
193
194
        return $fit;
195
    }
196
197
    /**
198
     * Prepare an array of boxes
199
     * @param array $boxes
200
     * @param array $items
201
     */
202
    protected function prepareBoxes(array &$boxes, array $items)
203
    {
204
        if (!empty($boxes) && !empty($items)) {
205
            $item = reset($items);
206
            foreach ($boxes as &$box) {
207
                $this->convertBox($box, $item);
208
            }
209
        }
210
    }
211
212
    /**
213
     * Prepare an array of items
214
     * @param array $items
215
     * @param array $box
216
     */
217
    protected function prepareItems(array &$items, array $box)
218
    {
219
        foreach ($items as &$item) {
220
            $this->convertItem($item, $box);
221
        }
222
    }
223
224
    /**
225
     * Converts unints in the box data
226
     * @param array $box
227
     * @param array $item
228
     */
229
    protected function convertBox(array &$box, array $item)
230
    {
231
        if ($box['size_unit'] !== $item['size_unit']) {
232
233
            $fields = array(
234
                'outer_width', 'outer_length',
235
                'outer_depth', 'inner_width',
236
                'inner_length', 'inner_depth'
237
            );
238
239
            foreach ($fields as $field) {
240
                $box[$field] = $this->convertor->convert($box[$field], $box['size_unit'], $item['size_unit']);
241
            }
242
243
            $box['size_unit'] = $item['size_unit'];
244
        }
245
246
        if ($box['weight_unit'] !== $item['weight_unit']) {
247
248
            foreach (array('max_weight', 'empty_weight') as $field) {
249
                $box[$field] = $this->convertor->convert($box[$field], $box['weight_unit'], $item['weight_unit']);
250
            }
251
252
            $box['weight_unit'] = $item['weight_unit'];
253
        }
254
    }
255
256
    /**
257
     * Convert item units
258
     * @param array $item
259
     * @param array $box
260
     */
261
    protected function convertItem(array &$item, array $box)
262
    {
263
        if ($item['size_unit'] !== $box['size_unit']) {
264
265
            foreach (array('length', 'width', 'height') as $field) {
266
                $item[$field] = $this->convertor->convert($item[$field], $item['size_unit'], $box['size_unit']);
267
            }
268
269
            $item['size_unit'] = $box['size_unit'];
270
        }
271
272
        if ($item['weight_unit'] !== $box['weight_unit']) {
273
            $item['weight'] = $this->convertor->convert($item['weight'], $item['weight_unit'], $box['weight_unit']);
274
            $item['weight_unit'] = $box['weight_unit'];
275
        }
276
277
        $item['volume'] = $item['length'] * $item['width'] * $item['height'];
278
    }
279
}
280