Last active
May 14, 2022 00:46
-
-
Save edwardyi/e441d036fbf248b389bef4c672a1b88d to your computer and use it in GitHub Desktop.
BatchBase crud for laravel
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <?php | |
| namespace App\Traits\Service\Batch; | |
| use App\Traits\Repository\Collectable; | |
| use App\Validate\UtilDate; | |
| use App\Exceptions\Batch\{ItemIdNotFoundException, ItemIdMissingException}; | |
| use Exception; | |
| use Illuminate\Support\{Arr, Collection}; | |
| /** | |
| * BatchBase | |
| */ | |
| abstract class BatchBase | |
| { | |
| use Collectable; | |
| /** | |
| * @param string $pkName; | |
| */ | |
| public $pkName = ''; | |
| /** | |
| * @param object $existingModels; | |
| */ | |
| public $existingModels; | |
| /** | |
| * @param object $dbParentItemModels; | |
| */ | |
| public $dbParentItemModels; | |
| /** | |
| * @param array $availableFields; | |
| */ | |
| public $availableFields; | |
| /** | |
| * getAllowedUserInputColumns | |
| * | |
| * @return array | |
| */ | |
| abstract public function getAllowedUserInputColumns(); | |
| /** | |
| * getModelId | |
| * | |
| * @return array | |
| */ | |
| abstract public function getModelId(); | |
| /** | |
| * getExistingModelByIds | |
| * | |
| * @param array $ids | |
| * | |
| * @return array | |
| */ | |
| abstract public function getExistingModelByIds(array $ids); | |
| /** | |
| * construct | |
| */ | |
| public function __construct() | |
| { | |
| $this->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 | |
| ] | |
| ]; | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <?php | |
| namespace App\Traits\Repository; | |
| use App\Models\Model; | |
| use Illuminate\Support\{Arr, Collection}; | |
| /** | |
| * Collectable | |
| */ | |
| trait Collectable | |
| { | |
| /** | |
| * keys | |
| */ | |
| public function keys(Collection $collection, string $keyIndex, $isUnique = true) | |
| { | |
| $keyObj = $collection->keyBy($keyIndex)->keys(); | |
| return $isUnique ? $keyObj->unique() : $keyObj; | |
| } | |
| /** | |
| * remove model append | |
| * | |
| * @param object $model | |
| * @param string $appendKey | |
| * | |
| * @return array | |
| */ | |
| public function removeModelAppend(Model $model, string $appendKey) | |
| { | |
| $result = $model->toArray(); | |
| unset($result[$appendKey]); | |
| return $result; | |
| } | |
| /** | |
| * removeModelAttributes | |
| * | |
| * @param Model $model | |
| * @param array $attributes = [] | |
| * @param bool $isRemove = true | |
| * | |
| * @return array | |
| */ | |
| public function removeModelAttributes(Model $model, array $attributes = [], $isRemove = true) | |
| { | |
| $result = $model->toArray(); | |
| if ($isRemove == false) { | |
| return $result; | |
| } | |
| foreach ($attributes as $rowAttribute) { | |
| unset($result[$rowAttribute]); | |
| } | |
| return $result; | |
| } | |
| /** | |
| * clean | |
| * | |
| * @param array $data | |
| * @param string $field | |
| * | |
| * @return array | |
| */ | |
| public function clean(&$data, $field) | |
| { | |
| if (is_null(Arr::get($data, $field))) { | |
| unset($data[$field]); | |
| } | |
| return $data; | |
| } | |
| /** | |
| * input | |
| * | |
| * @param array $data | |
| * @param array $modelFields | |
| * | |
| * @return array | |
| */ | |
| public function input($data, $modelFields) | |
| { | |
| $input = []; | |
| foreach ($data as $rowKey => $rowValue) { | |
| if (in_array($rowKey, $modelFields)) { | |
| $input[$rowKey] = $rowValue; | |
| } | |
| } | |
| return $input; | |
| } | |
| /** | |
| * inputDetailByItemLength | |
| * | |
| * @param array $data | |
| * @param integer $legnth | |
| * @param array $modelFields | |
| * | |
| * @return array | |
| */ | |
| public function inputDetailByItemLength(array $data,int $legnth, $modelFields) | |
| { | |
| $rowDetailInput = []; | |
| for($counter=0; $counter<$legnth; $counter++) { | |
| $rowDetailInput[$counter] = []; | |
| foreach ($modelFields as $rowField) { | |
| $rowDetailInput[$counter][$rowField] = Arr::get($data, sprintf('%s.%s', $rowField, $counter), ''); | |
| } | |
| } | |
| return $rowDetailInput; | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <?php | |
| namespace App\Traits\Service\Batch; | |
| use App\Models\ProductRouting as ProductRoutingModel; | |
| use App\Repositories\ProductRoutingRepository; | |
| /** | |
| * App\Traits\Service\Batch\ProductRouting | |
| */ | |
| class ProductRouting extends BatchBase | |
| { | |
| /** | |
| * @param string $pkName; | |
| */ | |
| public $pkName = 'product_routing.id'; | |
| /** | |
| * @param object $model; | |
| */ | |
| public $model; | |
| /** | |
| * @param object $productRoutingRepo; | |
| */ | |
| public $productRoutingRepo; | |
| /** | |
| * construct | |
| */ | |
| public function __construct( | |
| ProductRoutingModel $productRouting, | |
| ProductRoutingRepository $productRoutingRepo | |
| ) { | |
| $this->model = $productRouting; | |
| $this->productRoutingRepo = $productRoutingRepo; | |
| parent::__construct(); | |
| } | |
| /** | |
| * getAllowedUserInputColumns | |
| * | |
| * @return array | |
| */ | |
| public function getAllowedUserInputColumns() | |
| { | |
| return $this->availableFields; | |
| } | |
| /** | |
| * getModelId | |
| * | |
| * @return array | |
| */ | |
| public function getModelId() | |
| { | |
| return $this->productRoutingRepo->getIdNumber($this->model); | |
| } | |
| /** | |
| * getExistingModelByIds | |
| * | |
| * @param array $ids | |
| * | |
| * @return array | |
| */ | |
| public function getExistingModelByIds(array $ids) | |
| { | |
| return $this->productRoutingRepo->whereIn('ID', $ids)->get(); | |
| } | |
| /** | |
| * insert | |
| * | |
| * @param array $data | |
| * | |
| * @return bool | |
| */ | |
| public function insert(array $data) | |
| { | |
| return $this->model->insert($data); | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <?php | |
| namespace App\Validate; | |
| use \Illuminate\Support\Arr; | |
| use Carbon\Carbon; | |
| use \Exception; | |
| /** | |
| * UtilDate | |
| */ | |
| class UtilDate | |
| { | |
| /** | |
| * validate | |
| * | |
| * @param string $dateValue | |
| * @param string $hint | |
| * | |
| * @return string | |
| */ | |
| public static function validate(&$dateValue, $hint, $format='Y-m-d') | |
| { | |
| try { | |
| if (is_array($dateValue)) { | |
| foreach ($dateValue as $rowIndex => $rowDate) { | |
| $dateValue[$rowIndex] = Carbon::parse(trim($rowDate))->format($format); | |
| } | |
| } else { | |
| $dateValue = Carbon::parse(trim($dateValue))->format($format); | |
| } | |
| } catch (\Carbon\Exceptions\InvalidFormatException $e) { | |
| throw new Exception($hint); | |
| } | |
| return $dateValue; | |
| } | |
| /** | |
| * get | |
| * | |
| * @param array $dateValue | |
| * @param array $options | |
| * | |
| * @return string|bool | |
| */ | |
| public static function get(array &$data, array $options) | |
| { | |
| $field = Arr::get($options, 'field'); | |
| if (!array_key_exists($field, $data)) { | |
| return false; | |
| } | |
| try { | |
| $hint = Arr::get($options, 'hint'); | |
| $format = Arr::get($options, 'format', 'Y-m-d'); | |
| $defaultHint = is_null($hint) ? "The {$field} date is invalid" : $hint; | |
| if ($data[$field]) { | |
| $data[$field] = self::validate($data[$field], $defaultHint, $format); | |
| } | |
| } catch (Exception $e) { | |
| throw new Exception($e->getMessage()); | |
| } | |
| return $data[$field]; | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment