D7 ORM: Reference, OneToMany и ManyToMany

Введение

ORM (Object-Relational Mapping) в 1С-Битрикс позволяет взаимодействовать с базой данных через объекты, упрощая разработку и делая код более читаемым. Одной из ключевых возможностей ORM является описание связей между сущностями. В этой статье мы разберем три типа отношений — oneToOne, oneToMany и manyToMany — и покажем их применение на практических примерах.

1. Отношение Reference (Один к одному / 1:1)

Что это?

Одна запись в таблице связана ровно с одной записью в другой таблице. Например, пользователь и его паспортные данные.

Реальный пример:

Представим интернет-магазин, где каждый пользователь (User) имеет единственный профиль доставки (DeliveryProfile).

Реализация в Битрикс ORM:

Сущность User:


namespace Local\ORM\Entity;

use Bitrix\Main\ORM\Fields\IntegerField;
use Bitrix\Main\ORM\Fields\Relations\Reference;

class UserTable extends \Bitrix\Main\ORM\Data\DataManager
{
    public static function getTableName()
    {
        return 'b_user';
    }

    public static function getMap()
    {
        return [
            (new IntegerField('ID'))
                ->configurePrimaryKey(),
            
            // Связь 1:1 с DeliveryProfile
            (new Reference(
                'DELIVERY_PROFILE', // Название связи
                DeliveryProfileTable::class, // Целевая сущность
                \Bitrix\Main\ORM\Query\Join::on('this.ID', 'ref.USER_ID') // Ссылка на поле в DeliveryProfileTable (USER_ID → UserTable.ID)
             ))
                ->configureJoinType('inner')
        ];
    }
}

Сущность DeliveryProfile:


class DeliveryProfileTable extends \Bitrix\Main\ORM\Data\DataManager
{
    public static function getTableName()
    {
        return 'delivery_profiles';
    }

    public static function getMap()
    {
        return [
            (new IntegerField('ID'))
                ->configurePrimaryKey(),
            
            (new IntegerField('USER_ID')),
            
            // Обратная связь
            (new \Bitrix\Main\ORM\Fields\Relations\Reference(
                'USER',
                UserTable::class,
                \Bitrix\Main\ORM\Query\Join::on('this.USER_ID', 'ref.ID') // USER_ID в DeliveryProfileTable → ID в UserTable
            ))
        ];
    }
}

Как использовать:


$user = UserTable::getById(1)->fetchObject();
$deliveryProfile = $user->get('DELIVERY_PROFILE'); // Получаем профиль доставки

2. Отношение oneToMany (Один ко многим / 1:N)

Что это?

Одна запись главной сущности связана с несколькими записями зависимой сущности. Например, блог и его посты.

Реальный пример:

Система блогов, где один блог (Blog) содержит множество статей (Article).

Реализация в Битрикс ORM:

Сущность Blog:


class BlogTable extends \Bitrix\Main\ORM\Data\DataManager
{
    public static function getTableName()
    {
        return 'blogs';
    }

    public static function getMap()
    {
        return [
            (new IntegerField('ID'))
                ->configurePrimaryKey(),
            
            (new StringField('NAME')),
            
            // Связь oneToMany с Article
            (new \Bitrix\Main\ORM\Fields\Relations\OneToMany(
                'ARTICLES', // Название связи
                ArticleTable::class, // Целевая сущность
                'BLOG' // Ссылка на поле в ArticleTable (BLOG_ID → BlogTable.ID)
            ))
        ];
    }
}

Сущность Article:


class ArticleTable extends \Bitrix\Main\ORM\Data\DataManager
{
    public static function getMap()
    {
        return [
            (new IntegerField('ID'))
                ->configurePrimaryKey(),
            
            (new IntegerField('BLOG_ID')),
            (new StringField('TITLE')),
            
            // Связь с Blog
            (new \Bitrix\Main\ORM\Fields\Relations\Reference(
                'BLOG',
                BlogTable::class,
                \Bitrix\Main\ORM\Query\Join::on('this.BLOG_ID', 'ref.ID') // BLOG_ID в ArticleTable → ID в BlogTable
            ))
        ];
    }
}

Как использовать:


$blog = BlogTable::getById(1)->fetchObject();
$articles = $blog->get('ARTICLES'); // Все статьи блога
foreach ($articles as $article) {
    echo $article->get('TITLE');
}

3. Отношение manyToMany (Многие ко многим)

Что это?

