$ cd bitrix $ COMPOSER=composer-bx.json composer install |
{
"require-dev": {
"symfony/console": "4.1.*"
},
"autoload": {
"psr-4": {
"Vendor\\": "../local/classes"
}
}
}
|
$ COMPOSER=composer-bx.json composer dump-autoload --optimize |
$res = \Bitrix\Iblock\ElementTable::getList(array(
"select" => array("ID", "NAME"),
"filter" => array("IBLOCK_ID" => 25, "ID" => 4584),
"order" => array("ID" => "ASC")
));
while ($arItem = $res->fetch()) {
$dbProperty = \CIBlockElement::getProperty(
$arItem['IBLOCK_ID'],
$arItem['ID'], array("sort", "asc"),
array()
);
while ($arProperty = $dbProperty->GetNext()) {
$arItem["PROPERTIES"][$arProperty['CODE']] = $arProperty;
}
echo "<pre>".print_r($arItem, true)."</pre>";
} |
$res = \Bitrix\Iblock\ElementTable::getList(array(
"select" => array("ID", "*"),
"filter" => array("IBLOCK_ID" => 25, "ID" => 4584),
"order" => array("ID" => "ASC")
));
while ($arItem = $res->fetch()) {
$propRes = \Bitrix\Iblock\ElementPropertyTable::getList(array(
"select" => array("ID", "*"),
"filter" => array("IBLOCK_ELEMENT_ID" => $arItem["ID"],),
"order" => array("ID" => "ASC")
));
while($prop = $propRes->Fetch())
{
$arItem["PROPERTIES"][$prop["IBLOCK_PROPERTY_ID"]] = $prop;
}
echo "<pre>".print_r($arItem, true)."</pre>";
} |
[888] => Array ( [ID] => 28357 [IBLOCK_PROPERTY_ID] => 482 [IBLOCK_ELEMENT_ID] => 4584 [VALUE] => 5485 [VALUE_TYPE] => text [VALUE_ENUM] => [VALUE_NUM] => 5485.0000 [DESCRIPTION] => ) |
\Bitrix\Main\Loader::includeModule('iblock');
$iblockId = 2;
$r = CIBlockElement::getList(array(),array('IBLOCK_ID'=>$iblockId),false,false,array('ID','IBLOCK_ID','IBLOCK_SECTION_ID'));
while($dt = $r->fetch()){
$ELEMENT_ID = $dt['ID'];
$db_old_groups = CIBlockElement::GetElementGroups($ELEMENT_ID, true);
$ar_new_groups = array();
while($ar_group = $db_old_groups->Fetch()){
$ar_new_groups[] = $ar_group["ID"];
}
if(!empty($ar_new_groups) && !in_array($dt['IBLOCK_SECTION_ID'],$ar_new_groups)){
CIBlockElement::SetElementSection($ELEMENT_ID, $ar_new_groups);
CIBlockElement::RecalcSections($ELEMENT_ID,$ar_new_groups[0]);
}
}
|
../www/ | s1 (основной сайт) ../www/en/ | s2 ../www/ua/ | s3 ../ext_www/site1/ | a1 (филиалы) ../ext_www/site1/en/ | a2 ../ext_www/site1/ua/ | a3 ../ext_www/site2/ | b1 ../ext_www/site2/en/ | b2 ../ext_www/site3/ua/ | b3 ../src/ ../composer.json ....... |

