Work on refactoring student controller and test
This commit is contained in:
parent
c22f3ddadf
commit
9717ae852e
|
|
@ -2,10 +2,10 @@
|
||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use App\Http\Requests\StudentStoreRequest;
|
||||||
use App\Models\Audition;
|
use App\Models\Audition;
|
||||||
use App\Models\AuditLogEntry;
|
use App\Models\AuditLogEntry;
|
||||||
use App\Models\Student;
|
use App\Models\Student;
|
||||||
use App\Rules\UniqueFullNameAtSchool;
|
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
|
||||||
|
|
@ -31,58 +31,24 @@ class StudentController extends Controller
|
||||||
['students' => $students, 'auditions' => $auditions, 'shirtSizes' => $shirtSizes]);
|
['students' => $students, 'auditions' => $auditions, 'shirtSizes' => $shirtSizes]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Show the form for creating a new resource.
|
|
||||||
*/
|
|
||||||
public function create()
|
|
||||||
{
|
|
||||||
//
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Store a newly created resource in storage.
|
* Store a newly created resource in storage.
|
||||||
*/
|
*/
|
||||||
public function store(Request $request)
|
public function store(StudentStoreRequest $request)
|
||||||
{
|
{
|
||||||
if ($request->user()->cannot('create', Student::class)) {
|
if ($request->user()->cannot('create', Student::class)) {
|
||||||
abort(403);
|
abort(403);
|
||||||
}
|
}
|
||||||
$request->validate([
|
|
||||||
'first_name' => ['required'],
|
|
||||||
'last_name' => [
|
|
||||||
'required',
|
|
||||||
new UniqueFullNameAtSchool(request('first_name'), request('last_name'), Auth::user()->school_id),
|
|
||||||
],
|
|
||||||
'grade' => ['required', 'integer'],
|
|
||||||
'shirt_size' => [
|
|
||||||
'nullable',
|
|
||||||
function ($attribute, $value, $fail) {
|
|
||||||
if (! array_key_exists($value, Student::$shirtSizes)) {
|
|
||||||
$fail("The selected $attribute is invalid.");
|
|
||||||
}
|
|
||||||
},
|
|
||||||
],
|
|
||||||
]);
|
|
||||||
|
|
||||||
$student = Student::create([
|
$student = Student::create([
|
||||||
'first_name' => request('first_name'),
|
'first_name' => $request['first_name'],
|
||||||
'last_name' => request('last_name'),
|
'last_name' => $request['last_name'],
|
||||||
'grade' => request('grade'),
|
'grade' => $request['grade'],
|
||||||
'school_id' => Auth::user()->school_id,
|
'school_id' => Auth::user()->school_id,
|
||||||
]);
|
]);
|
||||||
if (request('shirt_size') !== 'none') {
|
if ($request['shirt_size'] !== 'none') {
|
||||||
$student->update(['optional_data->shirt_size' => $request['shirt_size']]);
|
$student->update(['optional_data->shirt_size' => $request['shirt_size']]);
|
||||||
}
|
}
|
||||||
$message = 'Created student #'.$student->id.' - '.$student->full_name().'<br>Grade: '.$student->grade.'<br>School: '.$student->school->name;
|
|
||||||
AuditLogEntry::create([
|
|
||||||
'user' => auth()->user()->email,
|
|
||||||
'ip_address' => request()->ip(),
|
|
||||||
'message' => $message,
|
|
||||||
'affected' => [
|
|
||||||
'students' => [$student->id],
|
|
||||||
'schools' => [$student->school_id],
|
|
||||||
],
|
|
||||||
]);
|
|
||||||
|
|
||||||
return redirect('/students')->with('success', 'Student Created');
|
return redirect('/students')->with('success', 'Student Created');
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Requests;
|
||||||
|
|
||||||
|
use App\Models\Student;
|
||||||
|
use App\Rules\UniqueFullNameAtSchool;
|
||||||
|
use Illuminate\Foundation\Http\FormRequest;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
|
||||||
|
use function array_key_exists;
|
||||||
|
use function request;
|
||||||
|
|
||||||
|
class StudentStoreRequest extends FormRequest
|
||||||
|
{
|
||||||
|
public function rules(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'first_name' => ['required'],
|
||||||
|
'last_name' => [
|
||||||
|
'required',
|
||||||
|
new UniqueFullNameAtSchool(request('first_name'), request('last_name'), Auth::user()->school_id),
|
||||||
|
],
|
||||||
|
'grade' => ['required', 'integer'],
|
||||||
|
'shirt_size' => [
|
||||||
|
'nullable',
|
||||||
|
function ($attribute, $value, $fail) {
|
||||||
|
if (! array_key_exists($value, Student::$shirtSizes)) {
|
||||||
|
$fail("The selected $attribute is invalid.");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function authorize(): bool
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,58 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Observers;
|
||||||
|
|
||||||
|
use App\Models\AuditLogEntry;
|
||||||
|
use App\Models\Student;
|
||||||
|
|
||||||
|
class StudentObserver
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Handle the Student "created" event.
|
||||||
|
*/
|
||||||
|
public function created(Student $student): void
|
||||||
|
{
|
||||||
|
$message = 'Created student #'.$student->id.' - '.$student->full_name().'<br>Grade: '.$student->grade.'<br>School: '.$student->school->name;
|
||||||
|
AuditLogEntry::create([
|
||||||
|
'user' => auth()->user()->email ?? 'none',
|
||||||
|
'ip_address' => request()->ip(),
|
||||||
|
'message' => $message,
|
||||||
|
'affected' => [
|
||||||
|
'students' => [$student->id],
|
||||||
|
'schools' => [$student->school_id],
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle the Student "updated" event.
|
||||||
|
*/
|
||||||
|
public function updated(Student $student): void
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle the Student "deleted" event.
|
||||||
|
*/
|
||||||
|
public function deleted(Student $student): void
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle the Student "restored" event.
|
||||||
|
*/
|
||||||
|
public function restored(Student $student): void
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle the Student "force deleted" event.
|
||||||
|
*/
|
||||||
|
public function forceDeleted(Student $student): void
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -12,11 +12,13 @@ use App\Models\Entry;
|
||||||
use App\Models\EntryFlag;
|
use App\Models\EntryFlag;
|
||||||
use App\Models\SchoolEmailDomain;
|
use App\Models\SchoolEmailDomain;
|
||||||
use App\Models\ScoreSheet;
|
use App\Models\ScoreSheet;
|
||||||
|
use App\Models\Student;
|
||||||
use App\Observers\BonusScoreObserver;
|
use App\Observers\BonusScoreObserver;
|
||||||
use App\Observers\EntryFlagObserver;
|
use App\Observers\EntryFlagObserver;
|
||||||
use App\Observers\EntryObserver;
|
use App\Observers\EntryObserver;
|
||||||
use App\Observers\SchoolEmailDomainObserver;
|
use App\Observers\SchoolEmailDomainObserver;
|
||||||
use App\Observers\ScoreSheetObserver;
|
use App\Observers\ScoreSheetObserver;
|
||||||
|
use App\Observers\StudentObserver;
|
||||||
use App\Services\AuditionService;
|
use App\Services\AuditionService;
|
||||||
use App\Services\DoublerService;
|
use App\Services\DoublerService;
|
||||||
use App\Services\DrawService;
|
use App\Services\DrawService;
|
||||||
|
|
@ -55,6 +57,7 @@ class AppServiceProvider extends ServiceProvider
|
||||||
Entry::observe(EntryObserver::class);
|
Entry::observe(EntryObserver::class);
|
||||||
SchoolEmailDomain::observe(SchoolEmailDomainObserver::class);
|
SchoolEmailDomain::observe(SchoolEmailDomainObserver::class);
|
||||||
ScoreSheet::observe(ScoreSheetObserver::class);
|
ScoreSheet::observe(ScoreSheetObserver::class);
|
||||||
|
Student::observe(StudentObserver::class);
|
||||||
EntryFlag::observe(EntryFlagObserver::class);
|
EntryFlag::observe(EntryFlagObserver::class);
|
||||||
|
|
||||||
// Model::preventLazyLoading(! app()->isProduction());
|
// Model::preventLazyLoading(! app()->isProduction());
|
||||||
|
|
|
||||||
|
|
@ -26,4 +26,13 @@ class StudentFactory extends Factory
|
||||||
'grade' => rand(7, 12),
|
'grade' => rand(7, 12),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function forSchool(School $school)
|
||||||
|
{
|
||||||
|
return $this->state(function (array $attributes) use ($school) {
|
||||||
|
return [
|
||||||
|
'school_id' => $school->id,
|
||||||
|
];
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ Route::middleware(['auth', 'verified'])->group(function () {
|
||||||
|
|
||||||
// Entry Related Routes
|
// Entry Related Routes
|
||||||
Route::middleware([
|
Route::middleware([
|
||||||
'auth', 'verified', 'can:create,App\Models\Entry',
|
'auth', 'verified',
|
||||||
])->controller(EntryController::class)->group(function () {
|
])->controller(EntryController::class)->group(function () {
|
||||||
Route::get('/entries', 'index')->name('entries.index');
|
Route::get('/entries', 'index')->name('entries.index');
|
||||||
Route::get('/entries/create', 'create')->name('entries.create');
|
Route::get('/entries/create', 'create')->name('entries.create');
|
||||||
|
|
@ -37,7 +37,7 @@ Route::middleware([
|
||||||
|
|
||||||
// Student Related Routes
|
// Student Related Routes
|
||||||
Route::middleware([
|
Route::middleware([
|
||||||
'auth', 'verified', 'can:create,App\Models\Student',
|
'auth', 'verified',
|
||||||
])->controller(StudentController::class)->group(function () {
|
])->controller(StudentController::class)->group(function () {
|
||||||
Route::get('/students', 'index')->name('students.index');
|
Route::get('/students', 'index')->name('students.index');
|
||||||
Route::post('students', 'store')->name('students.store');
|
Route::post('students', 'store')->name('students.store');
|
||||||
|
|
@ -61,7 +61,7 @@ Route::middleware(['auth', 'verified'])->controller(SchoolController::class)->gr
|
||||||
|
|
||||||
// Doubler Related Routes
|
// Doubler Related Routes
|
||||||
Route::middleware([
|
Route::middleware([
|
||||||
'auth', 'verified', 'can:viewAny,App\Models\DoublerRequest',
|
'auth', 'verified',
|
||||||
])->controller(DoublerRequestController::class)->prefix('doubler_request')->group(function () {
|
])->controller(DoublerRequestController::class)->prefix('doubler_request')->group(function () {
|
||||||
Route::get('/', 'index')->name('doubler_request.index');
|
Route::get('/', 'index')->name('doubler_request.index');
|
||||||
Route::post('/', 'makeRequest')->name('doubler_request.make_request');
|
Route::post('/', 'makeRequest')->name('doubler_request.make_request');
|
||||||
|
|
|
||||||
|
|
@ -68,7 +68,7 @@ it('shows students index only for a user with a school', function () {
|
||||||
$user = User::factory()->create();
|
$user = User::factory()->create();
|
||||||
$this->actingAs($user);
|
$this->actingAs($user);
|
||||||
get(route('students.index'))
|
get(route('students.index'))
|
||||||
->assertStatus(403);
|
->assertRedirect(route('dashboard'));
|
||||||
|
|
||||||
$school = School::factory()->create();
|
$school = School::factory()->create();
|
||||||
$user->school_id = $school->id;
|
$user->school_id = $school->id;
|
||||||
|
|
|
||||||
|
|
@ -146,7 +146,7 @@ it('logs the entry creation', function () {
|
||||||
$audition = Audition::factory()->create(['minimum_grade' => 9, 'maximum_grade' => 12]);
|
$audition = Audition::factory()->create(['minimum_grade' => 9, 'maximum_grade' => 12]);
|
||||||
$this->scribe->createEntry($student, $audition);
|
$this->scribe->createEntry($student, $audition);
|
||||||
$thisEntry = Entry::where('student_id', $student->id)->first();
|
$thisEntry = Entry::where('student_id', $student->id)->first();
|
||||||
$logEntry = AuditLogEntry::first();
|
$logEntry = AuditLogEntry::orderBy('id', 'desc')->first();
|
||||||
expect($logEntry->message)->toEqual('Entered '.$thisEntry->student->full_name().' from '.$thisEntry->student->school->name.' in '.$audition->name.'.')
|
expect($logEntry->message)->toEqual('Entered '.$thisEntry->student->full_name().' from '.$thisEntry->student->school->name.' in '.$audition->name.'.')
|
||||||
->and($logEntry->affected['entries'])->toEqual([$thisEntry->id])
|
->and($logEntry->affected['entries'])->toEqual([$thisEntry->id])
|
||||||
->and($logEntry->affected['students'])->toEqual([$thisEntry->student_id])
|
->and($logEntry->affected['students'])->toEqual([$thisEntry->student_id])
|
||||||
|
|
|
||||||
|
|
@ -203,7 +203,7 @@ it('logs changes', function () {
|
||||||
$originalEntry = Entry::find($this->entry->id);
|
$originalEntry = Entry::find($this->entry->id);
|
||||||
$newAudition = Audition::factory()->create(['minimum_grade' => 9, 'maximum_grade' => 10, 'name' => 'Alphorn']);
|
$newAudition = Audition::factory()->create(['minimum_grade' => 9, 'maximum_grade' => 10, 'name' => 'Alphorn']);
|
||||||
($this->entryScribe)($this->entry, ['audition' => $newAudition]);
|
($this->entryScribe)($this->entry, ['audition' => $newAudition]);
|
||||||
$logEntry = AuditLogEntry::latest()->first();
|
$logEntry = AuditLogEntry::orderBy('id', 'desc')->first();
|
||||||
expect($logEntry->affected['auditions'])->toEqual([$originalEntry->audition_id, $newAudition->id])
|
expect($logEntry->affected['auditions'])->toEqual([$originalEntry->audition_id, $newAudition->id])
|
||||||
->and($logEntry->affected['entries'])->toEqual([$this->entry->id])
|
->and($logEntry->affected['entries'])->toEqual([$this->entry->id])
|
||||||
->and($logEntry->affected['students'])->toEqual([$this->entry->student_id])
|
->and($logEntry->affected['students'])->toEqual([$this->entry->student_id])
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ use App\Actions\Tabulation\EnterNoShow;
|
||||||
use App\Actions\Tabulation\EnterScore;
|
use App\Actions\Tabulation\EnterScore;
|
||||||
use App\Exceptions\AuditionAdminException;
|
use App\Exceptions\AuditionAdminException;
|
||||||
use App\Models\Audition;
|
use App\Models\Audition;
|
||||||
|
use App\Models\AuditLogEntry;
|
||||||
use App\Models\Entry;
|
use App\Models\Entry;
|
||||||
use App\Models\SubscoreDefinition;
|
use App\Models\SubscoreDefinition;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
|
|
@ -29,7 +30,7 @@ it('can enter a no-show', function () {
|
||||||
|
|
||||||
it('creates a log entry when entering a no-show', function () {
|
it('creates a log entry when entering a no-show', function () {
|
||||||
($this->rollTaker)($this->entry);
|
($this->rollTaker)($this->entry);
|
||||||
$logEntry = \App\Models\AuditLogEntry::latest()->first();
|
$logEntry = AuditLogEntry::orderBy('id', 'desc')->first();
|
||||||
expect($logEntry->message)->toStartWith('No Show has been entered for');
|
expect($logEntry->message)->toStartWith('No Show has been entered for');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -40,7 +41,7 @@ it('can enter a failed prelim', function () {
|
||||||
|
|
||||||
it('creates a log entry when entering a failed prelim', function () {
|
it('creates a log entry when entering a failed prelim', function () {
|
||||||
($this->rollTaker)($this->entry, 'failprelim');
|
($this->rollTaker)($this->entry, 'failprelim');
|
||||||
$logEntry = \App\Models\AuditLogEntry::latest()->first();
|
$logEntry = AuditLogEntry::orderBy('id', 'desc')->first();
|
||||||
expect($logEntry->message)->toStartWith('Failed prelim has been entered for');
|
expect($logEntry->message)->toStartWith('Failed prelim has been entered for');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@
|
||||||
use App\Actions\Tabulation\EnterScore;
|
use App\Actions\Tabulation\EnterScore;
|
||||||
use App\Exceptions\AuditionAdminException;
|
use App\Exceptions\AuditionAdminException;
|
||||||
use App\Models\Audition;
|
use App\Models\Audition;
|
||||||
|
use App\Models\AuditLogEntry;
|
||||||
use App\Models\Entry;
|
use App\Models\Entry;
|
||||||
use App\Models\ScoreSheet;
|
use App\Models\ScoreSheet;
|
||||||
use App\Models\SubscoreDefinition;
|
use App\Models\SubscoreDefinition;
|
||||||
|
|
@ -122,6 +123,6 @@ it('removes a no-show flag from an entry', function () {
|
||||||
|
|
||||||
it('logs score entry', function () {
|
it('logs score entry', function () {
|
||||||
($this->scribe)($this->judge1, $this->entry1, $this->possibleScoreArray);
|
($this->scribe)($this->judge1, $this->entry1, $this->possibleScoreArray);
|
||||||
$logEntry = \App\Models\AuditLogEntry::latest()->first();
|
$logEntry = AuditLogEntry::orderBy('id', 'desc')->first();
|
||||||
expect($logEntry->message)->toStartWith('Entered Score for entry id ');
|
expect($logEntry->message)->toStartWith('Entered Score for entry id ');
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,52 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use App\Models\Audition;
|
||||||
|
use App\Models\School;
|
||||||
|
use App\Models\Student;
|
||||||
|
use App\Models\User;
|
||||||
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
|
|
||||||
|
uses(RefreshDatabase::class);
|
||||||
|
|
||||||
|
describe('Test the index method of the student controller', function () {
|
||||||
|
it('redirects to the dashboard if the user does not have a school', function () {
|
||||||
|
$user = User::factory()->create();
|
||||||
|
$this->actingAs($user);
|
||||||
|
$response = $this->get(route('students.index'));
|
||||||
|
$response->assertRedirect(route('dashboard'));
|
||||||
|
actAsAdmin();
|
||||||
|
$response = $this->get(route('students.index'));
|
||||||
|
$response->assertRedirect(route('dashboard'));
|
||||||
|
});
|
||||||
|
it('redirects to the students index if the user has a school', function () {
|
||||||
|
$user = User::factory()->create();
|
||||||
|
$school = School::factory()->create();
|
||||||
|
$user->school_id = $school->id;
|
||||||
|
$user->save();
|
||||||
|
$this->actingAs($user);
|
||||||
|
$response = $this->get(route('students.index'));
|
||||||
|
$response->assertOk();
|
||||||
|
});
|
||||||
|
it('returns the view students.index', function () {
|
||||||
|
$user = User::factory()->create();
|
||||||
|
$school = School::factory()->create();
|
||||||
|
$audition = Audition::factory()->create();
|
||||||
|
$student = Student::factory()->forSchool($school)->create();
|
||||||
|
$user->school_id = $school->id;
|
||||||
|
$user->save();
|
||||||
|
$this->actingAs($user);
|
||||||
|
$response = $this->get(route('students.index'));
|
||||||
|
$response->assertViewIs('students.index')
|
||||||
|
->assertViewHas('students', function ($students) use ($student) {
|
||||||
|
return $students->contains($student);
|
||||||
|
})
|
||||||
|
->assertViewHas('auditions', function ($auditions) use ($audition) {
|
||||||
|
return $auditions->contains($audition);
|
||||||
|
})
|
||||||
|
->assertSee(route('students.store'));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Test the store method of the student controller', function () {
|
||||||
|
|
||||||
|
});
|
||||||
Loading…
Reference in New Issue