diff --git a/local/php_interface/console/install-custom-orm.php b/local/php_interface/console/install-custom-orm.php new file mode 100644 index 0000000..a1be981 --- /dev/null +++ b/local/php_interface/console/install-custom-orm.php @@ -0,0 +1,63 @@ +isTableExists($entity::getTableName())) { + Base::getInstance($entity)->createDbTable(); + } +} + +/* +$connection = Application::getConnection(); + +$tableName = 'aholin_book_author'; + +if (!$connection->isTableExists($tableName)) { + $connection->queryExecute(" + CREATE TABLE {$tableName} ( + BOOK_ID int NOT NULL, + AUTHOR_ID int NOT NULL, + PRIMARY KEY (BOOK_ID, AUTHOR_ID) + ) + "); +} + +$tableName = 'aholin_editor_book'; + +if (!$connection->isTableExists($tableName)) { + $connection->queryExecute(" + CREATE TABLE {$tableName} ( + BOOK_ID int NOT NULL, + EDITOR_ID int NOT NULL, + PRIMARY KEY (BOOK_ID, EDITOR_ID) + ) + "); +}*/ \ No newline at end of file diff --git a/local/php_interface/include/classes/Otus/Models/AbstractIblockPropertyValuesTable.php b/local/php_interface/include/classes/Otus/Models/AbstractIblockPropertyValuesTable.php new file mode 100644 index 0000000..f38a3a7 --- /dev/null +++ b/local/php_interface/include/classes/Otus/Models/AbstractIblockPropertyValuesTable.php @@ -0,0 +1,287 @@ +initCache(3600, md5($cacheDir), $cacheDir)) { + $map = $cache->getVars(); + + } else { + $cache->startDataCache(); + + $map['IBLOCK_ELEMENT_ID'] = new IntegerField('IBLOCK_ELEMENT_ID', ['primary' => true]); + $map['ELEMENT'] = new ReferenceField( + 'ELEMENT', + ElementTable::class, + ['=this.IBLOCK_ELEMENT_ID' => 'ref.ID'] + ); + + foreach (static::getProperties() as $property) { + if ($property['MULTIPLE'] === 'Y') { + $map[$property['CODE']] = new ExpressionField( + $property['CODE'], + sprintf('(select group_concat(`VALUE` SEPARATOR "\0") as VALUE from %s as m where m.IBLOCK_ELEMENT_ID = %s and m.IBLOCK_PROPERTY_ID = %d)', + static::getTableNameMulti(), + '%s', + $property['ID'] + ), + ['IBLOCK_ELEMENT_ID'], + ['fetch_data_modification' => [static::class, 'getMultipleFieldValueModifier']] + ); + + if ($property['USER_TYPE'] === 'EList') { + $map[$property['CODE'].'_ELEMENT_NAME'] = new ExpressionField( + $property['CODE'].'_ELEMENT_NAME', + sprintf('(select group_concat(e.NAME SEPARATOR "\0") as VALUE from %s as m join b_iblock_element as e on m.VALUE = e.ID where m.IBLOCK_ELEMENT_ID = %s and m.IBLOCK_PROPERTY_ID = %d)', + static::getTableNameMulti(), + '%s', + $property['ID'] + ), + ['IBLOCK_ELEMENT_ID'], + ['fetch_data_modification' => [static::class, 'getMultipleFieldValueModifier']] + ); + } + + $map[$property['CODE'].'|SINGLE'] = new ReferenceField( + $property['CODE'].'|SINGLE', + $multipleValuesTableClass, + [ + '=this.IBLOCK_ELEMENT_ID' => 'ref.IBLOCK_ELEMENT_ID', + '=ref.IBLOCK_PROPERTY_ID' => new SqlExpression('?i', $property['ID']) + ] + ); + + continue; + } + + if ($property['PROPERTY_TYPE'] == PropertyTable::TYPE_NUMBER) { + $map[$property['CODE']] = new IntegerField("PROPERTY_{$property['ID']}"); + } elseif ($property['USER_TYPE'] === 'Date') { + $map[$property['CODE']] = new DatetimeField("PROPERTY_{$property['ID']}"); + } else { + $map[$property['CODE']] = new StringField("PROPERTY_{$property['ID']}"); + } + + if ($property['PROPERTY_TYPE'] === 'E' && ($property['USER_TYPE'] === 'EList' || is_null($property['USER_TYPE']))) { + $map[$property['CODE'].'_ELEMENT'] = new ReferenceField( + $property['CODE'].'_ELEMENT', + ElementTable::class, + ["=this.{$property['CODE']}" => 'ref.ID'] + ); + } + } + + if (empty($map)) { + $cache->abortDataCache(); + } else { + $cache->endDataCache($map); + } + } + + return $map; + } + + /** + * @param array $data + * + * @return bool + */ + public static function add(array $data): bool + { + static::$iblockElement ?? static::$iblockElement = new CIBlockElement(); + $fields = [ + 'NAME' => $data['NAME'], + 'IBLOCK_ID' => static::IBLOCK_ID, + 'PROPERTY_VALUES' => $data, + ]; + + return static::$iblockElement->Add($fields); + } + + /** + * @param $primary + * + * @return DeleteResult + * @throws NotImplementedException + */ + public static function delete($primary): DeleteResult + { + #TODO Implement function + throw new NotImplementedException(); + } + + /** + * @return array + * @throws ArgumentException + * @throws SystemException + * @throws ObjectPropertyException + */ + public static function getProperties(): array + { + if (isset(static::$properties[static::IBLOCK_ID])) { + return static::$properties[static::IBLOCK_ID]; + } + + $dbResult = PropertyTable::query() + ->setSelect(['ID', 'CODE', 'PROPERTY_TYPE', 'MULTIPLE', 'NAME', 'USER_TYPE']) + ->where('IBLOCK_ID', static::IBLOCK_ID) + ->exec(); + while ($row = $dbResult->fetch()) { + static::$properties[static::IBLOCK_ID][$row['CODE']] = $row; + } + + return static::$properties[static::IBLOCK_ID] ?? []; + } + + /** + * @param string $code + * + * @return int + * @throws ArgumentException + * @throws ObjectPropertyException + * @throws SystemException + */ + public static function getPropertyId(string $code): int + { + return (int) static::getProperties()[$code]['ID']; + } + + /** + * @return array + */ + public static function getMultipleFieldValueModifier(): array + { + return [fn ($value) => array_filter(explode("\0", $value))]; + } + + /** + * @param int|null $iblockId + */ + public static function clearPropertyMapCache(?int $iblockId = null): void + { + $iblockId = $iblockId ?: static::IBLOCK_ID; + if (empty($iblockId)) { + return; + } + + Cache::clearCache(true, "iblock_property_map/$iblockId"); + } + + /** + * @param string $propertyCode + * @param string $byKey + * + * @return array + * @throws ArgumentException + * @throws ObjectPropertyException + * @throws SystemException + */ + public static function getEnumPropertyOptions(string $propertyCode, string $byKey = 'ID'): array + { + $dbResult = PropertyEnumerationTable::getList([ + 'select' => ['ID', 'VALUE', 'XML_ID', 'SORT'], + 'filter' => ['=PROPERTY.CODE' => $propertyCode, 'PROPERTY.IBLOCK_ID' => static::IBLOCK_ID], + ]); + while ($row = $dbResult->fetch()) { + $enumPropertyOptions[$row[$byKey]] = $row; + } + + return $enumPropertyOptions ?? []; + } + + /** + * @return string + */ + private static function getMultipleValuesTableClass(): string + { + $className = end(explode('\\', static::class)); + $namespace = str_replace('\\'.$className, '', static::class); + $className = str_replace('Table', 'MultipleTable', $className); + + return $namespace.'\\'.$className; + } + + /** + * @return void + */ + private static function initMultipleValuesTableClass(): void + { + $className = end(explode('\\', static::class)); + $namespace = str_replace('\\'.$className, '', static::class); + $className = str_replace('Table', 'MultipleTable', $className); + + if (class_exists($namespace.'\\'.$className)) { + return; + } + + $iblockId = static::IBLOCK_ID; + +// $php = << new ReferenceField( + 'TASK', + TaskTable::class, + ['=this.ID' => 'ref.PRODUCT_ID'] + ) + ]; + + + return array_merge(parent::getMap(), $map); + } +} \ No newline at end of file diff --git a/local/php_interface/include/classes/Otus/Models/ClientPropertyValuesTable.php b/local/php_interface/include/classes/Otus/Models/ClientPropertyValuesTable.php new file mode 100644 index 0000000..fe91d3d --- /dev/null +++ b/local/php_interface/include/classes/Otus/Models/ClientPropertyValuesTable.php @@ -0,0 +1,29 @@ + new ReferenceField( + 'TASK', + TaskTable::class, + ['=this.ID' => 'ref.CLIENT_ID'] + ) + ]; + + + return array_merge(parent::getMap(), $map); + } +} \ No newline at end of file diff --git a/local/php_interface/include/classes/Otus/Orm/TaskTable.php b/local/php_interface/include/classes/Otus/Orm/TaskTable.php new file mode 100644 index 0000000..c6aa6c3 --- /dev/null +++ b/local/php_interface/include/classes/Otus/Orm/TaskTable.php @@ -0,0 +1,59 @@ +configurePrimary() + ->configureAutocomplete(), + + (new StringField('NAME')) + ->configureRequired() + ->configureSize(100), + + (new StringField('MANAGER')) + ->configureRequired() + ->configureSize(100), + + (new DateField('CREATE_DATE')), + + (new TextField('COMMENT')), + + (new BooleanField('COMPLETED')) + ->configureValues('N', 'Y') + ->configureDefaultValue('N'), + + (new IntegerField('CLIENT_ID')), + (new IntegerField('PRODUCT_ID')), + + (new Reference('CLIENT', ElementClientTable::class, Join::on('this.CLIENT_ID', 'ref.ID'))) + ->configureJoinType('INNER'), + + (new Reference('PRODUCT', ElementCatalogTable::class, Join::on('this.PRODUCT_ID', 'ref.ID'))) + ->configureJoinType('INNER'), + + ]; + } +} \ No newline at end of file diff --git a/orm/index.php b/orm/index.php new file mode 100644 index 0000000..d445ddb --- /dev/null +++ b/orm/index.php @@ -0,0 +1,109 @@ +SetTitle('Выборка ORM'); + +\Bitrix\Main\Loader::IncludeModule("iblock"); + +$tasks = TaskTable::query() + ->setSelect([ + 'ID' , + 'NAME', + 'MANAGER', + 'CREATE_DATE', + 'COMMENT', + 'COMPLETED', + 'COMPANY_NAME' => 'CLIENT.NAME', + 'PHONE' => 'CLIENT.PHONE', + 'PERSON' => 'CLIENT.PERSON', + 'TARGET' => 'PRODUCT.NAME', + 'TARGET_ID' => 'PRODUCT.ID', + 'PRICE' => 'CATALOG_PRICE.PRICE', + 'CURRENCY' => 'CATALOG_PRICE.CURRENCY' + ]) + ->where("COMPLETED", 'Y') + ->addOrder('CREATE_DATE', 'DESC') + ->registerRuntimeField( + 'CATALOG_PRICE', + new ReferenceField( + 'CATALOG_PRICE', + PriceTable::getEntity(), + [ + '=this.TARGET_ID' => 'ref.PRODUCT_ID', + '=ref.CATALOG_GROUP_ID' => new \Bitrix\Main\DB\SqlExpression('?', 1) + ], + ['join_type' => 'LEFT'] + ) + ) + ->setCacheTtl(60) + ->cacheJoins(true) + ->fetchAll(); + +echo "
"; var_dump($tasks); echo "
"; +echo "
"; + +$tasks = TaskTable::query() + ->setSelect([ + 'ID' , + 'NAME', + 'MANAGER', + 'CREATE_DATE', + 'COMMENT', + 'COMPLETED', + 'COMPANY_NAME' => 'CLIENT.NAME', + 'PHONE' => 'CLIENT.PHONE', + 'PERSON' => 'CLIENT.PERSON', + 'TARGET' => 'PRODUCT.NAME' + ]) + ->whereNot("COMPLETED", 'Y') + ->addOrder('CREATE_DATE', 'DESC') + ->setCacheTtl(60) + ->cacheJoins(true) + ->fetchAll(); + +echo "
"; var_dump($tasks); echo "
"; echo "
"; + + +$clients = ClientPropertyValuesTable::query() + ->setSelect([ + 'ID' => 'ELEMENT.ID' , + 'NAME' => 'ELEMENT.NAME', + 'PHONE', + 'PERSON', + 'TASK_NAME' => 'TASK.NAME', + 'TASK_COMMENT' => 'TASK.COMMENT' + ]) + ->where('ID', 1) + ->setCacheTtl(60) + ->cacheJoins(true) + ->fetchAll(); + + +echo "
"; var_dump($clients); echo "
"; echo "
"; + +$products = CatalogPropertyValuesTable::query() + ->setSelect([ + 'ID' => 'ELEMENT.ID' , + 'NAME' => 'ELEMENT.NAME', + 'COLOR', + 'TASK_NAME' => 'TASK.NAME', + 'TASK_COMMENT' => 'TASK.COMMENT' + ]) + ->setCacheTtl(60) + ->cacheJoins(true) + ->fetchAll(); + +echo "
"; var_dump($products); echo "
"; echo "
"; + +require_once $_SERVER['DOCUMENT_ROOT'] . '/bitrix/footer.php'; \ No newline at end of file