File: D:/HostingSpaces/ZelfVerkopen/zelfverkopen.nl/app/KommaApp/Realworks/Kms/RealWorksService.php
<?php
namespace App\KommaApp\Realworks\Kms;
use App\KommaApp\Kms\Core\Sections\SectionService;
use App\KommaApp\Languages\Models\Language;
use App\KommaApp\Pages\Models\Page;
use App\KommaApp\Realworks\Models\Huur;
use App\KommaApp\Realworks\Models\HuurTranslation;
use App\KommaApp\Realworks\Models\Koop;
use App\KommaApp\Realworks\Models\KoopTranslation;
use App\KommaApp\Realworks\Models\ObjectDetails;
use App\KommaApp\Realworks\Models\ObjectDetailsTranslation;
use App\KommaApp\Realworks\Models\RealworkObject;
use App\Mail\RealworksFailMail;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Log;
use ZipArchive;
class RealWorksService extends SectionService
{
protected $sortable = true;
protected static $DOWNLOAD_FOLDER_NAME = 'download'; //without trailing slash
protected static $XML_FILE_NAME = 'voorbeeld.wonen'; //without extension
public function __construct()
{
$this->forModelName = Page::class; //TODO Change me
parent::__construct();
}
/**
* Downloads the zipped xml from Realworks.
*
* Warning. Strict regulations by Realworks apply. Please check their documentation for the latest info on those
* regulations. At the time of writing they are:
*
* - Only call the api once a day after 08:30 in the morning.
* - The media that is defined in the xml must be downloaded for displaying.
*/
public function DownloadZipWithXML()
{
$storageFolder = $this->createAndGetRealworksStorageFolder(self::$DOWNLOAD_FOLDER_NAME);
$url = $this->getUrl();
try {
$file = file_get_contents($url);
}
catch (\Exception $exception)
{
$this->mailOrShowErrorAndDie('Could not save downloaded realworks zip');
}
$zipPath = $storageFolder . config('realworks.zip_name', 'realworks') . '.zip';
if(file_put_contents($zipPath, $file) === false) $this->mailOrShowErrorAndDie('Could not save downloaded realworks zip ("' . $zipPath . '")');
}
/**
* Extracts the realworks zip and deletes the zip file
*/
public function ProcessZip()
{
$storageFolder = $this->createAndGetRealworksStorageFolder(self::$DOWNLOAD_FOLDER_NAME);
$zipPath = $storageFolder . config('realworks.zip_name', 'realworks') . '.zip';
$zip = new ZipArchive;
if ($zip->open($zipPath) === true) {
$zip->extractTo($storageFolder);
$zip->close();
if(unlink($zipPath) === false) $this->mailOrShowErrorAndDie('Could delete realworks zip file after extraction: ' . $zipPath);
} else {
$this->mailOrShowErrorAndDie('Could not extract realworks zip file: ' . $zipPath);
}
}
/**
* Clears the storage folder in which the downloaded zip and its extracted data is placed
*/
public function ClearDownloadFolder()
{
self::rrmdir($this->createAndGetRealworksStorageFolder(self::$DOWNLOAD_FOLDER_NAME), false);
}
/**
* MUST be triggered by App\Console\Kernel to update Realworks api stuff.
*/
public function executeSchedule()
{
$this->ClearDownloadFolder();
$this->DownloadZipWithXML(); //WARNING! Only runnable once a day. Else we will get fined!
$this->ProcessZip();
$xml = $this->DownloadedXMLToArray();
$this->DownloadMediaFromXMLToPublicFolders($xml); //WARNING! Only runnable once a day. Else we will get fined!
$this->storeObjectsInDatabase($xml);
$this->deleteObjectsFromDatabase($xml);
$this->deletePublicFoldersFromNonExistingObjects();
}
/**
* Stores the properties in the database by using the xml
*
* @param array $xml
*/
public function storeObjectsInDatabase(array $xml)
{
if(! array_key_exists('Object', $xml)) return; //Empty
foreach($xml['Object'] as $objectArray)
{
if(! $this->hasValidMainPhoto($objectArray)) {
$objectId = $objectArray['ObjectSystemID'] ?? 'unknown';
Log::error('Main photo missing or invalid. Skipping object: ' . $objectId);
continue;
}
$object = $this->createObjectFromXML($objectArray);
}
}
/**
* Creates an object model from the input xml data
*
* @param $xml
* @return RealworkObject|null
*/
private function createObjectFromXML(array $xml): ?RealworkObject
{
if(! isset($xml['ObjectSystemID'])) $this->mailOrShowErrorAndDie('An object did not have the required ObjectSystemID key. Stopped processing');
$dutchLanguage = Language::where('native_name', '=', 'Nederlands')->first();
if(! $dutchLanguage) $this->mailOrShowErrorAndDie('The dutch language did not exists. The object details could not be created. Stopped processing');
$object = RealworkObject::where('system_id', '=', $xml['ObjectSystemID'])->first();
if(! $object) $object = new RealworkObject();
$object->raw_json = json_encode($xml);
if(isset($xml['NVMVestigingNR'])) $object->nvm_vestiging_nummer = $xml['NVMVestigingNR'];
if(isset($xml['ObjectCompany'])) $object->company = $xml['ObjectCompany'];
if(isset($xml['ObjectAfdeling'])) $object->afdeling = $xml['ObjectAfdeling'];
if(isset($xml['ObjectTiaraID'])) $object->tiara_id = $xml['ObjectTiaraID'];
if(isset($xml['ObjectSystemID'])) $object->system_id = $xml['ObjectSystemID'];
if(isset($xml['ObjectCode'])) $object->code = $xml['ObjectCode'];
if(! isset($xml['ObjectDetails'])) $this->mailOrShowErrorAndDie('An object did not contain object details. Stopped processing');
$object->save();
/** @var ObjectDetailsTranslation $objectDetailsTranslation */
$objectDetailsTranslation = $this->createObjectDetailsFromXML($xml['ObjectDetails'], $object, $dutchLanguage)->translations->first();
$object->public_path = $this->getPublicFolderPathForObject($object->system_id, $objectDetailsTranslation->woonplaats, $objectDetailsTranslation->straatnaam, $objectDetailsTranslation->huisnummer);
$object->public_path = str_replace(public_path(), '', $object->public_path);
$object->public_path = str_replace('\\', '/', $object->public_path);
$object->save();
return $object;
}
/**
* Creates an object details model from the input xml data
*
* @param array $xml
* @param RealworkObject $object
* @param Language $language
* @return ObjectDetails
*/
private function createObjectDetailsFromXML(array $xml, RealworkObject $object, Language $language): ObjectDetails {
/** @var ObjectDetails $objectDetails */
$objectDetails = $object->objectDetails()->first();
if($objectDetails) {
//Check if the object was updated yesterday. If not we are going to skip it
$updateDate = Carbon::createFromFormat('Y-m-d', $xml['DatumWijziging']);
$now = Carbon::now();
$now = Carbon::create($now->year, $now->month, $now->day);
$needsUpdate = ($updateDate && $now->subDay()->equalTo($updateDate)) ? true : false;
if(! $needsUpdate) return $objectDetails;
}
if(! $objectDetails) {
$objectDetails = new ObjectDetails();
$objectDetails->object()->associate($object);
$objectDetails->save();
}
$objectDetailsTranslation = $objectDetails->translations()->where('language_id', '=', $language->id)->first();
if(! $objectDetailsTranslation) {
$objectDetailsTranslation = new ObjectDetailsTranslation();
$objectDetailsTranslation->translatable()->associate($objectDetails);
$objectDetailsTranslation->language()->associate($language);
}
if(isset($xml['Adres']) && isset($xml['Adres']['Nederlands'])) {
$adresData = $xml['Adres']['Nederlands'];
if (isset($adresData['Straatnaam'])) $objectDetailsTranslation->straatnaam = $adresData['Straatnaam'];
if (isset($adresData['Huisnummer'])) $objectDetailsTranslation->huisnummer = $adresData['Huisnummer'];
if (isset($adresData['HuisnummerToevoeging'])) $objectDetailsTranslation->huisnummer_toevoeging = $adresData['HuisnummerToevoeging'];
if (isset($adresData['Postcode'])) $objectDetailsTranslation->postcode = $adresData['Postcode'];
if (isset($adresData['Woonplaats'])) $objectDetailsTranslation->woonplaats = $adresData['Woonplaats'];
if (isset($adresData['Land'])) $objectDetailsTranslation->land = $adresData['Land'];
}
if (isset($xml['Aanvaarding'])) $objectDetailsTranslation->aanvaarding = $xml['Aanvaarding'];
if (isset($xml['DatumAanvaarding'])) $objectDetailsTranslation->toelichting_aanvaarding = $xml['DatumAanvaarding'];
if (isset($xml['ToelichtingAanvaarding'])) $objectDetailsTranslation->toelichting_aanvaarding = $xml['ToelichtingAanvaarding'];
if (isset($xml['DatumAanvaarding'])) $this->updateYearMonthDate($objectDetailsTranslation, 'datum_aanvaarding', $xml['DatumAanvaarding']);
if (isset($xml['ObjectAanmelding'])) $objectDetailsTranslation->object_aanmelding = $xml['ObjectAanmelding'];
if (isset($xml['DatumInvoer'])) $this->updateYearMonthDate($objectDetailsTranslation, 'datum_invoer', $xml['DatumInvoer']);
if (isset($xml['DatumWijziging'])) $this->updateYearMonthDate($objectDetailsTranslation, 'datum_wijziging', $xml['DatumWijziging']);
if (isset($xml['DatumVeiling'])) $this->updateYearMonthDate($objectDetailsTranslation, 'datum_veiling', $xml['DatumVeiling']);
if (isset($xml['StatusBeschikbaarheid'])) {
$statusBeschikbaarheid = $xml['StatusBeschikbaarheid'];
if (isset($statusBeschikbaarheid['Status'])) $objectDetailsTranslation->status_beschikbaarheid = $statusBeschikbaarheid['Status'];
if (isset($statusBeschikbaarheid['VerkochtOnderVoorbehoud'])) {
$verkochtOnderVoorbehoud = $statusBeschikbaarheid['VerkochtOnderVoorbehoud'];
if(isset($verkochtOnderVoorbehoud['Datum'])) $this->updateYearMonthDate($objectDetailsTranslation, 'status_verkocht_onder_voorbehoud_datum', $verkochtOnderVoorbehoud['Datum']);
if(isset($verkochtOnderVoorbehoud['DatumVoorbehoudTot'])) $this->updateYearMonthDate($objectDetailsTranslation, 'status_verkocht_onder_voorbehoud_datum_tot', $verkochtOnderVoorbehoud['DatumVoorbehoudTot']);
}
if (isset($statusBeschikbaarheid['TransactieDatum'])) $this->updateYearMonthDate($objectDetailsTranslation, 'status_transactie_datum', $statusBeschikbaarheid['TransactieDatum']);
}
if (isset($xml['Bouwvorm'])) $objectDetailsTranslation->bouwvorm = $xml['Bouwvorm'];
if (isset($xml['Aanbiedingstekst'])) $objectDetailsTranslation->aanbiedingstekst = $xml['Aanbiedingstekst'];
$objectDetailsTranslation->save();
if(isset($xml['Koop'])) $this->createKoopFromXML($xml['Koop'], $objectDetails, $language);
if(isset($xml['Huur'])) $this->createHuurFromXml($xml['Huur'], $objectDetails, $language);
return $objectDetails;
}
private function createKoopFromXML(array $xml, ObjectDetails $objectDetails, Language $language): Koop
{
$koop = $objectDetails->koop()->first();
if(! $koop) {
$koop = new Koop();
$koop->objectDetails()->associate($objectDetails);
$koop->save();
}
$koopTranslation = $koop->translations()->where('language_id', '=', $language->id)->first();
if(! $koopTranslation) {
$koopTranslation = new KoopTranslation();
$koopTranslation->translatable()->associate($koop);
$koopTranslation->language()->associate($language);
}
if (isset($xml['Prijsvoorvoegsel'])) $koopTranslation->prijs_voorvoegsel = $xml['Prijsvoorvoegsel'];
if (isset($xml['Koopprijs'])) $koopTranslation->koop_prijs = $xml['Koopprijs'];
if (isset($xml['KoopConditie'])) $koopTranslation->koop_conditie = $xml['KoopConditie'];
if (isset($xml['KoopSpecificatie'])) $koopTranslation->koop_specificatie = $xml['KoopSpecificatie'];
if (isset($xml['WOZ'])) {
$woz = $xml['WOZ'];
if (isset($woz['WOZWaarde'])) $koopTranslation->woz_waarde = $woz['WOZWaarde'];
if (isset($woz['WOZWaardePeildatum'])) $koopTranslation->woz_waarde_peildatum = Carbon::createFromFormat('Y-m-d', $woz['WOZWaardePeildatum']);
}
$koopTranslation->save();
return $koop;
}
private function createHuurFromXml(array $xml, ObjectDetails $objectDetails, Language $language): Huur
{
$huur = $objectDetails->huur()->first();
if(! $huur) {
$huur = new Huur();
$huur->objectDetails()->associate($objectDetails);
$huur->save();
}
$huurTranslation = $huur->translations()->where('language_id', '=', $language->id)->first();
if(! $huurTranslation) {
$huurTranslation = new HuurTranslation();
$huurTranslation->translatable()->associate($huur);
$huurTranslation->language()->associate($language);
}
if (isset($xml['Huurprijs'])) $huurTranslation->huur_prijs = $xml['Huurprijs'];
if (isset($xml['HuurConditie'])) $huurTranslation->huur_conditie = $xml['HuurConditie'];
if (isset($xml['HuurSpecificatie'])) $huurTranslation->huur_specificatie = $xml['HuurSpecificatie'];
$huurTranslation->save();
return $huur;
}
/**
* Parses the XML into an PHP array that we can use for further processing
*
* @return array
*/
public function DownloadedXMLToArray():array
{
$storageFolder = $this->createAndGetRealworksStorageFolder(self::$DOWNLOAD_FOLDER_NAME);
$exampleFileName = self::$XML_FILE_NAME . '.xml';
$realFileName = $this->getTodaysXMLFileName();
$fileName = (file_exists($storageFolder . DIRECTORY_SEPARATOR . $realFileName)) ? $realFileName : $exampleFileName;
$xmlFile = file_get_contents($storageFolder . DIRECTORY_SEPARATOR . $fileName);
$xml = simplexml_load_string($xmlFile, 'SimpleXMLElement', LIBXML_NOCDATA);
$json = json_encode($xml);
$array = json_decode($json, true);
// Log::debug($array);
return $array;
}
private function getTodaysXMLFileName():string
{
return 'WONEN_' . date('Y') . date('m') . date('d') . '.xml';
}
/**
* Downloads all media from the given xml and passes it into a public folder
* @param array $xml
*/
private function DownloadMediaFromXMLToPublicFolders(array $xml)
{
$this->createAndGetPublicRealworksFolder();
if(! array_key_exists('Object', $xml)) return; //Empty
foreach($xml['Object'] as $objectArray)
{
if(! $this->hasValidMainPhoto($objectArray)) {
$objectId = $objectArray['ObjectSystemID'] ?? 'unknown';
Log::error('Main photo missing or invalid. Skipping object: ' . $objectId);
continue;
}
$this->downloadMediaListIfNotAlreadyFromObjectAndReturnMediaArray($objectArray);
}
}
/**
* @param string $objectSystemId
* @param string $place
* @param string $street
* @param string $houseNumber
* @return string
*/
public function getPublicFolderPathForObject(string $objectSystemId, string $place, string $street, string $houseNumber)
{
$folder = \Str::slug($place . '-' . $street . '-' . $houseNumber . '-' . $objectSystemId);
$rootFolder = $this->createAndGetPublicRealworksFolder($folder);
return $rootFolder;
}
/**
* Downloads every file from the MediaLijst array inside the given object and returns the MediaLijst array after that
*
* @param array $object
* @return array|mixed
*/
public function downloadMediaListIfNotAlreadyFromObjectAndReturnMediaArray(&$object = ['plainId' => 0, 'id' => 0, 'MediaLijst' => null], $id = 0)
{
$mediaReturnArray = [];
if(isset($object['MediaLijst']) && isset($object['ObjectSystemID']) && isset($object['ObjectDetails']))
{
//Validation
if(! is_array($object['MediaLijst'])) $this->mailOnError('At least one MediaLijst entry in the Realworks xml wasn\'t an array. Needs direct fix');
if(! is_array($object['ObjectDetails'])) $this->mailOnError('At least one ObjectDetails entry in the Realworks xml wasn\'t an array. Needs direct fix');
if(! is_string($object['ObjectSystemID'])) $this->mailOnError('At least one ObjectSystemID entry in the Realworks xml wasn\'t a string. Needs direct fix');
if(! isset($object['ObjectDetails']['Adres']) || ! isset($object['ObjectDetails']['Adres']['Nederlands']))
$this->mailOnError('At least one ObjectDetails entry in the Realworks xml wasn\'t properly formatted. Expecting keys ObjectDetails > Adres > Nederlands. Needs direct fix');
$addressArray = $object['ObjectDetails']['Adres']['Nederlands'];
if(! is_array($addressArray)) $this->mailOnError('At least one ObjectDetails > Adres > Nederlands entry in the Realworks xml wasn\'t an array. Needs direct fix');
if(count(array_diff(['Straatnaam', 'Huisnummer', 'Postcode', 'Woonplaats', 'Land'], array_keys($addressArray))) > 0)
$this->mailOnError("At least one ObjectDetails > Adres > Nederlands entry in the Realworks xml did not contain all keys: 'Straatnaam', 'Huisnummer', 'Postcode', 'Woonplaats', 'Land'. Needs direct fix " . json_encode($addressArray));
// Normaliseer MediaLijst → altijd array van media arrays
if (! isset($object['MediaLijst']) || empty($object['MediaLijst'])) {
$object['MediaLijst'] = [];
}
// Als MediaLijst geen array is, maak er één van
if (! is_array($object['MediaLijst'])) {
$object['MediaLijst'] = [$object['MediaLijst']];
}
// Loop door elke media groep
foreach ($object['MediaLijst'] as $index => &$mediaGroup) {
// Als mediaGroup geen array is → enkelvoudig item
if (! is_array($mediaGroup)) {
$mediaGroup = [$mediaGroup];
}
// Loop door elke individuele media entry
foreach ($mediaGroup as $i => &$mediaItem) {
// Wanneer mediaItem een string is → converteren naar array
// Voorbeeld: "HoofdFoto" wordt ["Groep" => "HoofdFoto"]
if (! is_array($mediaItem)) {
$mediaItem = ['Groep' => $mediaItem];
}
}
}
foreach($object['MediaLijst'] as $index => $mediaArrays) {
foreach($mediaArrays as $mediaArray)
{
if(! isset($mediaArray['Groep'])) $this->mailOnError("At least one MediaLijst item did not have 'Groep' set. " . json_encode($mediaArray));
if (
! isset($mediaArray['URL']) ||
empty($mediaArray['URL']) ||
! is_string($mediaArray['URL'])
) {
if(isset($mediaArray['Groep']) && $mediaArray['Groep'] === 'HoofdFoto') {
$objectId = $object['ObjectSystemID'] ?? 'unknown';
Log::error('Main photo missing or invalid. Skipping object: ' . $objectId);
return [];
}
$this->mailOnError('Media item missing or invalid URL field: ' . json_encode($mediaArray));
continue;
}
$propertyFolder = $this->getPublicFolderPathForObject($object['ObjectSystemID'], $addressArray['Woonplaats'], $addressArray['Straatnaam'], $addressArray['Huisnummer']);
$groupFolder = self::createFolderIfNotExitsOrFail($propertyFolder . DIRECTORY_SEPARATOR . $mediaArray['Groep']);
$url = trim($mediaArray['URL']);
$filenameWithExtension = self::getFilenameFromUrl($url);
$filePath = $groupFolder . DIRECTORY_SEPARATOR . $index . '_' . $filenameWithExtension;
//Check if the media was updated yesterday. If so we also need to refresh the image
$mediaUpdate = Carbon::createFromFormat('Y-m-d', $mediaArray['MediaUpdate']);
$now = Carbon::now();
$now = Carbon::create($now->year, $now->month, $now->day);
$needsUpdate = ($mediaUpdate && $now->subDay()->equalTo($mediaUpdate)) ? true : false;
if($needsUpdate) Log::debug('Going to download the following media item again since it was was updated yesterday: ' . $url);
if(! file_exists($filePath) || $needsUpdate) {
try {
$hSource = fopen($url, 'r');
$hDest = fopen($filePath, 'w');
while (! feof($hSource)) {
$chunk = fread($hSource, 5242880); //Read the url in a chunk of 5 MB
fwrite($hDest, $chunk); //Write the max 5MB of data to the file
unset($chunk); //Free up the 5MB of memory for garbage collection. Just to prevent memory exhaustion errors.
}
fclose($hSource);
fclose($hDest);
} catch(\Exception $exception) {
Log::debug('Tried to download a media file defined in the realworks xml but failed (' . $exception->getMessage() . '): ' . $url);
$this->mailOnError('Could not store media that needed to be download: ' . $exception->getMessage());
continue;
}
}
}
}
return $object['MediaLijst'];
}
return [];
}
/**
* @param array $object
* @return bool
*/
private function hasValidMainPhoto(array $object): bool
{
if(! isset($object['MediaLijst'])) return false;
$mediaLijst = $object['MediaLijst'];
if(! is_array($mediaLijst)) $mediaLijst = [$mediaLijst];
foreach($mediaLijst as $mediaGroup) {
if(! is_array($mediaGroup)) $mediaGroup = [$mediaGroup];
foreach($mediaGroup as $mediaItem) {
if(! is_array($mediaItem)) $mediaItem = ['Groep' => $mediaItem];
if(! isset($mediaItem['Groep']) || $mediaItem['Groep'] !== 'HoofdFoto') continue;
$url = $mediaItem['URL'] ?? null;
if(! is_string($url) || trim($url) === '') return false;
return true;
}
}
return false;
}
private function getUrl()
{
$base = config('realworks.api_url');
$queryParameters = [
'koppeling' => 'WEBSITE',
'user' => config('realworks.api_username'),
'password' => config('realworks.api_password'),
'og' => config('realworks.api_og_value'),
];
if(config('realworks.api_documentation') == 'true') $queryParameters['documentatie'] = 'true';
if(config('realworks.api_connected') == 'true') $queryParameters['connected'] = 'true';
if(config('realworks.api_version')) $queryParameters['versie'] = config('realworks.api_version');
if(config('realworks.api_office')) $queryParameters['kantoor'] = config('realworks.api_office');
//String "true" or "false" from config is changed into a real true or false. Putting that in the http_build_query causes it to change to 0 or 1. While realworks expects "true" or "false" for boolean
foreach($queryParameters as $index => $parameter)
{
if($parameter === true) $queryParameters[$index] = 'true';
elseif($parameter === false) $queryParameters[$index] = 'false';
}
$url = $base . '?' . http_build_query($queryParameters);
return $url;
}
/**
* @param string $url
* @return mixed
*/
public static function getFilenameFromUrl(string $url)
{
$path = parse_url($url, PHP_URL_PATH);
$exploded = explode('/', $path);
return last($exploded);
}
/**
* Creates a folder for the realworks zip and it's contents in laravels storage folder if needed and returns its absolute path
*
* @param string|null $subFolder
* @return string
*/
private static function createAndGetRealworksStorageFolder(string $subFolder = null):string
{
$folder = storage_path() . DIRECTORY_SEPARATOR . config('realworks.storagefolder_name', 'realworks') . DIRECTORY_SEPARATOR;
self::createFolderIfNotExitsOrFail($folder);
if($subFolder)
{
$absoluteSubFolder = self::createFolderIfNotExitsOrFail($folder . $subFolder);
return $absoluteSubFolder;
}
return $folder;
}
/**
* Creates a public folder for the frontend
*
* @param string|null $subFolder
* @return string
*/
private static function createAndGetPublicRealworksFolder(string $subFolder = null):string
{
$folder = public_path() . DIRECTORY_SEPARATOR . config('realworks.storagefolder_name', 'realworks') . DIRECTORY_SEPARATOR;
self::createFolderIfNotExitsOrFail($folder);
if($subFolder)
{
$absoluteSubFolder = $folder . $subFolder . DIRECTORY_SEPARATOR;
self::createFolderIfNotExitsOrFail($absoluteSubFolder);
return $absoluteSubFolder;
}
return $folder;
}
/**
* @param $folder
* @return mixed
*/
private static function createFolderIfNotExitsOrFail($folder)
{
if(! file_exists($folder)) {
if(! mkdir($folder, 0777, true)) throw new \RuntimeException('The realworks folder could not be created. ' . $folder);
}
return $folder;
}
/**
* Recursively deletes a the contents in a directory and if you specify the boolean argument to true the directory itself too
*
* @param $dir
* @param bool $deleteDir
*/
private static function rrmdir($dir, $deleteDir = true) {
if (is_dir($dir)) {
$objects = scandir($dir);
foreach ($objects as $object) {
if ($object != '.' && $object != '..') {
if (filetype($dir . DIRECTORY_SEPARATOR . $object) == 'dir')
self::rrmdir($dir . DIRECTORY_SEPARATOR . $object);
else {
if(! unlink($dir . DIRECTORY_SEPARATOR . $object)) throw new \RuntimeException('Could not empty the temporary folder to store the exported results in. Please contact your website builder ' . $dir . '/' . $object);
}
}
}
reset($objects);
if($deleteDir) rmdir($dir);
}
}
/**
* @param string $message
*/
private function mailOrShowErrorAndDie(string $message)
{
Log::error($message);
$mail = (new RealworksFailMail($message));
if(! \App::environment('local')) {
\Mail::send($mail);
// die();
} else {
// echo 'ERROR: '.$message.PHP_EOL;
// die();
}
}
/**
* @param string $message
*/
private function mailOnError(string $message)
{
Log::error($message);
$mail = (new RealworksFailMail($message));
if(! \App::environment('local')) {
\Mail::send($mail);
}
}
private function deletePublicFoldersFromNonExistingObjects()
{
//Get all realworks data property / object folders in the wwwroot
$publicRealworksFolder = $this->createAndGetPublicRealworksFolder();
$filesAndDirs = scandir($publicRealworksFolder);
$dirs = array_filter($filesAndDirs, function ($item) use ($publicRealworksFolder) {
$absolute = $publicRealworksFolder . $item;
return is_dir($absolute) && $item != '.' && $item != '..';
});
//Get the system ids from the property / object folders in the wwwroot
$objectSystemIdsFromStoredFiles = array_map(function ($dir) {
return last(explode('-', $dir));
}, $dirs);
//Get all system ids from objects in the database
$systemIdsPresentInDatabase = RealworkObject::get(['id', 'system_id'])->map(function (RealworkObject $object) {
return $object->system_id;
});
//Create al list of system ids which have a folder in wwwroot property / object folder but don't have a database entry
$systemIdsToRemoveFromPublicFolder = array_filter($objectSystemIdsFromStoredFiles, function ($systemId) use ($systemIdsPresentInDatabase) {
if($systemIdsPresentInDatabase->contains($systemId)) {
return false;
} else {
return true;
}
});
//Delete the wwwroot property / object folders which don't have a databse entry
foreach($systemIdsToRemoveFromPublicFolder as $systemIdToRemoveFromPublicFolder)
{
foreach($dirs as $dir)
{
$systemIdFromFolder = last(explode('-', $dir));
if($systemIdToRemoveFromPublicFolder == $systemIdFromFolder) {
$folderToDelete = $absolute = $publicRealworksFolder . $dir;
// echo $folderToDelete.PHP_EOL;
self::rrmdir($folderToDelete, true);
}
}
}
}
/**
* Build an array of ids that are listed in the XML
*
* @param array $xml
* @return array
*/
private function extractObjectSystemIds(array $xml) {
$xmlSystemIds = [];
if(! array_key_exists('Object', $xml)) $xmlSystemIds; //Empty
foreach($xml['Object'] as $objectArray)
{
if(! isset($objectArray['ObjectSystemID'])) $this->mailOrShowErrorAndDie('An object in the XML did not have a required ObjectSystemID. Stopped processing.');
$xmlSystemIds[] = $objectArray['ObjectSystemID'];
}
return $xmlSystemIds;
}
/**
* @return array
*/
private function getObjectIdsMissingInSystemsIdArray(Collection $realworksObjects, array $ids) {
//Loop over all our stored Object records and see if their are in the previously created xmlSystemIdsArray. If not, store them in an array.
return $realworksObjects->map(function (RealworkObject $object) use ($ids) {
if(! in_array((string) $object->system_id, $ids, true)) return $object->id;
return null;
})->filter(function ($systemId) { return $systemId !== null; })->toArray();
}
/**
* Delete al Objects that match some conditions.
*
* @param array $xml
*/
private function deleteObjectsFromDatabase(array $xml)
{
$this->createAndGetPublicRealworksFolder();
$allObjects = RealworkObject::with('objectDetails.translations')->get();
$xmlSystemIds = $this->extractObjectSystemIds($xml);
$objectIdsMissingInXml = $this->getObjectIdsMissingInSystemsIdArray($allObjects, $xmlSystemIds);
//Loop over all objects. Check if their ids exist in the objectIdsMissingInXml array. If so, return their ids based on some additional conditions.
$objectIdsToDelete = $allObjects->filter(function (RealworkObject $realworksObject) use ($objectIdsMissingInXml) {
//Check if this Object should be deleted sometime. If not return false to indicate that it should not.
if(! in_array($realworksObject->id, $objectIdsMissingInXml)) return false;
//Check if it has a dutch translation. If not it must be deleted directly.
/** @var ObjectDetailsTranslation $dutchTranslation */
$translation = $realworksObject->objectDetails->translations->where('language_id', '=', 104)->first();
if(! $translation) return true;
//If it is not "sold", delete it directly.
if(strtolower($translation->status_beschikbaarheid) !== 'verkocht') return true;
//If it is sold, we must keep it for a year
// return Carbon::now()->subYear() >= $translation->updated_at;
return Carbon::now()->subWeek(2) >= $translation->updated_at; //TODO. Comment this line when zelfverkopen has approved the change
})->pluck('id')->toArray();
RealworkObject::destroy($objectIdsToDelete);
}
private function updateYearMonthDate(Model $model, string $attribute, string $source) {
$source = Carbon::createFromFormat('Y-m-d', $source);
if(! $model->{$attribute}) $model->{$attribute} = Carbon::now();
$carbon = is_string($model->{$attribute}) ? Carbon::parse($model->{$attribute}) : $model->{$attribute};
if($carbon->year !== $source->year || $carbon->month !== $source->month || $carbon->day !== $source->day) {
$carbon->year = $source->year;
$carbon->month = $source->month;
$carbon->day = $source->day;
$model->{$attribute} = $carbon;
}
}
}