<?php
namespace MySpace\Helper;
use Bitrix\Main\Context;
use Bitrix\Main\SiteTable;
/**
* Class Site
* @package MySpace\Helper
*/
class Site
{
/**
* @var
*/
private $sDocRoot;
/**
* Site constructor.
*/
public function __construct()
{
$objServer = Context::getCurrent()->getServer();
$this->sDocRoot = $objServer->getDocumentRoot();
}
/**
* @return string
*/
public function getSiteID()
{
if (defined("SITE_ID")) {
switch (SITE_ID) {
case "en" :
case "ru" :
case "ua" :
return $this->getSiteIDByLang(SITE_ID);
break;
default :
return SITE_ID;
}
}
return "";
}
/**
* @param $sSiteLang
* @return string
*/
private function getSiteIDByLang($sSiteLang)
{
$rsSites = SiteTable::getList(
array(
"filter" => array(
"=DOC_ROOT" => $this->sDocRoot . "/",
"=LANGUAGE_ID" => $sSiteLang
)
)
);
if ($arSite = $rsSites->fetch()) {
return $arSite["LID"];
}
return "";
}
} |
...
/**
* init define constants for iblocks
*/
private static function initIBlockConstants()
{
$rsIBlock = \CIBlock::GetList(
array(),
array(
"=LID" => (new Site())->getSiteID(),
"CHECK_PERMISSIONS" => "N"
)
);
while ($arIBlock = $rsIBlock->Fetch()) {
switch ($arIBlock["IBLOCK_TYPE_ID"]) {
...
case "news":
self::define("IBLOCK_ID_NEWS", $arIBlock["ID"]);
break;
case "smi":
self::define("IBLOCK_ID_MONITORING_SMI", $arIBlock["ID"]);
break;
case "gallery":
self::define("IBLOCK_ID_GALLERY", $arIBlock["ID"]);
break;
case "materials_main":
self::define("IBLOCK_ID_MATERIARLS_MAIN", $arIBlock["ID"]);
break;
...
}
}
}
...
|
| Warning: stream_socket_client(): Peer certificate CN did not match expected |
| $context = stream_context_create( array( 'ssl' => array( 'verify_peer' => false, 'allow_self_signed' => true, ) ) ); $sockethandle = stream_socket_client(($ssl ? 'ssl://' : '').$host.':'.$port, $error_id, $error_msg, 10, STREAM_CLIENT_CONNECT, $context); |
| 'verify_peer' => false, 'verify_peer_name' => false, |
| $context = stream_context_create( array( 'ssl' => array( 'verify_peer' => false, 'allow_self_signed' => true, 'verify_peer' => false, 'verify_peer_name' => false, ) ) );$sockethandle = stream_socket_client(($ssl ? 'ssl://' : '').$host.':'.$port, $error_id, $error_msg, 10, STREAM_CLIENT_CONNECT, $context); |
use Symfony\Component\Console\Output\ConsoleOutput; $consoleOutput = new ConsoleOutput(); |
$consoleOutput->write('Сообщение без перехода на новую строку');
$consoleOutput->writeln('Сообщение c переходом на новую строку');
|
$consoleOutput->write('<info>Сообщение зеленого цвета</info>');
$consoleOutput->write('<error>Сообщение на красном фоне</error>');
|
use Symfony\Component\Console\Output\ConsoleOutput;
use Symfony\Component\Console\Formatter\OutputFormatterStyle;
....
$consoleOutput = new ConsoleOutput();
$outputStyle = new OutputFormatterStyle('blue', 'red'); //Синим по красному
$consoleOutput->getFormatter()->setStyle('blue-on-red', $outputStyle);
....
$consoleOutput->write('<blue-on-red>Синим по красному</blue-on-red>');
|
$logSection = $consoleOutput->section();
$statisticSection = $consoleOutput->section();
$iteratoration = 0;
while(true){
$logSection->writeln('Итерация ' . $iteratoration);
if ($iteratoration % 1000 === 0) {
// Последняя строка в консоле будет обновляться
$statisticSection->overwrite(($iteratoration / 1000) . ' тысяч итераций');
}
$iterator++;
}
|
/**
* Получаем все родительские секции, до текущей от корня
* @param $section_id
*/
function getParentSections($section_id){
$result = array();
$nav = CIBlockSection::GetNavChain(false, $section_id);
while($v = $nav->GetNext()) {
if($v['ID']) {
Bitrix\Main\Diag\Debug::writeToFile('ID => ' . $v['ID']);
Bitrix\Main\Diag\Debug::writeToFile('NAME => ' . $v['NAME']);
Bitrix\Main\Diag\Debug::writeToFile('DEPTH_LEVEL => ' . $v['DEPTH_LEVEL']);
$result[] = $v['ID'];
}
}
return $result;
} |
| //Обновляем св-во UF_BRAND_HBL секций товара if(is_array($arFields['IBLOCK_SECTION'])) { $sec = new CIBlockSection; Bitrix\Main\Diag\Debug::writeToFile("Секции товара => ".implode("|", $arFields['IBLOCK_SECTION'])); foreach ($arFields['IBLOCK_SECTION'] as $key=>$sec_id){ $sec_UF_BRAND_HBL_select_vals = array(); $rsSect = \CIBlockSection::GetList( array('ID' => 'DESC'), array('IBLOCK_ID' => $iblock_id, 'ID' => $sec_id), false, array('UF_BRAND_HBL') ); while($arSect = $rsSect->Fetch()) { //Bitrix\Main\Diag\Debug::writeToFile('$rsSect '); //Bitrix\Main\Diag\Debug::writeToFile($arSect); $sec_UF_BRAND_HBL_select_vals = $arSect['UF_BRAND_HBL']; } $sec_UF_BRAND_HBL_select_vals[] = $HBL_element['ID']; //ID елемента HBL(бренда) привязанного к товару $sec_UF_BRAND_HBL_select_vals = array_unique($sec_UF_BRAND_HBL_select_vals); if (!$sec->Update($sec_id, array('UF_BRAND_HBL' => $sec_UF_BRAND_HBL_select_vals))) { Bitrix\Main\Diag\Debug::writeToFile('Error ' . $sec->LAST_ERROR); } else { Bitrix\Main\Diag\Debug::writeToFile('Установили секции ' . $sec_id . ' значения для UF_BRAND_HBL:'); Bitrix\Main\Diag\Debug::writeToFile($sec_UF_BRAND_HBL_select_vals); } } } |
Изменение типов цен в зависимости от суммы коризны.
Если у одного из товаров отсутствует одна из цен то берется предыдущая для суммирования
Например если у товара "Гвоздь" есть все типы цен, а у товара "Молоток" есть только розница, то будет считать:
Крупный опт = Гвоздь (крупный опт) * кол-во + Молоток (розница) * кол-во
<?php
$eventManager = \Bitrix\Main\EventManager::getInstance();
$eventManager->addEventHandler("catalog", "OnGetOptimalPrice", ["amountForPrice", "onGetPriceEvent"]);
global $basketPrice;
$basketPrice = [];
class amountForPrice
{
/**
* [BASE ID: 1] при сумме заказа до 15 000 рублей
* [SALE_SMAL ID: 2] Мелкий опт от 15.000 руб.
* [SALE_MIDDLE ID: 4] Средний опт от 50.000 руб.
* [SALE_LARGE ID: 5] Крупный опт от 100.000 руб.
*/
// Проставляем ID типов цен
protected static $baseGroup = 1;
protected static $smallGroup = 2;
protected static $middleGroup = 4;
protected static $largeGroup = 5;
public function onGetPriceEvent($productID, $quantity = 1, $arUserGroups = [], $renewal = "N", $arPrices = array(), $siteID = false, $arDiscountCoupons = false)
{
// Через global, чтоб не вызывалось по несколько раз
// OnGetOptimalPrice вызывается для каждого товара
global $basketPrice;
if (empty($basketPrice)) {
$basketPrice = self::getBasketUser();
}
// Получаем доступные цены для товара
$resOptPrices = \Bitrix\Catalog\PriceTable::getList([
'filter' => ['=PRODUCT_ID' => $productID],
'select' => ['CATALOG_GROUP_ID', 'PRICE', 'CURRENCY'],
]);
while($optPrice = $resOptPrices->fetch()){
$arOptPrices[$optPrice['CATALOG_GROUP_ID']] = $optPrice;
}
$arPrice = self::getPrice($basketPrice, $arOptPrices);
$result = [
'PRICE' => [
"ID" => $productID,
'CATALOG_GROUP_ID' => $arPrice['group'],
'PRICE' => $arPrice['price'],
'CURRENCY' => $arPrice['currency'],
'ELEMENT_IBLOCK_ID' => $productID,
'VAT_INCLUDED' => "Y",
],
'DISCOUNT' => ['VALUE' => $discount, 'CURRENCY' => $arPrice['currency']],
];
return $result;
}
/**
* Функция смотрит цену корзины и выбирает подходящую цену
* Функции цен уже сам перебразывают цену в зависимости от доступности
*/
public function getPrice(array $sum, array $arPrices)
{
if ($sum[4] >= 100000) {
$result = self::priceLarge($arPrices);
} elseif ($sum[2] >= 50000) {
$result = self::priceMiddle($arPrices);
} elseif ($sum[1] >= 15000) {
$result = self::priceSmall($arPrices);
} else {
$result = self::priceBase($arPrices);
}
return $result;
}
public function priceLarge(array $arPrices)
{
if (empty($arPrices[self::$largeGroup])) {
$result = self::priceMiddle($arPrices);
} else {
$result = [
'group' => self::$largeGroup,
'price' => $arPrices[self::$largeGroup]['PRICE'],
'currency' => $arPrices[self::$largeGroup]['CURRENCY']
];
}
return $result;
}
public function priceMiddle(array $arPrices)
{
if (empty($arPrices[self::$middleGroup])) {
$result = self::priceSmall($arPrices);
} else {
$result = [
'group' => self::$middleGroup,
'price' => $arPrices[self::$middleGroup]['PRICE'],
'currency' => $arPrices[self::$middleGroup]['CURRENCY']
];
}
return $result;
}
public function priceSmall(array $arPrices)
{
if (empty($arPrices[self::$smallGroup])) {
$result = self::priceBase($arPrices);
} else {
$result = [
'group' => self::$smallGroup,
'price' => $arPrices[self::$smallGroup]['PRICE'],
'currency' => $arPrices[self::$smallGroup]['CURRENCY']
];
}
return $result;
}
public function priceBase(array $arPrices)
{
$result = [
'group' => 1,
'price' => $arPrices[self::$baseGroup]['PRICE'],
'currency' => $arPrices[self::$baseGroup]['CURRENCY']
];
return $result;
}
/**
* Получение суммы корзины текущего пользователя
* Получаем суммы всех типов цен
*/
public function getBasketUser()
{
$resultPrice = [];
$resultPrice[self::$baseGroup] = 0;
$resultPrice[self::$smallGroup] = 0;
$resultPrice[self::$middleGroup] = 0;
//$resultPrice[self::$largeGroup] = 0; // large мы не считаем т.к он уже не учавствует в выборке
$obBasket = \Bitrix\Sale\Basket::getList([
'filter' => [
'FUSER_ID' => \Bitrix\Sale\Fuser::getId(),
'LID' => SITE_ID,
'ORDER_ID' => NULL // Т.к корзина связана с заказами, то нам нужна корзина у которой нет заказа
],
'select' => ['QUANTITY', 'PRODUCT_ID', 'CAN_BUY', 'DELAY']
]);
while($arItem = $obBasket->Fetch()){
// Нам нужны только доступные товары остальные не считаем
if ($arItem['DELAY'] == 'N' && $arItem['CAN_BUY'] == 'Y') {
$resPrices = \Bitrix\Catalog\PriceTable::getList([
'filter' => ['=PRODUCT_ID' => $arItem['PRODUCT_ID']],
'select' => ['CATALOG_GROUP_ID', 'PRICE'],
]);
while ($price = $resPrices->fetch()) {
$arItem['PRICES'][$price['CATALOG_GROUP_ID']] = $price['PRICE'];
}
// Если вдруг нет у товара цены, то берем следующую т.к нам нужна именно сумма
$resultPrice[self::$middleGroup] += (($arItem['PRICES'][self::$middleGroup] ?? $arItem['PRICES'][self::$smallGroup] ?? $arItem['PRICES'][self::$baseGroup]) * $arItem['QUANTITY']);
$resultPrice[self::$smallGroup] += (($arItem['PRICES'][self::$smallGroup] ?? $arItem['PRICES'][self::$baseGroup]) * $arItem['QUANTITY']);
$resultPrice[self::$baseGroup] += ($arItem['PRICES'][self::$baseGroup] * $arItem['QUANTITY']);
}
}
return $resultPrice;
}
}
|
Github -
|| $typeID === 'hlblock' |
elseif($typeID === 'hlblock')
{
$entity = new \CUserTypeHlblock();
$dbResult = $entity->GetList($userField);
$items = array();
if(is_object($dbResult))
{
$count = 0;
while($ary = $dbResult->Fetch())
{
$items[$ary['ID']] = $ary["VALUE"];
if(++$count > 500)
{
break;
}
}
}
return array(
'params' => array('multiple' => 'Y'),
'items' => $items
);
} |
| Примечание. Обратите внимание, что в формировании списка стоит лимит на 500 записей, такой же лимит выставлен и для iblock_element, и для iblock_section. Это не есть хорошо, конечно же в идеале поле с большим количеством элементов, должно получать список элементов динамически с фильтрацией, но думаю и этого дождёмся. |
elseif($typeID === 'hlblock')
{
$entity = new \CUserTypeHlblock();
$dbResult = $entity->GetList($arUserField);
$items = array();
if(is_object($dbResult))
{
$count = 0;
while($ary = $dbResult->Fetch())
{
$items[$ary['ID']] = $ary["VALUE"];
if(++$count > 500)
{
break;
}
}
}
$arFilterFields[] = array(
'id' => $FIELD_NAME,
'name' => $fieldLabel,
'type' => 'list',
'params' => array('multiple' => 'Y'),
'items' => $items
);
continue;
} |
| Примечание. В этом файле также ставится лимит на количество записей для iblock_element и iblock_section, только теперь 200, но я оставил 500. |
//Класс установки модуля
class mycompany_component1 extends CModule{
//Пространство имён компонентов
public $MODULE_COMPONENTS_NAMESPACE = "mycompany";
//Компоненты модуля
public $arModuleComponents = ["component1"];
...
function UnInstallFiles()
{
...
//Удаление компонентов модуля из /local
foreach ($this->arModuleComponents as $componentName) {
if (strlen($componentName) > 0) {
$resDel=DeleteDirFilesEx("/local/components/" . $this->MODULE_COMPONENTS_NAMESPACE . '/' . $componentName . '/');
}
}
//TODO: удалять папку с компонентами, если она оказывается пустая
return true;
}
}
|
| Актуально для версии модуля Задачи <19.0.100 |
| Примечание. Очень был огорчён, что из настроек компонента search.title уже ничего не используется, другими словами на время решения проблемы отключить поиск по задачам можно было только закомментировав строки 951 и 952 в файле /bitrix/templates/bitrix24/components/bitrix/search.title/.default/script.js |
global $DB; |
$dbTIDs = $DB->Query($accessSql);
$arTIDs = [];
while($arTID = $dbTIDs->Fetch())
$arTIDs[] = $arTID['TASK_ID'];
$sql .= "T.ID IN (".implode(',', $arTIDs).")";
|
echo strtoupper("абвгд"); |
setlocale(LC_ALL, 'ru_RU.CP1251'); setlocale(LC_NUMERIC,'C'); |
locale -a | grep ru_RU |
localedef -c -i ru_RU -f CP1251 ru_RU.CP1251 |
locale -a | grep ru_RU |
service httpd restart |
Bitrix\Highloadblock\HighloadBlockTable::compileEntity(); |
CUserTypeManager::GetUserFields(); |
RegisterModuleDependences('main', 'OnUserTypeBuildList', 'my_module', '\My\UF\MyFieldType', 'GetUserTypeDescription', 9999); |
To begin with I would like to tell you that during Bitrix24 development many developers use Bitrix ORM implementation out fr om the box.
It is a quite convenient, fast and useful instrument.
However, there is a thing which you should always do if you want to use the ORM DataManager class. Describing table fields and relations are so typical, annoying especially if you have lots of different tables.
From my own perspective, I prefer to simplify that regular work. For these needs, I created the simple composer package wh ere implemented an extension of native Bitrix ORM DataManager.
Therefore, developers can spend more time for implementing business logic in the system. The main idea is to simplify the realization of the method — getMap() in the DataManager class. For installation, you can use the composer and include dependencies in init.php file.
| composer require sestrenskyi/orm |

Данный компонент наследует все возможности стандартного компонента news.list и превносит в него следующие улучшения:
Теперь в шаблон компонента можно добавить файл component_prolog.php. Данный файл будет автоматически подключен для данного шаблона перед выполнением основного тела компонента (component.php) и как следует до начала кеширования компонента. В файле будет доступна ссылка на экземпляр класса компонента ($this) и ссылка на свойство $this->arParams ($arParams). В шаблон .default папки компонента я этот файл уже добавил для примера. Также в этом файле будет доступен сформированный фильтр, если он был сформирован компонентом фильтра или собран из указанных параметров компонента (см. пункт 2).
Теперь появилась возможность задать фильтр для выборки по свойствам инфоблока прямо из формы редактирования компонента!!! (подобно тому, как программисты это делают, определяя ключ в массиве $GLOBALS перед подключением компонента). Для этого служит секция "Дополнительные параметры фильтрации для вывода элементов"