Связь кастомных таблиц - uniqcle/Bitrix GitHub Wiki
Настройки -> Производительность -> Таблицы
Настройки -> Инструменты -> SQL запрос
Таблица Books
create table books (
id INTEGER NOT NULL auto_increment PRIMARY KEY,
name VARCHAR(50),
text TEXT,
publish_date DATE,
ISBN VARCHAR(50),
author_id TINYINT(4),
publisher_id INT(11),
wikiprofile_id INT(11)
);
insert into books (id, name, text, publish_date, ISBN, author_id, publisher_id, wikiprofile_id) values (1, 'Vue.js и Laravel создание SPA приложений', 'Книга содержит подробное описание Vue.js - библиотеки JS', '2024-05-01', '978-5-1243-3454-1', 1, 1, 0);
insert into books (id, name, text, publish_date, ISBN, author_id, publisher_id, wikiprofile_id) values (2, 'Оно', 'Роман написанный в жанре ужасов', '1986-05-01', '978-5-1235-2435-2', 2, 2, 1);
insert into books (id, name, text, publish_date, ISBN, author_id, publisher_id, wikiprofile_id) values (3, 'Мгла', 'Книга вошла в десятку лучших произведений в жанре ужасов', '1980-05-01', '978-5-4366-4234-3', 2, 1, 3);
insert into books (id, name, text, publish_date, ISBN, author_id, publisher_id, wikiprofile_id) values (4, 'Космос', 'Научно-популярная книга охватывает широкий круг тем области антропологии, космологии, биологии.', '2017-05-01','978-5-3464-7534-4', 3, 1, 0);
insert into books (id, name, text, publish_date, ISBN, author_id, publisher_id, wikiprofile_id) values (5, 'Путеводитель по Node.js', 'нига содержит подробное описание технологии Node.js', '2017-05-01', '978-5-1453-6846-5', 1, 2, 0);
insert into books (id, name, text, publish_date, ISBN, author_id, publisher_id, wikiprofile_id) values (6, 'Кладбище домашних животных', 'Роман написанн о страшном месте.', '1983-05-01', '978-5-6785-8657-6', 1, 2, 2);
Таблица Authors
create table authors (
id INTEGER NOT NULL auto_increment PRIMARY KEY,
name VARCHAR(50)
);
insert into authors (id, name) values (1,'Эван Ю');
insert into authors (id, name) values (2, 'Стивен Кинг');
insert into authors (id, name) values (3, 'Карл Саган');
Таблица Publishers
create table publishers (
id INTEGER NOT NULL auto_increment PRIMARY KEY,
name VARCHAR(50)
);
insert into publishers (id, name) values (1,'Британская академия наук');
insert into publishers (id, name) values (2, 'Viking Press');
Таблица Stores
create table stores (
id INTEGER NOT NULL auto_increment PRIMARY KEY,
name VARCHAR(50)
);
insert into stores (id, name) values (1,'Книголюб');
insert into stores (id, name) values (2, 'Читайка');
Таблица book_publisher
create table book_publisher (
id INTEGER NOT NULL auto_increment PRIMARY KEY,
book_id INT(11),
publisher_id INT(11)
);
insert into book_publisher (id, book_id, publisher_id) values (1,1,1);
insert into book_publisher (id, book_id, publisher_id) values (2,2,2);
insert into book_publisher (id, book_id, publisher_id) values (3,2,2);
insert into book_publisher (id, book_id, publisher_id) values (4,2,2);
insert into book_publisher (id, book_id, publisher_id) values (5,4,2);
insert into book_publisher (id, book_id, publisher_id) values (7,6,2);
Таблица book_store
create table book_store (
id INTEGER NOT NULL auto_increment PRIMARY KEY,
book_id INT(11),
store_id INT(11)
);
insert into book_store (id, book_id, store_id) values (1,1,2);
insert into book_store (id, book_id, store_id) values (2,2,1);
insert into book_store (id, book_id, store_id) values (3,2,2);
Таблица wikiprofiles
create table wikiprofiles (
id INTEGER NOT NULL auto_increment PRIMARY KEY,
wikiprofile_ru VARCHAR(50),
wikiprofile_en VARCHAR(50),
book_id INT(11)
);
insert into wikiprofiles (id, wikiprofile_ru, wikiprofile_en, book_id) values (1,'https://ru.wikipedia.org/wiki/Оно_(роман)','https://en.wikipedia.org/wiki/It_(novel)',2);
insert into wikiprofiles (id, wikiprofile_ru, wikiprofile_en, book_id) values (2,'https://ru.wikipedia.org/wiki/Кладбище_домашних_животных_(роман','https://en.wikipedia.org/wiki/Pet_Sematary',6);
insert into wikiprofiles (id, wikiprofile_ru, wikiprofile_en, book_id) values (3,'https://ru.wikipedia.org/wiki/Туман_(повесть)','https://en.wikipedia.org/wiki/The_Mist_(novella)',3);
Таблица book_author
create table book_author (
id INTEGER NOT NULL auto_increment PRIMARY KEY,
book_id INT(11),
author_id INT(11)
);
insert into book_author (id, book_id, author_id) values (1,2,2);
insert into book_author (id, book_id, author_id) values (2,3,2);
insert into book_author (id, book_id, author_id) values (3,4,3);
Включаем галочку Разрешить генерацию таблетов для ORM
Настройки-Настройки продукта-Настройки модулей-Монитор производительности-Генератор таблетов-Разрешить генерацию таблетов для ORM
В контекстом меню таблицы генерируем ORM
Далее добавляем в модели.
/local/app/Models/BookTable.php
BookTable
namespace Models;
use Bitrix\Main\Localization\Loc;
use Bitrix\Main\ORM\Data\DataManager;
use Bitrix\Main\ORM\Fields\DateField;
use Bitrix\Main\ORM\Fields\IntegerField;
use Bitrix\Main\ORM\Fields\StringField;
use Bitrix\Main\ORM\Fields\TextField;
use Bitrix\Main\ORM\Fields\Validators\LengthValidator;
// также добавили пространства имен
use Bitrix\Main\ORM\Fields\Validator\Base,
Bitrix\Main\ORM\Fields\Validators\RegExpValidator,
Bitrix\Main\ORM\Fields\Relations\Reference,
Bitrix\Main\ORM\Fields\Relations\OneToMany,
Bitrix\Main\ORM\Fields\Relations\ManyToMany,
Bitrix\Main\Entity\Query\Join;
use Models\WikiprofileTable as Wikiprofile;
use Models\PublisherTable as Publisher;
use Models\AuthorTable as Author;
/**
* Class BookTable
* @package Models
**/
class BookTable extends DataManager
{
/**
* Returns DB table name for entity.
*
* @return string
*/
public static function getTableName()
{
return 'books';
}
/**
* Returns entity map definition.
*
* @return array
*/
public static function getMap()
{
return [
new IntegerField(
'id',
[
'primary' => true,
'autocomplete' => true,
'title' => Loc::getMessage('_ENTITY_ID_FIELD'),
]
),
new StringField(
'name',
[
'validation' => [__CLASS__, 'validateName'],
'title' => Loc::getMessage('_ENTITY_NAME_FIELD'),
]
),
new TextField(
'text',
[
'title' => Loc::getMessage('_ENTITY_TEXT_FIELD'),
]
),
new DateField(
'publish_date',
[
'title' => Loc::getMessage('_ENTITY_PUBLISH_DATE_FIELD'),
]
),
new StringField(
'ISBN',
[
'validation' => [__CLASS__, 'validateIsbn'],
'title' => Loc::getMessage('_ENTITY_ISBN_FIELD'),
]
),
new IntegerField(
'author_id',
[
'title' => Loc::getMessage('_ENTITY_AUTHOR_ID_FIELD'),
]
),
new IntegerField(
'publisher_id',
[
'title' => Loc::getMessage('_ENTITY_PUBLISHER_ID_FIELD'),
]
),
new IntegerField(
'wikiprofile_id',
[
'title' => Loc::getMessage('_ENTITY_WIKIPROFILE_ID_FIELD'),
]
),
// один к одному
(new Reference('WIKIPROFILE',
Wikiprofile::class,
Join::on('this.wikiprofile_id', 'ref.id')))
->configureJoinType('inner'),
// один ко многим. Одна книга, много издателей
(new Reference('PUBLISHER',
Publisher::class,
Join::on('this.publisher_id', 'ref.id')))
->configureJoinType('inner'),
// один ко многим
(new ManyToMany('AUTHORS', Author::class))
->configureTableName('book_author')
->configureLocalPrimary('id', 'book_id')
->configureLocalReference('BOOKS')
->configureRemotePrimary('id', 'author_id')
->configureRemoteReference('AUTHORS'),
];
}
/**
* Returns validators for name field.
*
* @return array
*/
public static function validateName()
{
return [
new LengthValidator(3, 50),
];
}
/**
* Returns validators for ISBN field.
*
* @return array
*/
public static function validateIsbn()
{
return
array(function($value) {
$clean = str_replace('-', '', $value);
if (preg_match('/[\d-]{13,}/', $clean))
{
return true;
}
else
{
return 'Код ISBN должен содержать 13 цифр.';
}
});
}
}
WikiprofileTable
namespace Models;
use Bitrix\Main\Localization\Loc;
use Bitrix\Main\ORM\Data\DataManager;
use Bitrix\Main\ORM\Fields\IntegerField;
use Bitrix\Main\ORM\Fields\StringField;
use Bitrix\Main\ORM\Fields\Validators\LengthValidator;
use Bitrix\Main\ORM\Fields\Relations\Reference,
Bitrix\Main\ORM\Fields\Relations\OneToMany,
Bitrix\Main\ORM\Fields\Relations\ManyToMany,
Bitrix\Main\Entity\Query\Join;
use Models\BookTable as Book;
/**
* Class Table
*
* Fields:
* <ul>
* <li> id int mandatory
* <li> wikiprofile_ru string(50) optional
* <li> wikiprofile_en string(50) optional
* <li> book_id int optional
* </ul>
*
* @package Bitrix\
**/
class WikiprofileTable extends DataManager
{
/**
* Returns DB table name for entity.
*
* @return string
*/
public static function getTableName()
{
return 'wikiprofiles';
}
/**
* Returns entity map definition.
*
* @return array
*/
public static function getMap()
{
return [
new IntegerField(
'id',
[
'primary' => true,
'autocomplete' => true,
'title' => Loc::getMessage('_ENTITY_ID_FIELD'),
]
),
new StringField(
'wikiprofile_ru',
[
'validation' => function()
{
return[
new LengthValidator(null, 50),
];
},
'title' => Loc::getMessage('_ENTITY_WIKIPROFILE_RU_FIELD'),
]
),
new StringField(
'wikiprofile_en',
[
'validation' => function()
{
return[
new LengthValidator(null, 50),
];
},
'title' => Loc::getMessage('_ENTITY_WIKIPROFILE_EN_FIELD'),
]
),
new IntegerField(
'book_id',
[
'title' => Loc::getMessage('_ENTITY_BOOK_ID_FIELD'),
]
),
(new Reference('BOOK',
Book::class,
Join::on('this.book_id', 'ref.id'))
)->configureJoinType('inner')
];
}
}
PublisherTable
namespace Models;
use Bitrix\Main\Localization\Loc;
use Bitrix\Main\ORM\Data\DataManager;
use Bitrix\Main\ORM\Fields\IntegerField;
use Bitrix\Main\ORM\Fields\StringField;
use Bitrix\Main\ORM\Fields\Validators\LengthValidator;
use Bitrix\Main\ORM\Fields\Relations\Reference,
Bitrix\Main\ORM\Fields\Relations\OneToMany,
Bitrix\Main\ORM\Fields\Relations\ManyToMany,
Bitrix\Main\Entity\Query\Join;
use Models\BookTable as Books;
/**
* Class PublisherTable
* @package Models
**/
class PublisherTable extends DataManager
{
/**
* Returns DB table name for entity.
*
* @return string
*/
public static function getTableName()
{
return 'publishers';
}
/**
* Returns entity map definition.
*
* @return array
*/
public static function getMap()
{
return [
new IntegerField(
'id',
[
'primary' => true,
'autocomplete' => true,
'title' => Loc::getMessage('_ENTITY_ID_FIELD'),
]
),
new StringField(
'name',
[
'validation' => function()
{
return[
new LengthValidator(null, 50),
];
},
'title' => Loc::getMessage('_ENTITY_NAME_FIELD'),
]
),
// Один издатель много книг
(new OneToMany('BOOKS',
Books::class,
'PUBLISHER')
)->configureJoinType('inner')
];
}
}
AuthorTable
namespace Models;
use Bitrix\Main\Localization\Loc;
use Bitrix\Main\ORM\Data\DataManager;
use Bitrix\Main\ORM\Fields\IntegerField;
use Bitrix\Main\ORM\Fields\StringField;
use Bitrix\Main\ORM\Fields\Validators\LengthValidator;
use Bitrix\Main\ORM\Fields\Relations\Reference,
Bitrix\Main\ORM\Fields\Relations\OneToMany,
Bitrix\Main\ORM\Fields\Relations\ManyToMany,
Bitrix\Main\Entity\Query\Join;
use Models\BookTable as Books;
/**
* Class Table
* @package Bitrix\
**/
class AuthorTable extends DataManager
{
/**
* Returns DB table name for entity.
*
* @return string
*/
public static function getTableName()
{
return 'authors';
}
/**
* Returns entity map definition.
*
* @return array
*/
public static function getMap()
{
return [
new IntegerField(
'id',
[
'primary' => true,
'autocomplete' => true,
'title' => Loc::getMessage('_ENTITY_ID_FIELD'),
]
),
new StringField(
'name',
[
'validation' => function()
{
return[
new LengthValidator(null, 50),
];
},
'title' => Loc::getMessage('_ENTITY_NAME_FIELD'),
]
),
(new ManyToMany('BOOKS', Books::class))
->configureTableName('book_author')
->configureLocalPrimary('id', 'author_id')
->configureLocalReference('AUTHORS')
->configureRemotePrimary('id', 'book_id')
->configureRemoteReference('BOOKS')
];
}
}
use Models\BookTable as Books;
// получем коллекцию книг
$collection = Books::getList([
'select' => [
'id',
'name',
'publish_date'
]
])->fetchCollection();
foreach ($collection as $key => $book) {
debug('название '.$book->getName(). ' дата выхода:' .$book->getPublishDate());
}
use Models\BookTable as Books;
use Models\WikiprofileTable as Wikiprofiles;
// отношение OneToOne
// выборка википрофиля со сороны книги
$book = Books::getByPrimary(3, [
'select' => [
'*',
'WIKIPROFILE'
]
])->fetchObject();
debug($book->getWikiprofile()->getWikiprofileRu());
/// Аналогично и из таблицы Wiki
$wikiprofile = Wikiprofiles::getByPrimary(3, [
'select' => [
'*',
'BOOK'
]
]) ->fetchObject();
debug($wikiprofile->getWikiprofileRu());
debug($wikiprofile->getBook()->getName());
debug($wikiprofile->getBook()->getPublishDate()->format("Y-m-d"));
В ORM BookTable
в методы getMap()
прописываем связь
// один к одному
(new Reference('WIKIPROFILE',
Wikiprofile::class,
Join::on('this.wikiprofile_id', 'ref.id')))
->configureJoinType('inner'),
А также в таблице WikiprofileTable
(new Reference('BOOK',
Book::class,
Join::on('this.book_id', 'ref.id'))
)->configureJoinType('inner')
У одного издателя много книг. У книги один издатель.
use Models\BookTable as Books;
use Models\PublisherTable as Publishers;
//////////////////////////////////////
// отношение OneToMany
//////////////////////////////////////
// Одна книга много издателей
// получем коллекцию книг и издателей
$collection = Books::getList([
'select' => [
'id',
'name',
'publish_date',
'publisher_id',
'PUBLISHER'
]
])->fetchCollection();
foreach ($collection as $key => $book) {
debug('название '.$book->getName().
' дата выхода:' .$book->getPublishDate().
' издатель:'.$book->getPublisher()->getName()
);
}
// Один издатель и много книг по нему
// Один издателя и книги по нему
$publisher = Publishers::getByPrimary(1, [
'select' => [
'*',
'BOOKS'
]
])->fetchObject();
foreach ($publisher->getBooks() as $book){
echo $book->getName();
}
В таблице BookTable
в методе getMap
прописываем связь
// один ко многим. Одна книга, много издателей
(new Reference('PUBLISHER',
Publisher::class,
Join::on('this.publisher_id', 'ref.id')))
->configureJoinType('inner'),
В таблице PublisherTable
в методе getMap
прописываем связь
// Один издатель много книг
(new OneToMany('BOOKS',
Books::class,
'PUBLISHER')
)->configureJoinType('inner')
use Models\BookTable as Books;
use Models\AuthorTable as Authors;
// отношение ManyToMany (если у книги несколько авторов)
// выборка книг со стороны автора
$author = Authors::getByPrimary(2, [
'select' => [
'*',
'BOOKS'
]
])->fetchObject();
foreach ($author->getBooks() as $book){
echo $book->getName().'<br/>';
}
// выборка авторов со сороны книги
// Одна книга - несколько авторов
$book = Books::getByPrimary(2, [
'select' => [
'*',
'AUTHORS'
]
])->fetchObject();
foreach ($book->getAuthors() as $author){
echo ' книга: '.$book->getName().' автор: '.$author->getName().'<br/>';
}
В таблице BookTable
прописываем связь через промежуточную таблицу book_author
// один ко многим
(new ManyToMany('AUTHORS', Author::class))
->configureTableName('book_author')
->configureLocalPrimary('id', 'book_id')
->configureLocalReference('BOOKS')
->configureRemotePrimary('id', 'author_id')
->configureRemoteReference('AUTHORS')
А в таблице AuthorTable
прописываем связь на таблицу BookTable
через промежуточную таблицу book_author
(new ManyToMany('BOOKS', Books::class))
->configureTableName('book_author')
->configureLocalPrimary('id', 'author_id')
->configureLocalReference('AUTHORS')
->configureRemotePrimary('id', 'book_id')
->configureRemoteReference('BOOKS')
use Bitrix\Main\Type;
use Models\BookTable as Books;
// добавление записи в таблицу books
$record = [
'name'=>'Жизнь замечательного человека',
'publish_date' => new Type\Date('1988-09-17', 'Y-m-d'),
'ISBN' =>'1234567891223'
];
$res = Books::add($record);
if(!$res->isSuccess()){
debug($res->getErrorMessages());
}
use Bitrix\Main\Type;
use Models\BookTable as Books;
// обновление записи в таблицу books
$record = [
'name'=>'Жизнь замечательного человека 2',
'publish_date' => new Type\Date('1988-09-17', 'Y-m-d'),
'ISBN' =>'1234567891223'
];
$res = Books::update(7, $record);
if(!$res->isSuccess()){
debug($res->getErrorMessages());
}
use Models\BookTable as Books;
$res = Books::delete(15);
if(!$res->isSuccess()){
debug($res->getErrorMessages());
}