Bitrix is a powerful enterprise CMS platform popular in Russia and Eastern Europe for building corporate websites, e-commerce platforms, and intranet solutions. While its learning curve is steep, mastering Bitrix development opens doors to enterprise projects with complex requirements. This guide covers essential Bitrix development patterns from a senior developer's perspective.
Why Bitrix
Bitrix offers compelling features for enterprise environments:
- All-in-One Platform: CMS, CRM, e-commerce in one system
- Information Blocks: Flexible content management structure
- Built-in Modules: Forums, blogs, e-commerce, workflows
- Enterprise Features: Clustering, caching, multi-site support
- Russian Market: Dominant CMS for the Russian enterprise sector
Understanding Information Blocks (Infoblocks)
Infoblocks are Bitrix's core data structure—think of them as configurable content types with properties, sections, and elements.
Structure Overview
Infoblock Type
└── Infoblock
├── Sections (categories)
└── Elements (content items)
└── Properties (custom fields)
Working with Infoblocks via API
<?php
// Include the Bitrix kernel
require_once($_SERVER["DOCUMENT_ROOT"] . "/bitrix/modules/main/include/prolog_before.php");
// Load the iblock module
CModule::IncludeModule("iblock");
// Get elements from the infoblock
$arFilter = array(
"IBLOCK_ID" => 5,
"ACTIVE" => "Y",
"SECTION_ID" => 10
);
$arSelect = array(
"ID",
"NAME",
"DETAIL_TEXT",
"PROPERTY_PRICE",
"PROPERTY_BRAND"
);
$res = CIBlockElement::GetList(
array("SORT" => "ASC"), // Order
$arFilter, // Filter
false, // Group
false, // Navigation
$arSelect // Select fields
);
while ($element = $res->GetNextElement()) {
$fields = $element->GetFields();
$props = $element->GetProperties();
echo $fields["NAME"] . ": " . $props["PRICE"]["VALUE"] . "<br>";
}
?>
Creating Infoblock Elements
<?php
CModule::IncludeModule("iblock");
$element = new CIBlockElement;
$arFields = array(
"IBLOCK_ID" => 5,
"IBLOCK_SECTION_ID" => 10,
"NAME" => "New Product",
"ACTIVE" => "Y",
"PREVIEW_TEXT" => "Short description",
"DETAIL_TEXT" => "Full product description",
"PROPERTY_VALUES" => array(
"PRICE" => 1999,
"BRAND" => "Samsung",
"FEATURES" => array("Feature 1", "Feature 2")
)
);
if ($elementId = $element->Add($arFields)) {
echo "Element created with ID: " . $elementId;
} else {
echo "Error: " . $element->LAST_ERROR;
}
?>
Copying Infoblock Properties and Forms
When you have multiple infoblocks of the same type and need to copy form configurations:
<?php
require_once($_SERVER["DOCUMENT_ROOT"] . "/bitrix/modules/main/bx_root.php");
require_once($_SERVER["DOCUMENT_ROOT"] . BX_PERSONAL_ROOT . "/modules/main/include/prolog_before.php");
if (!defined("B_PROLOG_INCLUDED") || B_PROLOG_INCLUDED !== true) die();
CModule::IncludeModule("iblock");
if (intval($_REQUEST["IBLOCK_ID_FORMS"]) > 0 && !empty($_REQUEST["IBLOCK_TYPE_ID"])) {
$masterIblockId = intval($_REQUEST["IBLOCK_ID_FORMS"]);
$iblockTypeId = $_REQUEST["IBLOCK_TYPE_ID"];
// Get master infoblock properties
$masterProperties = array();
$dbProperties = CIBlockProperty::GetList(
array("sort" => "asc", "name" => "asc"),
array("ACTIVE" => "Y", "IBLOCK_ID" => $masterIblockId)
);
while ($property = $dbProperties->GetNext()) {
$masterProperties[$property['CODE']] = 'PROPERTY_' . $property['ID'];
}
// Settings to copy
$settings = array(
array('name' => 'form_element_#ID#', 'group' => 'form'),
array('name' => 'form_section_#ID#', 'group' => 'form'),
array('name' => 'tbl_iblock_list_#MD5#', 'group' => 'list'),
array('name' => 'tbl_iblock_element_#MD5#', 'group' => 'list'),
);
// Get settings from master
foreach ($settings as $key => $value) {
$name = str_replace(
array('#ID#', '#MD5#'),
array($masterIblockId, md5($iblockTypeId . "." . $masterIblockId)),
$value['name']
);
$settings[$key]['value'] = CUserOptions::GetOption($value['group'], $name);
}
// Apply to all infoblocks of the same type
$dbResult = CIBlock::GetList(
array(),
array('=TYPE' => $iblockTypeId, '!ID' => $masterIblockId),
true
);
while ($iblock = $dbResult->Fetch()) {
// Map property IDs
$search = array();
$replacement = array();
$dbProps = CIBlockProperty::GetList(
array("sort" => "asc"),
array("ACTIVE" => "Y", "IBLOCK_ID" => $iblock['ID'])
);
while ($prop = $dbProps->GetNext()) {
if (!empty($masterProperties[$prop['CODE']])) {
$search[] = $masterProperties[$prop['CODE']];
$replacement[] = 'PROPERTY_' . $prop['ID'];
}
}
// Copy settings with property mapping
foreach ($settings as $value) {
$name = str_replace(
array('#ID#', '#MD5#'),
array($iblock['ID'], md5($iblock['IBLOCK_TYPE_ID'] . "." . $iblock['ID'])),
$value['name']
);
$newValue = array();
foreach ($value['value'] as $k => $string) {
$newValue[$k] = str_replace($search, $replacement, $string);
}
CUserOptions::DeleteOption($value['group'], $name);
CUserOptions::SetOption($value['group'], $name, $newValue, true);
}
}
echo "Forms copied successfully!";
}
?>
Forum Administration
Bitrix includes a built-in forum module. Here's how to manage forum content programmatically.
Delete All Messages from a User
Useful for removing spam:
<?php
// Configuration
$badUserId = 29541;
// Delete the user's messages
$deleteMessages = "DELETE FROM b_forum_message WHERE author_id = $badUserId";
// Update topic metadata after deletion
$updateTopics = "UPDATE b_forum_topic as t
LEFT JOIN (
SELECT
MAX(gm.id) as id,
gm.topic_id,
MAX(gm.post_date) as post_date
FROM b_forum_message as gm
GROUP BY topic_id
) as g ON t.id = g.topic_id
LEFT JOIN b_forum_message as m ON m.id = g.id
SET
t.last_message_id = g.id,
t.last_poster_id = m.author_id,
t.last_poster_name = m.author_name,
t.last_post_date = g.post_date,
t.abs_last_message_id = g.id,
t.abs_last_poster_id = m.author_id,
t.abs_last_poster_name = m.author_name,
t.abs_last_post_date = g.post_date
WHERE t.abs_last_message_id <> g.id";
// Connect using Bitrix configuration
include $_SERVER["DOCUMENT_ROOT"] . '/bitrix/php_interface/dbconn.php';
$db = mysqli_connect($DBHost, $DBLogin, $DBPassword, $DBName);
mysqli_set_charset($db, 'utf8');
mysqli_query($db, $deleteMessages);
mysqli_query($db, $updateTopics);
echo "Spam removed and topics updated.";
?>
Component Development
Bitrix uses components for reusable functionality.
Basic Component Structure
/local/components/mycompany/mycomponent/
├── .description.php # Component metadata
├── .parameters.php # Input parameters definition
├── component.php # Main logic
├── class.php # OOP component class
└── templates/
└── .default/
├── template.php
├── style.css
└── script.js
Component Class Example
<?php
// /local/components/mycompany/product.list/class.php
if (!defined("B_PROLOG_INCLUDED") || B_PROLOG_INCLUDED !== true) die();
use Bitrix\Main\Loader;
class ProductListComponent extends CBitrixComponent
{
protected function checkModules()
{
if (!Loader::includeModule('iblock')) {
throw new \Exception('Module iblock not installed');
}
}
protected function getProducts()
{
$products = array();
$filter = array(
"IBLOCK_ID" => $this->arParams["IBLOCK_ID"],
"ACTIVE" => "Y"
);
if (!empty($this->arParams["SECTION_ID"])) {
$filter["SECTION_ID"] = $this->arParams["SECTION_ID"];
}
$res = \CIBlockElement::GetList(
array("SORT" => "ASC"),
$filter,
false,
array("nPageSize" => $this->arParams["PAGE_SIZE"]),
array("ID", "NAME", "PREVIEW_PICTURE", "DETAIL_PAGE_URL")
);
while ($element = $res->GetNextElement()) {
$fields = $element->GetFields();
$fields["PROPERTIES"] = $element->GetProperties();
$products[] = $fields;
}
return $products;
}
public function executeComponent()
{
try {
$this->checkModules();
$this->arResult["PRODUCTS"] = $this->getProducts();
$this->includeComponentTemplate();
} catch (\Exception $e) {
ShowError($e->getMessage());
}
}
}
?>
D7 (Modern API)
Bitrix D7 is the modern ORM-based API:
<?php
use Bitrix\Main\Loader;
use Bitrix\Iblock\ElementTable;
Loader::includeModule('iblock');
// Query with D7
$elements = ElementTable::getList(array(
'select' => array('ID', 'NAME', 'IBLOCK_ID'),
'filter' => array(
'=IBLOCK_ID' => 5,
'=ACTIVE' => 'Y'
),
'order' => array('SORT' => 'ASC'),
'limit' => 10
));
while ($element = $elements->fetch()) {
echo $element['NAME'] . "<br>";
}
// Use DataManager for custom tables
use Bitrix\Main\Entity;
class MyCustomTable extends Entity\DataManager
{
public static function getTableName()
{
return 'my_custom_table';
}
public static function getMap()
{
return array(
'ID' => new Entity\IntegerField('ID', array(
'primary' => true,
'autocomplete' => true
)),
'NAME' => new Entity\StringField('NAME', array(
'required' => true
)),
'CREATED_AT' => new Entity\DatetimeField('CREATED_AT')
);
}
}
?>
Event Handlers
Register handlers for Bitrix events:
<?php
// /local/php_interface/init.php
AddEventHandler("iblock", "OnBeforeIBlockElementAdd", "MyOnBeforeElementAdd");
AddEventHandler("iblock", "OnAfterIBlockElementAdd", "MyOnAfterElementAdd");
function MyOnBeforeElementAdd(&$arFields)
{
// Validate or modify before save
if (empty($arFields["NAME"])) {
global $APPLICATION;
$APPLICATION->ThrowException("Name is required");
return false;
}
// Auto-generate code from name
$arFields["CODE"] = CUtil::translit(
$arFields["NAME"],
"ru",
array("replace_space" => "-", "replace_other" => "-")
);
return true;
}
function MyOnAfterElementAdd(&$arFields)
{
// Log or notify after save
CEventLog::Add(array(
"SEVERITY" => "INFO",
"AUDIT_TYPE_ID" => "IBLOCK_ELEMENT_ADD",
"MODULE_ID" => "iblock",
"ITEM_ID" => $arFields["ID"],
"DESCRIPTION" => "Element added: " . $arFields["NAME"]
));
}
?>
Caching
Bitrix provides powerful caching mechanisms:
<?php
$cacheTime = 3600; // 1 hour
$cacheId = "my_cache_" . $IBLOCK_ID . "_" . $SECTION_ID;
$cachePath = "/my_component/";
$cache = new CPHPCache();
if ($cache->InitCache($cacheTime, $cacheId, $cachePath)) {
// Get from cache
$vars = $cache->GetVars();
$arResult = $vars["arResult"];
} elseif ($cache->StartDataCache()) {
// Generate data
$arResult = getExpensiveData();
// Save to cache
$cache->EndDataCache(array("arResult" => $arResult));
}
?>
Key Takeaways
- Learn Infoblocks: Core of Bitrix content management
- Use the D7 API: Modern, ORM-based approach
- Component architecture: Reusable functionality units
- Event-driven: Extend behavior via handlers
- Cache aggressively: Essential for performance
- Database knowledge: Direct SQL is sometimes necessary
Bitrix has a steep learning curve but rewards investment with powerful enterprise capabilities—master these fundamentals and you'll handle complex Bitrix projects confidently.