Browse Source

update widget form

jqh 5 years ago
parent
commit
af470d3190
5 changed files with 339 additions and 103 deletions
  1. 2 0
      src/Admin.php
  2. 59 0
      src/Controllers/HandleFormController.php
  3. 4 84
      src/Form.php
  4. 154 0
      src/Traits/HasFormResponse.php
  5. 120 19
      src/Widgets/Form.php

+ 2 - 0
src/Admin.php

@@ -302,6 +302,8 @@ class Admin
                     $router->resource('auth/roles', 'RoleController');
                     $router->resource('auth/permissions', 'PermissionController');
                 }
+
+                $router->post('_handle_form_', 'HandleFormController@handle')->name('admin.handle-form');
             });
 
             $authController = config('admin.auth.controller', AuthController::class);

+ 59 - 0
src/Controllers/HandleFormController.php

@@ -0,0 +1,59 @@
+<?php
+
+namespace Dcat\Admin\Controllers;
+
+use Dcat\Admin\Widgets\Form;
+use Exception;
+use Illuminate\Http\Request;
+use Illuminate\Routing\Controller;
+use Symfony\Component\HttpFoundation\Response;
+
+class HandleFormController extends Controller
+{
+    /**
+     * @param Request $request
+     *
+     * @return Response
+     */
+    public function handle(Request $request)
+    {
+        $form = $this->resolveForm($request);
+
+        if ($errors = $form->validate($request)) {
+            return $form->validationErrorsResponse($errors);
+        }
+
+        $input = $form->sanitize($request->all());
+
+        return $form->handle($input) ?: $form->success();
+    }
+
+    /**
+     * @param Request $request
+     *
+     * @throws Exception
+     *
+     * @return Form
+     */
+    protected function resolveForm(Request $request)
+    {
+        if (! $request->has('_form_')) {
+            throw new Exception('Invalid form request.');
+        }
+
+        $formClass = $request->get('_form_');
+
+        if (! class_exists($formClass)) {
+            throw new Exception("Form [{$formClass}] does not exist.");
+        }
+
+        /** @var Form $form */
+        $form = app($formClass);
+
+        if (! method_exists($form, 'handle')) {
+            throw new Exception("Form method {$formClass}::handle() does not exist.");
+        }
+
+        return $form;
+    }
+}

+ 4 - 84
src/Form.php

@@ -11,6 +11,7 @@ use Dcat\Admin\Form\Field;
 use Dcat\Admin\Form\Row;
 use Dcat\Admin\Form\Tab;
 use Dcat\Admin\Traits\HasBuilderEvents;
+use Dcat\Admin\Traits\HasFormResponse;
 use Dcat\Admin\Widgets\ModalForm;
 use Illuminate\Contracts\Support\MessageProvider;
 use Illuminate\Contracts\Support\Renderable;
@@ -85,6 +86,7 @@ use Symfony\Component\HttpFoundation\Response;
 class Form implements Renderable
 {
     use HasBuilderEvents,
+        HasFormResponse,
         Concerns\HasEvents,
         Concerns\HasFiles,
         Concerns\HasSteps,
@@ -645,7 +647,7 @@ class Form implements Renderable
 
         // Handle validation errors.
         if ($validationMessages = $this->validationMessages($data)) {
-            return $this->makeValidationErrorsResponse($validationMessages);
+            return $this->validationErrorsResponse($validationMessages);
         }
 
         if (($response = $this->prepare($data))) {
@@ -655,38 +657,6 @@ class Form implements Renderable
         }
     }
 
