248 lines
9.2 KiB
PHP
248 lines
9.2 KiB
PHP
|
|
<?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();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
}
|