init(); } /** * init * * @return void */ public function init() { $this->availableFields = $this->model->getFillable(); } /** * setDbParentItemModels * * @param object $dbParentItemModels * * @return object */ public function setDbParentItemModels($dbParentItemModels) { // convert to collection if (gettype($dbParentItemModels) == 'array') { $dbParentItemModels = collect($dbParentItemModels); } $this->dbParentItemModels = $dbParentItemModels; } /** * getFilteredAllowInput * * @return array */ public function getFilteredAllowInput($excludes = ['ID']) { return array_filter($this->getAllowedUserInputColumns(), function($item) use ($excludes) { return !in_array($item, $excludes); }); } /** * validateItemUpdateByFilterOptions * * @param array $inputItems * @param array $filterOptions * @param string $keyBy = 'ID' * * @return array */ public function validateItemUpdateByFilterOptions($inputItems, $filterOptions, $keyBy = 'ID') { $isUpdate = count($filterOptions) == 0; if ($isUpdate) { // validate item id not exists array_filter($inputItems, function($rowItem) use ($keyBy) { if (!array_key_exists($keyBy, $rowItem)) { throw new ItemIdMissingException("item明細設定有誤:".$this->pkName); } }); } } /** * addValidatedDateToUserInputs * * @param array $inputs * @param array $dateFields * * @return array */ public function addValidatedDateToUserInputs(array $inputs, array $dateFields) { $inputItems = $inputs['ITEMS']; foreach ($inputItems as $rowIndex => $rowItems) { foreach ($dateFields as $rowDate) { if (isset($rowItems[$rowDate])) { UtilDate::get($rowItems, ['field' => $rowDate, 'hint' => $rowDate.'日期格式有誤']); } } $inputItems[$rowIndex] = $rowItems; } $inputs['ITEMS'] = $inputItems; return $inputs; } /** * addCodeToUserInputs * * @param array $data * * @return array */ public function addCodeToUserInputs(array $data) { // count how many records in database $dbSn = $this->model->get()->count(); $this->setExistingModelsFromInput($data['ITEMS']); $existingData = $this->existingModels->toArray(); foreach ($data['ITEMS'] as $rowIndex => $rowItem) { $hasId = Arr::get($rowItem, 'ID', false); $hasExistingData = $hasId ? Arr::get($existingData, $hasId, false) : false; // default using existing data or get a new serial number $dbSnCode = $hasExistingData ? $hasExistingData['CODE'] : SerialNumber::getSnLeftPadFromLength($rowIndex + $dbSn + 1, $this->getSerialNumberLength()); // replace with user CODE from input if (array_key_exists('CODE', $rowItem)) { $dbSnCode = $rowItem['CODE']; } $data['ITEMS'][$rowIndex]['CODE'] = $dbSnCode; } return $data; } /** * setExistingModelsFromInput * * @param array $data * * @return array */ public function setExistingModelsFromInput(array $rawItemInputs) { if (!$this->existingModels) { $collectInputs = collect($rawItemInputs); $ids = $collectInputs->keyBy('ID') ->filter(function($item, $rowKey) { return $rowKey != ""; }) ->keys() ->toArray(); // get existing data by ids $this->existingModels = $this->getExistingModelByIds($ids)->keyBy('ID'); } } /** * getItemInputs * * @param array $rawItemInputs * * @return array */ public function getItemInputs(array $rawItemInputs) { if (!method_exists($this, 'getAllowedUserInputColumns')) { throw new Exception("[BatchBase] getAllowedUserInputColumns not defined!"); } if (!method_exists($this, 'getModelId')) { throw new Exception("[BatchBase] getModelId not defined!"); } if (!method_exists($this, 'getExistingModelByIds')) { throw new Exception("[BatchBase] getExistingModelByIds not defined"); } $cleans = []; $cleans['insert'] = []; $cleans['update'] = []; $cleans['delete'] = []; // get all ids for existing model $collectInputs = collect($rawItemInputs); $this->setExistingModelsFromInput($rawItemInputs); $existingData = $this->existingModels->toArray(); // build format inputs by fillabe fields start $model = $this->model; $formattedInputs = []; foreach ($this->availableFields as $rowField) { $collectInputs->map(function($item, $itemIndex) use(&$formattedInputs, $rowField, $model) { $formattedInputs[$itemIndex][$rowField] = Arr::get($item, $rowField, $model->$rowField); }); } unset($rawItemInputs); unset($collectInputs); // build format inputs by fillabe fields end foreach ($formattedInputs as $rowIndex => $rowItemInput) { $hasId = Arr::get($rowItemInput, 'ID', true); $rowId = is_null($hasId) ? $this->getModelId() : $hasId; // merge existing data $rowExistingData = Arr::get($existingData, $rowId, []); $isInsert = count($rowExistingData) == 0; $rowItemInput = array_merge($rowExistingData, $rowItemInput); $rowAction = ($isInsert === true) ? 'insert' : 'update'; $cleans[$rowAction][$rowIndex] = $this->input($rowItemInput, $this->getAllowedUserInputColumns()); $cleans[$rowAction][$rowIndex]['ID'] = $rowId; if ($isInsert === true) { $cleans[$rowAction][$rowIndex]['INSERT_USER'] = $this->getInsertUser(); $cleans[$rowAction][$rowIndex]['INSERT_DATE'] = $this->getCurrentDateFromFormat("Y-m-d H:i:s"); $cleans[$rowAction][$rowIndex]['MODIFY_USER'] = NULL; $cleans[$rowAction][$rowIndex]['MODIFY_DATE'] = NULL; } else { $cleans[$rowAction][$rowIndex]['INSERT_USER'] = $rowItemInput['INSERT_USER']; $cleans[$rowAction][$rowIndex]['INSERT_DATE'] = $rowItemInput['INSERT_DATE']; $cleans[$rowAction][$rowIndex]['MODIFY_USER'] = $this->getInsertUser(); $cleans[$rowAction][$rowIndex]['MODIFY_DATE'] = $this->getCurrentDateFromFormat("Y-m-d H:i:s"); } // sort with key ksort($cleans[$rowAction][$rowIndex]); } // find extra item to delete if ($this->dbParentItemModels) { $dbItemIds = $this->dbParentItemModels->keyBy('ID')->keys()->toArray(); $inputItemIds = collect($formattedInputs)->keyBy('ID')->keys()->toArray(); $cleans['delete'] = array_diff($dbItemIds, $inputItemIds); } return $cleans; } /** * updateBatch * * @param array $data * * @return array */ public function updateBatch(array $data) { $result = []; $updateData = collect($data)->keyBy('ID'); $this->existingModels->map(function($item, $rowIndex) use ($updateData, &$result) { $rowItemId = $item->ID; $isUpdate = $item->update($updateData[$rowItemId]); $result[$rowItemId] = ['status' => $isUpdate, 'item' => $item->toArray()]; }); return $result; } /** * execute * * @param array $cleanInputs * * @return array */ public function execute(array $cleanInputs) { $deleteInputs = $cleanInputs['delete']; $insertOrUpdateInputs = array_merge($cleanInputs['insert'], $cleanInputs['update']); $isDeleteResult = false; $insertOrUpdateResult = false; // delete for update if (count($deleteInputs) > 0) { $isDeleteResult = $this->model->destroy($deleteInputs); } if (count($insertOrUpdateInputs) > 0) { $insertOrUpdateResult = $this->model::insertOnDuplicateKey($insertOrUpdateInputs, array_keys(reset($insertOrUpdateInputs))); } return [ 'is_delete' => [ 'status' => $isDeleteResult, 'data' => $deleteInputs ], 'insert_or_update' => [ 'status' => $insertOrUpdateResult, 'data' => $insertOrUpdateInputs ] ]; } }