Add ability to edit forms and add new fields

This commit adds the ability to edit and modify existing forms.
On the technical side, it also adds a new reusable validation Facade which helps reduce duplicated code.
This commit is contained in:
2020-07-15 06:48:49 +01:00
parent 1f50faaea7
commit bca6020ab0
16 changed files with 337 additions and 51 deletions

View File

@@ -0,0 +1,14 @@
<?php
namespace App\Facades;
use Illuminate\Support\Facades\Facade;
class ContextAwareValidation extends Facade
{
protected static function getFacadeAccessor()
{
return 'contextAwareValidator';
}
}

View File

@@ -0,0 +1,72 @@
<?php
namespace App\Helpers;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Collection;
class ContextAwareValidator
{
/**
* The getValidator() method will take an array of fields from the request body, iterates through them,
* and dynamically adds validation rules for them. Depending on parameters, it may or may not generate
* a form structure for rendering purposes.
*
* This method is mostly meant by internal use by means of static proxies (Facades), in order to reduce code repetition;
* Using it outside it's directed scope may cause unexpected results; For instance, the method expects inputs to be in array format, e.g. myFieldNameID1[],
* myFieldNameID2[], and so on and so forth.
*
* This isn't checked by the code yet, but if you're implementing it this way in the HTML markup, make sure it's consistent (e.g. use a loop).
*
* P.S This method automatically ignores the CSRF token for validation.
*
* @param array $fields The request form fields
* @param bool $generateStructure Whether to incldue a JSON-ready form structure for rendering
* @param bool $includeFormName Whether to include formName in the list of validation rules
* @return Validator|Collection A validator instance you can use to check for validity, or a Collection with a validator and structure (validator, structure)
*/
public function getValidator(array $fields, bool $generateStructure = false, bool $includeFormName = false)
{
$formStructure = [];
$validator = [];
$excludedNames = [
'_token',
'_method',
'formName'
];
if ($includeFormName)
{
$validator['formName'] = 'required|string|max:100';
}
foreach ($fields as $fieldName => $field)
{
if(!in_array($fieldName, $excludedNames))
{
$validator[$fieldName . ".0"] = 'required|string';
$validator[$fieldName . ".1"] = 'required|string';
if ($generateStructure)
{
$formStructure['fields'][$fieldName]['title'] = $field[0];
$formStructure['fields'][$fieldName]['type'] = $field[1];
}
}
}
$validatorInstance = Validator::make($fields, $validator);
return ($generateStructure) ?
collect([
'validator' => $validatorInstance,
'structure' => json_encode($formStructure)
])
: $validatorInstance;
}
}

View File

@@ -7,6 +7,8 @@ use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Facades\Auth;
use ContextAwareValidator;
class FormController extends Controller
{
@@ -29,39 +31,17 @@ class FormController extends Controller
{
$this->authorize('create', Form::class);
$fields = $request->all();
$formFields = $request->all();
$contextValidation = ContextAwareValidator::getValidator($fields, true, true);
$formStructure = [];
$excludedNames = [
'_token',
'formName' // It's added outside the loop. Not excluding causes unwanted duplication.
];
$validator = [
'formName' => 'required|string|max:100'
];
foreach ($formFields as $fieldName => $field)
if (!$contextValidation->get('validator')->fails())
{
if(!in_array($fieldName, $excludedNames))
{
$validator[$fieldName . ".0"] = 'required|string';
$validator[$fieldName . ".1"] = 'required|string';
$formStructure['fields'][$fieldName]['title'] = $field[0];
$formStructure['fields'][$fieldName]['type'] = $field[1];
}
}
$validation = Validator::make($formFields, $validator);
if (!$validation->fails())
{
$storableFormStructure = json_encode($formStructure);
$storableFormStructure = $contextValidation->get('structure');
Form::create(
[
'formName' => $formFields['formName'],
'formName' => $fields['formName'],
'formStructure' => $storableFormStructure,
'formStatus' => 'ACTIVE'
]
@@ -71,7 +51,7 @@ class FormController extends Controller
return redirect()->to(route('showForms'));
}
$request->session()->flash('errors', $validation->errors()->getMessages());
$request->session()->flash('errors', $contextValidation->get('validator')->errors()->getMessages());
return redirect()->back();
}
@@ -107,7 +87,41 @@ class FormController extends Controller
{
return view('dashboard.administration.formpreview')
->with('form', json_decode($form->formStructure, true))
->with('title', $form->formName);
->with('title', $form->formName)
->with('formID', $form->id);
}
public function edit(Request $request, Form $form)
{
return view('dashboard.administration.editform')
->with('formStructure', json_decode($form->formStructure, true))
->with('title', $form->formName)
->with('formID', $form->id);
}
public function update(Request $request, Form $form)
{
$contextValidation = ContextAwareValidator::getValidator($request->all(), true);
$this->authorize('update', $form);
if (!$contextValidation->get('validator')->fails())
{
// Add the new structure into the form. New, subsquent fields will be identified by the "new" prefix
// This prefix doesn't actually change the app's behavior when it receives applications.
// Additionally, old applications won't of course display new and updated fields, because we can't travel into the past and get data for them
$form->formStructure = $contextValidation->get('structure');
$form->save();
$request->session()->flash('success', 'Hooray! Your form was updated. New applications for it\'s vacancy will use it.');
}
else
{
$request->session()->flash('errors', $contextValidation->get('validator')->errors()->getMessages());
}
return redirect()->to(route('previewForm', ['form' => $form->id]));
}
}

View File

@@ -57,7 +57,7 @@ class FormPolicy
*/
public function update(User $user, Form $form)
{
// unused
return $user->can('admin.hiring.forms');
}
/**

View File

@@ -0,0 +1,34 @@
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use App;
class ContextAwareValidatorProvider extends ServiceProvider
{
/**
* Register services.
*
* @return void
*/
public function register()
{
//
}
/**
* Bootstrap services.
*
* @return void
*/
public function boot()
{
App::bind('contextAwareValidator', function(){
return new App\Helpers\ContextAwareValidator();
});
}
}