validate(); if ($this->isNewModel()) { $this->beforeCreate(); } $data = $this->modelToYamlArray(); if ($this->yamlSection) { $fileData = $this->originalFileData; // Save the section data only if the section is not empty. if ($data) { $originalData = $this->preserveOriginal === true ? ($fileData[$this->yamlSection] ?? []) : []; $fileData[$this->yamlSection] = $this->arrayMergeMany($originalData, $data); } else { if (array_key_exists($this->yamlSection, $fileData)) { unset($fileData[$this->yamlSection]); } } $data = $fileData; } $dumper = new YamlDumper(); if ($data !== null) { $yamlData = $dumper->dump($data, 20, 0, false, true); } else { $yamlData = ''; } $filePath = File::symbolizePath($this->getFilePath()); $isNew = $this->isNewModel(); if (File::isFile($filePath)) { if ($isNew || $this->originalFilePath != $filePath) { throw new ValidationException(['fileName' => Lang::get('rainlab.builder::lang.common.error_file_exists', ['path'=>basename($filePath)])]); } } $fileDirectory = dirname($filePath); if (!File::isDirectory($fileDirectory)) { if (!File::makeDirectory($fileDirectory, 0777, true, true)) { throw new ApplicationException(Lang::get('rainlab.builder::lang.common.error_make_dir', ['name'=>$fileDirectory])); } } if (@File::put($filePath, $yamlData) === false) { throw new ApplicationException(Lang::get('rainlab.builder::lang.yaml.save_error', ['name'=>$filePath])); } @File::chmod($filePath); if ($this->isNewModel()) { $this->afterCreate(); } if ($this->yamlSection) { $this->originalFileData = $data; } if (strlen($this->originalFilePath) > 0 && $this->originalFilePath != $filePath) { @File::delete($this->originalFilePath); } $this->originalFilePath = $filePath; } /** * load */ protected function load($filePath) { $filePath = File::symbolizePath($filePath); if (!File::isFile($filePath)) { throw new ApplicationException('Cannot load the model - the original file is not found: '.basename($filePath)); } try { $data = Yaml::parse(File::get($filePath)); } catch (Exception $ex) { throw new ApplicationException(sprintf('Cannot parse the YAML file %s: %s', basename($filePath), $ex->getMessage())); } $this->originalFilePath = $filePath; if ($this->yamlSection) { $this->originalFileData = $data; if (!is_array($this->originalFileData)) { $this->originalFileData = []; } if (array_key_exists($this->yamlSection, $data)) { $data = $data[$this->yamlSection]; } else { $data = []; } } $this->yamlArrayToModel($data); } /** * deleteModel */ public function deleteModel() { if (!File::isFile($this->originalFilePath)) { throw new ApplicationException('Cannot load the model - the original file is not found: '.$filePath); } if (strtolower(substr($this->originalFilePath, -5)) !== '.yaml') { throw new ApplicationException('Cannot delete the model - the original file should be a YAML document'); } File::delete($this->originalFilePath); } /** * initDefaults */ public function initDefaults() { } /** * isNewModel */ public function isNewModel() { return !strlen($this->originalFilePath); } /** * beforeCreate */ protected function beforeCreate() { } /** * afterCreate */ protected function afterCreate() { } /** * getArrayKeySafe */ protected function getArrayKeySafe($array, $key, $default = null) { return array_key_exists($key, $array) ? $array[$key] : $default; } /** * arrayMergeMany is a deep array merge function used to preserve existing * YAML properties and splicing in new ones from builder. */ protected function arrayMergeMany($arr1, $arr2) { $arrMerge = array_merge($arr1, $arr2); foreach ($arrMerge as $key => $val) { if (!is_array($val) || !$this->isArrayAssociative($val)) { continue; } if (isset($arr1[$key]) && isset($arr2[$key])) { $arrMerge[$key] = $this->arrayMergeMany($arr1[$key], $arr2[$key]); } } return $arrMerge; } /** * isArrayAssociative retruns true if the array is associative */ protected function isArrayAssociative($arr) { return is_array($arr) && array_keys($arr) !== range(0, count($arr) - 1); } /** * Converts the model's data to an array before it's saved to a YAML file. * @return array */ abstract protected function modelToYamlArray(); /** * Load the model's data from an array. * @param array $array An array to load the model fields from. */ abstract protected function yamlArrayToModel($array); /** * Returns a file path to save the model to. * @return string Returns a path. */ abstract protected function getFilePath(); }