-    /**
-     * Get ajax response.
-     *
-     * @param $message
-     * @param null $redirect
-     * @param bool $status
-     *
-     * @return bool|\Illuminate\Http\JsonResponse
-     */
-    public function ajaxResponse($message, $redirect = null, bool $status = true)
-    {
-        if ($this->isAjaxRequest()) {
-            return response()->json([
-                'status'   => $status,
-                'message'  => $message,
-                'redirect' => $redirect,
-            ]);
-        }
-
-        return false;
-    }
-
-    /**
-     * Ajax but not pjax.
-     *
-     * @return bool
-     */
-    public function isAjaxRequest()
-    {
-        return $this->request->ajax() && ! $this->request->pjax();
-    }
-
     /**
      * Prepare input data for insert or update.
      *
@@ -831,7 +801,7 @@ class Form implements Renderable
 
         // Handle validation errors.
         if ($validationMessages = $this->validationMessages($data)) {
-            return $this->makeValidationErrorsResponse(
+            return $this->validationErrorsResponse(
                 $isEditable ? Arr::dot($validationMessages->toArray()) : $validationMessages
             );
         }
@@ -841,56 +811,6 @@ class Form implements Renderable
         }
     }
 
-    /**
-     * @param array|MessageBag $validationMessages
-     *
-     * @return \Illuminate\Http\JsonResponse|\Illuminate\Http\RedirectResponse
-     */
-    protected function makeValidationErrorsResponse($validationMessages)
-    {
-        if (! $this->isAjaxRequest()) {
-            return back()->withInput()->withErrors($validationMessages);
-        }
-
-        return response()->json([
-            'errors' => is_array($validationMessages) ? $validationMessages : $validationMessages->getMessages(),
-        ], 422);
-    }
-
-    /**
-     * Get redirect response.
-     *
-     * @param string       $url
-     * @param array|string $options
-     *
-     * @return \Illuminate\Http\JsonResponse|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
-     */
-    public function redirect(?string $url, $options = null)
-    {
-        if (is_string($options)) {
-            $message = $options;
-            $options = [];
-        } else {
-            $message = $options['message'] ?? null;
-        }
-
-        $status = (bool) ($options['status'] ?? true);
-
-        if ($this->isAjaxRequest()) {
-            $message = $message ?: trans('admin.save_succeeded');
-
-            return $this->ajaxResponse($message, $url, $status);
-        }
-
-        $status = (int) ($options['status_code'] ?? 302);
-
-        if ($message) {
-            admin_alert($message);
-        }
-
-        return redirect($url, $status);
-    }
-
     /**
      * @param $key
      * @param $redirectTo

+ 154 - 0
src/Traits/HasFormResponse.php

@@ -0,0 +1,154 @@
+<?php
+
+namespace Dcat\Admin\Traits;
+
+use Illuminate\Http\Request;
+use Illuminate\Support\MessageBag;
+
+trait HasFormResponse
+{
+    /**
+     * Get ajax response.
+     *
+     * @param $message
+     * @param null $redirect
+     * @param bool $status
+     *
+     * @return bool|\Illuminate\Http\JsonResponse
+     */
+    public function ajaxResponse($message, $redirect = null, bool $status = true)
+    {
+        if ($this->isAjaxRequest()) {
+            return response()->json([
+                'status'   => $status,
+                'message'  => $message,
+                'redirect' => $redirect,
+            ]);
+        }
+
+        return false;
+    }
+
+    /**
+     * Ajax but not pjax.
+     *
+     * @param Request $request
+     *
+     * @return bool
+     */
+    public function isAjaxRequest(Request $request = null)
+    {
+        /* @var Request $request */
+        $request = $request ?: (empty($this->request) ? request() : $this->request);
+
+        return $request->ajax() && ! $request->pjax();
+    }
+
+    /**
+     * @param string $message
+     * @param string $redirectTo
+     *
+     * @return \Illuminate\Http\JsonResponse|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
+     */
+    public function success($message = null, $redirectTo = null)
+    {
+        $redirectTo = $redirectTo ?: $this->getCurrentUrl();
+
+        return $this->redirect($redirectTo, [
+            'message'     => $message,
+            'status'      => true,
+            'status_code' => 200,
+        ]);
+    }
+
+    /**
+     * @param Request|null $request
+     *
+     * @return string
+     */
+    protected function getCurrentUrl(Request $request = null)
+    {
+        /* @var Request $request */
+        $request = $request ?: (empty($this->request) ? request() : $this->request);
+
+        if ($current = $request->get('_current_')) {
+            return url($current);
+        }
+
+        $query = $request->query();
+
+        if (method_exists($this, 'sanitize')) {
+            $query = $this->sanitize($query);
+        }
+
+        return url($request->path().'?'.http_build_query($query));
+    }
+
+    /**
+     * @param string $message
+     * @param string $redirectTo
+     * @param int    $statusCode
+     *
+     * @return \Illuminate\Http\JsonResponse|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
+     */
+    public function error($message = null, $redirectTo = null, int $statusCode = 200)
+    {
+        $redirectTo = $redirectTo ?: $this->getCurrentUrl();
+
+        return $this->redirect($redirectTo, [
+            'message'     => $message,
+            'status'      => false,
+            'status_code' => $statusCode,
+        ]);
+    }
+
+    /**
+     * Get redirect response.
+     *
+     * @param string       $url
+     * @param array|string $options
+     *
+     * @return \Illuminate\Http\JsonResponse|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
+     */
+    public function redirect(?string $url, $options = null)
+    {
+        if (is_string($options)) {
+            $message = $options;
+            $options = [];
+        } else {
+            $message = $options['message'] ?? null;
+        }
+
+        $status = (bool) ($options['status'] ?? true);
+
+        if ($this->isAjaxRequest()) {
+            $message = $message ?: trans('admin.save_succeeded');
+
+            return $this->ajaxResponse($message, $url, $status);
+        }
+
+        $status = (int) ($options['status_code'] ?? 302);
+
+        if ($message) {
+            admin_alert($message);
+        }
+
+        return redirect($url, $status);
+    }
+
+    /**
+     * @param array|MessageBag $validationMessages
+     *
+     * @return \Illuminate\Http\JsonResponse|\Illuminate\Http\RedirectResponse
+     */
+    public function validationErrorsResponse($validationMessages)
+    {
+        if (! $this->isAjaxRequest()) {
+            return back()->withInput()->withErrors($validationMessages);
+        }
+
+        return response()->json([
+            'errors' => is_array($validationMessages) ? $validationMessages : $validationMessages->getMessages(),
+        ], 422);
+    }
+}

