1C-Bitrix 25.700.0
Загрузка...
Поиск...
Не найдено
productimage.php
См. документацию.
1<?php
2
3namespace Bitrix\Catalog\Controller;
4
5use Bitrix\Catalog\Access\ActionDictionary;
6use Bitrix\Iblock\PropertyTable;
7use Bitrix\Main\Application;
8use Bitrix\Main\DB\SqlQueryException;
9use Bitrix\Main\Engine\Response\DataType\Page;
10use Bitrix\Main\Error;
11use Bitrix\Main\FileTable;
12use Bitrix\Main\Loader;
13use Bitrix\Main\Result;
14
15final class ProductImage extends Controller
16{
17 use CheckExists; // default implementation of existence check
18
19 //region Actions
29 public function getFieldsAction(): array
30 {
31 return [$this->getServiceItemName() => $this->getViewFields()];
32 }
33
43 public function listAction(int $productId, array $select = [], \CRestServer $restServer = null): ?Page
44 {
45 if ($productId <= 0)
46 {
47 $this->addError(new Error('Empty productID'));
48
49 return null;
50 }
51
52 $product = $this->getProduct($productId);
53 if (!$product)
54 {
55 $this->addError(new Error('Product was not found'));
56
57 return null;
58 }
59
60 $r = $this->checkPermissionProductRead($product);
61 if (!$r->isSuccess())
62 {
63 $this->addErrors($r->getErrors());
64
65 return null;
66 }
67
68 $imageIds = [];
69 if ($product['PREVIEW_PICTURE'])
70 {
71 $imageIds[] = $product['PREVIEW_PICTURE'];
72 }
73 if ($product['DETAIL_PICTURE'])
74 {
75 $imageIds[] = $product['DETAIL_PICTURE'];
76 }
77 if ($this->getMorePhotoPropertyId((int)$product['IBLOCK_ID']))
78 {
79 $imageIds = [...$imageIds, ...$this->getMorePhotoPropertyValues($product)];
80 }
81
82 $result = [];
83 $fileTableResult = FileTable::getList(['filter' => ['=ID' => $imageIds]]);
84 while ($image = $fileTableResult->fetch())
85 {
86 if ($product['PREVIEW_PICTURE'] === $image['ID'])
87 {
88 $type = 'PREVIEW_PICTURE';
89 }
90 elseif ($product['DETAIL_PICTURE'] === $image['ID'])
91 {
92 $type = 'DETAIL_PICTURE';
93 }
94 else
95 {
96 $type = 'MORE_PHOTO';
97 }
98 $image['TYPE'] = $type;
99 $image['PRODUCT_ID'] = $product['ID'];
100
101 $result[] = $this->prepareFileStructure($image, $restServer, $select);
102 }
103
104 return new Page(
105 'PRODUCT_IMAGES',
106 $result,
108 );
109 }
110
120 public function getAction(int $id, int $productId, \CRestServer $restServer = null): ?array
121 {
122 $product = $this->getProduct($productId);
123 if (!$product)
124 {
125 $this->addError(new Error('Product was not found'));
126
127 return null;
128 }
129
130 $r = $this->checkPermissionProductRead($product);
131 if (!$r->isSuccess())
132 {
133 $this->addErrors($r->getErrors());
134
135 return null;
136 }
137
138 $image = $this->getImageById($id, $product);
139 if (!$image)
140 {
141 $this->addError($this->getErrorEntityNotExists());
142
143 return null;
144 }
145
146 return [$this->getServiceItemName() => $this->prepareFileStructure($image, $restServer)];
147 }
148
158 public function addAction(array $fields, array $fileContent, \CRestServer $restServer = null): ?array
159 {
160 if (!Loader::includeModule('rest'))
161 {
162 return null;
163 }
164
165 $product = $this->getProduct((int)$fields['PRODUCT_ID']);
166 if (!$product)
167 {
168 $this->addError(new Error('Product was not found'));
169 return null;
170 }
171
172 $r = $this->checkPermissionProductWrite($product);
173 if (!$r->isSuccess())
174 {
175 $this->addErrors($r->getErrors());
176 return null;
177 }
178
179 $fileData = \CRestUtil::saveFile($fileContent);
180 if (!$fileData)
181 {
182 $this->addError(new Error('Could not save image.'));
183 return null;
184 }
185
186 $checkPictureResult = \CFile::CheckFile($fileData, 0 ,false, \CFile::GetImageExtensions());
187 if ($checkPictureResult !== '')
188 {
189 $this->addError(new Error($checkPictureResult));
190 return null;
191 }
192
193 if ($fields['TYPE'] === 'DETAIL_PICTURE' || $fields['TYPE'] === 'PREVIEW_PICTURE')
194 {
195 $updateFields = [$fields['TYPE'] => $fileData];
196 }
197 else
198 {
199 $fields['TYPE'] = 'MORE_PHOTO';
200 $morePhotoProperty = $this->getMorePhotoProperty((int)$product['IBLOCK_ID']);
201 if (!$morePhotoProperty->isSuccess())
202 {
203 $this->addErrors($morePhotoProperty->getErrors());
204
205 return null;
206 }
207
208 $updateFields = [
209 'n0' => [
210 'VALUE' => $fileData,
211 ],
212 ];
213 }
214
215 $connection = Application::getConnection();
216 $connection->startTransaction();
217 try
218 {
219 if ($fields['TYPE'] === 'DETAIL_PICTURE' || $fields['TYPE'] === 'PREVIEW_PICTURE')
220 {
221 $error = $this->updateProductImage((int)$product['ID'], $updateFields);
222 }
223 else
224 {
225 $error = $this->updateProductMorePhoto((int)$product['ID'], (int)$product['IBLOCK_ID'], $updateFields);
226 }
227 }
228 catch (SqlQueryException)
229 {
230 $error = 'Internal error adding product image. Try adding again.';
231 }
232 if ($error !== '')
233 {
234 $connection->rollbackTransaction();
235 $this->addError(new Error($error));
236
237 return null;
238 }
239 $connection->commitTransaction();
240
241 if ($fields['TYPE'] === 'DETAIL_PICTURE' || $fields['TYPE'] === 'PREVIEW_PICTURE')
242 {
243 $product = $this->getProduct((int)$fields['PRODUCT_ID'], [$fields['TYPE']]);
244 $imageId = $product[$fields['TYPE']];
245 }
246 else
247 {
248 $morePhotoIds = $this->getMorePhotoPropertyValues($product);
249 if (!$morePhotoIds)
250 {
251 $this->addError(new Error('Empty image.'));
252
253 return null;
254 }
255 $imageId = end($morePhotoIds);
256 }
257 $image = FileTable::getRowById($imageId);
258 if (!$image)
259 {
260 $this->addError($this->getErrorEntityNotExists());
261
262 return null;
263 }
264 $image['TYPE'] = $fields['TYPE'];
265 $image['PRODUCT_ID'] = $fields['PRODUCT_ID'];
266
267 return [$this->getServiceItemName() => $this->prepareFileStructure($image, $restServer)];
268 }
269
278 public function deleteAction(int $id, int $productId): ?bool
279 {
280 if (!$this->exists($id))
281 {
282 $this->addError($this->getErrorEntityNotExists());
283
284 return null;
285 }
286
287 $product = $this->getProduct($productId);
288 if (!$product)
289 {
290 $this->addError(new Error('Product was not found'));
291
292 return null;
293 }
294
295 $r = $this->checkPermissionProductWrite($product);
296 if (!$r->isSuccess())
297 {
298 $this->addErrors($r->getErrors());
299
300 return null;
301 }
302
303 if ($id === (int)$product['PREVIEW_PICTURE'])
304 {
305 $updateFields = ['PREVIEW_PICTURE' => \CIBlock::makeFileArray(null, true)];
306 }
307 elseif ($id === (int)$product['DETAIL_PICTURE'])
308 {
309 $updateFields = ['DETAIL_PICTURE' => \CIBlock::makeFileArray(null, true)];
310 }
311 else
312 {
313 $morePhotoPropertyValueId = $this->getMorePhotoPropertyValueId($product, $id);
314 if (!$morePhotoPropertyValueId)
315 {
316 $this->addError($this->getErrorEntityNotExists());
317
318 return null;
319 }
320 $updateFields = [$morePhotoPropertyValueId => \CIBlock::makeFileArray(null, true)];
321 }
322
323 $connection = Application::getConnection();
324 $connection->startTransaction();
325 try
326 {
327 if ($id === (int)$product['PREVIEW_PICTURE'] || $id === (int)$product['DETAIL_PICTURE'])
328 {
329 $error = $this->updateProductImage((int)$product['ID'], $updateFields);
330 }
331 else
332 {
333 $error = $this->updateProductMorePhoto((int)$product['ID'], (int)$product['IBLOCK_ID'], $updateFields);
334 }
335 }
336 catch (SqlQueryException)
337 {
338 $error = 'Internal error deleting product image. Try deleting again.';
339 }
340 if ($error !== '')
341 {
342 $connection->rollbackTransaction();
343 $this->addError(new Error($error));
344
345 return null;
346 }
347 $connection->commitTransaction();
348
349 return true;
350 }
351 //endregion
352
353 private function prepareFileStructure(
354 array $image,
355 \CRestServer $restServer = null,
356 array $selectedFields = null
357 ): array
358 {
359 $result = [];
360 if (!$selectedFields)
361 {
362 $selectedFields = array_keys($this->getViewManager()->getView($this)->getFields());
363 }
364
365 foreach ($selectedFields as $name)
366 {
367 if ($name === 'ID')
368 {
369 $result[$name] = (int)$image['ID'];
370 }
371 if ($name === 'NAME')
372 {
373 $result[$name] = $image['FILE_NAME'];
374 }
375 elseif ($name === 'DETAIL_URL')
376 {
377 $result[$name] = \CFile::getFileSRC($image);
378 }
379 elseif ($name === 'DOWNLOAD_URL')
380 {
381 $result[$name] =
382 $restServer
383 ? \CRestUtil::getDownloadUrl(['id' => $image['ID']], $restServer)
384 : \CFile::getFileSRC($image)
385 ;
386 }
387 elseif ($name === 'CREATE_TIME')
388 {
389 $result[$name] = $image['TIMESTAMP_X'];
390 }
391 elseif ($name === 'PRODUCT_ID')
392 {
393 $result[$name] = (int)$image['PRODUCT_ID'];
394 }
395 elseif ($name === 'TYPE')
396 {
397 $result[$name] = $image['TYPE'];
398 }
399 }
400
401 return $result;
402 }
403
404 protected function getEntityTable()
405 {
406 return new FileTable();
407 }
408
409 private function getProduct(int $productId, ?array $select = null): ?array
410 {
411 return \Bitrix\Iblock\ElementTable::getRow([
412 'select' => $select ?: ['ID', 'IBLOCK_ID', 'PREVIEW_PICTURE', 'DETAIL_PICTURE'],
413 'filter' => ['=ID' => $productId],
414 ]);
415 }
416
417 private function getImageById(int $id, array $product): ?array
418 {
419 if ((int)$product['PREVIEW_PICTURE'] === $id)
420 {
421 $type = 'PREVIEW_PICTURE';
422 }
423 elseif ((int)$product['DETAIL_PICTURE'] === $id)
424 {
425 $type = 'DETAIL_PICTURE';
426 }
427 else
428 {
429 $morePhotoIds = $this->getMorePhotoPropertyValues($product);
430 if (!in_array($id, $morePhotoIds))
431 {
432 return null;
433 }
434
435 $type = 'MORE_PHOTO';
436 }
437
438 $image = FileTable::getRowById($id);
439 if (!$image)
440 {
441 return null;
442 }
443
444 $image['TYPE'] = $type;
445 $image['PRODUCT_ID'] = $product['ID'];
446
447 return $image;
448 }
449
450 private function getMorePhotoPropertyValueId(array $product, int $value): ?int
451 {
452 $morePhotoPropertyId = $this->getMorePhotoPropertyId((int)$product['IBLOCK_ID']);
453 if (!$morePhotoPropertyId)
454 {
455 return null;
456 }
457
458 $propertyValuesResult = $this->getPropertyValues($product, $morePhotoPropertyId, true);
459 $morePhotoPropertyIds = $propertyValuesResult[$morePhotoPropertyId];
460 if (!$morePhotoPropertyIds)
461 {
462 return null;
463 }
464
465 $valueIndex = array_search($value, $morePhotoPropertyIds);
466 if ($valueIndex === false)
467 {
468 return null;
469 }
470
471 return (int)$propertyValuesResult['PROPERTY_VALUE_ID'][$morePhotoPropertyId][$valueIndex] ?? null;
472 }
473
474 private function getMorePhotoPropertyValues(array $product): array
475 {
476 $morePhotoPropertyId = $this->getMorePhotoPropertyId((int)$product['IBLOCK_ID']);
477 if (!$morePhotoPropertyId)
478 {
479 return [];
480 }
481 $propertyValuesResult = $this->getPropertyValues($product, $morePhotoPropertyId);
482
483 return $propertyValuesResult[$morePhotoPropertyId] ?? [];
484 }
485
486 private function getPropertyValues(array $product, int $propertyId, bool $extMode = false): array
487 {
488 return \CIBlockElement::getPropertyValues(
489 $product['IBLOCK_ID'],
490 [
491 'ID' => $product['ID'],
492 'IBLOCK_ID' => $product['IBLOCK_ID'],
493 ],
494 $extMode,
495 [
496 'ID' => $propertyId,
497 ],
498 )->Fetch() ?: [];
499 }
500
501 private function getMorePhotoPropertyId(int $iblockId): ?int
502 {
503 return PropertyTable::getRow([
504 'select' => ['ID'],
505 'filter' => [
506 '=IBLOCK_ID' => $iblockId,
507 '=CODE' => 'MORE_PHOTO',
508 '=ACTIVE' => 'Y',
509 '=PROPERTY_TYPE' => PropertyTable::TYPE_FILE,
510 ],
511 'cache' => [
512 'ttl' => 86400,
513 ],
514 ])['ID'] ?? null;
515 }
516
517 private function getMorePhotoProperty(int $iblockId): Result
518 {
519 $result = new Result();
520 $row = PropertyTable::getRow([
521 'select' => [
522 'ID',
523 'ACTIVE',
524 'PROPERTY_TYPE',
525 ],
526 'filter' => [
527 '=IBLOCK_ID' => $iblockId,
528 '=CODE' => 'MORE_PHOTO',
529 ],
530 'cache' => [
531 'ttl' => 86400,
532 ],
533 ]);
534
535 if (!$row)
536 {
537 $result->addError(new Error(
538 'Image product property does not exists. Create MORE_PHOTO property'
539 ));
540
541 return $result;
542 }
543
544 if ($row['ACTIVE'] !== 'Y')
545 {
546 $result->addError(new Error(
547 'Image product property does not active. Activate MORE_PHOTO property'
548 ));
549
550 return $result;
551 }
552
553 if ($row['PROPERTY_TYPE'] !== PropertyTable::TYPE_FILE)
554 {
555 $result->addError(new Error(
556 'Image product property is of the wrong type'
557 ));
558
559 return $result;
560 }
561
562 $result->setData(['ID' => (int)$row['ID']]);
563
564 return $result;
565 }
566
567 private function updateProductImage(int $productId, array $updateFields): string
568 {
569 $iblockElement = new \CIBlockElement();
570 $iblockElement->update($productId, $updateFields);
571
572 return $iblockElement->getLastError();
573 }
574
575 private function updateProductMorePhoto(int $productId, int $iblockId, array $updateFields): string
576 {
577 \CIBlockElement::SetPropertyValues(
578 $productId,
579 $iblockId,
580 $updateFields,
581 'MORE_PHOTO',
582 );
583 $exception = self::getApplication()->GetException();
584 if ($exception)
585 {
586 return $exception->GetString();
587 }
588
589 return '';
590 }
591
592 private function checkPermissionProductRead(array $product): Result
593 {
594 $r = $this->checkReadPermissionEntity();
595 if (!$r->isSuccess())
596 {
597 return $r;
598 }
599
600 return $this->checkPermissionProduct($product, self::IBLOCK_ELEMENT_READ, $this->getErrorCodeReadAccessDenied());
601 }
602
603 private function checkPermissionProductWrite(array $product): Result
604 {
605 $r = $this->checkModifyPermissionEntity();
606 if (!$r->isSuccess())
607 {
608 return $r;
609 }
610
611 return $this->checkPermissionProduct($product, self::IBLOCK_ELEMENT_EDIT, $this->getErrorCodeModifyAccessDenied());
612 }
613
614 private function checkPermissionProduct(array $product, string $permission, int $errorCode): Result
615 {
616 $r = new Result();
617 if(!\CIBlockElementRights::UserHasRightTo($product['IBLOCK_ID'], $product['ID'], $permission))
618 {
619 $r->addError(new Error('Access Denied', $errorCode));
620 }
621
622 return $r;
623 }
624
626 {
627 $r = new Result();
628
629 if (!$this->accessController->check(ActionDictionary::ACTION_CATALOG_VIEW))
630 {
631 $r->addError($this->getErrorModifyAccessDenied());
632 }
633
634 return $r;
635 }
636
637 protected function checkReadPermissionEntity(): Result
638 {
639 $r = new Result();
640
641 if (
642 !$this->accessController->check(ActionDictionary::ACTION_CATALOG_READ)
643 && !$this->accessController->check(ActionDictionary::ACTION_CATALOG_VIEW)
644 )
645 {
646 $r->addError($this->getErrorReadAccessDenied());
647 }
648
649 return $r;
650 }
651}
$connection
Определения actionsdefinitions.php:38
$type
Определения options.php:106
static getApplication()
Определения controller.php:56
getAction(int $id, int $productId, \CRestServer $restServer=null)
Определения productimage.php:120
addAction(array $fields, array $fileContent, \CRestServer $restServer=null)
Определения productimage.php:158
listAction(int $productId, array $select=[], \CRestServer $restServer=null)
Определения productimage.php:43
deleteAction(int $id, int $productId)
Определения productimage.php:278
const TYPE_FILE
Определения propertytable.php:67
Определения error.php:15
Определения file.php:45
Определения rest.php:24
</td ></tr ></table ></td ></tr >< tr >< td class="bx-popup-label bx-width30"><?=GetMessage("PAGE_NEW_TAGS")?> array( $site)
Определения file_new.php:804
$fileContent
Определения file_property.php:47
$result
Определения get_property_values.php:14
$iblockId
Определения iblock_catalog_edit.php:30
$select
Определения iblock_catalog_list.php:194
$name
Определения menu_edit.php:35
exists($id)
Определения checkexists.php:30
trait CheckExists
Определения checkexists.php:8
Определения aliases.php:54
trait Error
Определения error.php:11
if( $daysToExpire >=0 &&$daysToExpire< 60 elseif)( $daysToExpire< 0)
Определения prolog_main_admin.php:393
</p ></td >< td valign=top style='border-top:none;border-left:none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;padding:0cm 2.0pt 0cm 2.0pt;height:9.0pt'>< p class=Normal align=center style='margin:0cm;margin-bottom:.0001pt;text-align:center;line-height:normal'>< a name=ТекстовоеПоле54 ></a ><?=($taxRate > count( $arTaxList) > 0) ? $taxRate."%"
Определения waybill.php:936
$error
Определения subscription_card_product.php:20
$fields
Определения yandex_run.php:501