Joywork/import/XmlImportComponent.php

248 lines
9.2 KiB
PHP
Raw Permalink Normal View History

2026-05-22 20:21:54 +02:00
<?php
/**
* Компонент импорта данных из xml файла. Памяти много не кушает, т.к. используется потоковое чтение.
*/
class XmlImportComponent
{
const SECTION_BUILDERS = 1;
const SECTION_SUBWAYS = 2;
const SECTION_REGIONS = 3;
const SECTION_ROOMTYPES = 4;
const SECTION_FLATTYPES = 5;
const SECTION_BANKS = 6;
const SECTION_BLOCKS = 7;
const SECTION_BUILDINGS = 8;
const SECTION_APARTMENTS = 9;
const SECTION_BLOCKSUBWAYS = 10;
const SECTION_MORTGAGES = 11;
const SECTION_BANK_PROGRAMS = 12;
const SECTION_BANK_PROGRAM_RATES = 13;
const SECTION_CLASSES = 14;
private static $sectionsXpath = [
self::SECTION_BUILDERS => 'Ads/Builders',
self::SECTION_SUBWAYS => 'Ads/Subways',
self::SECTION_REGIONS => 'Ads/Regions',
self::SECTION_ROOMTYPES => 'Ads/RoomTypes',
self::SECTION_FLATTYPES => 'Ads/FlatTypes',
self::SECTION_BANKS => 'Ads/Banks',
self::SECTION_BANK_PROGRAMS => 'Ads/BankPrograms',
self::SECTION_BANK_PROGRAM_RATES => 'Ads/BankProgramRates',
self::SECTION_CLASSES => 'Ads/Classes',
self::SECTION_BLOCKS => 'Ads/Blocks',
self::SECTION_BUILDINGS => 'Ads/Buildings',
self::SECTION_APARTMENTS => 'Ads/Apartments',
self::SECTION_BLOCKSUBWAYS => 'Ads/BlockSubways',
self::SECTION_MORTGAGES => 'Ads/Mortgages'
];
private $filename = '';
private $builders = [];
private $currentPath = [];
public function setBuilder($sectionId, ModelBuilder $builder)
{
assert('isset(self::$sectionsXpath[$sectionId])');
$this->builders[$sectionId] = $builder;
}
/**
* @var ImporterService
*/
private $importerService;
public function __construct($filename)
{
if (!is_file($filename)) {
throw new Exception('File not found');
}
$this->importerService = new ImporterService();
$this->filename = $filename;
}
private function getAttributesArray(\XmlReader $reader)
{
$attrs = [];
while ($reader->moveToNextAttribute()) {
$attrs[$reader->localName] = $reader->value;
}
return $attrs;
}
private function getNextSection(\XmlReader $reader)
{
$sectionsByXpaths = array_flip(self::$sectionsXpath);
if ($reader->nodeType == \XmlReader::END_ELEMENT) {
$this->closeElement($reader->localName);
}
while ($reader->read()) {
switch ($reader->nodeType) {
case \XMLReader::ELEMENT:
$this->currentPath[] = $reader->localName;
$curPath = implode('/', $this->currentPath);
if (isset($sectionsByXpaths[$curPath])) {
return $sectionsByXpaths[$curPath];
}
break;
case \XMLReader::END_ELEMENT:
$this->closeElement($reader->localName);
break;
}
}
return 0;
}
private function closeElement($elementName)
{
while ($this->currentPath && array_pop($this->currentPath) !== $elementName);
}
private function getElements(\XmlReader $reader, ModelBuilder $builder)
{
$newElementsCount = 0;
$count = 0;
while ($reader->read() && $reader->nodeType != \XmlReader::END_ELEMENT) {
if ($reader->nodeType == \XmlReader::ELEMENT) {
$model = $builder->getModel($data = $this->getAttributesArray($reader));
assert('$model instanceof ImportRecord');
// price history for nb charts
if ($model instanceof Apartment) {
if ($ap_model = Apartment_sale::findOne((int)$data['id'])) {
if ($ap_model->meter_price != (int)$data['metrecostbase']) {
$sql = "INSERT INTO price_history (ap_id, discount_meter_price, created_at) VALUES
('" . $ap_model->id . "', '" . $ap_model->discount_meter_price . "', '" . $ap_model->updated_at . "')";
try {
mysql_query($sql);
} catch (Exception $e) {
echo $e->getMessage();
return false;
}
$ap_model->discount_meter_price = (int)$data['metrecostwithdiscounts'];
// $ap_model->updated_at = date('Y-m-d H:i:s');
}
$ap_model->save();
} else {
$ap_model = new Apartment_sale;
$ap_model->id = $data['id'];
$ap_model->block_id = (int)$data['blockid'];
$ap_model->building_id = (int)$data['buildingid'];
$ap_model->room_type_id = (int)$data['rooms']+1;
$ap_model->space_total = Convertor::strToFloat($data['stotal']);
$ap_model->height = Convertor::strToFloat($data['height']);
$ap_model->number = $data['number'];
$ap_model->section = $data['section'];
$ap_model->base_price = (int)$data['flatcostbase'];
$ap_model->meter_price = (int)$data['metrecostbase'];
$ap_model->price = (int)$data['flatcostwithdiscounts'];
$ap_model->discount_price = (int)$data['flatcostwithdiscounts'];
$ap_model->discount_meter_price = (int)$data['metrecostwithdiscounts'];
$ap_model->space_kitchen = $data['skitchen'];
$ap_model->space_corridor = $data['scorridor'];
$ap_model->space_room = $data['sroom'];
$ap_model->space_balcony = $data['sbalcony'];
$ap_model->space_watercloset = $data['swatercloset'];
$ap_model->flat_type_id = (int)$data['flattypeid']+1;
$ap_model->decoration_id = DecorationType::getIdByName($data['decoration']);
$ap_model->subsidy = (int)$data['subsidy'];
$ap_model->creditend = (int)$data['creditend'];
$ap_model->flat_floor = (int)$data['flatfloor'];
$ap_model->save();
}
}
// price history for nb charts END
$isNew = $model->getIsNewRecord();
if ($model->save()) {
if ($isNew) {
++$newElementsCount;
}
++$count;
} else {
echo 'Cannt save model!';
//print_r($model->attributes);
//print_r($model->errors);
}
}
}
return [$newElementsCount, $count];
}
public function loadData(Callable $afterSectionLoad=null)
{
$reader = new \XmlReader();
$reader->open($this->filename);
$this->importerService->setStatus(1);
$this->importerService->setForceImport(0);
try {
while($sectionId = $this->getNextSection($reader)) {
if (!isset($this->builders[$sectionId])) {
continue;
}
$builder = $this->builders[$sectionId];
$modelClass = $builder->getModelClassName();
$startTime = time();
// на случай если все произошло очень быстро
sleep(1);
list($newElementsCount, $count) = $this->getElements($reader, $builder);
$wastedTime = time() - $startTime;
$model = new $modelClass();
$delElementsCount = null;
if ($model instanceof Apartment) {
$cond = '(UNIX_TIMESTAMP(updated_at) > 0 AND UNIX_TIMESTAMP() - UNIX_TIMESTAMP(updated_at) > ' .$wastedTime . ')
or (UNIX_TIMESTAMP(updated_at) = 0 and UNIX_TIMESTAMP() - UNIX_TIMESTAMP(created_at) > ' .$wastedTime . ')';
$result = mysql_query("SELECT * FROM apartments WHERE ".$cond);
$data = [];
while ($obj = mysql_fetch_assoc($result)) {
$sql = "UPDATE apartments_sales SET is_sold=1 WHERE id='" . $obj['id']."'";
try {
mysql_query($sql);
} catch (Exception $e) {
echo $e->getMessage();
return false;
}
}
$delElementsCount = $modelClass::deleteAll($cond);
}
if ($afterSectionLoad) {
$afterSectionLoad($modelClass, $wastedTime, $newElementsCount, $delElementsCount, $count);
}
}
} catch (\Exception $e) {
//todo log error
echo $e;
} finally {
$this->importerService->setStatus(0);
$reader->close();
}
}
}