1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618 |
- <?php
- namespace Encore\Admin;
- use Closure;
- use Encore\Admin\Exception\Handler;
- use Encore\Admin\Form\Builder;
- use Encore\Admin\Form\Field;
- use Encore\Admin\Form\Row;
- use Encore\Admin\Form\Tab;
- use Illuminate\Contracts\Support\Renderable;
- use Illuminate\Database\Eloquent\Model;
- use Illuminate\Database\Eloquent\Relations;
- use Illuminate\Database\Eloquent\SoftDeletes;
- use Illuminate\Http\Request;
- use Illuminate\Support\Arr;
- use Illuminate\Support\Facades\DB;
- use Illuminate\Support\Facades\Input;
- use Illuminate\Support\MessageBag;
- use Illuminate\Support\Str;
- use Illuminate\Validation\Validator;
- use Spatie\EloquentSortable\Sortable;
- use Symfony\Component\HttpFoundation\Response;
- /**
- * Class Form.
- *
- * @method Field\Text text($column, $label = '')
- * @method Field\Checkbox checkbox($column, $label = '')
- * @method Field\Radio radio($column, $label = '')
- * @method Field\Select select($column, $label = '')
- * @method Field\MultipleSelect multipleSelect($column, $label = '')
- * @method Field\Textarea textarea($column, $label = '')
- * @method Field\Hidden hidden($column, $label = '')
- * @method Field\Id id($column, $label = '')
- * @method Field\Ip ip($column, $label = '')
- * @method Field\Url url($column, $label = '')
- * @method Field\Color color($column, $label = '')
- * @method Field\Email email($column, $label = '')
- * @method Field\Mobile mobile($column, $label = '')
- * @method Field\Slider slider($column, $label = '')
- * @method Field\Map map($latitude, $longitude, $label = '')
- * @method Field\Editor editor($column, $label = '')
- * @method Field\File file($column, $label = '')
- * @method Field\Image image($column, $label = '')
- * @method Field\Date date($column, $label = '')
- * @method Field\Datetime datetime($column, $label = '')
- * @method Field\Time time($column, $label = '')
- * @method Field\Year year($column, $label = '')
- * @method Field\Month month($column, $label = '')
- * @method Field\DateRange dateRange($start, $end, $label = '')
- * @method Field\DateTimeRange datetimeRange($start, $end, $label = '')
- * @method Field\TimeRange timeRange($start, $end, $label = '')
- * @method Field\Number number($column, $label = '')
- * @method Field\Currency currency($column, $label = '')
- * @method Field\HasMany hasMany($relationName, $callback)
- * @method Field\SwitchField switch($column, $label = '')
- * @method Field\Display display($column, $label = '')
- * @method Field\Rate rate($column, $label = '')
- * @method Field\Divide divider()
- * @method Field\Password password($column, $label = '')
- * @method Field\Decimal decimal($column, $label = '')
- * @method Field\Html html($html, $label = '')
- * @method Field\Tags tags($column, $label = '')
- * @method Field\Icon icon($column, $label = '')
- * @method Field\Embeds embeds($column, $label = '')
- * @method Field\MultipleImage multipleImage($column, $label = '')
- * @method Field\MultipleFile multipleFile($column, $label = '')
- * @method Field\Captcha captcha($column, $label = '')
- * @method Field\Listbox listbox($column, $label = '')
- */
- class Form implements Renderable
- {
- /**
- * Eloquent model of the form.
- *
- * @var Model
- */
- protected $model;
- /**
- * @var \Illuminate\Validation\Validator
- */
- protected $validator;
- /**
- * @var Builder
- */
- protected $builder;
- /**
- * Submitted callback.
- *
- * @var Closure[]
- */
- protected $submitted = [];
- /**
- * Saving callback.
- *
- * @var Closure[]
- */
- protected $saving = [];
- /**
- * Saved callback.
- *
- * @var Closure[]
- */
- protected $saved = [];
- /**
- * Callbacks after getting editing model.
- *
- * @var Closure[]
- */
- protected $editing = [];
- /**
- * Data for save to current model from input.
- *
- * @var array
- */
- protected $updates = [];
- /**
- * Data for save to model's relations from input.
- *
- * @var array
- */
- protected $relations = [];
- /**
- * Input data.
- *
- * @var array
- */
- protected $inputs = [];
- /**
- * Available fields.
- *
- * @var array
- */
- public static $availableFields = [];
- /**
- * Form field alias.
- *
- * @var array
- */
- public static $fieldAlias = [];
- /**
- * Ignored saving fields.
- *
- * @var array
- */
- protected $ignored = [];
- /**
- * Collected field assets.
- *
- * @var array
- */
- protected static $collectedAssets = [];
- /**
- * @var Form\Tab
- */
- protected $tab = null;
- /**
- * Remove flag in `has many` form.
- */
- const REMOVE_FLAG_NAME = '_remove_';
- /**
- * Field rows in form.
- *
- * @var array
- */
- public $rows = [];
- /**
- * @var bool
- */
- protected $isSoftDeletes = false;
- /**
- * @var Closure
- */
- protected static $initCallback;
- /**
- * Create a new form instance.
- *
- * @param $model
- * @param \Closure $callback
- */
- public function __construct($model, Closure $callback = null)
- {
- $this->model = $model;
- $this->builder = new Builder($this);
- if ($callback instanceof Closure) {
- $callback($this);
- }
- $this->isSoftDeletes = in_array(SoftDeletes::class, class_uses($this->model));
- if (static::$initCallback instanceof Closure) {
- call_user_func(static::$initCallback, $this);
- }
- }
- /**
- * Initialize with user pre-defined default disables, etc.
- *
- * @param Closure $callback
- */
- public static function init(Closure $callback = null)
- {
- static::$initCallback = $callback;
- }
- /**
- * @param Field $field
- *
- * @return $this
- */
- public function pushField(Field $field)
- {
- $field->setForm($this);
- $this->builder->fields()->push($field);
- return $this;
- }
- /**
- * @return Model
- */
- public function model()
- {
- return $this->model;
- }
- /**
- * @return Builder
- */
- public function builder()
- {
- return $this->builder;
- }
- /**
- * Generate a edit form.
- *
- * @param $id
- *
- * @return $this
- */
- public function edit($id)
- {
- $this->builder->setMode(Builder::MODE_EDIT);
- $this->builder->setResourceId($id);
- $this->setFieldValue($id);
- return $this;
- }
- /**
- * Use tab to split form.
- *
- * @param string $title
- * @param Closure $content
- *
- * @return $this
- */
- public function tab($title, Closure $content, $active = false)
- {
- $this->getTab()->append($title, $content, $active);
- return $this;
- }
- /**
- * Get Tab instance.
- *
- * @return Tab
- */
- public function getTab()
- {
- if (is_null($this->tab)) {
- $this->tab = new Tab($this);
- }
- return $this->tab;
- }
- /**
- * Destroy data entity and remove files.
- *
- * @param $id
- *
- * @return mixed
- */
- public function destroy($id)
- {
- collect(explode(',', $id))->filter()->each(function ($id) {
- $builder = $this->model()->newQuery();
- if ($this->isSoftDeletes) {
- $builder = $builder->withTrashed();
- }
- $model = $builder->with($this->getRelations())->findOrFail($id);
- if ($this->isSoftDeletes && $model->trashed()) {
- $this->deleteFiles($model, true);
- $model->forceDelete();
- return;
- }
- $this->deleteFiles($model);
- $model->delete();
- });
- return true;
- }
- /**
- * Remove files in record.
- *
- * @param Model $model
- * @param bool $forceDelete
- */
- protected function deleteFiles(Model $model, $forceDelete = false)
- {
- // If it's a soft delete, the files in the data will not be deleted.
- if (!$forceDelete && $this->isSoftDeletes) {
- return;
- }
- $data = $model->toArray();
- $this->builder->fields()->filter(function ($field) {
- return $field instanceof Field\File;
- })->each(function (Field\File $file) use ($data) {
- $file->setOriginal($data);
- $file->destroy();
- });
- }
- /**
- * Store a new record.
- *
- * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|\Illuminate\Http\JsonResponse
- */
- public function store()
- {
- $data = Input::all();
- // Handle validation errors.
- if ($validationMessages = $this->validationMessages($data)) {
- return back()->withInput()->withErrors($validationMessages);
- }
- if (($response = $this->prepare($data)) instanceof Response) {
- return $response;
- }
- DB::transaction(function () {
- $inserts = $this->prepareInsert($this->updates);
- foreach ($inserts as $column => $value) {
- $this->model->setAttribute($column, $value);
- }
- $this->model->save();
- $this->updateRelation($this->relations);
- });
- if (($response = $this->callSaved()) instanceof Response) {
- return $response;
- }
- if ($response = $this->ajaxResponse(trans('admin.save_succeeded'))) {
- return $response;
- }
- return $this->redirectAfterStore();
- }
- /**
- * Get ajax response.
- *
- * @param string $message
- *
- * @return bool|\Illuminate\Http\JsonResponse
- */
- protected function ajaxResponse($message)
- {
- $request = Request::capture();
- // ajax but not pjax
- if ($request->ajax() && !$request->pjax()) {
- return response()->json([
- 'status' => true,
- 'message' => $message,
- ]);
- }
- return false;
- }
- /**
- * Prepare input data for insert or update.
- *
- * @param array $data
- *
- * @return mixed
- */
- protected function prepare($data = [])
- {
- if (($response = $this->callSubmitted()) instanceof Response) {
- return $response;
- }
- $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 = array_except($this->inputs, array_keys($this->relations));
- }
- /**
- * Remove ignored fields from input.
- *
- * @param array $input
- *
- * @return array
- */
- protected function removeIgnoredFields($input)
- {
- array_forget($input, $this->ignored);
- return $input;
- }
- /**
- * Get inputs for relations.
- *
- * @param array $inputs
- *
- * @return array
- */
- protected function getRelationInputs($inputs = [])
- {
- $relations = [];
- foreach ($inputs as $column => $value) {
- if (method_exists($this->model, $column)) {
- $relation = call_user_func([$this->model, $column]);
- if ($relation instanceof Relations\Relation) {
- $relations[$column] = $value;
- }
- }
- }
- return $relations;
- }
- /**
- * Call editing callbacks.
- *
- * @return void
- */
- protected function callEditing()
- {
- foreach ($this->editing as $func) {
- call_user_func($func, $this);
- }
- }
- /**
- * Call submitted callback.
- *
- * @return mixed
- */
- protected function callSubmitted()
- {
- foreach ($this->submitted as $func) {
- if ($func instanceof Closure && ($ret = call_user_func($func, $this)) instanceof Response) {
- return $ret;
- }
- }
- }
- /**
- * Call saving callback.
- *
- * @return mixed
- */
- protected function callSaving()
- {
- foreach ($this->saving as $func) {
- if ($func instanceof Closure && ($ret = call_user_func($func, $this)) instanceof Response) {
- return $ret;
- }
- }
- }
- /**
- * Callback after saving a Model.
- *
- * @return mixed|null
- */
- protected function callSaved()
- {
- foreach ($this->saved as $func) {
- if ($func instanceof Closure && ($ret = call_user_func($func, $this)) instanceof Response) {
- return $ret;
- }
- }
- }
- /**
- * Handle update.
- *
- * @param int $id
- *
- * @return \Symfony\Component\HttpFoundation\Response
- */
- public function update($id, $data = null)
- {
- $data = ($data) ?: Input::all();
- $isEditable = $this->isEditable($data);
- $data = $this->handleEditable($data);
- $data = $this->handleFileDelete($data);
- if ($this->handleOrderable($id, $data)) {
- return response([
- 'status' => true,
- 'message' => trans('admin.update_succeeded'),
- ]);
- }
- /* @var Model $this->model */
- $builder = $this->model();
- if ($this->isSoftDeletes) {
- $builder = $builder->withTrashed();
- }
- $this->model = $builder->with($this->getRelations())->findOrFail($id);
- $this->setFieldOriginalValue();
- // Handle validation errors.
- if ($validationMessages = $this->validationMessages($data)) {
- if (!$isEditable) {
- return back()->withInput()->withErrors($validationMessages);
- } else {
- return response()->json(['errors' => array_dot($validationMessages->getMessages())], 422);
- }
- }
- if (($response = $this->prepare($data)) instanceof Response) {
- return $response;
- }
- DB::transaction(function () {
- $updates = $this->prepareUpdate($this->updates);
- foreach ($updates as $column => $value) {
- /* @var Model $this->model */
- $this->model->setAttribute($column, $value);
- }
- $this->model->save();
- $this->updateRelation($this->relations);
- });
- if (($result = $this->callSaved()) instanceof Response) {
- return $result;
- }
- if ($response = $this->ajaxResponse(trans('admin.update_succeeded'))) {
- return $response;
- }
- return $this->redirectAfterUpdate($id);
- }
- /**
- * Get RedirectResponse after store.
- *
- * @return \Illuminate\Http\RedirectResponse
- */
- protected function redirectAfterStore()
- {
- $resourcesPath = $this->resource(0);
- $key = $this->model->getKey();
- return $this->redirectAfterSaving($resourcesPath, $key);
- }
- /**
- * Get RedirectResponse after update.
- *
- * @param mixed $key
- *
- * @return \Illuminate\Http\RedirectResponse
- */
- protected function redirectAfterUpdate($key)
- {
- $resourcesPath = $this->resource(-1);
- return $this->redirectAfterSaving($resourcesPath, $key);
- }
- /**
- * Get RedirectResponse after data saving.
- *
- * @param string $resourcesPath
- * @param string $key
- *
- * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
- */
- protected function redirectAfterSaving($resourcesPath, $key)
- {
- if (request('after-save') == 1) {
- // continue editing
- $url = rtrim($resourcesPath, '/')."/{$key}/edit";
- } elseif (request('after-save') == 2) {
- // continue creating
- $url = rtrim($resourcesPath, '/').'/create';
- } elseif (request('after-save') == 3) {
- // view resource
- $url = rtrim($resourcesPath, '/')."/{$key}";
- } else {
- $url = request(Builder::PREVIOUS_URL_KEY) ?: $resourcesPath;
- }
- admin_toastr(trans('admin.save_succeeded'));
- return redirect($url);
- }
- /**
- * Check if request is from editable.
- *
- * @param array $input
- *
- * @return bool
- */
- protected function isEditable(array $input = [])
- {
- return array_key_exists('_editable', $input);
- }
- /**
- * Handle editable update.
- *
- * @param array $input
- *
- * @return array
- */
- protected function handleEditable(array $input = [])
- {
- if (array_key_exists('_editable', $input)) {
- $name = $input['name'];
- $value = $input['value'];
- array_forget($input, ['pk', 'value', 'name']);
- array_set($input, $name, $value);
- }
- return $input;
- }
- /**
- * @param array $input
- *
- * @return array
- */
- protected function handleFileDelete(array $input = [])
- {
- if (array_key_exists(Field::FILE_DELETE_FLAG, $input)) {
- $input[Field::FILE_DELETE_FLAG] = $input['key'];
- unset($input['key']);
- }
- Input::replace($input);
- return $input;
- }
- /**
- * Handle orderable update.
- *
- * @param int $id
- * @param array $input
- *
- * @return bool
- */
- protected function handleOrderable($id, array $input = [])
- {
- if (array_key_exists('_orderable', $input)) {
- $model = $this->model->find($id);
- if ($model instanceof Sortable) {
- $input['_orderable'] == 1 ? $model->moveOrderUp() : $model->moveOrderDown();
- return true;
- }
- }
- return false;
- }
- /**
- * Update relation data.
- *
- * @param array $relationsData
- *
- * @return void
- */
- protected 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);
- 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 = array_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:
- $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->getForeignKey()}) {
- $this->model->{$relation->getForeignKey()} = $parent->getKey();
- $this->model->save();
- }
- break;
- case $relation instanceof Relations\MorphOne:
- $related = $this->model->$name;
- if (is_null($related)) {
- $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(array_get($related, $keyName));
- if ($related[static::REMOVE_FLAG_NAME] == 1) {
- $instance->delete();
- continue;
- }
- array_forget($related, static::REMOVE_FLAG_NAME);
- $instance->fill($related);
- $instance->save();
- }
- break;
- }
- }
- }
- /**
- * Prepare input data for update.
- *
- * @param array $updates
- * @param bool $oneToOneRelation If column is one-to-one relation.
- *
- * @return array
- */
- protected function prepareUpdate(array $updates, $oneToOneRelation = false)
- {
- $prepared = [];
- /** @var Field $field */
- foreach ($this->builder->fields() as $field) {
- $columns = $field->column();
- // If column not in input array data, then continue.
- if (!array_has($updates, $columns)) {
- continue;
- }
- if ($this->invalidColumn($columns, $oneToOneRelation)) {
- continue;
- }
- $value = $this->getDataByColumn($updates, $columns);
- $value = $field->prepare($value);
- if (is_array($columns)) {
- foreach ($columns as $name => $column) {
- array_set($prepared, $column, $value[$name]);
- }
- } elseif (is_string($columns)) {
- array_set($prepared, $columns, $value);
- }
- }
- return $prepared;
- }
- /**
- * @param string|array $columns
- * @param bool $oneToOneRelation
- *
- * @return bool
- */
- protected function invalidColumn($columns, $oneToOneRelation = false)
- {
- foreach ((array) $columns as $column) {
- if ((!$oneToOneRelation && Str::contains($column, '.')) ||
- ($oneToOneRelation && !Str::contains($column, '.'))) {
- return true;
- }
- }
- return false;
- }
- /**
- * Prepare input data for insert.
- *
- * @param $inserts
- *
- * @return array
- */
- protected function prepareInsert($inserts)
- {
- if ($this->isHasOneRelation($inserts)) {
- $inserts = array_dot($inserts);
- }
- foreach ($inserts as $column => $value) {
- if (is_null($field = $this->getFieldByColumn($column))) {
- unset($inserts[$column]);
- continue;
- }
- $inserts[$column] = $field->prepare($value);
- }
- $prepared = [];
- foreach ($inserts as $key => $value) {
- array_set($prepared, $key, $value);
- }
- return $prepared;
- }
- /**
- * Is input data is has-one relation.
- *
- * @param array $inserts
- *
- * @return bool
- */
- protected function isHasOneRelation($inserts)
- {
- $first = current($inserts);
- if (!is_array($first)) {
- return false;
- }
- if (is_array(current($first))) {
- return false;
- }
- return Arr::isAssoc($first);
- }
- /**
- * Set after getting editing model callback.
- *
- * @param Closure $callback
- *
- * @return void
- */
- public function editing(Closure $callback)
- {
- $this->editing[] = $callback;
- }
- /**
- * Set submitted callback.
- *
- * @param Closure $callback
- *
- * @return void
- */
- public function submitted(Closure $callback)
- {
- $this->submitted[] = $callback;
- }
- /**
- * Set saving callback.
- *
- * @param Closure $callback
- *
- * @return void
- */
- public function saving(Closure $callback)
- {
- $this->saving[] = $callback;
- }
- /**
- * Set saved callback.
- *
- * @param Closure $callback
- *
- * @return void
- */
- public function saved(Closure $callback)
- {
- $this->saved[] = $callback;
- }
- /**
- * Ignore fields to save.
- *
- * @param string|array $fields
- *
- * @return $this
- */
- public function ignore($fields)
- {
- $this->ignored = array_merge($this->ignored, (array) $fields);
- return $this;
- }
- /**
- * @param array $data
- * @param string|array $columns
- *
- * @return array|mixed
- */
- protected function getDataByColumn($data, $columns)
- {
- if (is_string($columns)) {
- return array_get($data, $columns);
- }
- if (is_array($columns)) {
- $value = [];
- foreach ($columns as $name => $column) {
- if (!array_has($data, $column)) {
- continue;
- }
- $value[$name] = array_get($data, $column);
- }
- return $value;
- }
- }
- /**
- * Find field object by column.
- *
- * @param $column
- *
- * @return mixed
- */
- protected function getFieldByColumn($column)
- {
- return $this->builder->fields()->first(
- function (Field $field) use ($column) {
- if (is_array($field->column())) {
- return in_array($column, $field->column());
- }
- return $field->column() == $column;
- }
- );
- }
- /**
- * Set original data for each field.
- *
- * @return void
- */
- protected function setFieldOriginalValue()
- {
- // static::doNotSnakeAttributes($this->model);
- $values = $this->model->toArray();
- $this->builder->fields()->each(function (Field $field) use ($values) {
- $field->setOriginal($values);
- });
- }
- /**
- * Set all fields value in form.
- *
- * @param $id
- *
- * @return void
- */
- protected function setFieldValue($id)
- {
- $relations = $this->getRelations();
- $builder = $this->model();
- if ($this->isSoftDeletes) {
- $builder = $builder->withTrashed();
- }
- $this->model = $builder->with($relations)->findOrFail($id);
- $this->callEditing();
- // static::doNotSnakeAttributes($this->model);
- $data = $this->model->toArray();
- $this->builder->fields()->each(function (Field $field) use ($data) {
- if (!in_array($field->column(), $this->ignored)) {
- $field->fill($data);
- }
- });
- }
- /**
- * Don't snake case attributes.
- *
- * @param Model $model
- *
- * @return void
- */
- protected static function doNotSnakeAttributes(Model $model)
- {
- $class = get_class($model);
- $class::$snakeAttributes = false;
- }
- /**
- * Get validation messages.
- *
- * @param array $input
- *
- * @return MessageBag|bool
- */
- public function validationMessages($input)
- {
- $failedValidators = [];
- /** @var Field $field */
- foreach ($this->builder->fields() as $field) {
- if (!$validator = $field->getValidator($input)) {
- continue;
- }
- if (($validator instanceof Validator) && !$validator->passes()) {
- $failedValidators[] = $validator;
- }
- }
- $message = $this->mergeValidationMessages($failedValidators);
- return $message->any() ? $message : false;
- }
- /**
- * Merge validation messages from input validators.
- *
- * @param \Illuminate\Validation\Validator[] $validators
- *
- * @return MessageBag
- */
- protected function mergeValidationMessages($validators)
- {
- $messageBag = new MessageBag();
- foreach ($validators as $validator) {
- $messageBag = $messageBag->merge($validator->messages());
- }
- return $messageBag;
- }
- /**
- * Get all relations of model from callable.
- *
- * @return array
- */
- public function getRelations()
- {
- $relations = $columns = [];
- /** @var Field $field */
- foreach ($this->builder->fields() as $field) {
- $columns[] = $field->column();
- }
- foreach (array_flatten($columns) as $column) {
- if (str_contains($column, '.')) {
- list($relation) = explode('.', $column);
- if (method_exists($this->model, $relation) &&
- $this->model->$relation() instanceof Relations\Relation
- ) {
- $relations[] = $relation;
- }
- } elseif (method_exists($this->model, $column) &&
- !method_exists(Model::class, $column)
- ) {
- $relations[] = $column;
- }
- }
- return array_unique($relations);
- }
- /**
- * Set action for form.
- *
- * @param string $action
- *
- * @return $this
- */
- public function setAction($action)
- {
- $this->builder()->setAction($action);
- return $this;
- }
- /**
- * Set field and label width in current form.
- *
- * @param int $fieldWidth
- * @param int $labelWidth
- *
- * @return $this
- */
- public function setWidth($fieldWidth = 8, $labelWidth = 2)
- {
- $this->builder()->fields()->each(function ($field) use ($fieldWidth, $labelWidth) {
- /* @var Field $field */
- $field->setWidth($fieldWidth, $labelWidth);
- });
- $this->builder()->setWidth($fieldWidth, $labelWidth);
- return $this;
- }
- /**
- * Set view for form.
- *
- * @param string $view
- *
- * @return $this
- */
- public function setView($view)
- {
- $this->builder()->setView($view);
- return $this;
- }
- /**
- * Set title for form.
- *
- * @param string $title
- *
- * @return $this
- */
- public function setTitle($title = '')
- {
- $this->builder()->setTitle($title);
- return $this;
- }
- /**
- * Add a row in form.
- *
- * @param Closure $callback
- *
- * @return $this
- */
- public function row(Closure $callback)
- {
- $this->rows[] = new Row($callback, $this);
- return $this;
- }
- /**
- * Tools setting for form.
- *
- * @param Closure $callback
- */
- public function tools(Closure $callback)
- {
- $callback->call($this, $this->builder->getTools());
- }
- /**
- * Disable form submit.
- *
- * @return $this
- *
- * @deprecated
- */
- public function disableSubmit(bool $disable = true)
- {
- $this->builder()->getFooter()->disableSubmit($disable);
- return $this;
- }
- /**
- * Disable form reset.
- *
- * @return $this
- *
- * @deprecated
- */
- public function disableReset(bool $disable = true)
- {
- $this->builder()->getFooter()->disableReset($disable);
- return $this;
- }
- /**
- * Disable View Checkbox on footer.
- *
- * @return $this
- */
- public function disableViewCheck(bool $disable = true)
- {
- $this->builder()->getFooter()->disableViewCheck($disable);
- return $this;
- }
- /**
- * Disable Editing Checkbox on footer.
- *
- * @return $this
- */
- public function disableEditingCheck(bool $disable = true)
- {
- $this->builder()->getFooter()->disableEditingCheck($disable);
- return $this;
- }
- /**
- * Disable Creating Checkbox on footer.
- *
- * @return $this
- */
- public function disableCreatingCheck(bool $disable = true)
- {
- $this->builder()->getFooter()->disableCreatingCheck($disable);
- return $this;
- }
- /**
- * Footer setting for form.
- *
- * @param Closure $callback
- */
- public function footer(Closure $callback)
- {
- call_user_func($callback, $this->builder()->getFooter());
- }
- /**
- * Get current resource route url.
- *
- * @param int $slice
- *
- * @return string
- */
- public function resource($slice = -2)
- {
- $segments = explode('/', trim(app('request')->getUri(), '/'));
- if ($slice != 0) {
- $segments = array_slice($segments, 0, $slice);
- }
- return implode('/', $segments);
- }
- /**
- * Render the form contents.
- *
- * @return string
- */
- public function render()
- {
- try {
- return $this->builder->render();
- } catch (\Exception $e) {
- return Handler::renderException($e);
- }
- }
- /**
- * Get or set input data.
- *
- * @param string $key
- * @param null $value
- *
- * @return array|mixed
- */
- public function input($key, $value = null)
- {
- if (is_null($value)) {
- return array_get($this->inputs, $key);
- }
- return array_set($this->inputs, $key, $value);
- }
- /**
- * Register builtin fields.
- *
- * @return void
- */
- public static function registerBuiltinFields()
- {
- $map = [
- 'button' => Field\Button::class,
- 'checkbox' => Field\Checkbox::class,
- 'color' => Field\Color::class,
- 'currency' => Field\Currency::class,
- 'date' => Field\Date::class,
- 'dateRange' => Field\DateRange::class,
- 'datetime' => Field\Datetime::class,
- 'dateTimeRange' => Field\DatetimeRange::class,
- 'datetimeRange' => Field\DatetimeRange::class,
- 'decimal' => Field\Decimal::class,
- 'display' => Field\Display::class,
- 'divider' => Field\Divide::class,
- 'divide' => Field\Divide::class,
- 'embeds' => Field\Embeds::class,
- 'editor' => Field\Editor::class,
- 'email' => Field\Email::class,
- 'file' => Field\File::class,
- 'hasMany' => Field\HasMany::class,
- 'hidden' => Field\Hidden::class,
- 'id' => Field\Id::class,
- 'image' => Field\Image::class,
- 'ip' => Field\Ip::class,
- 'map' => Field\Map::class,
- 'mobile' => Field\Mobile::class,
- 'month' => Field\Month::class,
- 'multipleSelect' => Field\MultipleSelect::class,
- 'number' => Field\Number::class,
- 'password' => Field\Password::class,
- 'radio' => Field\Radio::class,
- 'rate' => Field\Rate::class,
- 'select' => Field\Select::class,
- 'slider' => Field\Slider::class,
- 'switch' => Field\SwitchField::class,
- 'text' => Field\Text::class,
- 'textarea' => Field\Textarea::class,
- 'time' => Field\Time::class,
- 'timeRange' => Field\TimeRange::class,
- 'url' => Field\Url::class,
- 'year' => Field\Year::class,
- 'html' => Field\Html::class,
- 'tags' => Field\Tags::class,
- 'icon' => Field\Icon::class,
- 'multipleFile' => Field\MultipleFile::class,
- 'multipleImage' => Field\MultipleImage::class,
- 'captcha' => Field\Captcha::class,
- 'listbox' => Field\Listbox::class,
- ];
- foreach ($map as $abstract => $class) {
- static::extend($abstract, $class);
- }
- }
- /**
- * Register custom field.
- *
- * @param string $abstract
- * @param string $class
- *
- * @return void
- */
- public static function extend($abstract, $class)
- {
- static::$availableFields[$abstract] = $class;
- }
- /**
- * Set form field alias.
- *
- * @param string $field
- * @param string $alias
- *
- * @return void
- */
- public static function alias($field, $alias)
- {
- static::$fieldAlias[$alias] = $field;
- }
- /**
- * Remove registered field.
- *
- * @param array|string $abstract
- */
- public static function forget($abstract)
- {
- array_forget(static::$availableFields, $abstract);
- }
- /**
- * Find field class.
- *
- * @param string $method
- *
- * @return bool|mixed
- */
- public static function findFieldClass($method)
- {
- // If alias exists.
- if (isset(static::$fieldAlias[$method])) {
- $method = static::$fieldAlias[$method];
- }
- $class = array_get(static::$availableFields, $method);
- if (class_exists($class)) {
- return $class;
- }
- return false;
- }
- /**
- * Collect assets required by registered field.
- *
- * @return array
- */
- public static function collectFieldAssets()
- {
- if (!empty(static::$collectedAssets)) {
- return static::$collectedAssets;
- }
- $css = collect();
- $js = collect();
- foreach (static::$availableFields as $field) {
- if (!method_exists($field, 'getAssets')) {
- continue;
- }
- $assets = call_user_func([$field, 'getAssets']);
- $css->push(array_get($assets, 'css'));
- $js->push(array_get($assets, 'js'));
- }
- return static::$collectedAssets = [
- 'css' => $css->flatten()->unique()->filter()->toArray(),
- 'js' => $js->flatten()->unique()->filter()->toArray(),
- ];
- }
- /**
- * Getter.
- *
- * @param string $name
- *
- * @return array|mixed
- */
- public function __get($name)
- {
- return $this->input($name);
- }
- /**
- * Setter.
- *
- * @param string $name
- * @param $value
- */
- public function __set($name, $value)
- {
- return array_set($this->inputs, $name, $value);
- }
- /**
- * Generate a Field object and add to form builder if Field exists.
- *
- * @param string $method
- * @param array $arguments
- *
- * @return Field
- */
- public function __call($method, $arguments)
- {
- if ($className = static::findFieldClass($method)) {
- $column = array_get($arguments, 0, ''); //[0];
- $element = new $className($column, array_slice($arguments, 1));
- $this->pushField($element);
- return $element;
- }
- admin_error('Error', "Field type [$method] does not exist.");
- return new Field\Nullable();
- }
- }
|