Я не знал что в настройках главного модуля есть такая опция »
Сохранять исходные имена загружаемых файлов», и врезультате все картинки на нашем сайте имеют вот такие некрасивые адреса:
/upload/iblock/00d/00d7e1282969b7830d06e7917ea9103b.jpg
Сам PHP код не сложный, но будет выполняться довольно продолжительное время, если изображений у вас много. Обратите внимание на значения $iblock_ids, $ob[‘IBLOCK_ID’]==2, $ob[‘IBLOCK_ID’]==28, ‘MORE_PICT’, ‘MORE_PICT_TORG ‘, их нужно заменить на свои значения.
В результате у вас получится что-то вроде
/upload/iblock/00d/name_element.jpg
CModule::IncludeModule("iblock"); $arSelect = Array("ID", "IBLOCK_ID", "NAME", "CODE", "PREVIEW_PICTURE", "DETAIL_PICTURE"); $iblock_ids = array(2,28); // ID инфоблоков, у которых будем менять имена картинок // Получаем имена элементов инфоблока, именно их и будет использовать для наших картинок $arFilter = Array("IBLOCK_ID"=>$iblock_ids, "INCLUDE_SUBSECTIONS"=>"Y"); $res = CIBlockElement::GetList(Array("ID"=>"ASC"), $arFilter, false, false, $arSelect); global $DB; while($ob = $res->GetNext()){ $new_code = CUtil::translit($ob['NAME'], "ru", array()); // транслитерация имени файла // Для картинки анонса if(!empty($ob['PREVIEW_PICTURE'])) { $first_pic_orig = CFile::GetPath($ob['PREVIEW_PICTURE']); $path_file = explode('/',$first_pic_orig); $first_pic = explode('.',$path_file[4]); $file_name = $first_pic[0]; // имя файла $file_exe = $first_pic[1]; // расширение файла $NEW_NAME_FILE = $path_file[0].'/'.$path_file[1].'/'.$path_file[2].'/'.$path_file[3].'/'.$new_code.'.'.$file_exe; // новое имя с полным путем if($first_pic_orig != $NEW_NAME_FILE){ // переименовываем файл rename($_SERVER['DOCUMENT_ROOT'].$first_pic_orig, $_SERVER['DOCUMENT_ROOT'].$NEW_NAME_FILE); // Исправляем значение в базе if(!empty($ob['PREVIEW_PICTURE'])) $DB->Query('UPDATE b_file SET FILE_NAME="'.$new_code.'.'.$file_exe.'",ORIGINAL_NAME="'.$new_code.'.'.$file_exe.'" WHERE ID='.$ob['PREVIEW_PICTURE']); } } // Для детальной картинки if(!empty($ob['DETAIL_PICTURE'])) { $first_pic_orig = CFile::GetPath($ob['DETAIL_PICTURE']); $path_file = explode('/',$first_pic_orig); $first_pic = explode('.',$path_file[4]); $file_name = $first_pic[0]; // имя файла $file_exe = $first_pic[1]; // расширение файла $NEW_NAME_FILE = $path_file[0].'/'.$path_file[1].'/'.$path_file[2].'/'.$path_file[3].'/'.$new_code.'.'.$file_exe; if($first_pic_orig != $NEW_NAME_FILE){ // переименовываем файл rename($_SERVER['DOCUMENT_ROOT'].$first_pic_orig, $_SERVER['DOCUMENT_ROOT'].$NEW_NAME_FILE); // Исправляем значение в базе if(!empty($ob['DETAIL_PICTURE'])) $DB->Query('UPDATE b_file SET FILE_NAME="'.$new_code.'.'.$file_exe.'",ORIGINAL_NAME="'.$new_code.'.'.$file_exe.'" WHERE ID='.$ob['DETAIL_PICTURE']); } } // Для дополнительных фото в свойстве элемента $arFilter_prop = Array("IBLOCK_ID"=>$iblock_ids, "ID"=>$ob['ID']); $res_prop = CIBlockElement::GetList(Array(), $arFilter_prop); $MORE_PICT_VALUE = array(); if ($ob_prop = $res_prop->GetNextElement()){ $arFields = $ob_prop->GetFields(); // поля элемента $arProps = $ob_prop->GetProperties(); // свойства элемента if($ob['IBLOCK_ID']==2) $MORE_PICT_VALUE = $arProps['MORE_PICT']['VALUE']; // 'MORE_PICT' - свойство, ['VALUE'] - значение для IBLOCK_ID = 2 if($ob['IBLOCK_ID']==28) $MORE_PICT_VALUE = $arProps['MORE_PICT_TORG']['VALUE']; // 'MORE_PICT_TORG' для IBLOCK_ID = 28 $more_pic = array(); if(!empty($MORE_PICT_VALUE) && count($MORE_PICT_VALUE)>0){ for($i=0;$i<count($MORE_PICT_VALUE);$i++){ $more_pic_orig[$i] = CFile::GetPath($MORE_PICT_VALUE[$i]); $path = explode('/',$more_pic_orig[$i]); $more_pic[$i] = explode('.',$path[4]); $file_name = $more_pic[$i][0]; // имя файла $file_exe = $more_pic[$i][1]; // расширение файла $NEW_NAME_FILE_MORE = $path[0].'/'.$path[1].'/'.$path[2].'/'.$path[3].'/'.$new_code.'_'.($i+1).'.'.$file_exe; // ($i+1) - порядковый номер картинки if($NEW_NAME_FILE_MORE!=$more_pic_orig[$i]){ rename($_SERVER['DOCUMENT_ROOT'].$more_pic_orig[$i], $_SERVER['DOCUMENT_ROOT'].$NEW_NAME_FILE_MORE); // переименуем файл // Исправляем значение в базе для IBLOCK_ID = 2 if($ob['IBLOCK_ID']==2) $DB->Query('UPDATE b_file SET FILE_NAME="'.$new_code.'_'.($i+1).'.'.$file_exe.'",ORIGINAL_NAME="'.$new_code.'_'.($i+1).'.'.$file_exe.'" WHERE ID='.$arProps['MORE_PICT']['VALUE'][$i]); // Исправляем значение в базе для IBLOCK_ID = 28 if($ob['IBLOCK_ID']==28) $DB->Query('UPDATE b_file SET FILE_NAME="'.$new_code.'_'.($i+1).'.'.$file_exe.'",ORIGINAL_NAME="'.$new_code.'_'.($i+1).'.'.$file_exe.'" WHERE ID='.$arProps['MORE_PICT_TORG']['VALUE'][$i]); } } } } } |
У пары товаров не сложно у вручную изменить цену, а если товаров несколько сотен, да еще и раскиданных по десяткам разделов, тут на помощь придёт простая форма для выгрузки из Excel, CSV.
Сама выгрузка предельно простая. 2 столбца: Артикул и Цена
Сохранить файл выгрузки нужно как CSV (MS-DOS)
Форма загрузки на сайт, с краткой памяткой:
<form enctype="multipart/form-data" action="" method="POST" class="form_csv"> <input type="hidden" name="MAX_FILE_SIZE" value="30000"> Если через Excel, то должны быть только 2 столбца: <u>Артикул</u> и <u>Цена</u>.<br> Файл должен быть сохранен как <b>CSV (MS-DOS):</b><br><br> ID инфоблока, где лежат товары: <input name="find_section_section" type="text" value=""> <br> <input name="userfile" type="file" accept=".csv"> <input type="submit" value="Изменить цены"> </form> <br> Скорость обработки: ~ 30 цен за 1 секунду |
Сам PHP код:
if($_FILES['userfile']){ if ($_FILES['userfile']['tmp_name']){ echo "ID - ARTIKUL - PRICE - RESULT<br>"; $row = 1; $prop_artic = 275; // ID свойства товара, отвечающего за артикул, у вас будет другое число, см. в настройках инфоблока if (($handle = fopen($_FILES['userfile']['tmp_name'], "r")) !== FALSE){ while(($data = fgetcsv($handle, 4000, ";")) !== FALSE){ $num = count($data); $row++; $artikul = trim($data[0]); // артикул из файла $price = trim($data[1]); $price = str_replace(" ", "", $price); $price = str_replace(",", ".", $price); // цена из файла, убираем пробелы, а копейки отделяем через точку $iblock_id = $_POST['find_section_section']; $res_hl = $DB->Query("SELECT * FROM b_iblock_element_property WHERE IBLOCK_PROPERTY_ID=".$prop_artic." AND VALUE='".$artikul."'"); // для декорико $result_db = $res_hl->result->num_rows; if($result_db>0){ while ($this_val = $res_hl->Fetch()) { $ELEMENT_ID = $this_val["IBLOCK_ELEMENT_ID"]; } // Ищем цены в базе $res_chek = $DB->Query("SELECT * FROM b_catalog_price WHERE PRODUCT_ID=".$ELEMENT_ID); if($res_chek->result->num_rows == 0){ // Добавляем если нет в базе $arFields = Array( "PRODUCT_ID" => $ELEMENT_ID, "CATALOG_GROUP_ID" => 1, "PRICE" => $price, "CURRENCY" => "RUB" ); CPrice::Add($arFields); } else { if($res_chek->result->num_rows == 2 || $res_chek->result->num_rows == 3 || $res_chek->result->num_rows == 4){ // если цены задвоились, то их сначала удаляем старые данные, потом добавляем/обновляем цены $res_del = $DB->Query("SELECT MIN(ID) FROM b_catalog_price WHERE PRODUCT_ID=".$ELEMENT_ID.""); while ($pr = $res_del ->Fetch()) { $min_id = (int)$pr["MIN(ID)"]; $DB->Query("DELETE FROM b_catalog_price WHERE ID=".$min_id.""); } } // Изменяем цены, если есть в базе $DB->Query("UPDATE b_catalog_price SET PRICE='".$price."',PRICE_SCALE='".$price."' WHERE PRODUCT_ID=".$ELEMENT_ID); } // ДАЛЕЕ РАБОТАЕТ С ТОРГОВЫМИ ПРЕДЛОЖЕНИЯМИ $arSKU = 0; $arSKU = CCatalogSKU::getOffersList($ELEMENT_ID, $iblock_id, array('ACTIVE' => 'Y'), array(), array()); // получаем торговые предложения товара, для нашего инфоблока $iblock_id по ID товара $ELEMENT_ID if(count($arSKU)>0) { // если есть торг. предложения foreach($arSKU as $item_s){ foreach($item_s as $item_sku) { $ar_res = CCatalogProduct::GetByID($item_sku['ID']); // получаем свойства торг. предложения // получаем цену и id цены $db_res = CPrice::GetList(array(),array("PRODUCT_ID" => $item_sku["ID"],"CATALOG_GROUP_ID" => "1")); if($ar_res_sku = $db_res->Fetch()){ $ar_res_sku["PRICE"]; } // делаем новую цену $res_chek = $DB->Query("SELECT * FROM b_catalog_price WHERE PRODUCT_ID=".$item_sku["ID"]); if($res_chek->result->num_rows == 0){ // Добавляем, если нет $arFields = Array( "PRODUCT_ID" => $item_sku["ID"], "CATALOG_GROUP_ID" => 1, "PRICE" => $price, "CURRENCY" => "RUB" ); CPrice::Add($arFields); } else { if($res_chek->result->num_rows == 2 || $res_chek->result->num_rows == 3 || $res_chek->result->num_rows == 4){ $res_del = $DB->Query("SELECT MIN(ID) FROM b_catalog_price WHERE PRODUCT_ID=".$item_sku["ID"].""); while ($pr = $res_del ->Fetch()) { $min_id = (int)$pr["MIN(ID)"]; $DB->Query("DELETE FROM b_catalog_price WHERE ID=".$min_id.""); } } // Изменяем, если есть $DB->Query("UPDATE b_catalog_price SET PRICE='".$price."',PRICE_SCALE='".$price."' WHERE PRODUCT_ID=".$item_sku["ID"]); } } } } echo $ELEMENT_ID." - ".$artikul." - новая цена: ".$price." <br>"; } } fclose($handle); } //echo "Файл корректен и был успешно загружен.\n"; } else { echo "Файл не выбран !\n"; } } |
Сегодня покажу как сделать форму для массовой выгрузки параметров товара Bitrix: длина, ширина, высота, вес, доступное количество из Excel (CSV) на сайт.
Изначально подготовим в Excel выгрузку, которая должна содержать 6 столбцов по порядку: Артикул, Длина, Ширина, Высота, Вес, Количество.
После заполнения сохраняем в формате csv (MS-DOS)
Создаем страницу и добавляем туда:
<h2>Выгрузка параметров Длина, Ширина, Высота, ВЕС через CSV: </h2> <form enctype="multipart/form-data" action="?file=csv" method="POST" class="form_csv"> <input type="hidden" name="file" value="csv"> <input type="hidden" name="lang" value="ru"> <input type="hidden" name="MAX_FILE_SIZE" value="30000"> <input name="userfile" type="file" accept=".csv"> <input type="submit" value="Загрузить"> </form> |
PHP код:
global $DB; if($_FILES['userfile'] && $_GET['file']=='csv'){ if ($_FILES['userfile']['tmp_name']){ $row = 1; $prop_artic = 275; // ID свойства товара, отвечающего за артикул, у вас будет другое число, смотрите в настройках инфоблока /bitrix/admin/iblock_admin.php?type=catalog&lang=ru if (($handle = fopen($_FILES['userfile']['tmp_name'], "r"))!==FALSE ){ while(($data = fgetcsv($handle, 4000, ";")) !== FALSE){ $num = count($data); $row++; if(mb_detect_encoding($data[0])!="UTF-8"){ // проверяем кодировку, лучше чтобы она была UTF-8 $artikul = trim(iconv(mb_detect_encoding($data[0]), "utf-8", $data[0])); } else $artikul = trim($data[0]); // Наши параметры: $dlina = trim($data[1]); $shirina = trim($data[2]); $visota = trim($data[3]); $ves = trim($data[4]); $dostupno = trim($data[5]); $val_ar = $DB->Query('SELECT * FROM b_iblock_element_property WHERE IBLOCK_PROPERTY_ID='.$prop_artic.' AND VALUE="'.$artikul.'"'); // находи ID товара по артикулу while ($arr_id_el = $val_ar->Fetch()) { if(!empty($arr_id_el['IBLOCK_ELEMENT_ID'])){ // если ID найден - устанавливаем параметры $DB->Query('UPDATE b_catalog_product SET WEIGHT='.$ves.', LENGTH='.$dlina.', WIDTH='.$shirina.', HEIGHT='.$visota.' QUANTITY='.$dostupno.' WHERE ID='.$arr_id_el['IBLOCK_ELEMENT_ID']); echo $artikul." - успешно <br>"; } } } } } } |
Мало кто знает, что в Битриксе есть стандартные средства для отправки AJAX запросов, без использования jQuery или чистого JS.
BX.ajax({ url: '/include/page.php', data: { }, method: 'GET', timeout: 0, async: true, processData: true, scriptsRunFirst: true, emulateOnload: true, start: true, cache: true, onsuccess: function(data){ // выполняем в случае успеха }, onfailure: function(){ // в случае ошибки } }); |
В Битриксе есть стандартная js библиотека для создания таких окон, но почему-то она плохо задокументировано.
А ведь там все просто:
Подключаем JS библиотеку через php на странице:
CJSCore::Init(array('window')); |
Воздать окно с вызовом /include/page.php в нем:
var popup = new BX.CDialog({ 'title':'Выбрать товары', 'content_url':'/include/page.php', 'width':'550', 'height':'350' }); |
где content_url — ссылка на страницу, которая будет показана, вместо этого параметра можно использовать content — который выведет произвольное содержимое, width и height размеры окна
Показать окно:
popup.Show(); |
Закрыть окно:
popup.Close(); |
Изменить на лету содержимое окна:
popup.SetContent("html внутри окна") |
Изменять CSS стили окна можно как душе угодно.
Тут все просто, в документации функция CIBlockElement::SetPropertyValuesEx добавляет/обновляет свойство.
Есть момент, для свойств «список», нужно указывать id значения свойства,
а для свойств типа «справочник» нужно указывать символьный код значения.
Пример использования:
CIBlockElement::SetPropertyValuesEx($ID, false, array('SVOYSTVO' => 228)); |
где $ID — ид элемента, SVOYSTVO — символьный код свойства, 228 — значение свойства (т.к. у меня свойство список, указывает id нужного значения)
Команда замены в MySQL очень простая REPLACE.
Предположим, нам нужно найти во всех записях wp_posts слово «staroe» и заменить его на «novoe»
UPDATE `wp_posts` SET `post_content` = REPLACE(`post_content`, 'staroe', 'novoe') WHERE post_content LIKE "%staroe%"; |
где post_content — имя столбца (в нашем случае тело записи)
Функцию можно добавить в init.php и получить минимальную цену любого товара.
CModule::IncludeModule('catalog'); function GetOfferMinPrice($IBLOCK_ID,$item_id){ // получаем все торг. предложения $arSKU = CCatalogSKU::getOffersList($item_id, $IBLOCK_ID, array('ACTIVE' => 'Y'), array(), array()); // Если у товара есть торговые предложения if(count($arSKU)>0){ $arr_price = array(); foreach($arSKU as $item_s) { foreach($item_s as $item_sku) { // получаем цену, где 1 - тип цены $ret_sku = GetCatalogProductPrice($item_sku['ID'], 1); if ($ret_sku['PRICE']){ $arr_price[] = ceil($ret_sku['PRICE']); } } } $itog_price_sku = min($arr_price); } else { $ret_sku = GetCatalogProductPrice($item_id, 1); if ($ret_sku['PRICE']){ $itog_price_sku = ceil($ret_sku['PRICE']); } } return $itog_price_sku; } |
где, $IBLOCK_ID — ид инфоблока ,$item_id — ид товара с торговыми предложениями, если тп нет — вернет цену товара.
Пример вызова:
GetOfferMinPrice(2,10); |
Для добавления своего пункта меню в административный раздел Битрикс, нужно в init.php
добавить функцию:
AddEventHandler("main", "OnBuildGlobalMenu", "ChangeIblockMenu"); function ChangeIblockMenu(&$adminMenu, &$moduleMenu){ $moduleMenu[] = array( "parent_menu" => "global_menu_services", // поместим в раздел "Сервис" "section" => "change_iblock_elements", "sort" => 10, // сортировка пункта меню "url" => "change_iblock_elements.php?lang=".LANG, // ссылка на пункте меню "text" => 'Изменить категорию', // текст пункта меню "title" => 'Изменить категорию', // текст всплывающей подсказки "icon" => "fileman_menu_icon", // малая иконка "page_icon" => "fileman_page_icon", // большая иконка "items_id" => "change_iblock_elements", // идентификатор ветви "items" => array() // остальные уровни меню сформируем ниже. ); } |
где global_menu_services — параметр ID одного из глобальных разделов (в нашем случае Сервис), узнать можно через инспектор кода в dom html нужного раздела id=»***»;
change_iblock_elements — название нашего пункта меню в виде символьного кода;
«change_iblock_elements.php?lang=».LANG — путь к файлу php, все они должны храниться в /bitrix/admin/ ;
fileman_menu_icon — название класса css иконки рядом с пунктом меню, можно добавить свою или использовать с другого пункта меню (в нашем случае взяли в пункта «Структура сайта»)
Предположим у нас уже есть Ид торгового, сначало нужно получить Ид основного товара, а потом по нем все его торговые предложения.
Получаем ID основного товара:
$MainElenemt = CCatalogSku::GetProductInfo( $ID_SKU ); |
где $ID_SKU — id торгового предложения, а $MainElenemt[‘ID’] — id товара
Получаем все остальные предложения в виде массива:
$arSKU = CCatalogSKU::getOffersList($MainElenemt['ID'], $IBLOCK_ID, array('ACTIVE' => 'Y'), array(), array()); foreach($arSKU as $item_s) { foreach($item_s as $item_sku) { var_dump($item_sku); } } |
где $MainElenemt[‘ID’] — ид основного товара, $IBLOCK_ID — ид инфоблока, $item_sku — массив торговых предложений