1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace BitrixToolkit\BitrixEntityMapper; |
4
|
|
|
|
5
|
|
|
use CIBlock; |
6
|
|
|
use CIBlockProperty; |
7
|
|
|
use CSite; |
8
|
|
|
use InvalidArgumentException; |
9
|
|
|
use BitrixToolkit\BitrixEntityMapper\Annotation\Entity\InfoBlock; |
10
|
|
|
use BitrixToolkit\BitrixEntityMapper\Annotation\Property\Property; |
11
|
|
|
use BitrixToolkit\BitrixEntityMapper\Map\EntityMap; |
12
|
|
|
|
13
|
|
|
class SchemaBuilder |
14
|
|
|
{ |
15
|
|
|
/** |
16
|
|
|
* @param EntityMap $entityMap |
17
|
|
|
* @return bool |
18
|
|
|
* @throws InvalidArgumentException |
19
|
|
|
*/ |
20
|
3 |
|
public static function build(EntityMap $entityMap) |
21
|
|
|
{ |
22
|
3 |
|
self::buildInfoBlock($entityMap->getAnnotation()); |
23
|
2 |
|
foreach ($entityMap->getProperties() as $propertyMap) { |
24
|
2 |
|
$propAnnotation = $propertyMap->getAnnotation(); |
25
|
2 |
|
if ($propAnnotation instanceof Property) { |
26
|
2 |
|
self::buildProperty($entityMap->getAnnotation(), $propAnnotation); |
27
|
|
|
} |
28
|
|
|
} |
29
|
|
|
|
30
|
2 |
|
return true; |
31
|
|
|
} |
32
|
|
|
|
33
|
|
|
/** |
34
|
|
|
* @param InfoBlock $annotation |
35
|
|
|
* @return int |
36
|
|
|
* @throws InvalidArgumentException |
37
|
|
|
*/ |
38
|
3 |
|
protected static function buildInfoBlock(InfoBlock $annotation) |
39
|
|
|
{ |
40
|
3 |
|
$type = $annotation->getType(); |
41
|
3 |
|
self::assert($type, 'Не указан тип инфоблока.'); |
42
|
2 |
|
$code = $annotation->getCode(); |
43
|
2 |
|
self::assert($code, 'Не указан код инфоблока.'); |
44
|
2 |
|
$name = $annotation->getName(); |
45
|
|
|
|
46
|
2 |
|
$fields = [ |
47
|
2 |
|
'LID' => self::getSiteCodes(), |
48
|
2 |
|
'CODE' => $code, |
49
|
2 |
|
'IBLOCK_TYPE_ID' => $type, |
50
|
2 |
|
'NAME' => $name ? $name : $code, |
51
|
2 |
|
'GROUP_ID' => ['1' => 'X', '2' => 'W'] |
52
|
2 |
|
]; |
53
|
|
|
|
54
|
2 |
|
$exist = self::getBitrixInfoBlock($type, $code); |
55
|
2 |
|
if (!empty($exist['ID'])) { |
56
|
1 |
|
$iBlockId = $exist['ID']; |
57
|
1 |
|
$cIBlock = new CIBlock(); |
58
|
1 |
|
$isUpdated = $cIBlock->Update($iBlockId, $fields); |
59
|
1 |
|
self::assert($isUpdated, strip_tags($cIBlock->LAST_ERROR)); |
60
|
|
|
} else { |
61
|
1 |
|
$cIBlock = new CIBlock(); |
62
|
|
|
/** @var int|bool $iBlockId */ |
63
|
1 |
|
$iBlockId = $cIBlock->Add($fields); |
64
|
1 |
|
self::assert($iBlockId, strip_tags($cIBlock->LAST_ERROR)); |
65
|
|
|
} |
66
|
|
|
|
67
|
2 |
|
return $iBlockId; |
68
|
|
|
} |
69
|
|
|
|
70
|
|
|
/** |
71
|
|
|
* @return string[] |
72
|
|
|
*/ |
73
|
2 |
|
protected static function getSiteCodes() |
74
|
|
|
{ |
75
|
2 |
|
$siteCodes = []; |
76
|
2 |
|
$rs = CSite::GetList($by, $order); |
77
|
2 |
|
while ($site = $rs->Fetch()) { |
78
|
2 |
|
$siteCodes[] = $site['LID']; |
79
|
|
|
} |
80
|
|
|
|
81
|
2 |
|
return $siteCodes; |
82
|
|
|
} |
83
|
|
|
|
84
|
|
|
/** |
85
|
|
|
* @param mixed $term |
86
|
|
|
* @param string $msg |
87
|
|
|
* @throws InvalidArgumentException |
88
|
|
|
*/ |
89
|
3 |
|
protected static function assert($term, $msg) |
90
|
|
|
{ |
91
|
3 |
|
if (!$term) { |
92
|
1 |
|
throw new InvalidArgumentException($msg); |
93
|
|
|
} |
94
|
|
|
} |
95
|
|
|
|
96
|
|
|
/** |
97
|
|
|
* @param string $type |
98
|
|
|
* @param string $code |
99
|
|
|
* @return array |
100
|
|
|
* @throws InvalidArgumentException |
101
|
|
|
*/ |
102
|
2 |
|
protected static function getBitrixInfoBlock($type, $code) |
103
|
|
|
{ |
104
|
2 |
|
self::assert($type, 'Не указан тип инфоблока.'); |
105
|
2 |
|
self::assert($code, 'Не указан код инфоблока.'); |
106
|
|
|
|
107
|
2 |
|
$iBlock = CIBlock::GetList(null, [ |
108
|
2 |
|
'=TYPE' => $type, |
109
|
2 |
|
'=CODE' => $code, |
110
|
2 |
|
'CHECK_PERMISSIONS' => 'N' |
111
|
2 |
|
])->Fetch(); |
112
|
|
|
|
113
|
2 |
|
return $iBlock; |
114
|
|
|
} |
115
|
|
|
|
116
|
|
|
/** |
117
|
|
|
* @param int $iBlockId |
118
|
|
|
* @param string $code |
119
|
|
|
* @return array|null |
120
|
|
|
* @throws InvalidArgumentException |
121
|
|
|
*/ |
122
|
2 |
|
protected static function getBitrixProperty($iBlockId, $code) |
123
|
|
|
{ |
124
|
2 |
|
self::assert($iBlockId, 'Не указан ID инфоблока.'); |
125
|
2 |
|
self::assert($code, 'Не указан код свойства.'); |
126
|
|
|
|
127
|
2 |
|
$prop = CIBlockProperty::GetList(null, [ |
128
|
2 |
|
'IBLOCK_ID' => $iBlockId, |
129
|
2 |
|
'CODE' => $code, |
130
|
2 |
|
'CHECK_PERMISSIONS' => 'N' |
131
|
2 |
|
])->Fetch(); |
132
|
|
|
|
133
|
2 |
|
return $prop; |
134
|
|
|
} |
135
|
|
|
|
136
|
|
|
protected static $propertyMap = [ |
137
|
|
|
Property::TYPE_INTEGER => ['PROPERTY_TYPE' => 'N', 'USER_TYPE' => false], |
138
|
|
|
Property::TYPE_FLOAT => ['PROPERTY_TYPE' => 'N', 'USER_TYPE' => false], |
139
|
|
|
Property::TYPE_DATETIME => ['PROPERTY_TYPE' => 'S', 'USER_TYPE' => 'DateTime'], |
140
|
|
|
Property::TYPE_FILE => ['PROPERTY_TYPE' => 'F', 'USER_TYPE' => false], |
141
|
|
|
Property::TYPE_BOOLEAN => ['PROPERTY_TYPE' => 'L', 'LIST_TYPE' => 'C', 'USER_TYPE' => false], |
142
|
|
|
Property::TYPE_ENTITY => ['PROPERTY_TYPE' => 'E', 'USER_TYPE' => false], |
143
|
|
|
Property::TYPE_STRING => ['PROPERTY_TYPE' => 'S', 'USER_TYPE' => false], |
144
|
|
|
]; |
145
|
|
|
|
146
|
|
|
/** |
147
|
|
|
* @param InfoBlock $entityAnnotation |
148
|
|
|
* @param Property $propertyAnnotation |
149
|
|
|
* @return bool |
150
|
|
|
* @throws InvalidArgumentException |
151
|
|
|
*/ |
152
|
2 |
|
protected static function buildProperty(InfoBlock $entityAnnotation, Property $propertyAnnotation) |
153
|
|
|
{ |
154
|
2 |
|
self::assert($propertyAnnotation->getCode(), 'Не указан код свойства.'); |
155
|
2 |
|
self::assert($propertyAnnotation->getType(), 'Не указан тип свойства.'); |
156
|
|
|
|
157
|
2 |
|
$iBlock = self::getBitrixInfoBlock($entityAnnotation->getType(), $entityAnnotation->getCode()); |
158
|
2 |
|
self::assert( |
159
|
2 |
|
!empty($iBlock['ID']), |
160
|
2 |
|
"Инфоблок с кодом {$entityAnnotation->getCode()} и типом {$entityAnnotation->getType()} не найден." |
161
|
2 |
|
); |
162
|
|
|
|
163
|
2 |
|
$fields = self::generateBitrixFields($iBlock['ID'], $propertyAnnotation); |
164
|
|
|
|
165
|
2 |
|
$exist = self::getBitrixProperty($iBlock['ID'], $propertyAnnotation->getCode()); |
166
|
2 |
|
if (!empty($exist['ID'])) { |
167
|
1 |
|
$propId = $exist['ID']; |
168
|
1 |
|
self::updateProperty($propId, $propertyAnnotation, $fields); |
169
|
|
|
} else { |
170
|
1 |
|
$propId = self::addProperty($propertyAnnotation, $fields); |
171
|
|
|
} |
172
|
|
|
|
173
|
2 |
|
return $propId; |
174
|
|
|
} |
175
|
|
|
|
176
|
|
|
/** |
177
|
|
|
* @param int $iBlockId |
178
|
|
|
* @param Property $propertyAnnotation |
179
|
|
|
* @return array |
180
|
|
|
* @throws InvalidArgumentException |
181
|
|
|
*/ |
182
|
2 |
|
protected static function generateBitrixFields($iBlockId, Property $propertyAnnotation) |
183
|
|
|
{ |
184
|
2 |
|
$fields = [ |
185
|
2 |
|
'IBLOCK_ID' => $iBlockId, |
186
|
2 |
|
'CODE' => $propertyAnnotation->getCode(), |
187
|
2 |
|
'NAME' => $propertyAnnotation->getName() ? $propertyAnnotation->getName() : $propertyAnnotation->getCode(), |
188
|
2 |
|
'MULTIPLE' => $propertyAnnotation->isMultiple() ? 'Y' : 'N', |
189
|
2 |
|
'FILTRABLE' => 'Y' |
190
|
2 |
|
]; |
191
|
|
|
|
192
|
2 |
|
if (array_key_exists($propertyAnnotation->getType(), self::$propertyMap)) { |
193
|
2 |
|
$fields += self::$propertyMap[$propertyAnnotation->getType()]; |
194
|
|
|
} |
195
|
|
|
|
196
|
2 |
|
return $fields; |
197
|
|
|
} |
198
|
|
|
|
199
|
|
|
/** |
200
|
|
|
* @param int $propId |
201
|
|
|
* @param Property $propertyAnnotation |
202
|
|
|
* @param array $fields |
203
|
|
|
* @return bool |
204
|
|
|
* @throws InvalidArgumentException |
205
|
|
|
*/ |
206
|
1 |
|
protected static function updateProperty($propId, Property $propertyAnnotation, array $fields) |
207
|
|
|
{ |
208
|
1 |
|
$type = $propertyAnnotation->getType(); |
209
|
1 |
|
if ($type === Property::TYPE_BOOLEAN) { |
210
|
1 |
|
$existEnum = CIBlockProperty::GetPropertyEnum($propId, null, ['XML_ID' => 'Y', 'VALUE' => 'Y'])->Fetch(); |
211
|
1 |
|
if (!$existEnum) { |
212
|
1 |
|
$fields += ['VALUES' => [['XML_ID' => 'Y', 'VALUE' => 'Y', 'DEF' => 'N']]]; |
213
|
|
|
} |
214
|
|
|
} |
215
|
|
|
|
216
|
1 |
|
$cIBlockProperty = new CIBlockProperty(); |
217
|
1 |
|
$isUpdated = $cIBlockProperty->Update($propId, $fields); |
218
|
1 |
|
self::assert($isUpdated, strip_tags($cIBlockProperty->LAST_ERROR)); |
219
|
|
|
|
220
|
1 |
|
return $isUpdated; |
221
|
|
|
} |
222
|
|
|
|
223
|
|
|
/** |
224
|
|
|
* @param Property $propertyAnnotation |
225
|
|
|
* @param array $fields |
226
|
|
|
* @return int |
227
|
|
|
* @throws InvalidArgumentException |
228
|
|
|
*/ |
229
|
1 |
|
protected static function addProperty(Property $propertyAnnotation, array $fields) |
230
|
|
|
{ |
231
|
1 |
|
$type = $propertyAnnotation->getType(); |
232
|
1 |
|
if ($type === Property::TYPE_BOOLEAN) { |
233
|
1 |
|
$fields += ['VALUES' => [['XML_ID' => 'Y', 'VALUE' => 'Y', 'DEF' => 'N']]]; |
234
|
|
|
} |
235
|
|
|
|
236
|
1 |
|
$cIBlockProperty = new CIBlockProperty(); |
237
|
1 |
|
$propId = $cIBlockProperty->Add($fields); |
238
|
1 |
|
self::assert($propId, strip_tags($cIBlockProperty->LAST_ERROR)); |
239
|
|
|
|
240
|
1 |
|
return $propId; |
241
|
|
|
} |
242
|
|
|
} |