1 | <?php |
||||
2 | /** |
||||
3 | * ActiveRecord for API |
||||
4 | * |
||||
5 | * @link https://github.com/hiqdev/yii2-hiart |
||||
6 | * @package yii2-hiart |
||||
7 | * @license BSD-3-Clause |
||||
8 | * @copyright Copyright (c) 2015-2019, HiQDev (http://hiqdev.com/) |
||||
9 | */ |
||||
10 | |||||
11 | namespace hiqdev\hiart; |
||||
12 | |||||
13 | use Closure; |
||||
14 | use Yii; |
||||
15 | use yii\base\Component; |
||||
16 | use yii\base\InvalidCallException; |
||||
17 | use yii\base\InvalidConfigException; |
||||
18 | use yii\base\InvalidValueException; |
||||
19 | use yii\base\Model; |
||||
20 | use yii\base\ModelEvent; |
||||
21 | use yii\helpers\ArrayHelper; |
||||
22 | |||||
23 | /** |
||||
24 | * Class Collection manages the collection of the models. |
||||
25 | * |
||||
26 | * @var ActiveRecord[] the array of models in the collection |
||||
27 | * |
||||
28 | * @property Model[] $models |
||||
29 | */ |
||||
30 | class Collection extends Component |
||||
31 | { |
||||
32 | const EVENT_BEFORE_INSERT = 'beforeInsert'; |
||||
33 | const EVENT_BEFORE_UPDATE = 'beforeUpdate'; |
||||
34 | const EVENT_BEFORE_VALIDATE = 'beforeValidate'; |
||||
35 | const EVENT_AFTER_VALIDATE = 'afterValidate'; |
||||
36 | const EVENT_AFTER_SAVE = 'afterSave'; |
||||
37 | const EVENT_BEFORE_LOAD = 'beforeLoad'; |
||||
38 | const EVENT_AFTER_LOAD = 'afterLoad'; |
||||
39 | const EVENT_BEFORE_DELETE = 'beforeDelete'; |
||||
40 | const EVENT_AFTER_DELETE = 'afterDelete'; |
||||
41 | |||||
42 | /** |
||||
43 | * @var boolean Whether to check, that all [[$models]] are instance of the same class |
||||
44 | * @see isConsistent |
||||
45 | */ |
||||
46 | public $checkConsistency = true; |
||||
47 | |||||
48 | /** |
||||
49 | * @var ActiveRecord[] array of models |
||||
50 | */ |
||||
51 | protected $models = []; |
||||
52 | |||||
53 | /** |
||||
54 | * @var string the name of the form. Sets automatically on [[set()]] |
||||
55 | * @see set() |
||||
56 | */ |
||||
57 | public $formName; |
||||
58 | |||||
59 | /** |
||||
60 | * @var callable the function to format loaded data. Gets three attributes: |
||||
61 | * - model (instance of operating model) |
||||
62 | * - key - the key of the loaded item |
||||
63 | * - value - the value of the loaded item |
||||
64 | * Should return array, where the first item is the new key, and the second - a new value. Example: |
||||
65 | * ``` |
||||
66 | * return [$key, $value]; |
||||
67 | * ``` |
||||
68 | */ |
||||
69 | public $loadFormatter; |
||||
70 | |||||
71 | /** |
||||
72 | * @var ActiveRecord the template model instance. May be set manually by [[setModel()]] or |
||||
73 | * automatically on [[set()]] call |
||||
74 | * @see setModel() |
||||
75 | * @see set() |
||||
76 | */ |
||||
77 | protected $model; |
||||
78 | |||||
79 | /** |
||||
80 | * @var array options that will be passed to the new model when loading data in [[load]] |
||||
81 | * @see load() |
||||
82 | */ |
||||
83 | public $modelOptions = []; |
||||
84 | |||||
85 | /** |
||||
86 | * @var array options that will be passed to [[ActiveRecord::query()]] method as third argument |
||||
87 | * @see ActiveRecord::query() |
||||
88 | */ |
||||
89 | public $queryOptions = []; |
||||
90 | |||||
91 | /** |
||||
92 | * @var ActiveRecord the first model of the set. Fills automatically by [[set()]] |
||||
93 | * @see set() |
||||
94 | */ |
||||
95 | public $first; |
||||
96 | |||||
97 | /** |
||||
98 | * @var array the model's attributes that will be saved |
||||
99 | */ |
||||
100 | public $attributes; |
||||
101 | |||||
102 | /** |
||||
103 | * @var Closure a closure that will used to collect data from [[models]] before saving. |
||||
104 | * Signature: |
||||
105 | * ```php |
||||
106 | * function ($model, $collection) |
||||
107 | * ``` |
||||
108 | * |
||||
109 | * Method must return array of two elements: |
||||
110 | * - 0: key of the model in resulting array |
||||
111 | * - 1: corresponding value |
||||
112 | * |
||||
113 | * @see collectData |
||||
114 | */ |
||||
115 | public $dataCollector; |
||||
116 | |||||
117 | public function init() |
||||
118 | { |
||||
119 | if (!isset($this->queryOptions['batch'])) { |
||||
120 | $this->queryOptions['batch'] = true; |
||||
121 | } |
||||
122 | } |
||||
123 | |||||
124 | /** |
||||
125 | * Sets the model of the collection. |
||||
126 | * @param ActiveRecord|array $model if the model is an instance of [[Model]] - sets it, otherwise - creates the model |
||||
127 | * using given options array |
||||
128 | * @return object|ActiveRecord |
||||
129 | */ |
||||
130 | public function setModel($model) |
||||
131 | { |
||||
132 | if ($model instanceof Model) { |
||||
133 | $this->model = $model; |
||||
134 | } else { |
||||
135 | $this->model = Yii::createObject($model); |
||||
136 | } |
||||
137 | |||||
138 | $model = $this->model; |
||||
139 | $this->updateFormName(); |
||||
140 | |||||
141 | if (empty($this->getScenario())) { |
||||
142 | $this->setScenario($model->scenario); |
||||
143 | } |
||||
144 | |||||
145 | return $this->model; |
||||
146 | } |
||||
147 | |||||
148 | /** |
||||
149 | * Returns the [[model]]. |
||||
150 | * @return ActiveRecord |
||||
151 | */ |
||||
152 | public function getModel() |
||||
153 | { |
||||
154 | return $this->model; |
||||
155 | } |
||||
156 | |||||
157 | public function getIds() |
||||
158 | { |
||||
159 | $ids = []; |
||||
160 | foreach ($this->models as $model) { |
||||
161 | $ids[] = $model->getPrimaryKey(); |
||||
162 | } |
||||
163 | |||||
164 | return $ids; |
||||
165 | } |
||||
166 | |||||
167 | /** |
||||
168 | * @return ActiveRecord[] models |
||||
169 | */ |
||||
170 | public function getModels() |
||||
171 | { |
||||
172 | return $this->models; |
||||
173 | } |
||||
174 | |||||
175 | /** |
||||
176 | * Sets the scenario of the default model. |
||||
177 | * @param $value string scenario |
||||
178 | */ |
||||
179 | public function setScenario($value) |
||||
180 | { |
||||
181 | $this->modelOptions['scenario'] = $value; |
||||
182 | } |
||||
183 | |||||
184 | /** |
||||
185 | * Gets the scenario the default model. |
||||
186 | * @return string|null the scenario |
||||
187 | */ |
||||
188 | public function getScenario(): ?string |
||||
189 | { |
||||
190 | return $this->modelOptions['scenario'] ?? null; |
||||
191 | } |
||||
192 | |||||
193 | /** |
||||
194 | * Updates [[formName]] from the current [[model]]. |
||||
195 | * @return string the form name |
||||
196 | */ |
||||
197 | public function updateFormName() |
||||
198 | { |
||||
199 | if (!($this->model instanceof Model)) { |
||||
200 | throw new InvalidCallException('The model should be set first'); |
||||
201 | } |
||||
202 | |||||
203 | return $this->formName = $this->model->formName(); |
||||
204 | } |
||||
205 | |||||
206 | /** |
||||
207 | * We can load data from 3 different structures:. |
||||
208 | * 1) POST: [ |
||||
209 | * 'ModelName' => [ |
||||
210 | * 'attribute1' => 'value1', |
||||
211 | * 'attribute2' => 'value2' |
||||
212 | * ] |
||||
213 | * ] |
||||
214 | * 2) POST: [ |
||||
215 | * 'ModelName' => [ |
||||
216 | * 1 => [ |
||||
217 | * 'attribute1' => 'value1', |
||||
218 | * 'attribute2' => 'value2' |
||||
219 | * ], |
||||
220 | * 2 => [ |
||||
221 | * ... |
||||
222 | * ] |
||||
223 | * ] |
||||
224 | * } |
||||
225 | * 3) foreach ($selection as $id) { |
||||
226 | * $res[$id] = [reset($model->primaryKey()) => $id]; |
||||
227 | * }. |
||||
228 | * @param array|callable $data - the data to be proceeded. |
||||
229 | * If is callable - gets arguments: |
||||
230 | * - model |
||||
231 | * - fromName |
||||
232 | * @throws InvalidConfigException |
||||
233 | * @return Collection |
||||
234 | */ |
||||
235 | public function load($data = null) |
||||
236 | { |
||||
237 | $models = []; |
||||
238 | $finalData = []; |
||||
239 | |||||
240 | if ($data === null) { |
||||
241 | $data = Yii::$app->request->post(); |
||||
242 | |||||
243 | if (isset($data[$this->formName])) { |
||||
244 | $data = $data[$this->formName]; |
||||
245 | |||||
246 | $is_batch = true; |
||||
247 | foreach ($data as $k => $v) { |
||||
248 | if (!is_array($v)) { |
||||
249 | $is_batch = false; |
||||
250 | break; |
||||
251 | } |
||||
252 | } |
||||
253 | |||||
254 | if (!$is_batch) { |
||||
255 | $data = [$data]; |
||||
256 | } |
||||
257 | } elseif (isset($data['selection']) && is_iterable($data['selection'])) { |
||||
258 | $res = []; |
||||
259 | foreach ($data['selection'] as $id) { |
||||
260 | $array = $this->model->primaryKey(); |
||||
261 | $res[$id] = [reset($array) => $id]; |
||||
262 | } |
||||
263 | $data = $res; |
||||
264 | } |
||||
265 | } elseif ($data instanceof Closure) { |
||||
266 | $data = call_user_func($data, $this->model, $this->formName); |
||||
267 | } |
||||
268 | |||||
269 | foreach ($data as $key => $value) { |
||||
270 | if ($this->loadFormatter instanceof Closure) { |
||||
271 | $item = call_user_func($this->loadFormatter, $this->model, $key, $value); |
||||
272 | $key = $item[0]; |
||||
273 | } else { |
||||
274 | $item = [$key, $value]; |
||||
275 | } |
||||
276 | $options = ArrayHelper::merge(['class' => $this->model->className()], $this->modelOptions); |
||||
0 ignored issues
–
show
|
|||||
277 | $models[$key] = Yii::createObject($options); |
||||
278 | |||||
279 | $finalData[$this->formName][$key] = $item[1]; |
||||
280 | } |
||||
281 | $this->model->loadMultiple($models, $finalData); |
||||
282 | |||||
283 | return $this->set($models); |
||||
284 | } |
||||
285 | |||||
286 | /** |
||||
287 | * Sets the array of AR models to the collection. |
||||
288 | * @param array|ActiveRecord $models - array of AR Models or a single model |
||||
289 | * @return $this |
||||
290 | */ |
||||
291 | public function set($models) |
||||
292 | { |
||||
293 | if ($models instanceof ActiveRecord) { |
||||
294 | $models = [$models]; |
||||
295 | } |
||||
296 | |||||
297 | $first = reset($models); |
||||
298 | if ($first === false) { |
||||
299 | return $this; |
||||
300 | } |
||||
301 | $this->first = $first; |
||||
302 | |||||
303 | $this->formName = $first->formName(); |
||||
304 | $this->model = $this->setModel($first); |
||||
305 | $this->models = $models; |
||||
306 | |||||
307 | if ($this->checkConsistency && !$this->isConsistent()) { |
||||
308 | throw new InvalidValueException('Models are not objects of same class or not follow same operation'); |
||||
309 | } |
||||
310 | |||||
311 | return $this; |
||||
312 | } |
||||
313 | |||||
314 | /** |
||||
315 | * Saves the current collection. |
||||
316 | * This method will call [[insert()]] or [[update()]]. |
||||
317 | * @param bool $runValidation whether to perform validation before saving the collection |
||||
318 | * @param array $attributes list of attribute names that need to be saved. Defaults to null, |
||||
319 | * meaning all attributes that are loaded will be saved. If the scenario is specified, will use only |
||||
320 | * fields from the scenario |
||||
321 | * @param array $options the array of options that will be passed to [[insert]] or [[update]] methods to override |
||||
322 | * model parameters |
||||
323 | * @return bool whether the saving succeeds |
||||
324 | */ |
||||
325 | public function save($runValidation = true, $attributes = null, $options = []) |
||||
326 | { |
||||
327 | if ($this->isEmpty()) { |
||||
328 | throw new InvalidCallException('Collection is empty, nothing to save'); |
||||
329 | } |
||||
330 | $options = array_merge($this->queryOptions, $options); |
||||
331 | |||||
332 | if ($this->first->getIsNewRecord()) { |
||||
333 | return $this->insert($runValidation, $attributes, $options); |
||||
334 | } else { |
||||
335 | return $this->update($runValidation, $attributes, $options); |
||||
336 | } |
||||
337 | } |
||||
338 | |||||
339 | public function insert($runValidation = true, $attributes = null, array $queryOptions = []) |
||||
340 | { |
||||
341 | if (!$attributes) { |
||||
342 | $attributes = $this->attributes ?: $this->first->activeAttributes(); |
||||
343 | } |
||||
344 | if ($runValidation && !$this->validate($attributes)) { |
||||
345 | return false; |
||||
346 | } |
||||
347 | if (!$this->beforeSave(true)) { |
||||
348 | return false; |
||||
349 | } |
||||
350 | |||||
351 | $data = $this->collectData($attributes); |
||||
352 | $results = $this->performOperation('create', $data, $queryOptions); |
||||
353 | $pk = $this->first->primaryKey()[0]; |
||||
354 | foreach ($this->models as $key => $model) { |
||||
355 | $values = &$data[$key]; |
||||
356 | $result = &$results[$key]; |
||||
357 | if (!$result) { |
||||
358 | $result = $this->findAssociatedModelData($results, $model, $pk); |
||||
359 | } |
||||
360 | |||||
361 | $model->{$pk} = $result['id']; |
||||
362 | if ($pk !== 'id') { |
||||
363 | $values[$pk] = $result['id']; |
||||
364 | } |
||||
365 | $changedAttributes = array_fill_keys(array_keys($values), null); |
||||
366 | $model->setOldAttributes($values); |
||||
367 | $model->afterSave(true, $changedAttributes); |
||||
368 | } |
||||
369 | |||||
370 | $this->afterSave(); |
||||
371 | |||||
372 | return true; |
||||
373 | } |
||||
374 | |||||
375 | public function update($runValidation = true, $attributes = null, array $queryOptions = []) |
||||
376 | { |
||||
377 | if (!$attributes) { |
||||
378 | $attributes = $this->attributes ?: $this->first->activeAttributes(); |
||||
379 | } |
||||
380 | if ($runValidation && !$this->validate($attributes)) { |
||||
381 | return false; |
||||
382 | } |
||||
383 | if (!$this->beforeSave()) { |
||||
384 | return false; |
||||
385 | } |
||||
386 | |||||
387 | $data = $this->collectData($attributes); |
||||
388 | $results = $this->performOperation('update', $data, $queryOptions); |
||||
389 | |||||
390 | foreach ($this->models as $key => $model) { |
||||
391 | $changedAttributes = []; |
||||
392 | $values = array_key_exists($key, $data) ? $data[$key] : $data[$model->id]; /// XXX not good |
||||
393 | foreach ($values as $name => $value) { |
||||
394 | $changedAttributes[$name] = $model->getOldAttribute($name); |
||||
395 | $model->setOldAttribute($name, $value); |
||||
396 | } |
||||
397 | $model->afterSave(false, $changedAttributes); |
||||
398 | // update models |
||||
399 | foreach ($results as $id => $payload) { |
||||
400 | $pk = $this->first->primaryKey()[0]; |
||||
401 | if ((string)$model->{$pk} === (string)$id) { |
||||
402 | $model->setAttributes($payload); |
||||
403 | break; |
||||
404 | } |
||||
405 | } |
||||
406 | } |
||||
407 | |||||
408 | $this->afterSave(); |
||||
409 | |||||
410 | return true; |
||||
411 | } |
||||
412 | |||||
413 | public function delete() |
||||
414 | { |
||||
415 | if (!$this->beforeDelete()) { |
||||
416 | return false; |
||||
417 | } |
||||
418 | |||||
419 | $data = $this->collectData(); |
||||
420 | $results = $this->performOperation('delete', $data); |
||||
421 | |||||
422 | $this->afterDelete(); |
||||
423 | |||||
424 | return $results; |
||||
425 | } |
||||
426 | |||||
427 | /** |
||||
428 | * Collects data from the stored models. |
||||
429 | * @param string|array $attributes list of attributes names |
||||
430 | * @return array |
||||
431 | */ |
||||
432 | public function collectData($attributes = null) |
||||
433 | { |
||||
434 | $data = []; |
||||
435 | foreach ($this->models as $model) { |
||||
436 | if ($this->dataCollector instanceof Closure) { |
||||
437 | [$key, $row] = call_user_func($this->dataCollector, $model, $this); |
||||
438 | } else { |
||||
439 | $key = $model->getPrimaryKey(); |
||||
440 | $row = $model->getAttributes($attributes); |
||||
0 ignored issues
–
show
It seems like
$attributes can also be of type string ; however, parameter $names of yii\base\Model::getAttributes() does only seem to accept array|null , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
441 | } |
||||
442 | |||||
443 | if ($key) { |
||||
444 | $data[$key] = $row; |
||||
445 | } else { |
||||
446 | $data[] = $row; |
||||
447 | } |
||||
448 | } |
||||
449 | |||||
450 | return $data; |
||||
451 | } |
||||
452 | |||||
453 | /** |
||||
454 | * Whether one of models has an error. |
||||
455 | * @return bool |
||||
456 | */ |
||||
457 | public function hasErrors() |
||||
458 | { |
||||
459 | foreach ($this->models as $model) { |
||||
460 | if ($model->hasErrors()) { |
||||
461 | return true; |
||||
462 | } |
||||
463 | } |
||||
464 | |||||
465 | return false; |
||||
466 | } |
||||
467 | |||||
468 | /** |
||||
469 | * Returns the first error of the collection. |
||||
470 | * @return bool|mixed |
||||
471 | */ |
||||
472 | public function getFirstError() |
||||
473 | { |
||||
474 | foreach ($this->models as $model) { |
||||
475 | if ($model->hasErrors()) { |
||||
476 | $errors = $model->getFirstErrors(); |
||||
477 | |||||
478 | return array_shift($errors); |
||||
479 | } |
||||
480 | } |
||||
481 | |||||
482 | return false; |
||||
483 | } |
||||
484 | |||||
485 | public function count() |
||||
486 | { |
||||
487 | return is_array($this->models) ? count($this->models) : 0; |
||||
488 | } |
||||
489 | |||||
490 | public function validate($attributes = null) |
||||
491 | { |
||||
492 | if (!$this->beforeValidate()) { |
||||
493 | return false; |
||||
494 | } |
||||
495 | |||||
496 | if (!$this->first->validateMultiple($this->models, $attributes)) { |
||||
497 | return false; |
||||
498 | } |
||||
499 | |||||
500 | $this->afterValidate(); |
||||
501 | |||||
502 | return true; |
||||
503 | } |
||||
504 | |||||
505 | public function beforeValidate() |
||||
506 | { |
||||
507 | $event = new ModelEvent(); |
||||
508 | $this->triggerAll(self::EVENT_BEFORE_VALIDATE, $event); |
||||
509 | |||||
510 | return $event->isValid; |
||||
511 | } |
||||
512 | |||||
513 | public function afterValidate() |
||||
514 | { |
||||
515 | $event = new ModelEvent(); |
||||
516 | |||||
517 | $this->triggerAll(self::EVENT_AFTER_VALIDATE, $event); |
||||
518 | |||||
519 | return $event->isValid; |
||||
520 | } |
||||
521 | |||||
522 | public function beforeSave($insert = false) |
||||
523 | { |
||||
524 | $event = new ModelEvent(); |
||||
525 | if ($this->isEmpty()) { |
||||
526 | $event->isValid = false; |
||||
527 | } |
||||
528 | $this->triggerAll($insert ? self::EVENT_BEFORE_INSERT : self::EVENT_BEFORE_UPDATE, $event); |
||||
529 | |||||
530 | return $event->isValid; |
||||
531 | } |
||||
532 | |||||
533 | public function afterSave() |
||||
534 | { |
||||
535 | $this->triggerAll(self::EVENT_AFTER_SAVE); |
||||
536 | } |
||||
537 | |||||
538 | public function beforeLoad() |
||||
539 | { |
||||
540 | $event = new ModelEvent(); |
||||
541 | $this->trigger(self::EVENT_BEFORE_LOAD, $event); |
||||
542 | |||||
543 | return $event->isValid; |
||||
544 | } |
||||
545 | |||||
546 | public function afterLoad() |
||||
547 | { |
||||
548 | $this->trigger(self::EVENT_AFTER_LOAD); |
||||
549 | } |
||||
550 | |||||
551 | public function beforeDelete() |
||||
552 | { |
||||
553 | $event = new ModelEvent(); |
||||
554 | $this->trigger(self::EVENT_BEFORE_DELETE, $event); |
||||
555 | |||||
556 | return $event->isValid; |
||||
557 | } |
||||
558 | |||||
559 | public function afterDelete() |
||||
560 | { |
||||
561 | $this->trigger(self::EVENT_AFTER_DELETE); |
||||
562 | } |
||||
563 | |||||
564 | /** |
||||
565 | * Iterates over all of the models and triggers some event. |
||||
566 | * @param string $name the event name |
||||
567 | * @param ModelEvent $event |
||||
568 | * @return bool whether is valid |
||||
569 | */ |
||||
570 | public function triggerModels($name, ModelEvent $event = null) |
||||
571 | { |
||||
572 | if ($event === null) { |
||||
573 | $event = new ModelEvent(); |
||||
574 | } |
||||
575 | foreach ($this->models as $model) { |
||||
576 | $model->trigger($name, $event); |
||||
577 | } |
||||
578 | |||||
579 | return $event->isValid; |
||||
580 | } |
||||
581 | |||||
582 | /** |
||||
583 | * Calls [[triggerModels()]], then calls [[trigger()]]. |
||||
584 | * @param string $name the event name |
||||
585 | * @param ModelEvent $event |
||||
586 | * @return bool whether is valid |
||||
587 | */ |
||||
588 | public function triggerAll($name, ModelEvent $event = null) |
||||
589 | { |
||||
590 | if ($event === null) { |
||||
591 | $event = new ModelEvent(); |
||||
592 | } |
||||
593 | if ($this->triggerModels($name, $event)) { |
||||
594 | $this->trigger($name, $event); |
||||
595 | } |
||||
596 | |||||
597 | return $event->isValid; |
||||
598 | } |
||||
599 | |||||
600 | public function isConsistent() |
||||
601 | { |
||||
602 | $new = $this->first->getIsNewRecord(); |
||||
603 | $className = $this->first->className(); |
||||
604 | foreach ($this->models as $model) { |
||||
605 | if ($new !== $model->getIsNewRecord() || $className !== $model->className()) { |
||||
606 | return false; |
||||
607 | } |
||||
608 | } |
||||
609 | |||||
610 | return true; |
||||
611 | } |
||||
612 | |||||
613 | public function isEmpty() |
||||
614 | { |
||||
615 | return empty($this->models); |
||||
616 | } |
||||
617 | |||||
618 | /** |
||||
619 | * Try to find the model data if the response from the API came without an index by ID. |
||||
620 | * |
||||
621 | * @param $data |
||||
622 | * @param $model |
||||
623 | * @param $pk |
||||
624 | * @return mixed |
||||
625 | */ |
||||
626 | protected function findAssociatedModelData($data, $model, $pk) |
||||
627 | { |
||||
628 | if (isset($data[$pk])) { |
||||
629 | return $data; |
||||
630 | } |
||||
631 | |||||
632 | // todo: Add implementation for batch response |
||||
633 | throw new InvalidValueException('There is no implementation for a response from api without an index on ID'); |
||||
634 | } |
||||
635 | |||||
636 | /** |
||||
637 | * Perform operation with collection models |
||||
638 | * @param string $command (create||update||delete) |
||||
639 | * @param array $data |
||||
640 | * @param array $queryOptions |
||||
641 | * @return array |
||||
642 | */ |
||||
643 | protected function performOperation(string $command, array $data, array $queryOptions = []) : array |
||||
644 | { |
||||
645 | return $this->first->batchQuery($command, $data, $queryOptions); |
||||
646 | } |
||||
647 | } |
||||
648 |
This function has been deprecated. The supplier of the function has supplied an explanatory message.
The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.