Completed
Pull Request — master (#23)
by
unknown
07:21
created

YmlCatalog::writeModel()   D

Complexity

Conditions 9
Paths 48

Size

Total Lines 39
Code Lines 28

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 22
CRAP Score 9

Importance

Changes 0
Metric Value
dl 0
loc 39
ccs 22
cts 22
cp 1
rs 4.909
c 0
b 0
f 0
cc 9
eloc 28
nc 48
nop 2
crap 9
1
<?php
2
namespace pastuhov\ymlcatalog;
3
4
use pastuhov\ymlcatalog\models\BaseModel;
5
use pastuhov\ymlcatalog\models\Category;
6
use pastuhov\ymlcatalog\models\Currency;
7
use pastuhov\ymlcatalog\models\LocalDeliveryCost;
8
use pastuhov\ymlcatalog\models\Shop;
9
use pastuhov\ymlcatalog\models\SimpleOffer;
10
use pastuhov\ymlcatalog\models\DeliveryOption;
11
use Yii;
12
use pastuhov\FileStream\BaseFileStream;
13
use yii\base\Exception;
14
15
/**
16
 * Yml генератор каталога.
17
 *
18
 * @package pastuhov\ymlcatalog
19
 */
20
class YmlCatalog
21
{
22
    /**
23
     * @var BaseFileStream
24
     */
25
    protected $handle;
26
    /**
27
     * @var string
28
     */
29
    protected $shopClass;
30
    /**
31
     * @var string
32
     */
33
    protected $currencyClass;
34
    /**
35
     * @var string
36
     */
37
    protected $categoryClass;
38
    /**
39
     * @var null|string
40
     */
41
    protected $localDeliveryCostClass;
42
    /**
43
     * @var string
44
     */
45
    protected $offerClass;
46
    /**
47
     * @var null|string
48
     */
49
    protected $date;
50
51
    /**
52
     * @var null|callable
53
     */
54
    protected $onValidationError;
55
56
    /**
57
     * @var null|string
58
     */
59
    protected $customOfferClass;
60
61
    /**
62
     * @var null|string
63
     */
64
    protected $deliveryOptionClass;
65
66
    /**
67
     * @param BaseFileStream $handle
68
     * @param string $shopClass class name
69
     * @param string $currencyClass class name
70
     * @param string $categoryClass class name
71 9
     * @param string $localDeliveryCostClass class name
72
     * @param array $offerClasses
73
     * @param null|string $date
74 2
     * @param null|callable $onValidationError
75
     * @param null|string $customOfferClass
76
     */
77
    public function __construct(
78
        BaseFileStream $handle,
79
        $shopClass,
80
        $currencyClass,
81
        $categoryClass,
82 9
        $localDeliveryCostClass = null,
83 9
        Array $offerClasses,
84 9
        $date = null,
85 9
        $onValidationError = null,
86 9
        $customOfferClass = null,
87 9
        $deliveryOptionClass = null
88 9
    ) {
89 9
        $this->handle = $handle;
90 9
        $this->shopClass = $shopClass;
91 9
        $this->currencyClass = $currencyClass;
92
        $this->categoryClass = $categoryClass;
93
        $this->localDeliveryCostClass = $localDeliveryCostClass;
94
        $this->offerClasses = $offerClasses;
0 ignored issues
show
Bug introduced by
The property offerClasses does not seem to exist. Did you mean offerClass?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
95
        $this->date = $date;
96 9
        $this->onValidationError = $onValidationError;
97
        $this->customOfferClass = $customOfferClass;
98 9
        $this->deliveryOptionClass = $deliveryOptionClass;
99
    }
100 9
101 9
    /**
102 9
     * @throws Exception
103 9
     */
104 6
    public function generate()
105
    {
106 9
        $date = $this->getDate();
107 9
108 9
        $this->write(
109 9
            '<?xml version="1.0" encoding="utf-8"?>' . PHP_EOL .
110 9
            '<!DOCTYPE yml_catalog SYSTEM "shops.dtd">' . PHP_EOL .
111 9
            '<yml_catalog date="' . $date . '">' . PHP_EOL
112 9
        );
113 9
114 9
        $this->writeTag('shop');
115 9
        $this->writeModel(new Shop(), new $this->shopClass());
116 9
        $this->writeTag('currencies');
117 9
        $this->writeEachModel($this->currencyClass);
118 4
        $this->writeTag('/currencies');
119 6
        $this->writeTag('categories');
120 8
        $this->writeEachModel($this->categoryClass);
121
        $this->writeTag('/categories');
122 6
        if($this->deliveryOptionClass) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->deliveryOptionClass of type null|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
123 6
            $this->writeTag('delivery-options');
124
            $this->writeModel(new DeliveryOption(), new $this->deliveryOptionClass());
125
            $this->writeTag('/delivery-options');
126
        }
127
        if($this->localDeliveryCostClass) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->localDeliveryCostClass of type null|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
128 9
            $this->writeModel(new LocalDeliveryCost(), new $this->localDeliveryCostClass());
0 ignored issues
show
Deprecated Code introduced by
The class pastuhov\ymlcatalog\models\LocalDeliveryCost has been deprecated.

This class, trait or interface has been deprecated.

Loading history...
129
        }
130 9
        $this->writeTag('offers');
131
        foreach ($this->offerClasses as $offerClass) {
0 ignored issues
show
Bug introduced by
The property offerClasses does not seem to exist. Did you mean offerClass?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
132 9
            $this->writeEachModel($offerClass);
133 3
        }
134 2
        $this->writeTag('/offers');
135
        $this->writeTag('/shop');
136 9
137
        $this->write('</yml_catalog>');
138
    }
139
140
    /**
141
     * @return null|string
142
     */
143 9
    protected function getDate()
144 6
    {
145 9
        $date = $this->date;
146 9
147
        if ($date === null) {
148
            $date = Yii::$app->formatter->asDatetime(new \DateTime(), 'php:Y-m-d H:i');
149
        }
150
151 9
        return $date;
152 6
    }
153 9
154 9
    /**
155
     * @param string $string
156
     * @throws \Exception
157
     */
158
    protected function write($string)
159
    {
160
        $this->handle->write($string);
161 9
    }
162
163 9
    /**
164 9
     * @param string $string tag name
165 9
     */
166 9
    protected function writeTag($string)
167
    {
168 9
        $this->write('<' . $string . '>' . PHP_EOL);
169 6
    }
170
171 9
    /**
172 9
     * @param BaseModel $model model
173 6
     * @param $valuesModel
174 4
     * @throws Exception
175 9
     */
176 6
    protected function writeModel(BaseModel $model, $valuesModel)
177 4
    {
178
        $attributes = [];
179 9
        $deliveryOptions = [];
180 9
        foreach ($model->attributes() as $attribute) {
181 9
            $methodName = 'get' . ucfirst($attribute);
182 6
            $attributeValue = $valuesModel->$methodName();
183 3
            if($attribute == 'delivery_options') {
184
                $deliveryOptions = $attributeValue;
185 6
            }
186 9
            $attributes[$attribute] = $attributeValue;
187
        }
188 9
189
        $model->load($attributes, '');
190 9
        if (method_exists($valuesModel, 'getParams')) {
191
            $model->setParams($valuesModel->getParams());
192
        }
193
        if (method_exists($valuesModel, 'getPictures')) {
194
            $model->setPictures($valuesModel->getPictures());
195 9
        }
196
197 9
        if (!$model->validate()) {
198 9
            if (is_callable($onValidationError = $this->onValidationError)) {
199
                $onValidationError($model);
200 9
            } else {
201
                throw new Exception('Model values is invalid ' . serialize($model->getErrors()));
202
            }
203 9
        } else {
204
            if($model instanceof SimpleOffer && count($deliveryOptions) > 0) {
205 9
                $string = $model->getYmlWithoutEndTag();
206 9
                $this->write($string);
207 9
                $this->writeDeliveryOptions($deliveryOptions);
208 6
                $this->writeTag('/offer');
209 6
            } else {
210 9
                $string = $model->getYml();
211
                $this->write($string);
212
            }
213
        }
214
    }
215
216
    /**
217 9
     * @param string|array $modelClass class name
218
     */
219 9
    protected function writeEachModel($modelClass)
220
    {
221 9
        $class = isset($modelClass['class']) ? $modelClass['class'] : $modelClass;
222 9
        $findParams = isset($modelClass['findParams']) ? $modelClass['findParams'] : [];
223 6
224 9
        $newModel = $this->getNewModel($class);
225 9
226 3
        /* @var \yii\db\ActiveQuery $query */
227 3
        $query = $class::findYml($findParams);
228 6
229 6
        foreach ($query->batch(100) as $models) {
230 4
            foreach ($models as $model) {
231
                $this->writeModel($newModel, $model);
232
            }
233
        }
234 9
    }
235
236
    /**
237
     * @param $modelClass
238
     * @return Category|Currency|SimpleOffer
239
     * @throws Exception
240
     */
241
    protected function getNewModel($modelClass)
242
    {
243
        $obj = new $modelClass();
244
245
        if ($obj instanceof CurrencyInterface) {
246
            $model = new Currency();
247
        } elseif ($obj instanceof CategoryInterface) {
248
            $model = new Category();
249
        } elseif ($obj instanceof CustomOfferInterface && $this->customOfferClass !== null && class_exists($this->customOfferClass)) {
250
            $class = $this->customOfferClass;
251
            $model = new $class();
252
        } elseif ($obj instanceof SimpleOfferInterface) {
253
            $model = new SimpleOffer();
254
        } else {
255
            throw new Exception('Model ' . $modelClass. ' has unknown interface');
256
        }
257
258
        return $model;
259
    }
260
261
    /**
262
     * Записывает в тело xml документа опции доставки
263
     *
264
     * @param $deliveryOptions[]
265
     * @throws Exception
266
     */
267
    protected function writeDeliveryOptions($deliveryOptions) {
268
        if(count($deliveryOptions) > 5) {
269
            throw new Exception('Maximum count of delivery options array is 5');
270
        }
271
        $this->writeTag('delivery-options');
272
        foreach($deliveryOptions as $deliveryOption) {
273
            $this->writeModel(new DeliveryOption(), $deliveryOption);
274
        }
275
        $this->writeTag('/delivery-options');
276
    }
277
}
278