From c2af9676e6a068f702b9f587befea8f533d3cffa Mon Sep 17 00:00:00 2001 From: Ivan Kolobaev Date: Mon, 18 Dec 2017 18:32:19 +0300 Subject: [PATCH] Iblock schema (#20) --- src/Application/Application.php | 5 + src/Iblock/Command/ExportCommand.php | 118 +++++++++++++ src/Iblock/Command/ImportCommand.php | 117 +++++++++++++ src/Iblock/Command/MigrationCommandTrait.php | 147 ++++++++++++++++ src/Iblock/Exception/ExportException.php | 12 ++ src/Iblock/Exception/IblockException.php | 12 ++ src/Iblock/Exception/ImportException.php | 12 ++ src/Iblock/Exporter.php | 175 +++++++++++++++++++ src/Iblock/Importer.php | 173 ++++++++++++++++++ src/Iblock/MigrationInterface.php | 15 ++ 10 files changed, 786 insertions(+) create mode 100644 src/Iblock/Command/ExportCommand.php create mode 100644 src/Iblock/Command/ImportCommand.php create mode 100644 src/Iblock/Command/MigrationCommandTrait.php create mode 100644 src/Iblock/Exception/ExportException.php create mode 100644 src/Iblock/Exception/IblockException.php create mode 100644 src/Iblock/Exception/ImportException.php create mode 100644 src/Iblock/Exporter.php create mode 100644 src/Iblock/Importer.php create mode 100644 src/Iblock/MigrationInterface.php diff --git a/src/Application/Application.php b/src/Application/Application.php index 1edd212..867b2db 100644 --- a/src/Application/Application.php +++ b/src/Application/Application.php @@ -16,6 +16,8 @@ use Notamedia\ConsoleJedi\Environment\Command\InitCommand; use Notamedia\ConsoleJedi\Module\Command as Module; use Notamedia\ConsoleJedi\Search\Command\ReIndexCommand; +use Notamedia\ConsoleJedi\Iblock\Command\ExportCommand; +use Notamedia\ConsoleJedi\Iblock\Command\ImportCommand; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; @@ -151,6 +153,9 @@ protected function getBitrixCommands() new ClearCommand(), new InitCommand(), new ReIndexCommand(), + new ExportCommand(), + new ImportCommand(), + new ReIndexCommand(), ], Module\ModuleCommand::getCommands() ); diff --git a/src/Iblock/Command/ExportCommand.php b/src/Iblock/Command/ExportCommand.php new file mode 100644 index 0000000..99bcf04 --- /dev/null +++ b/src/Iblock/Command/ExportCommand.php @@ -0,0 +1,118 @@ +setName('iblock:export') + ->setDescription('Export information block(s) to xml') + ->addArgument( + 'type', + InputArgument::REQUIRED, + 'Information block type' + ) + ->addArgument( + 'code', + InputArgument::REQUIRED, + 'Information block code' + ) + ->addArgument( + 'dir', + InputArgument::OPTIONAL, + 'Directory to export' + ) + ->addOption( + 'sections', + 's', + InputOption::VALUE_OPTIONAL, + 'Export sections [ "active", "all", "none" ]', + 'none' + ) + ->addOption( + 'elements', + 'e', + InputOption::VALUE_OPTIONAL, + 'Export elements [ "active", "all", "none" ]', + 'none' + ); + } + + /** + * {@inheritdoc} + */ + protected function initialize(InputInterface $input, OutputInterface $output) + { + Loader::includeModule('iblock'); + } + + /** + * {@inheritdoc} + */ + protected function interact(InputInterface $input, OutputInterface $output) + { + $this->setDir($input); + $this->setIblocks($input); + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $formatter = new FormatterHelper(); + + if (count($this->errors) > 0) { + $output->writeln($formatter->formatBlock($this->errors, 'error')); + return false; + } + + $exporter = new Exporter(); + $exporter + ->setSections($input->getOption('sections')) + ->setElements($input->getOption('elements')); + + foreach ($this->iblocks as $iblock) { + + try { + $xml_id = \CIBlockCMLExport::GetIBlockXML_ID($iblock['ID']); + $path = implode(DIRECTORY_SEPARATOR, [$this->dir, $xml_id]) . $this->extension; + + $exporter + ->setPath($path) + ->setId($iblock['ID']) + ->execute(); + + $output->writeln(sprintf('%s iblock %s %s', 'success', $iblock['CODE'], $path)); + + } catch (IblockException $e) { + $output->writeln(sprintf('%s iblock %s', 'fail', $iblock['CODE'])); + if ($output->getVerbosity() >= OutputInterface::VERBOSITY_VERBOSE) { + $output->writeln($e->getMessage()); + } + } + } + + return true; + } +} \ No newline at end of file diff --git a/src/Iblock/Command/ImportCommand.php b/src/Iblock/Command/ImportCommand.php new file mode 100644 index 0000000..f00031b --- /dev/null +++ b/src/Iblock/Command/ImportCommand.php @@ -0,0 +1,117 @@ +setName('iblock:import') + ->setDescription('Import information block(s) from xml') + ->addArgument( + 'type', + InputArgument::REQUIRED, + 'Information block type' + ) + ->addArgument( + 'sites', + InputArgument::REQUIRED, + 'Sites to which the information block will be bound (if it is to be created)' + ) + ->addArgument( + 'dir', + InputArgument::OPTIONAL, + 'Directory to import' + ) + ->addOption( + 'sections', + 's', + InputOption::VALUE_OPTIONAL, + 'If an existing section is no longer in the source file [ leave: "N", deactivate: "A", delete: "D" ]', + 'A' + ) + ->addOption( + 'elements', + 'e', + InputOption::VALUE_OPTIONAL, + 'If an existing element is no longer in the source file [ leave: "N", deactivate: "A", delete: "D" ]', + 'A' + ); + + } + + /** + * {@inheritdoc} + */ + protected function initialize(InputInterface $input, OutputInterface $output) + { + Loader::includeModule('iblock'); + } + + + /** + * {@inheritdoc} + */ + protected function interact(InputInterface $input, OutputInterface $output) + { + $this->setDir($input); + $this->setType($input); + $this->setSites($input); + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $formatter = new FormatterHelper(); + + if (count($this->errors) > 0) { + $output->writeln($formatter->formatBlock($this->errors, 'error')); + return false; + } + + $importer = new Importer(); + $importer + ->setType($this->type) + ->setSites($this->sites) + ->setActionSection($input->getOption('sections')) + ->setActionElement($input->getOption('elements'));; + + foreach (glob(implode(DIRECTORY_SEPARATOR . '*', [$this->dir, $this->extension])) as $file) { + + try { + $importer + ->setPath($file) + ->execute(); + + $output->writeln(sprintf('%s file %s', 'success', $file)); + + } catch (IblockException $e) { + $output->writeln(sprintf('%s file %s', 'fail', $file)); + if ($output->getVerbosity() >= OutputInterface::VERBOSITY_VERBOSE) { + $output->writeln($e->getMessage()); + } + } + } + } +} \ No newline at end of file diff --git a/src/Iblock/Command/MigrationCommandTrait.php b/src/Iblock/Command/MigrationCommandTrait.php new file mode 100644 index 0000000..8a07b35 --- /dev/null +++ b/src/Iblock/Command/MigrationCommandTrait.php @@ -0,0 +1,147 @@ +dir + * + * @param InputInterface $input + */ + protected function setDir(InputInterface $input) + { + $app = new Application(); + $filesystem = new Filesystem(); + $dir = $input->getArgument('dir'); + + if (!$dir) { + $dir = $app->getRoot(); + } elseif (!$filesystem->isAbsolutePath($dir)) { + $dir = $app->getRoot() . DIRECTORY_SEPARATOR . $dir; + } + $dir = rtrim($dir, DIRECTORY_SEPARATOR); + + if (!$filesystem->exists($dir)) { + $this->errors[] = "Directory $dir not found"; + } + + $this->dir = $dir; + } + + /** + * Check arguments type and code, set $this->iblocks + * + * @param InputInterface $input + */ + protected function setIblocks(InputInterface $input) + { + $iblocks = IblockTable::query() + ->setFilter([ + 'IBLOCK_TYPE_ID' => $input->getArgument('type'), + 'CODE' => $input->getArgument('code') + ]) + ->setSelect(['ID', 'CODE']) + ->exec(); + + if ($iblocks->getSelectedRowsCount() <= 0) { + $this->errors[] = 'Iblock(s) not found'; + } + + $this->iblocks = $iblocks->fetchAll(); + } + + /** + * Check argument type and set $this->type + * + * @param InputInterface $input + */ + protected function setType(InputInterface $input) + { + $type = TypeTable::query() + ->setFilter([ + 'ID' => $input->getArgument('type'), + ]) + ->setSelect(['ID']) + ->exec(); + + if ($type->getSelectedRowsCount() <= 0) { + $this->errors[] = 'Type not found'; + } + + $this->type = $type->fetch()['ID']; + } + + /** + * Check argument sites and set $this->sites + * + * @param InputInterface $input + */ + protected function setSites(InputInterface $input) + { + $sites = SiteTable::query() + ->setFilter([ + 'LID' => $input->getArgument('sites'), + ]) + ->setSelect(['LID']) + ->exec(); + + if ($sites->getSelectedRowsCount() <= 0) { + $this->errors[] = 'Sites not found'; + } + + $this->sites = $sites->fetchAll(); + } +} \ No newline at end of file diff --git a/src/Iblock/Exception/ExportException.php b/src/Iblock/Exception/ExportException.php new file mode 100644 index 0000000..27b766f --- /dev/null +++ b/src/Iblock/Exception/ExportException.php @@ -0,0 +1,12 @@ +config = [ + 'id' => '', + 'path' => '', + 'sections' => 'none', + 'elements' => 'none', + 'interval' => 0 + ]; + + Loader::includeModule('iblock'); + $this->export = new \CIBlockCMLExport(); + } + + /** + * Set id information block + * + * @param int $id + * @return $this + */ + public function setId($id) + { + $this->config['id'] = intval($id); + return $this; + } + + /** + * Set file path to export + * + * @param string $path + * @return $this + */ + public function setPath($path) + { + $this->config['path'] = $path; + return $this; + } + + /** + * Set settings export sections + * + * @param string $sections + * @return $this + */ + public function setSections($sections) + { + $this->config['sections'] = $sections; + return $this; + } + + /** + * Set settings export elements + * + * @param string $elements + * @return $this + */ + public function setElements($elements) + { + $this->config['elements'] = $elements; + return $this; + } + + /** + * @inheritdoc + */ + public function execute() + { + $pathinfo = pathinfo($this->config['path']); + $this->session = [ + "property_map" => false, + "section_map" => false, + "work_dir" => $pathinfo['dirname'] . DIRECTORY_SEPARATOR, + "file_dir" => $pathinfo['filename'] . "_files" . DIRECTORY_SEPARATOR, + ]; + + $this->export(); + } + + /** + * Direct export + * + * @return $this + * @throws ExportException + */ + protected function export() + { + $filesystem = new Filesystem(); + $handle = fopen($this->config['path'] . $this->prefix, "w"); + + $checkPermissions = true; + if (PHP_SAPI == 'cli') { + $checkPermissions = false; + } + + if (!$this->export->Init( + $handle, + $this->config["id"], + false, + true, + $this->session["work_dir"], + $this->session["file_dir"], + $checkPermissions + ) + ) { + throw new ExportException('Failed to initialize export'); + } + + $this->export->DoNotDownloadCloudFiles(); + $this->export->StartExport(); + + $this->export->StartExportMetadata(); + $this->export->ExportProperties($this->session["property_map"]); + $this->export->ExportSections( + $this->session["section_map"], + time(), + $this->config['interval'], + $this->config["sections"], + $this->session["property_map"] + ); + $this->export->EndExportMetadata(); + + $this->export->StartExportCatalog(); + $this->export->ExportElements( + $this->session["property_map"], + $this->session["section_map"], + time(), + $this->config['interval'], + 0, + $this->config["elements"] + ); + $this->export->EndExportCatalog(); + + $this->export->ExportProductSets(); + $this->export->EndExport(); + + fclose($handle); + $filesystem->remove($this->config['path']); + $filesystem->rename($this->config['path'] . $this->prefix, $this->config['path'], true); + } +} \ No newline at end of file diff --git a/src/Iblock/Importer.php b/src/Iblock/Importer.php new file mode 100644 index 0000000..0d51027 --- /dev/null +++ b/src/Iblock/Importer.php @@ -0,0 +1,173 @@ +config = [ + 'type' => '', + 'lids' => [], + 'path' => '', + 'action_section' => 'A', + 'action_element' => 'A', + 'preview' => 'Y', + 'detail' => 'Y', + 'interval' => 0 + ]; + + Loader::includeModule('iblock'); + $this->xml = new \CIBlockXMLFile(); + $this->import = new \CIBlockCMLImport(); + + } + + /** + * Set type information block + * + * @param string $type + * @return $this + */ + public function setType($type) + { + $this->config['type'] = $type; + return $this; + } + + /** + * Set file path to import + * + * @param string $path + * @return $this + */ + public function setPath($path) + { + $this->config['path'] = $path; + return $this; + } + + /** + * Set binding sites for importing information block + * @param array $lids + * @return $this + */ + public function setSites($lids) + { + $this->config['lids'] = $lids; + return $this; + } + + /** + * What doing with existing section + * + * @param string $action + * @return $this + */ + public function setActionSection($action) + { + $this->config['action_section'] = $action; + return $this; + } + + /** + * What doing with existing element + * + * @param string $action + * @return $this + */ + public function setActionElement($action) + { + $this->config['action_element'] = $action; + return $this; + } + + /** + * @inheritdoc + */ + public function execute() + { + $this->session = [ + "section_map" => false, + "prices_map" => false, + "work_dir" => pathinfo($this->config['path'], PATHINFO_DIRNAME) . DIRECTORY_SEPARATOR + ]; + + $this->read(); + $this->import(); + } + + /** + * Read file + * + * @throws ImportException + */ + protected function read() + { + $handle = fopen($this->config['path'], "r"); + + if (!$handle) + throw new ImportException('Unable to open file, or file not exist'); + + if (!$this->import->CheckIfFileIsCML($this->config['path'])) { + throw new ImportException('File is not valid'); + } + + $this->xml->DropTemporaryTables(); + $this->xml->CreateTemporaryTables(); + $this->xml->ReadXMLToDatabase($handle, $this->session, $this->config['interval']); + $this->xml->IndexTemporaryTables(); + } + + /** + * Direct import + */ + protected function import() + { + $this->import->Init( + $this->config, + $this->session['work_dir'], + true, + $this->config["preview"], + $this->config["detail"], + true + ); + $this->import->ImportMetaData([1, 2], $this->config["type"], $this->config["lids"]); + + $this->import->ImportSections(); + $this->import->DeactivateSections($this->config["action_section"]); + + $this->import->ReadCatalogData($this->session["section_map"], $this->session["prices_map"]); + $this->import->ImportElements(time(), $this->config["interval"]); + $this->import->DeactivateElement($this->config["action_element"], time(), $this->config["interval"]); + + $this->import->ImportProductSets(); + } +} \ No newline at end of file diff --git a/src/Iblock/MigrationInterface.php b/src/Iblock/MigrationInterface.php new file mode 100644 index 0000000..ff06eba --- /dev/null +++ b/src/Iblock/MigrationInterface.php @@ -0,0 +1,15 @@ +