attrs = new FormAttrs(); $this->model = $model; $this->dataUrl = admin_api_url(request()->path()); $this->isGetData = request()->header('getData')=="true"; $this->actions = new FormActions($this); } public static function make($model = null) { $form = new static($model); $form->action(admin_api_url(request()->path().'?'.\http_build_query(request()->query()))); return $form; } /** * 快捷生成字段 * @param $prop * @param string $label * @param string $field * @return FormItem */ public function item($prop, $label = '', $field = '') { $item = $this->addItem($prop, $label, $field); $this->row(function (Row $row) use ($item) { $row->item($item); }); return $item; } /** * 多列布局字段 * * @param $prop * @param string $label * @param string $field * * @return FormItem */ public function rowItem($prop, $label = '', $field = '') { return $this->addItem($prop, $label, $field); } /** * 表单自定义布局 * * @param \Closure $closure * * @return $this */ public function row(\Closure $closure) { $row = new Row(); call_user_func($closure, $row, $this); $this->tab("default", function (FormTab $formTab) use ($row) { $formTab->row($row); }); return $this; } /** * 自定义tab布局 * @param $tabName * @param \Closure $closure * @return $this */ public function tab($tabName, \Closure $closure) { $tab = collect($this->formItemLayout)->filter(function (FormTab $formTab) use ($tabName) { return $formTab->getName() == $tabName; })->first(); if (empty($tab)) { $tab = new FormTab($tabName, $this); call_user_func($closure, $tab, $this); $this->formItemLayout[] = $tab; } else { call_user_func($closure, $tab, $this); } return $this; } /** * tab位置 * * @param $tabPosition * * @return $this */ public function tabPosition($tabPosition) { $this->tabPosition = $tabPosition; return $this; } /** * @param $prop * @param $label * @param $field * * @return FormItem */ protected function addItem($prop, $label, $field) { $item = new FormItem($prop, $label, $field); $item->setForm($this); $this->formItems[] = $item; return $item; } /** * @param array $items */ protected function items($items = []) { $this->ignoreEmptyProps = collect($items)->filter(function (FormItem $item) { return $item->isIgnoreEmpty(); })->map(function (FormItem $item) { return $item->getProp(); })->flatten()->all(); // 根据所处模式抛弃组件 $this->formItemsAttr = collect($items)->filter(function (FormItem $item) { return $this->isMode($item->gethiddenMode()); })->map(function (FormItem $item) { return $item->getProp(); }); /**@var FormItem $item */ foreach ($items as $item) { Arr::set($this->formItemsValue, $item->getProp(), $item->getDefaultValue()); Arr::set($this->formRules, $item->getProp(), $item->getRules()); } } /** * 自定义表单动作 * * @param $closure * * @return $this */ public function actions(\Closure $closure) { call_user_func($closure, $this->actions); return $this; } /** * 表单头部组件 * * @param $closure * * @return $this */ public function top($closure) { $this->top = new Content(); call_user_func($closure, $this->top); return $this; } /** * 表单底部组件 * * @param $closure * * @return $this */ public function bottom($closure) { $this->bottom = new Content(); call_user_func($closure, $this->bottom); return $this; } /** * @return string */ public function getAction(): string { if ($this->action) { return $this->action; } if ($this->isMode(static::MODE_EDIT)) { return $this->resource() . '/' . $this->id; } if ($this->isMode(static::MODE_CREATE)) { return $this->resource(-1); } return admin_api_url(request()->path()); } /** * 设置表单编辑模式获取编辑数据地址 * * @param string $dataUrl * * @return $this */ public function dataUrl(string $dataUrl) { $this->dataUrl = $dataUrl; return $this; } /** * 设置表单提交地址 * * @param string $action * * @return $this */ public function action($action) { $this->action = $action; return $this; } protected function setMode($mode = 'create') { $this->mode = $mode; } public function isMode($mode): bool { return $this->mode === $mode; } public function setResourceId($id) { $this->id = $id; } public function getResourceId() { return $this->id; } public function resource($slice = -1): string { $segments = explode('/', trim(admin_api_url(request()->path()), '/')); if ($slice !== 0) { $segments = array_slice($segments, 0, $slice); } return '/' . implode('/', $segments); } /** * @return string */ public function getMode(): string { return $this->mode; } /** * 获取模型 * @return Model */ public function model() { return $this->model; } /** * 设置清除模型缓存 * * @param bool $cachePut * * @return self */ public function cachePut(bool $cachePut = true) { if (property_exists($this->model, 'useCacheBuilder')) { $this->model->useCacheBuilder = $cachePut; } return $this; } /** * 获取表单是否是编辑模式 * @return bool */ public function isEdit() { return $this->isEdit; } /** * 获取表单是否是编辑模式 * * @param bool $isEdit * * @return void */ public function setEdit($isEdit = false) { $this->isEdit = $isEdit; } /** * 添加表单验证规则 * * @param $rules * @param $message * * @return $this */ public function addValidatorRule($rules, $message = []) { $this->addRule = $rules; $this->addRuleMessage = $message; return $this; } /** * @param $data * * @return string */ protected function validatorData($data) { $rules = []; $ruleMessages = []; $field = []; /* @var FormItem $formItem */ foreach ($this->formItems as $formItem) { if (empty($formItem->getServeRole())) { continue; } $field[$formItem->getField()] = $formItem->getLabel(); $rules[$formItem->getField()] = $formItem->getServeRole(); $messages = $formItem->getServeRulesMessage(); if (is_array($messages)) { foreach ($messages as $key => $message) { $ruleMessages[$formItem->getField() . '.' . $key] = $message; } } } $rules = array_merge($rules, $this->addRule); $ruleMessages = array_merge($ruleMessages, $this->addRuleMessage); $validator = new Validate($rules, $ruleMessages, $field); if ($validator->check($data) !== true) { throw new ValidateException(422, (string)$validator->getError()); } } public function input($key, $value = null) { if (is_null($value)) { return Arr::get($this->inputs, $key); } return Arr::set($this->inputs, $key, $value); } protected function prepare($data = []) { //处理要过滤的字段 $this->inputs = array_merge($this->removeIgnoredFields($data), $this->inputs); //处理表单提交时事件 if (($response = $this->callSaving()) instanceof Response) { return $response; } //处理关联字段 $this->relations = $this->getRelationInputs($this->inputs); $this->updates = Arr::except($this->inputs, array_keys($this->relations)); } protected function removeIgnoredFields($input): array { Arr::forget($input, $this->ignored); return $input; } protected function getRelationInputs($inputs = []): array { $relations = []; foreach ($inputs as $column => $value) { $column = Str::camel($column); if (!method_exists($this->model, $column)) { continue; } $relation = call_user_func([$this->model, $column]); if ($relation instanceof Relation) { $relations[$column] = $value; } } return $relations; } protected function prepareInsert($inserts) { $prepared = []; $columns = Schema::getColumnListing($this->model()->getTable()); foreach ($inserts as $key => $value) { if (in_array($key, $columns)) { Arr::set($prepared, $key, $value); } } return $prepared; } public function getRelations(): array { $relations = []; $columns = collect($this->formItems)->map(function (FormItem $item) { return $item->getProp(); })->toArray(); foreach (Arr::flatten($columns) as $column) { if (Str::contains($column, '.')) { [$relation] = explode('.', $column); if (method_exists($this->model, $relation) && $this->model->$relation() instanceof Relation ) { $relations[] = $relation; } } else if (method_exists($this->model, $column)) { $relations[] = $column; } } return array_unique($relations); } public function store() { if (($result = $this->callSubmitted()) instanceof Response) { return $result; } $data = request()->all(); if ($validationMessages = $this->validatorData($data)) { return UI::responseError($validationMessages); } if (($response = $this->prepare($data)) instanceof Response) { return $response; } DB::transaction(function () use ($data) { $inserts = $this->prepareInsert($this->updates); foreach ($inserts as $key => $value) { $this->model->setAttribute($key, $value); } $this->model->save(); $this->updateRelation($this->relations); if (($result = $this->callDbTransaction()) instanceof Response) { throw new \Exception(400, $result->getBody()->getContents()); } }); if (($result = $this->callSaved()) instanceof Response) { return $result; } return UI::responseMessage('保存成功'); } /** * 编辑 * * @param $id * * @return array|string */ public function edit($id = 0) { $this->isEdit = true; $this->setMode(self::MODE_EDIT); $this->setResourceId($id); return $this; } protected function deleteFiles(Model $model, $forceDelete = false) { $data = $model->toArray(); collect($this->formItems)->filter(function (FormItem $formItem) { return $formItem->getComponent() instanceof Upload; })->each(function (FormItem $formItem) use ($data) { $formItem->setOriginal($data); /**@var Upload $component */ $component = $formItem->getComponent(); $component->destroy($formItem); }); } /** * 模型删除 * @param $id * @return mixed */ public function destroy($id) { try { if (($ret = $this->callDeleting($id)) instanceof Response) { return $ret; } collect(explode(',', $id))->each(function ($id) { $builder = $this->model()->newQuery(); $relations = $this->getRelations(); $this->model = $model = $builder->with($relations)->findOrFail($id); //删除文件 $this->deleteFiles($model); //删除关联模型数据 $this->deleteRelation($relations); $model->delete(); }); if (($ret = $this->callDeleted()) instanceof Response) { return $ret; } return UI::responseMessage('删除成功'); } catch (\Exception $exception) { return UI::responseError($exception->getMessage() ?: '删除成功'); } } /** * @param $id * @param null $data * * @return \Illuminate\Http\JsonResponse * @throws \Throwable */ public function update($id, $data = null) { $this->isEdit = true; if (($result = $this->callSubmitted()) instanceof Response) { return $result; } $data = ($data) ?: request()->all(); $this->setResourceId($id); $builder = $this->model(); $this->model = $builder->findOrFail($id); $this->validatorData($data); if (($response = $this->prepare($data)) instanceof Response) { return $response; } DB::transaction(function () use ($data) { $updates = $this->prepareUpdate($this->updates); foreach ($updates as $key => $value) { $this->model->setAttribute($key, $value); } $this->model->save(); $this->updateRelation($this->relations); if (($result = $this->callDbTransaction()) instanceof Response) { throw new \Exception(400, $result->getBody()->getContents()); } }); if (($result = $this->callSaved()) instanceof Response) { return $result; } return UI::responseMessage('修改成功'); } protected function prepareUpdate(array $updates, $oneToOneRelation = false) { $prepared = []; $columns = Schema::getColumnListing($this->model()->getTable()); foreach ($updates as $key => $value) { if (in_array($key, $columns)) { Arr::set($prepared, $key, $value); } } return $prepared; } private function deleteRelation($relations) { foreach ($relations as $name) { if (!method_exists($this->model, $name)) { continue; } $relation = $this->model->$name(); switch (true) { case $relation instanceof Relations\HasOne: $relation->delete(); break; } } } private function updateRelation($relationsData) { foreach ($relationsData as $name => $values) { if (!method_exists($this->model, $name)) { continue; } $relation = $this->model->$name(); $oneToOneRelation = $relation instanceof Relations\HasOne || $relation instanceof Relations\MorphOne || $relation instanceof Relations\BelongsTo; //$prepared = $this->prepareUpdate([$name => $values], $oneToOneRelation); $prepared = [$name => $values]; if (empty($prepared)) { continue; } switch (true) { case $relation instanceof Relations\BelongsToMany: case $relation instanceof Relations\MorphToMany: if (isset($prepared[$name])) { $relation->sync($prepared[$name]); } break; case $relation instanceof Relations\HasOne: $related = $this->model->$name; // if related is empty if (is_null($related)) { $related = $relation->getRelated(); $qualifiedParentKeyName = $relation->getQualifiedParentKeyName(); $localKey = Arr::last(explode('.', $qualifiedParentKeyName)); $related->{$relation->getForeignKeyName()} = $this->model->{$localKey}; } foreach ($prepared[$name] as $column => $value) { $related->setAttribute($column, $value); } $related->save(); break; case $relation instanceof Relations\BelongsTo: case $relation instanceof Relations\MorphTo: $parent = $this->model->$name; // if related is empty if (is_null($parent)) { $parent = $relation->getRelated(); } foreach ($prepared[$name] as $column => $value) { $parent->setAttribute($column, $value); } $parent->save(); // When in creating, associate two models if (!$this->model->{$relation->getForeignKeyName()}) { $this->model->{$relation->getForeignKeyName()} = $parent->getKey(); $this->model->save(); } break; case $relation instanceof Relations\MorphOne: $related = $this->model->$name; if ($related === null) { $related = $relation->make(); } foreach ($prepared[$name] as $column => $value) { $related->setAttribute($column, $value); } $related->save(); break; case $relation instanceof Relations\HasMany: case $relation instanceof Relations\MorphMany: foreach ($prepared[$name] as $related) { /** @var Relations\Relation $relation */ $relation = $this->model()->$name(); $keyName = $relation->getRelated()->getKeyName(); $instance = $relation->findOrNew(Arr::get($related, $keyName)); //处理已删除的关联 try { if ($related[static::REMOVE_FLAG_NAME] == 1) { $instance->delete(); continue; } Arr::forget($related, static::REMOVE_FLAG_NAME); } catch (\Exception $exception) { } //过滤不存在的字段 foreach ($related as $key => $value) { if (Schema::hasColumn($instance->getTable(), $key)) { $instance->setAttribute($key, $value); } } $instance->save(); } break; } } } /** * 获取编辑数据 * * @param $id * * @return array */ public function editData($id) { $this->isEdit = true; if (($result = $this->callEditing($id)) instanceof Response) { return $result; } $this->setMode(self::MODE_EDIT); $this->setResourceId($id); $this->editData = $this->model = $this->model->with($this->getRelations())->findOrFail($this->getResourceId()); $data = []; /**@var FormItem $formItem */ foreach ($this->formItems as $formItem) { $field = $formItem->getField(); $prop = $formItem->getProp(); $component = $formItem->getComponent(); // 利用model的hidden属性 if (in_array($prop, $this->model->getHidden())) { Arr::set($data, $prop, $formItem->getData(null, $this->model, $component)); } else { Arr::set($data, $prop, $formItem->getData(Arr::get($this->editData, $prop), $this->model, $component)); } } foreach ($this->formItems as $formItem) { $prop = $formItem->getProp(); if ($formItem->getCopyProp()) { Arr::set($data, $prop, Arr::get($data, $formItem->getCopyProp())); } } $this->editData = $data; if (($result = $this->callEdiQuery($data)) instanceof Response) { return $result; } return [ 'code' => 200, 'data' => $this->editData, ]; } /** * 设置是否加载数据 * * @param bool $isGetData * * @return $this */ public function isGetData(bool $isGetData) { $this->isGetData = $isGetData; return $this; } /** * @inheritDoc */ public function jsonSerialize() { if ($this->isGetData) { return $this->editData($this->getResourceId()); } $this->items($this->formItems); return array_filter([ 'componentName' => $this->componentName, 'action' => $this->getAction(), 'dataUrl' => $this->dataUrl, 'mode' => $this->getMode(), 'attrs' => $this->attrs, 'ignoreEmptyProps' => $this->ignoreEmptyProps, 'formItemLayout' => $this->formItemLayout, 'tabPosition' => $this->tabPosition, 'defaultValues' => (object)array_merge($this->formItemsValue, $this->formValue), 'formRules' => (object)$this->formRules, 'ref' => $this->ref, 'refData' => $this->refData, 'formRefData' => $this->FormRefDataBuild(), 'top' => $this->top, 'bottom' => $this->bottom, 'actions' => $this->actions->builderActions() ]); } /** * 填充表单默认值 * * @param $name * @param string $value * * @return $this */ public function setFormValue($name, $value = '') { if (is_array($name)) { $this->formValue = $name; return $this; } if ($value === null) { unset($this->formValue[$name]); return $this; } $this->formValue[$name] = $value; return $this; } public function __get($name) { return $this->input($name); } public function __set($name, $value) { return Arr::set($this->inputs, $name, $value); } }