Поступила мне очень интересная задача: реализовать url-ы изображений к товарам от корня сайта в формате:
/#PRODUCT_CODE#.jpg - основное фото товара
/#PRODUCT_CODE#-preview.jpg - анонсовое фото товара для списка
/#PRODUCT_CODE#-X.jpg - X-овое дополнительное фото товара
Сначала задача от сео-шников показалась бредовой, но интересной :) После реализации - понял, на сколько такой вариант может быть полезным.
Вводные данные:
#PRODUCT_CODE# - символьный код товара. Он уникальный в рамках каталога товаров
Сайт развернут на типовом битриксовом веб-окружении BitrixVM актуальной версии.
Первая же проблема, с которой столкнулся - несуществующие пути к изображениям обрубаются на уровне nginx и на уровне php, понятное дело, нет возможности их обработать! Системный администратор сервера, на котором развернут сайт, долгое время искал пути решения данной задачи и пришел к очень простому выводу:
1. В файле /etc/nginx/bx/conf/bitrix_general.conf находим location вида:
location ~* \.(css|js|gif|png|jpg|jpeg|ico|ogg|ttf|woff|eot|otf|svg|woff2)$ { error_page 404 /404.html; expires 30d; }
2. Для изображений JPG выносим обработку отдельно и отключаем 404-ю ошибку:
location ~* \.(css|js|gif|png|ico|ogg|ttf|woff|eot|otf|svg|woff2)$ { error_page 404 /404.html; expires 30d; } location ~* \.(jpg|jpeg)$ { error_page 404 =200 /imgget.php?request_uri=$request_uri; expires 30d; }
В результате, в php коде используя $_GET["request_uri"] получаем путь к изображению, с которым уже можно дальше работать.
Далее, в файле-обработчике imgget.php делаем саму обработку получения изображений:
define("STOP_STATISTICS", true); define('NO_AGENT_CHECK', true); use Bitrix\Main\Application; use \Bitrix\Main\Data\Cache; require($_SERVER['DOCUMENT_ROOT'] . '/bitrix/modules/main/include/prolog_before.php'); function GetImagesByCode($code){ $IBLOCK_ID = 2; $cache = Cache::createInstance(); $cache_id=md5(serialize(['function'=>__FUNCTION__,'CODE'=>$code,'IBLOCK_ID'=>$IBLOCK_ID])); $cache_time = 60*60*24*7; $cache_dir = 'static_prod_images'; if(isset($_GET['clear_cache'])){ $cache_time = 0; $cache->clean($cache_id,$cache_dir); } $result = []; if($cache->initCache($cache_time,$cache_id,$cache_dir)){ $result = $cache->getVars(); } elseif ($cache->startDataCache() && \Bitrix\Main\Loader::includeModule('iblock')){ $arOrder = array("SORT" => "ASC"); $arFilter = array('IBLOCK_ID'=>$IBLOCK_ID,'ACTIVE'=>'Y','CODE'=>$code); $arSelectFields = array("ID", "ACTIVE", "NAME",'PREVIEW_PICTURE','DETAIL_PICTURE','IBLOCK_ID'); $rsElements = \CIBlockElement::GetList($arOrder, $arFilter, FALSE, FALSE, $arSelectFields); if ($arElement = $rsElements->GetNext()) { $arMorePhotos = []; $rs = CIBlockElement::GetProperty($arElement['IBLOCK_ID'],$arElement['ID'],"sort","asc", ['CODE'=>'MORE_PHOTO']); while ($ob = $rs->GetNext()){ $arMorePhotos[] = [ 'ID'=>intval($ob['VALUE']), 'SRC'=>\CFile::GetPath(intval($ob['VALUE'])), ]; } $result = [ 'PREVIEW_PICTURE'=>(intval($arElement['PREVIEW_PICTURE'])>0) ? [ 'ID'=>intval($arElement['PREVIEW_PICTURE']), 'SRC'=>\CFile::GetPath(intval($arElement['PREVIEW_PICTURE'])) ] : false, 'DETAIL_PICTURE'=>(intval($arElement['DETAIL_PICTURE'])>0) ? [ 'ID'=>intval($arElement['DETAIL_PICTURE']), 'SRC'=>\CFile::GetPath(intval($arElement['DETAIL_PICTURE'])) ] : false, 'MORE_PHOTO'=>(!empty($arMorePhotos)>0) ? $arMorePhotos : false, ]; } $cache->endDataCache($result); } return $result; } function GetImagesSrcSet($image) { $sizes = [ '0' => [100, 100, 50], '1920' => [300, 400, 80], '960' => [200, 267, 80], '480' => [200, 267, 80], ]; $result = []; foreach ($sizes as $key => $arSize) { if (isset($image['ID'])) { $renderImg = \CFile::ResizeImageGet( $image['ID'], array("width" => $arSize[0], "height" => $arSize[1]), BX_RESIZE_IMAGE_PROPORTIONAL, false, false, false, $arSize[2] ); if (!isset($renderImg['src']) || strlen($renderImg['src']) <= 0) { if (isset($image['SRC'])) { $renderImg = $image['SRC']; } else { $renderImg = \CFile::GetPath($image['ID']); } } else { $renderImg = $renderImg['src']; } $result[$key] = $renderImg; } } $result['set'] = implode(', ', [ $result['1920'] . ' 1920w', $result['960'] . ' 960w', $result['480'] . ' 480w', ]); return $result; } $requestedDir = current(explode('?',$_REQUEST['request_uri'])); $mode = 'full'; $arCodes = false; $Scale = false; if(preg_match('|-x([\d]+)\.jpg$|',$requestedDir,$matches)){ // Если в шаблоне зашито масштабирование фото $Scale = $matches[1]; $requestedDir = preg_replace('|-x([\d]+)\.jpg$|','.jpg',$requestedDir); } if(preg_match('|-preview\.jpg$|',$requestedDir)){ // если это ссылка на превью фото $mode = 'preview'; $requestedDir = str_replace('-preview','',$requestedDir); } elseif (preg_match('|-([\d]+)\.jpg$|',$requestedDir,$matches)){ // если указан порядковый номер $arCodes = [ [ 'num'=>false, 'code'=>current(explode('.jpg',substr($requestedDir,1))), 'mode'=>'full' ], [ 'num'=>$matches[1], 'code'=>preg_replace('|-([\d]+)\.jpg$|','',substr($requestedDir,1)), 'mode'=>'additional' ] ]; } $src = false; $photo_id = false; if(empty($arCodes)){ $code = current(explode('.jpg',substr($requestedDir,1))); $CodePhotos = GetImagesByCode($code); if(!empty($CodePhotos)){ if($mode=='full'){ if($CodePhotos['DETAIL_PICTURE']){ $src = $CodePhotos['DETAIL_PICTURE']['SRC']; $photo_id = $CodePhotos['DETAIL_PICTURE']['ID']; } elseif($CodePhotos['PREVIEW_PICTURE']){ $src = $CodePhotos['PREVIEW_PICTURE']['SRC']; $photo_id = $CodePhotos['PREVIEW_PICTURE']['ID']; } elseif (!empty($CodePhotos['MORE_PHOTO'])){ $src = $CodePhotos['MORE_PHOTO'][0]['SRC']; $photo_id = $CodePhotos['MORE_PHOTO'][0]['ID']; } else { $src = SITE_TEMPLATE_PATH.'/img/no_photo.jpg'; } } elseif($mode == 'preview') { if($CodePhotos['PREVIEW_PICTURE']){ $src = $CodePhotos['PREVIEW_PICTURE']['SRC']; $photo_id = $CodePhotos['PREVIEW_PICTURE']['ID']; } elseif($CodePhotos['DETAIL_PICTURE']){ // TODO: реализовать ресайз изображения $src = $CodePhotos['DETAIL_PICTURE']['SRC']; $photo_id = $CodePhotos['DETAIL_PICTURE']['ID']; } elseif (!empty($CodePhotos['MORE_PHOTO'])){ $src = $CodePhotos['MORE_PHOTO'][0]['SRC']; $photo_id = $CodePhotos['MORE_PHOTO'][0]['ID']; } else { $src = SITE_TEMPLATE_PATH.'/img/no_photo.jpg'; } } } } else { foreach ($arCodes as $arCode){ // может быть порядковый номер, а может быть часть символьного кода товара $mode = $arCode['mode']; $CodePhotos = GetImagesByCode($arCode['code']); if(!empty($CodePhotos)){ if($mode=='full'){ if($CodePhotos['DETAIL_PICTURE']){ $src = $CodePhotos['DETAIL_PICTURE']['SRC']; $photo_id = $CodePhotos['DETAIL_PICTURE']['ID']; } elseif($CodePhotos['PREVIEW_PICTURE']){ $src = $CodePhotos['PREVIEW_PICTURE']['SRC']; $photo_id = $CodePhotos['PREVIEW_PICTURE']['ID']; } elseif (!empty($CodePhotos['MORE_PHOTO'])){ $src = $CodePhotos['MORE_PHOTO'][0]['SRC']; $photo_id = $CodePhotos['MORE_PHOTO'][0]['ID']; } else { $src = SITE_TEMPLATE_PATH.'/img/no_photo.jpg'; } } elseif($mode == 'additional') { if (!empty($CodePhotos['MORE_PHOTO']) && !empty($CodePhotos['MORE_PHOTO'][(intval($arCode['num'])-1)])){ $src = $CodePhotos['MORE_PHOTO'][(intval($arCode['num'])-1)]['SRC']; $photo_id = $CodePhotos['MORE_PHOTO'][(intval($arCode['num'])-1)]['ID']; } } } } } if($src){ if($Scale){ $srcSet = GetImagesSrcSet(['ID'=>$photo_id,'SRC'=>$src]); if(isset($srcSet[$Scale])){ $src = $srcSet[$Scale]; } else { // тут делаю сжатие в квадрат, но можно внести правки в шаблон и вытягивать нужные размеры ... $renderImg = \CFile::ResizeImageGet( $photo_id, array("width" => $Scale, "height" => $Scale), BX_RESIZE_IMAGE_PROPORTIONAL, false, false, false ); if (!isset($renderImg['src']) || strlen($renderImg['src']) <= 0) { $renderImg = $src; } else { $renderImg = $renderImg['src']; } $src = $renderImg; } } header("Content-type: image/jpeg"); echo file_get_contents(Application::getDocumentRoot().$src); exit(); } else { Bitrix\Iblock\Component\Tools::process404( '', true, true, true, false ); }
Вот таким нехитрым способом получили пути к изображениям в нужном нам формате!
Разработка сайта
Подайте заявку на разработку сайта на базе готового решения от компании 1С-Битрикс или одного из партнеров компании. Максимально подробно опишите, чему будет посвящен сайт, если это интернет-магазин - что он будет продавать, нужна ли мультиязычность, будут ли разные типы цен (розница, опт, крупный опт), будет ли интеграция с 1С, будет ли выгрузка товаров на различные торговые площадки...
Сопровождение сайта
Вы можете подать заявку на сопровождение вашего сайта на базе 1С-Битрикс. Сопровождение включает в себя: проверка актуальности обновлений сайта, проверка актуальности резервной копии, консультации по сайту. Опишите в заявке, какие еще объемы планируются на сопровождении и на какой срок вы планируете заключить договор на сопровождение - мы подберем подходящий вам бюджет на сопровождение
Работы по сайту
Вы можете подать заявку на выполнение определенного объема работ по сайту. Опишите в заявке объем работ. Это может быть разработка какого-то нового функционала, доработки по имеющемуся функционалу, доработки под требования сео-специалистов. На основании заявки вам будет сформирован бюджет работ, а также названы сроки на выполнение тех или иных работ.