+ 120 - 19
src/Widgets/Form.php

@@ -6,14 +6,18 @@ use Closure;
 use Dcat\Admin\Admin;
 use Dcat\Admin\Form\Field;
 use Dcat\Admin\Support\Helper;
+use Dcat\Admin\Traits\HasFormResponse;
 use Dcat\Admin\Traits\HasHtmlAttributes;
 use Illuminate\Contracts\Support\Arrayable;
 use Illuminate\Contracts\Support\Renderable;
+use Illuminate\Http\Request;
 use Illuminate\Support\Arr;
 use Illuminate\Support\Collection;
 use Illuminate\Support\Fluent;
+use Illuminate\Support\MessageBag;
 use Illuminate\Support\Str;
 use Illuminate\Support\Traits\Macroable;
+use Illuminate\Validation\Validator;
 
 /**
  * Class Form.
@@ -75,9 +79,11 @@ use Illuminate\Support\Traits\Macroable;
  */
 class Form implements Renderable
 {
-    use HasHtmlAttributes, Macroable {
-        __call as macroCall;
-    }
+    use HasHtmlAttributes,
+        HasFormResponse,
+        Macroable {
+            __call as macroCall;
+        }
 
     /**
      * @var string
@@ -137,7 +143,9 @@ class Form implements Renderable
      */
     public function __construct($data = [], $key = null)
     {
-        $this->data($data);
+        if ($data) {
+            $this->fill($data);
+        }
         $this->key($key);
 
         $this->initFields();
@@ -224,13 +232,15 @@ class Form implements Renderable
     /**
      * @param array|Arrayable|Closure $data
      *
-     * @return $this
+     * @return Fluent
      */
-    public function data($data)
+    public function data()
     {
-        $this->data = new Fluent(Helper::array($data));
+        if (! $this->data) {
+            $this->fill([]);
+        }
 
-        return $this;
+        return $this->data;
     }
 
     /**
@@ -240,7 +250,9 @@ class Form implements Renderable
      */
     public function fill($data)
     {
-        return $this->data($data);
+        $this->data = new Fluent(Helper::array($data));
+
+        return $this;
     }
 
     /**
@@ -248,11 +260,7 @@ class Form implements Renderable
      */
     public function model()
     {
-        if (! $this->data) {
-            $this->data([]);
-        }
-
-        return $this->data;
+        return $this->data();
     }
 
     /**
@@ -300,6 +308,55 @@ class Form implements Renderable
         return $this->fields;
     }
 
+    /**
+     * Validate this form fields.
+     *
+     * @param Request $request
+     *
+     * @return bool|MessageBag
+     */
+    public function validate(Request $request)
+    {
+        if (method_exists($this, 'form')) {
+            $this->form();
+        }
+
+        $failedValidators = [];
+
+        /** @var \Dcat\Admin\Form\Field $field */
+        foreach ($this->fields() as $field) {
+            if (! $validator = $field->getValidator($request->all())) {
+                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;
+    }
+
     /**
      * Disable Pjax.
      *
@@ -422,11 +479,11 @@ class Form implements Renderable
         }
 
         return [
-            'start'      => $this->open(),
-            'end'        => $this->close(),
-            'fields'     => $this->fields,
-            'method'     => $this->getHtmlAttribute('method'),
-            'buttons'    => $this->buttons,
+            'start'   => $this->open(),
+            'end'     => $this->close(),
+            'fields'  => $this->fields,
+            'method'  => $this->getHtmlAttribute('method'),
+            'buttons' => $this->buttons,
         ];
     }
 
@@ -531,6 +588,9 @@ HTML;
         return $this->useAjaxSubmit === true;
     }
 
+    /**
+     * @return void
+     */
     protected function setupSubmitScript()
     {
         Admin::script(
@@ -564,6 +624,43 @@ JS
         );
     }
 
+    /**
+     * @param array $input
+     *
+     * @return array
+     */
+    public function sanitize(array $input)
+    {
+        Arr::forget($input, ['_form_', '_token', '_current_']);
+
+        return $input;
+    }
+
+    protected function prepareForm()
+    {
+        if (method_exists($this, 'form')) {
+            $this->form();
+        }
+
+        if (! $this->data && method_exists($this, 'default')) {
+            $data = $this->default();
+
+            if (is_array($data)) {
+                $this->fill($data);
+            }
+        }
+    }
+
+    protected function prepareHandle()
+    {
+        if (method_exists($this, 'handle')) {
+            $this->method('POST');
+            $this->action(admin_url('_handle_form_'));
+            $this->hidden('_form_')->default(get_called_class());
+            $this->hidden('_current_')->default($this->getCurrentUrl());
+        }
+    }
+
     /**
      * Render the form.
      *
@@ -571,6 +668,10 @@ JS
      */
     public function render()
     {
+        $this->prepareForm();
+
+        $this->prepareHandle();
+
         if ($this->allowAjaxSubmit()) {
             $this->setupSubmitScript();
         }