Development of a 1C-Bitrix content personalization module

Our company is engaged in the development, support and maintenance of Bitrix and Bitrix24 solutions of any complexity. From simple one-page sites to complex online stores, CRM systems with 1C and telephony integration. The experience of developers is confirmed by certificates from the vendor.
Our competencies:
Development stages
Latest works
  • image_website-b2b-advance_0.png
    B2B ADVANCE company website development
    1173
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Website development for FIXPER company
    811
  • image_bitrix-bitrix-24-1c_development_of_an_online_appointment_booking_widget_for_a_medical_center_594_0.webp
    Development based on Bitrix, Bitrix24, 1C for the company Development of an Online Appointment Booking Widget for a Medical Center
    564
  • image_bitrix-bitrix-24-1c_mirsanbel_458_0.webp
    Development based on 1C Enterprise for MIRSANBEL
    745
  • image_crm_dolbimby_434_0.webp
    Website development on CRM Bitrix24 for DOLBIMBY
    655
  • image_crm_technotorgcomplex_453_0.webp
    Development based on Bitrix24 for the company TECHNOTORGKOMPLEKS
    976

Developing a Content Personalization Module for 1C-Bitrix

Personalization is showing different content to different users depending on their characteristics. To a new visitor — explaining product value. To a returning non-buyer — emphasizing benefits. To a customer — cross-selling related to the last order. Without personalization, everyone sees the same landing page. Bitrix has no tools for this — only user groups with access restriction, which is a completely different thing.

Data Model

Module vendor.personalize:

  • b_vendor_ps_rule — personalization rules: id, name, priority, conditions (JSON), action (JSON), is_active, created_at
  • b_vendor_ps_segment — audience segments: id, name, conditions (JSON), is_active
  • b_vendor_ps_user_segment — user segment membership: user_id, segment_id, assigned_at
  • b_vendor_ps_impression — impressions of personalized content: id, rule_id, user_id, session_id, element_id, created_at
  • b_vendor_ps_profile — behavior profile: user_id or session_id, data (JSON: viewed sections, categories, source, RFM-metrics)

Segmentation

Segments are defined by a set of conditions. Example conditions:

{
  "logic": "AND",
  "rules": [
    {"type": "visit_count",     "operator": ">=", "value": 3},
    {"type": "has_orders",      "operator": "=",  "value": false},
    {"type": "last_visit_days", "operator": "<=", "value": 7},
    {"type": "traffic_source",  "operator": "=",  "value": "google"}
  ]
}

This is the "warm users" segment — visited 3+ times in the last 7 days, came from search, haven't purchased yet.

Condition evaluation:

class SegmentEvaluator
{
    public function evaluate(array $conditions, PersonalityProfile $profile): bool
    {
        $logic = $conditions['logic'] ?? 'AND';
        $results = [];

        foreach ($conditions['rules'] as $rule) {
            $results[] = $this->evaluateRule($rule, $profile);
        }

        return $logic === 'AND'
            ? !in_array(false, $results)
            : in_array(true, $results);
    }

    private function evaluateRule(array $rule, PersonalityProfile $profile): bool
    {
        return match ($rule['type']) {
            'visit_count'     => $this->compare($profile->getVisitCount(), $rule['operator'], $rule['value']),
            'has_orders'      => $profile->hasOrders() === (bool)$rule['value'],
            'last_visit_days' => $this->compare($profile->getDaysSinceLastVisit(), $rule['operator'], $rule['value']),
            'traffic_source'  => $profile->getTrafficSource() === $rule['value'],
            'city_id'         => in_array($profile->getCityId(), (array)$rule['value']),
            default           => true,
        };
    }
}

Behavioral Profile

With each visit, the session/user profile is updated:

// Initialized in init.php via OnProlog event
PersonalityProfileCollector::collect([
    'page'           => $_SERVER['REQUEST_URI'],
    'referrer'       => $_SERVER['HTTP_REFERER'] ?? null,
    'utm_source'     => $_GET['utm_source'] ?? null,
    'iblock_section' => $APPLICATION->GetCurDir(), // current section
]);

Profile is stored in b_vendor_ps_profile as JSON and contains: visit counter, viewed categories, first and last visit date, first visit source, order presence flag.

Personalized Blocks

Component vendor:personalize.block — replacement for static content block:

// In page template:
$APPLICATION->IncludeComponent('vendor:personalize.block', '', [
    'ELEMENT_ID' => 'hero_cta', // block identifier on page
]);

The component finds an active personalization rule for the current user and renders appropriate content. Rule actions can be:

  • show_text — show text/HTML
  • show_iblock_element — show specific infoblock element
  • show_banner — show banner from b_vendor_ps_banner
  • redirect — redirect to URL

Segment Recalculation

Segment membership is not determined on every request — it's expensive. Agent recalculates segments at night:

// For each active segment — recalculate from b_vendor_ps_profile
// Result — INSERT/DELETE in b_vendor_ps_user_segment
SegmentCalculator::recalculate();

For new session users (unauthorized) — real-time evaluation based on session data.

Analytics

  • CTR of personalized blocks vs standard (comparison with control group)
  • User distribution across segments
  • Conversion by segments to orders

Development Timeline

Stage Duration
ORM-tables, segment and rule model 1 day
Behavioral profile, data collection 2 days
Segmentation condition evaluation 2 days
Segment recalculation agent 1 day
Personalized blocks component 2 days
Analytics and administrative interface 2 days
Testing 1 day

Total: 11 working days. Integration with ML recommendation models (collaborative filtering) — separate project.