Add Dynamic Form Processing for Custom Forms

This commit is contained in:
Miguel Nogueira 2020-05-08 08:10:25 +01:00
parent 4c6a435e34
commit cf7cc142a7
8 changed files with 174 additions and 19 deletions

View File

@ -6,5 +6,11 @@ use Illuminate\Database\Eloquent\Model;
class Application extends Model class Application extends Model
{ {
// public $fillable = [
'applicantUserID',
'applicantFormResponseID',
'applicantStatus'
];
} }

View File

@ -2,8 +2,13 @@
namespace App\Http\Controllers; namespace App\Http\Controllers;
use App\Application;
use Illuminate\Support\Facades\Log;
use App\Response;
use App\Vacancy; use App\Vacancy;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Validator;
class ApplicationController extends Controller class ApplicationController extends Controller
{ {
@ -59,4 +64,66 @@ class ApplicationController extends Controller
} }
} }
public function saveApplicationAnswers(Request $request, $vacancySlug)
{
$vacancy = Vacancy::with('forms')->where('vacancySlug', $vacancySlug)->get();
Log::info('Processing new application!');
$formStructure = json_decode($vacancy->first()->forms->formStructure, true);
$responseStructure = [];
$excludedNames = [
'_token',
];
$validator = [];
foreach($request->all() as $fieldName => $value)
{
if(!in_array($fieldName, $excludedNames))
{
$validator[$fieldName] = 'required|string';
$responseStructure['responses'][$fieldName]['type'] = $formStructure['fields'][$fieldName]['type'] ?? 'Unavailable';
$responseStructure['responses'][$fieldName]['title'] = $formStructure['fields'][$fieldName]['title'];
$responseStructure['responses'][$fieldName]['response'] = $value;
}
}
Log::info('Built response & validator structure!');
$validation = Validator::make($request->all(), $validator);
if (!$validation->fails())
{
$response = Response::create([
'responseFormID' => $vacancy->first()->forms->id,
'associatedVacancyID' => $vacancy->first()->id, // Since a form can be used by multiple vacancies, we can only know which specific vacancy this response ties to by using a vacancy ID
'responseData' => json_encode($responseStructure)
]);
Log::info('Registered form response for user ' . Auth::user()->name . ' for vacancy ' . $vacancy->first()->vacancyName);
Application::create([
'applicantUserID' => Auth::user()->id,
'applicantFormResponseID' => $response->id,
'applicationStatus' => 'STAGE_SUBMITTED',
]);
Log::info('Submitted application for user ' . Auth::user()->name . ' with response ID' . $response->id);
$request->session()->flash('success', 'Thank you for your application! It will be reviewed as soon as possible.');
return redirect()->to(route('userPendingApps'));
}
else
{
Log::warning('Application form for ' . Auth::user()->name . ' contained errors, resetting!');
$request->session()->flash('error', 'There are one or more errors in your application. Please make sure none of your fields are empty, since they are all required.');
}
return redirect()->back();
}
} }

View File

@ -14,6 +14,9 @@ class HomeController extends Controller
*/ */
public function index() public function index()
{ {
// TODO: Relationships for Applications, Users and Responses
// Also prevent apps if user already has one in the space of 30d
// Display apps in the relevant menus
return view('home') return view('home')
->with('positions', Vacancy::where('vacancyStatus', 'OPEN')->get()); ->with('positions', Vacancy::where('vacancyStatus', 'OPEN')->get());
} }

View File

@ -6,5 +6,9 @@ use Illuminate\Database\Eloquent\Model;
class Response extends Model class Response extends Model
{ {
// public $fillable = [
'responseFormID',
'associatedVacancyID',
'responseData'
];
} }

View File

@ -0,0 +1,32 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddVacancyIDToResponses extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('responses', function (Blueprint $table) {
$table->bigInteger('associatedVacancyID')->unsigned()->after('responseFormID');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('responses', function (Blueprint $table) {
$table->dropColumn('associatedVacancyID');
});
}
}

View File

@ -6,6 +6,24 @@
<h1>My Account / Apply / {{$vacancy->vacancyName}} Application</h1> <h1>My Account / Apply / {{$vacancy->vacancyName}} Application</h1>
@stop @stop
@section('js')
@if (session()->has('success'))
<script>
toastr.success("{{session('success')}}")
</script>
@elseif(session()->has('error'))
<script>
toastr.error("{{session('error')}}")
</script>
@endif
@stop
@section('content') @section('content')
<div class="modal fade" tabindex="-1" id="confirm" role="dialog" aria-labelledby="modalConfirmLabel" aria-hidden="true"> <div class="modal fade" tabindex="-1" id="confirm" role="dialog" aria-labelledby="modalConfirmLabel" aria-hidden="true">
@ -24,7 +42,7 @@
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button type="button" class="btn btn-success"><i class="fas fa-check-double"></i> Accept & Send</button> <button type="button" class="btn btn-success" onclick="document.getElementById('submitApplicationForm').submit()"><i class="fas fa-check-double"></i> Accept & Send</button>
<button type="button" class="btn btn-secondary" data-dismiss="modal">Review</button> <button type="button" class="btn btn-secondary" data-dismiss="modal">Review</button>
</div> </div>
</div> </div>
@ -65,38 +83,42 @@
<div class="card-body"> <div class="card-body">
@foreach($preprocessedForm['fields'] as $fieldName => $field) <form action="{{route('saveApplicationForm', ['vacancySlug' => $vacancy->vacancySlug])}}" method="POST" id="submitApplicationForm">
@csrf
@foreach($preprocessedForm['fields'] as $fieldName => $field)
@switch ($field['type']) @switch ($field['type'])
@case('textarea') @case('textarea')
<div class="form-group mt-2 mb-2"> <div class="form-group mt-2 mb-2">
<label for="{{$fieldName}}">{{$field['title']}}</label> <label for="{{$fieldName}}">{{$field['title']}}</label>
<textarea class="form-control" rows="7" name="{{$fieldName}}" id="{{$fieldName}}"> <textarea class="form-control" rows="7" name="{{$fieldName}}" id="{{$fieldName}}">
</textarea> </textarea>
</div> </div>
@break @break
@case('textbox') @case('textbox')
<div class="form-group mt-2 mb-2"> <div class="form-group mt-2 mb-2">
<label for="{{$fieldName}}">{{$field['title']}}</label> <label for="{{$fieldName}}">{{$field['title']}}</label>
<input type="text" name="{{$fieldName}}" id="{{$fieldName}}" class="form-control"> <input type="text" name="{{$fieldName}}" id="{{$fieldName}}" class="form-control">
</div> </div>
@break @break
@endswitch @endswitch
@endforeach @endforeach
</form>
</div> </div>

View File

@ -8,6 +8,24 @@
@stop @stop
@section('js')
@if (session()->has('success'))
<script>
toastr.success("{{session('success')}}")
</script>
@elseif(session()->has('error'))
<script>
toastr.error("{{session('error')}}")
</script>
@endif
@stop
@section('content') @section('content')
<div class="row"> <div class="row">

View File

@ -50,6 +50,9 @@ Route::group(['middleware' => 'auth'], function(){
Route::get('positions/{vacancySlug}', 'ApplicationController@renderApplicationForm') Route::get('positions/{vacancySlug}', 'ApplicationController@renderApplicationForm')
->name('renderApplicationForm'); ->name('renderApplicationForm');
Route::post('positions/{vacancySlug}/submit', 'ApplicationController@saveApplicationAnswers')
->name('saveApplicationForm');
}); });
Route::group(['prefix' => '/profile'], function (){ Route::group(['prefix' => '/profile'], function (){