Записи одной сущности могут быть связаны с несколькими записями другой сущности через промежуточную таблицу. Например, студенты и курсы.

Реальный пример:

Учебная платформа, где студенты (Student) могут записываться на множество курсов (Course), а курс включает много студентов. Реализация в Битрикс ORM:

Промежуточная таблица StudentCourse:


class StudentCourseTable extends \Bitrix\Main\ORM\Data\DataManager
{
    public static function getTableName()
    {
        return 'student_course';
    }

    public static function getMap()
    {
        return [
            (new IntegerField('STUDENT_ID'))
                ->configurePrimary(),
            
            (new IntegerField('COURSE_ID'))
                ->configurePrimary(),
            
            // Связи с Student и Course
            (new \Bitrix\Main\ORM\Fields\Relations\Reference(
                'STUDENT',
                StudentTable::class,
                \Bitrix\Main\ORM\Query\Join::on('this.STUDENT_ID', 'ref.ID')
            )),
            
            (new \Bitrix\Main\ORM\Fields\Relations\Reference(
                'COURSE',
                CourseTable::class,
                \Bitrix\Main\ORM\Query\Join::on('this.COURSE_ID', 'ref.ID')
            ))
        ];
    }
}

Сущности Student и Course:


class StudentTable extends \Bitrix\Main\ORM\Data\DataManager
{
    public static function getMap()
    {
        return [
            (new IntegerField('ID'))
                ->configurePrimaryKey(),
            
            (new StringField('NAME')),
            
            // Связь manyToMany через промежуточную таблицу
            (new \Bitrix\Main\ORM\Fields\Relations\ManyToMany(
                'COURSES', // Название связи
                CourseTable::class, // Целевая сущность (курсы)
            ))
            ->configureTableName(StudentCourseTable::getTableName())
            ->configureLocalPrimary('ID', 'C_STUDENT_ID') //указывает, как будет называться привязка к полю из первичного ключа текущей сущности
            ->configureLocalReference('C_STUDENT') // зададим имя референса к текущей сущности
            ->configureRemotePrimary('ID', 'C_COURCE_ID') // указывает, как будет называться привязка к полю из первичного ключа сущности партнера
            ->configureRemoteReference('C_COURCE') // зададим имя референса к сущности партнера
        ];
    }
}

class CourseTable extends \Bitrix\Main\ORM\Data\DataManager
{
    public static function getMap()
    {
        return [
            (new IntegerField('ID'))
                ->configurePrimaryKey(),
            
            (new StringField('NAME')),
            
            // Связь manyToMany через промежуточную таблицу
            (new \Bitrix\Main\ORM\Fields\Relations\ManyToMany(
                'STUDENTS', // Название связи
                StudentTable::class, // Целевая сущность (студенты)
            ))
            ->configureTableName(StudentCourseTable::getTableName())
            ->configureLocalPrimary('ID', 'C_COURCE_ID') //указывает, как будет называться привязка к полю из первичного ключа текущей сущности
            ->configureLocalReference('C_COURCE') // зададим имя референса к текущей сущности
            ->configureRemotePrimary('ID', 'C_STUDENT_ID') // указывает, как будет называться привязка к полю из первичного ключа сущности партнера
            ->configureRemoteReference('C_STUDENT') // зададим имя референса к сущности партнера
        ];
    }
}

Как использовать:


$student = StudentTable::getById(1)->fetchObject();
$courses = $student->get('COURSES'); // Все курсы студента
foreach ($courses as $course) {
    echo $course->get('NAME');
}

Советы и частые ошибки

  • Индексы в промежуточных таблицах: Для manyToMany всегда добавляйте индексы на поля связи (например, STUDENT_ID и COURSE_ID), чтобы ускорить выборки.
  • Каскадное удаление: Если запись главной сущности удаляется, определите поведение для зависимых данных. Например, в oneToMany можно использовать ->configureCascadeDeletePolicy(Bitrix\Main\ORM\Fields\Relations\CascadePolicy::FOLLOW).
  • Оптимизация запросов: Используйте ->addSelect('RELATION') при работе с getList(), чтобы избежать N+1 проблемы.

Заключение

Правильное использование отношений в Битрикс ORM делает код структурированным и эффективным. Реальные примеры, подобные описанным, помогут вам проектировать сложные системы с легкостью. Экспериментируйте, тестируйте и не забывайте документацию Битрикс!

© 2026 MB

Desing by mb4design