В качестве значения свойства иметь картинку с превью.
Это могут быть например фотки гостиницы на туристическом сайте или что-то подобное.
[spoiler]
Одно из предложений прозвучало так: хранить изображения в отдельном инфоблоке и показывать в компоненте связанные элементы.
Вот что удалось сделать за 1 1/2 часа на
AddEventHandler("iblock", "OnIBlockPropertyBuildList", array("CIBlockPropertyPicture", "GetUserTypeDescription")); AddEventHandler("iblock", "OnBeforeIBlockElementDelete", array("CIBlockPropertyPicture", "OnBeforeIBlockElementDelete")); class CIBlockPropertyPicture { function GetUserTypeDescription() { return array( "PROPERTY_TYPE" =>"E", "USER_TYPE" =>"Picture", "DESCRIPTION" =>"Картинка", "GetPropertyFieldHtml" =>array("CIBlockPropertyPicture", "GetPropertyFieldHtml"), "GetPublicViewHTML" =>array("CIBlockPropertyPicture", "GetPublicViewHTML"), "ConvertToDB" =>array("CIBlockPropertyPicture", "ConvertToDB"), //"GetPublicEditHTML" =>array("CIBlockPropertyPicture","GetPublicEditHTML"), //"GetAdminListViewHTML" =>array("CIBlockPropertyPicture","GetAdminListViewHTML"), //"CheckFields" =>array("CIBlockPropertyPicture","CheckFields"), //"ConvertFromDB" =>array("CIBlockPropertyPicture","ConvertFromDB"), //"GetLength" =>array("CIBlockPropertyPicture","GetLength"), ); } function GetPropertyFieldHtml($arProperty, $value, $strHTMLControlName) { $LINK_IBLOCK_ID = intval($arProperty["LINK_IBLOCK_ID"]); if($LINK_IBLOCK_ID) { $ELEMENT_ID = intval($value["VALUE"]); if($ELEMENT_ID) { $rsElement = CIBlockElement::GetList(array(), array("IBLOCK_ID" => $arProperty["LINK_IBLOCK_ID"], "ID" => $value["VALUE"]), false, false, array("ID", "PREVIEW_PICTURE", "DETAIL_PICTURE")); $arElement = $rsElement->Fetch(); if(is_array($arElement)) $file_id = $arElement["DETAIL_PICTURE"]; else $file_id = 0; } else { $file_id = 0; } if($file_id) { $db_img = CFile::GetByID($file_id); $db_img_arr = $db_img->Fetch(); if($db_img_arr) { $strImageStorePath = COption::GetOptionString("main", "upload_dir", "upload"); $sImagePath = "/".$strImageStorePath."/".$db_img_arr["SUBDIR"]."/".$db_img_arr["FILE_NAME"]; return '<label><input name="'.$strHTMLControlName["VALUE"].'[del]" value="Y" type="checkbox"> Удалить файл '.$sImagePath.'</label>' .'<input name="'.$strHTMLControlName["VALUE"].'[old]" value="'.$ELEMENT_ID.'" type="hidden">'; } } return '<input type="file" size="'.$arProperty["COL_COUNT"].'" name="'.$strHTMLControlName["VALUE"].'"/>'; } else { return "Ошибка настройки свойства. Укажите инфоблок в котором будут храниться картинки."; } } function GetPublicViewHTML($arProperty, $value, $strHTMLControlName) { $LINK_IBLOCK_ID = intval($arProperty["LINK_IBLOCK_ID"]); if($LINK_IBLOCK_ID) { $ELEMENT_ID = intval($value["VALUE"]); if($ELEMENT_ID) { $rsElement = CIBlockElement::GetList(array(), array("IBLOCK_ID" => $arProperty["LINK_IBLOCK_ID"], "ID" => $value["VALUE"]), false, false, array("ID", "PREVIEW_PICTURE", "DETAIL_PICTURE")); $arElement = $rsElement->Fetch(); if(is_array($arElement)) return CFile::Show2Images($arElement["PREVIEW_PICTURE"], $arElement["DETAIL_PICTURE"]); } } return ""; } function ConvertToDB($arProperty, $value) { $arResult = array("VALUE" => "", "DESCRIPTION" => ""); $LINK_IBLOCK_ID = intval($arProperty["LINK_IBLOCK_ID"]); if($LINK_IBLOCK_ID) { if( is_array($value["VALUE"]) && is_array($value["VALUE"]["error"]) && $value["VALUE"]["error"]["VALUE"] == 0 && $value["VALUE"]["size"]["VALUE"] > 0 ) { $arDetailPicture = array( "name" => $value["VALUE"]["name"]["VALUE"], "type" => $value["VALUE"]["type"]["VALUE"], "tmp_name" => $value["VALUE"]["tmp_name"]["VALUE"], "error" => $value["VALUE"]["error"]["VALUE"], "size" => $value["VALUE"]["size"]["VALUE"], ); $obElement = new CIBlockElement; $arResult["VALUE"] = $obElement->Add(array( "IBLOCK_ID" => $LINK_IBLOCK_ID, "NAME" => $arDetailPicture["name"], "DETAIL_PICTURE" => $arDetailPicture, ), false, false, true); } elseif( is_array($value["VALUE"]) && isset($value["VALUE"]["size"]) && !is_array($value["VALUE"]["size"]) && $value["VALUE"]["size"] > 0 ) { $arDetailPicture = array( "name" => $value["VALUE"]["name"], "type" => $value["VALUE"]["type"], "tmp_name" => $value["VALUE"]["tmp_name"], "error" => intval($value["VALUE"]["error"]), "size" => $value["VALUE"]["size"], ); $obElement = new CIBlockElement; $arResult["VALUE"] = $obElement->Add(array( "IBLOCK_ID" => $LINK_IBLOCK_ID, "NAME" => $arDetailPicture["name"], "DETAIL_PICTURE" => $arDetailPicture, ), false, false, true); } elseif($value["VALUE"]["del"]) { $obElement = new CIBlockElement; $obElement->Delete($value["VALUE"]["old"]); } elseif($value["VALUE"]["old"]) { $arResult["VALUE"] = $value["VALUE"]["old"]; } elseif(!is_array($value["VALUE"]) && intval($value["VALUE"])) { $arResult["VALUE"] = $value["VALUE"]; } } return $arResult; } function OnBeforeIBlockElementDelete($ELEMENT_ID) { $arProperties = array(); $rsElement = CIBlockElement::GetList(array(), array("ID" => $ELEMENT_ID), false, false, array("ID", "IBLOCK_ID")); $arElement = $rsElement->Fetch(); if($arElement) { $rsProperties = CIBlockProperty::GetList(array(), array("IBLOCK_ID" => $arElement["IBLOCK_ID"], "USER_TYPE" => "Picture")); while($arProperty = $rsProperties->Fetch()) $arProperties[] = $arProperty; } $arElements = array(); foreach($arProperties as $arProperty) { $rsPropValues = CIBlockElement::GetProperty($arElement["IBLOCK_ID"], $arElement["ID"], array( "EMPTY" => "N", "ID" => $arProperty["ID"], )); while($arPropValue = $rsPropValues->Fetch()) { $ID = intval($arPropValue["VALUE"]); if($ID > 0) $arElements[$ID] = $ID; } } foreach($arElements as $to_delete) { CIBlockElement::Delete($to_delete); } } } |
Что мы в итоге имеем:
1. Интерфейс редактирования элемента с возможностью добавления и удаления изображений.
2. При удалении элемента связанная с ним информация удаляется.
3. Поддержка компонент публичной части.
Инструкция по применению:
1. Этот код надо разместить в файле /bitrix/php_interface/init.php
2. Создать инфоблок для хранения изображений и в его настройках указать параметры генерации картинки предварительного просмотра из детальной (на вкладке "поля").
3. В инфоблоке "гостинницы" добавить свойство типа "Картинка" и в доп. настроках этого свойства указать созданный на первом шаге инфоблок. Не забудьте указать символьный код свойства.
4. Создать элемент и "поиграться" со значениям этого свойства.
5. В публичной части например в компоненте news в параметрах настройки списка элементов выбрать это свойство.
6. Пойти выпить кофе.
PS предлагаю дополнить и развить этот код с целью будущего включения в поставку продукта.
все отлично работает, мне как раз для доски объявлений и автосалона, где много фото в детальном.
а, например, в доске объявлений простому пользователю при подаче объявления это не доступно, название свойства появилось, а возможность загрузить фото - нет. Подскажите, что дополнительно надо сделать.
Fatal error: Cannot use string offset as an array in /var/www/bitrix/php_interface/init.php on line 679
Да, классно придумано, а главное удобно.
По самому подходу решения задачи выскажу свои против:
1. Создавать заранее тумбнейлы непрактично. Понимаешь это при первом же редизайне, когда картинок очень много и нужны другие размеры. Куда удобнее их создавать по месту, как это делается в блогах.
2. Организация хранения такого рода тумбнейлов в отдельных инфоблоках а) нерационально, б) требует лишних телодвижений (создать отдельный инфоблок).
3. Использован тип юзертайпа E (элемент), хотя фактически это файлы (F). Не исключено, что это вызовет дополнительные трудности. Например, для вывода в шаблоне всех связей с элементами, появляется дополнитльное условие "или", и теоретически это "или" появляется автоматом у всех.
4. Типоразмеров для тумбнейлов картинки бывает больше одного. И по моему скромному опыту, обычно нужно три. Т.е. негде хранить дополнительные изображения, а подключать сюда еще и свойства это уже чересчур.
5. Тумбнейлы бывают разных типов. Например, должны быть железно указанных размеров, и не важно прямоугольной или квадратной формы источник. Т.е. штатные средства ресайзинга в инфоблоках не подойдут. Ну, и ватремарки, ручное/визуальное масштабирование, рамки и многое многое другое.
В общем, если нужны обычные тумбнейлы (без сложных текстовых описаний, сортировок, участием в поиске и т.п.), то лучше просто загружать в обычное свойство файл большие картинки и уменьшать их как требуется в дизайне по месту вывода. Естественно, с сохранением результата на диск. Правда этот способ тоже не очень хорош, если на списочных страницах много элементов с большим количеством картинок и оригиналы очень большие - первая генерация страницы будет очень долгой, а на всяких "мастерхостах" может привести и к ауту.
Но причина написания комментария не в анализе подхода.
Я хотел бы поинтересоваться у Максима Смринова, почему был использован именно тип E, а не логичный для задачи тип F?
Вернее, догадываюсь почему, и в очередной раз бы молча прошел мимо, но здесь автор поста сам разработчик модуля инфоблоков, человек, у которого есть ключи от запертой двери.
В общем для себя и для других интересующихся хотел бы уточнить, тип E выбран потому что для типа F негде хранить настройки ресайзинга, и вообще сейчас нельзя для всех типов, кроме типа S, задать хоть какие-то кастомные настройки?
Если я прав, то сколько еще будет продолжаться такая ситуация? Не лучше ли довести до ума интерфейсы юзертайпов и раскрыть всех их потенциал? А потенциал там просто огромен.
Или в очередной раз таким вот примером вы показываете как все прекрасно и гибко, а партнеры не понятно чего ноют? Тогда пытаясь доказать, что это не так и параллельно отвечая на Ваше предложение дополнить и развить код с целью будущего включения в поставку внесу свои предложения:
- главное: перевести на тип F
- расширение функционала: добавить возможность для одной картинки делать несколько миниатюр
- расширение функционала: добавить возможность сортировки картинок и добавить поле для текствого описания
Создавать заранее тумбнейлы непрактично. Понимаешь это при первом же редизайне, когда картинок очень много и нужны другие размеры. Куда удобнее их создавать по месту, как это делается в блогах.
Можно пройтись скриптом и пережать заново, для других размеров. Делается один раз в пол года, тогда когда меняется дизайн.
Мне кажется что с автоматическим ресайзингом гемороя больше.
Например, если автообжималка, где в качестве параметра указывается размер:
тогда злоумышленник может положить сервер запросами этой картинки с разными размерами.
Например, если автообжималка, где в качестве параметра указывается размер:
Мне кажется что с автоматическим ресайзингом гемороя больше.
Если есть возможность, то посмотрите функцию CSocNetTools::InitImage(), может быть она натолкнет вас на мысли.
Я не стал счастливым обладателем модуля социальных сетей.
А сами размеры передаем не через адресную строку, а задаем прямо в компоненте или шаблоне.
Еще я немного не въеахал, эти картинки чистятся когда удаляешся кеш, или они постоянно скапливаются в папке upload?
"/".$uploadDirName."/resize_cache/"....
$uploadDirName - по умолчанию = /upload/
Я так понял что эта функция работает только для рисунков, которые хранятся в базе:
И файл сохраняется в
Может есть надежда, что когда удаляется элемент инфоблока, то удаляются и другие его картинки, которые хранятся в $file["SUBDIR"] ?
Пока я вижу что с таким подходом через пол года папка resize_cache хорошо распухнет, если, конечно, не повесить её автоочистку раз в месяц (И в это день сервер может лечь, пока перегенерит все картинки для всего сайта).
Сам подоход очень интересный, пожалуй, теперь во всех проектах буду использовать его.
Вот только с кешем кривовато получается.
Почему?
Если доп. картинки автоматом удаляются при удалении основного - то тогда проблем никаких.
Если не удалось посмотреть, то скажу вам, файлы удаляются при удалении основного.
Интересно, где вы это нашли? посмотрел фукнции CIblockElement::Delete() и CFile::Delete(), там не видно чтобы что-то удалялось из папки resize_cache.
Конечно все реализовано с большими костылями. Многое в работающей системе не устраивает. Так например хочется научить визуальный редактор на лету отображать картинки по вышеописаной схеме. Еще хочется в Битриксе расширить функционал для доп. свойств. Так например для привязки различных элементов к региону сделали ИБ с деревом регионов, а в свойствах элемента доп. поле с привязкой к разделу. Теперь чтобы например получить все новости из Федерального округа, нужно сначала перелопатить ИБ "Регионы" на предмет вложенных разделов, а потом искать в ИБ "Новости" по доп. свойству привзки к региону. Зато для своего ИБ - INCLUDE_SUBSECTIONS и все.
А с фотками вообще хотел реализовать штуку: льешь исходник в любом формате, а в папочке tmp создаются превьюшки нужных размеров с определенным временем жизни. Если долго их никто не смотрит они умирают, при повторном обращении создаются.
Почему-то в фотогалерее не реализовали работу с ICMP инфой из фоток. Да и форматов могли поболе сделать.
Вот. Наболело. Извините что возможны ламерские рассуждения, но я не так давно полез в Битрикс, с кризисом приходится осваивать новые профессии
В компонентах которые выводят вские вещи вроде связанных картинок и файлов (скриншоты программ, демонстрации и прочее) происходит следующее:
1 - получили для свойства типа F массив ID'ентификаторов файлов (обычно на один элемент ИБ может быть от 1 до 10 файлов: презентации, документы и.т.д).
Потом в цикле обходим его и вызываем CFile::GetFileArray или CFile::GetByID.
Почему бы не передавать массив идентификаторов? Тогда был бы один запрос к БД. Да, я понимаю что в GetByID используется CACHE_MANAGER и не каждый запрос к файлу порождает SQL-запрос.
Я сейчас посмотрел в модуле main в классе CAllFile появилась функция GetList
Судя по коду - это то о чём я спрашиваю. В
Т.е. если передать строкой, то всё ОК
$arFilesID = array("@ID" => '38078, 38080');
Странно как то, приходится конверить массив перед передачей в функцию.
Это так задумано?
$rcsFiles = CFile::GetList(false, $arFilesID, false);
И @ID в плейсхолдере сборщика запросов. Это тоже что то новое для пользовательского API
1 - $arOrder = Array(), - в коде жёстко задана сортировка
"FROM b_file f ".
$strSqlSearch." ".
"ORDER BY f.ID ASC";
3 - $arParams = Array() - в теле функции его нет вообше похоже.
Состав полей возвращаемых CFile::GetFileArray и CFile::GetList различается.
CFile::GetFileArray добавляет ещё поле SRC, осуществляя сборку полного пути до этого файла с учётом наличия константы BX_IMG_SERVER.
$src = "/".(COption::GetOptionString("main", "upload_dir", "upload"))."/".$arFile["SUBDIR"]."/".$arFile["FILE_NAME"];
$src = str_replace("//","/",$src);
if(defined("BX_IMG_SERVER"))
$src = BX_IMG_SERVER.$src;
$arReturn = $arFile + Array("SRC" => $src);
CFile::GetList не осуществляет этого возвращая просто объект CDBResult
Как устаканится, думаю нужно будет написать в документации, что бы не позабыли это сделать тогда сами.
Хотя да, думаю каждый кто использует CFile::GetList напишет свою обёртку, что бы возвращать массивы уже, а не объект и фетчить его.
Константа BX_IMG_SERVER используется если все ресурсы хранятся на другом сервере?
в доках про него ничего
доб.: наврал про побитые, но имен нету
Но есть 2 замечания. Для того чтобы она правильно отработала нужно явно указать высоту и ширину:
У меня так и не получилось обжать картинку до ширины 100 пикселей, а чтобы высота вычислась автоматически пропорционально.
В дизайне обычно такие картинки имеют либо одинаковую ширину, либо одинаковую высоту. И реже нужно чтобы была заданная и высота и ширина.
Потом, рисунки jpg обжимаются в ужасном качестве, с заметным муаром. Так как по умолчанию качество стоит 75, а нужно 100.
На сервере библиотека GD последней версии.
А так функция действительно очень полезная, хорошо бы её включить в документацию. Обжатие происходит довольно быстро, у меня на сервере 10 картинок с размером 400х400 обжимаются до размера 100х100 за 0,2 сек. Так как это делается только один раз при первой генерации страницы, то выгода очевидна.
Ну, напишите свою функцию - быстрее и надежнее будет, и под ваши требования. Будут дорабатывать штатную или нет - вопрос весьма спорный. Для информации (и смешно и грустно становится), почти идентичный функционал обработки картинок присутсвует в пяти модулях: в блогах, в инфоблоках, в главном модуле, в фотогалерее и в соц.сетях. В общем, каждый разработчик модуля пишет данный функционал под себя. Вот такой вот фреймворк получается...
это называется с пылесоса делать ракету
в чем может быть причина?
Собираюсь использовать в нескольких проектах - не охота обновлять данный "хак" в /bitrix/php_interface/init.php после каждого обновления БУСа.
init.php не обновляется. Этот файл специально для таких случаев.
Попробовал. Просто отлично! То что нужно.
Но вот как бы этим фоткам еще и заголовок при загрузке прописывать? Куда?
Элементы по умолчанию получают название картинки типа IMG1236.jpg - дословно, из названия файла...
Что-то типа принудительного "называния" элементов инфоблока с картинками не помешало бы.
Где бы это "подкрутить"?
В
В методе ConvertToDB передать в NAME соотв. значение из формы. Не забываем, что это поле обязательное и не может быть пустым. Поэтому надо предусмотреть "дефолтное" значение.
Все.
Мне никак не удается в шаблоне получить отдельно значения пути к картинке, детальной картинке и их дескрипшену. По умолчанию выводится что-то наподобие
CFile::GetPath, который выручал в большинстве случаев, тут, ясно, не помогает... Чем воспользоваться? CFile::GetFileArray ?
Пока просто вывожу и кое-как размещаю стилями, но это же не выход:
CIBlockElement::GetList(array("SORT"=>"ASC",array("ID"=>$value),false,false,array("ID", "NAME", "DETAIL_PICTURE", "PREVIEW_PICTURE") + Fetch и будет счастье.
Понимаю, что и так разжевали по самое не могу, но - пожалуйста - еще раз - последний - хоть чуть поподробнее. Дальше уж возможно разберусь.
Или подскажите где формируется код
Я правильно понял, то есть писать свое свойство?
проверку ID свойства. Иначе удаляются не только связные картинки, а вообще все связные элементы
Вам надо заняться отладкой.
Уже на энном сайте использую этот ваш код, все всегда просто отлично.
Но, вот, проблема появилась:
на новом сайте периодически слетает привязка картинок к элементу.
Не удается уловить все закономерности, но одна выяснилась:
Привязка (в таблице b_iblock_element_property) совершенно точно слетает у элементов при массовом редактировании других свойств этих элементов прямо в списках инфоблока. Слетает только привязка у "элемента-родителя", все картинки остаются на своих местах... ну как-то так
Казалось бы, при чем здесь дополнительные картинки?
Разница с другими сайтами, на которых все ок, одна-единственная: все таблицы сайта в InnoDB, пока мне неясно важно это или нет.
Поможете? Кто как не автор может понять все подноготные своего кода?
- добавлена сортировка прямо в списке.
- добавлен вывод картинок в админке.