Entry of doulber requests by users working. #70
|
|
@ -0,0 +1,89 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\AuditLogEntry;
|
||||
use App\Models\DoublerRequest;
|
||||
use App\Models\Event;
|
||||
use App\Models\Student;
|
||||
use App\Services\DoublerService;
|
||||
use Barryvdh\Debugbar\Facades\Debugbar;
|
||||
use Psr\Container\ContainerExceptionInterface;
|
||||
use Psr\Container\NotFoundExceptionInterface;
|
||||
|
||||
use function auth;
|
||||
use function compact;
|
||||
use function request;
|
||||
use function to_route;
|
||||
|
||||
class DoublerRequestController extends Controller
|
||||
{
|
||||
public function index(DoublerService $doublerService)
|
||||
{
|
||||
$events = Event::all();
|
||||
$students = auth()->user()->school->students;
|
||||
$studentIds = $students->pluck('id');
|
||||
$existingRequests = DoublerRequest::whereIn('student_id', $studentIds)->get();
|
||||
$doublers = [];
|
||||
foreach ($events as $event) {
|
||||
$event_doublers = $doublerService->doublersForEvent($event);
|
||||
$doublers[$event->id] = $event_doublers;
|
||||
}
|
||||
|
||||
return view('doubler_request.index', compact('events', 'doublers', 'students', 'existingRequests'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws ContainerExceptionInterface
|
||||
* @throws NotFoundExceptionInterface
|
||||
*/
|
||||
public function makeRequest()
|
||||
{
|
||||
foreach (request()->get('doubler_requests') as $event_id => $requests) {
|
||||
if (! Event::find($event_id)->exists()) {
|
||||
return to_route('doubler_request.index')->with('error', 'Invalid event id specified');
|
||||
}
|
||||
$thisEvent = Event::find($event_id);
|
||||
foreach ($requests as $student_id => $request) {
|
||||
if (! Student::find($student_id)->exists()) {
|
||||
return to_route('doubler_request.index')->with('error', 'Invalid student id specified');
|
||||
}
|
||||
$thisStudent = Student::find($student_id);
|
||||
if (! $request) {
|
||||
$oldRequest = DoublerRequest::where('student_id', $student_id)
|
||||
->where('event_id', $event_id)
|
||||
->first();
|
||||
if ($oldRequest) {
|
||||
Debugbar::info('hit');
|
||||
AuditLogEntry::create([
|
||||
'user' => auth()->user()->email,
|
||||
'ip_address' => request()->ip(),
|
||||
'message' => 'Removed doubler request for '.$thisStudent->full_name().' in '.$thisEvent->name,
|
||||
'affected' => ['students' => [$student_id]],
|
||||
]);
|
||||
$oldRequest->delete();
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
DoublerRequest::upsert([
|
||||
'event_id' => $event_id,
|
||||
'student_id' => $student_id,
|
||||
'request' => $request,
|
||||
],
|
||||
uniqueBy: ['event_id', 'student_id'],
|
||||
update: ['request']
|
||||
);
|
||||
AuditLogEntry::create([
|
||||
'user' => auth()->user()->email,
|
||||
'ip_address' => request()->ip(),
|
||||
'message' => 'Made doubler request for '.$thisStudent->full_name().' in '.$thisEvent->name.'<br>Request: '.$request,
|
||||
'affected' => ['students' => [$student_id]],
|
||||
]);
|
||||
}
|
||||
}
|
||||
echo 'hi';
|
||||
|
||||
return to_route('doubler_request.index')->with('success', 'Recorded doubler requests');
|
||||
}
|
||||
}
|
||||
|
|
@ -10,6 +10,8 @@ class DoublerRequest extends Model
|
|||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $guarded = [];
|
||||
|
||||
public function student(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Student::class);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,68 @@
|
|||
<?php
|
||||
|
||||
namespace App\Policies;
|
||||
|
||||
use App\Models\DoublerRequest;
|
||||
use App\Models\User;
|
||||
use Illuminate\Auth\Access\Response;
|
||||
|
||||
class DoublerRequestPolicy
|
||||
{
|
||||
/**
|
||||
* Determine whether the user can view any models.
|
||||
*/
|
||||
public function viewAny(User $user): Response
|
||||
{
|
||||
return $user->school_id === null
|
||||
? Response::denyWithStatus(405)
|
||||
: Response::allow();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can view the model.
|
||||
*/
|
||||
public function view(User $user, DoublerRequest $doublerRequest): bool
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can create models.
|
||||
*/
|
||||
public function create(User $user): bool
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can update the model.
|
||||
*/
|
||||
public function update(User $user, DoublerRequest $doublerRequest): bool
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can delete the model.
|
||||
*/
|
||||
public function delete(User $user, DoublerRequest $doublerRequest): bool
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can restore the model.
|
||||
*/
|
||||
public function restore(User $user, DoublerRequest $doublerRequest): bool
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can permanently delete the model.
|
||||
*/
|
||||
public function forceDelete(User $user, DoublerRequest $doublerRequest): bool
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
||||
|
|
@ -4,4 +4,5 @@ return [
|
|||
App\Providers\AppServiceProvider::class,
|
||||
App\Providers\FortifyServiceProvider::class,
|
||||
App\Providers\InvoiceDataServiceProvider::class,
|
||||
Barryvdh\Debugbar\ServiceProvider::class,
|
||||
];
|
||||
|
|
|
|||
|
|
@ -13,11 +13,12 @@ return new class extends Migration
|
|||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('doubler_request', function (Blueprint $table) {
|
||||
Schema::create('doubler_requests', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignIdFor(Event::class)->constrained()->cascadeOnDelete()->cascadeOnUpdate();
|
||||
$table->foreignIdFor(Student::class)->constrained()->cascadeOnDelete()->cascadeOnUpdate();
|
||||
$table->string('request');
|
||||
$table->unique(['student_id', 'event_id']);
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
|
@ -27,6 +28,6 @@ return new class extends Migration
|
|||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('doubler_request');
|
||||
Schema::dropIfExists('doubler_requests');
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -0,0 +1,44 @@
|
|||
<x-layout.app>
|
||||
<x-slot:page_title>Doubler Requests</x-slot:page_title>
|
||||
<x-form.form method="POST" action="{{route('doubler_request.make_request')}}">
|
||||
@foreach($events as $event)
|
||||
<x-card.card class="mb-5">
|
||||
<x-card.heading>{{ $event->name }}</x-card.heading>
|
||||
<x-table.table>
|
||||
<thead>
|
||||
<tr>
|
||||
<x-table.th>Student Name</x-table.th>
|
||||
<x-table.th>Entries</x-table.th>
|
||||
<x-table.th>Request</x-table.th>
|
||||
</tr>
|
||||
</thead>
|
||||
<x-table.body>
|
||||
@foreach($students as $student)
|
||||
@continue(! array_key_exists($student->id, $doublers[$event->id]))
|
||||
@php
|
||||
$existingRequest = $existingRequests
|
||||
->where('student_id',$student->id)
|
||||
->where('event_id',$event->id);
|
||||
$value = $existingRequest?->first()?->request ?? '';
|
||||
@endphp
|
||||
<tr>
|
||||
<x-table.td>{{ $student->full_name() }}</x-table.td>
|
||||
<x-table.td>
|
||||
@foreach($doublers[$event->id][$student->id]['entries'] as $entry)
|
||||
<p>{{$entry->audition->name}}</p>
|
||||
@endforeach
|
||||
</x-table.td>
|
||||
<x-table.td>
|
||||
<x-form.field
|
||||
value="{{$value}}"
|
||||
name="doubler_requests[{{$event->id}}][{{$student->id}}]"/>
|
||||
</x-table.td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</x-table.body>
|
||||
</x-table.table>
|
||||
</x-card.card>
|
||||
@endforeach
|
||||
<x-form.button type="submit">Save Doubler Requests</x-form.button>
|
||||
</x-form.form>
|
||||
</x-layout.app>
|
||||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
// Dashboard Related Routes
|
||||
use App\Http\Controllers\DashboardController;
|
||||
use App\Http\Controllers\DoublerRequestController;
|
||||
use App\Http\Controllers\EntryController;
|
||||
use App\Http\Controllers\PdfInvoiceController;
|
||||
use App\Http\Controllers\SchoolController;
|
||||
|
|
@ -18,7 +19,9 @@ Route::middleware(['auth', 'verified'])->group(function () {
|
|||
});
|
||||
|
||||
// Entry Related Routes
|
||||
Route::middleware(['auth', 'verified', 'can:create,App\Models\Entry'])->controller(EntryController::class)->group(function () {
|
||||
Route::middleware([
|
||||
'auth', 'verified', 'can:create,App\Models\Entry',
|
||||
])->controller(EntryController::class)->group(function () {
|
||||
Route::get('/entries', 'index')->name('entries.index');
|
||||
Route::get('/entries/create', 'create')->name('entries.create');
|
||||
Route::post('/entries', 'store')->name('entries.store');
|
||||
|
|
@ -32,7 +35,9 @@ Route::middleware(['auth', 'verified'])->controller(UserController::class)->grou
|
|||
});
|
||||
|
||||
// Student Related Routes
|
||||
Route::middleware(['auth', 'verified', 'can:create,App\Models\Student'])->controller(StudentController::class)->group(function () {
|
||||
Route::middleware([
|
||||
'auth', 'verified', 'can:create,App\Models\Student',
|
||||
])->controller(StudentController::class)->group(function () {
|
||||
Route::get('/students', 'index')->name('students.index');
|
||||
Route::post('students', 'store')->name('students.store');
|
||||
Route::get('/students/{student}/edit', 'edit')->name('students.edit');
|
||||
|
|
@ -48,3 +53,11 @@ Route::middleware(['auth', 'verified'])->controller(SchoolController::class)->gr
|
|||
Route::get('/schools/{school}', 'show')->name('schools.show');
|
||||
Route::patch('/schools/{school}', 'update')->name('schools.update');
|
||||
});
|
||||
|
||||
// Doubler Related Routes
|
||||
Route::middleware([
|
||||
'auth', 'verified', 'can:viewAny,App\Models\DoublerRequest',
|
||||
])->controller(DoublerRequestController::class)->prefix('doubler_request')->group(function () {
|
||||
Route::get('/', 'index')->name('doubler_request.index');
|
||||
Route::post('/', 'makeRequest')->name('doubler_request.make_request');
|
||||
});
|
||||
|
|
|
|||
|
|
@ -0,0 +1,107 @@
|
|||
<?php
|
||||
|
||||
use App\Actions\Entries\CreateEntry;
|
||||
use App\Exceptions\ManageEntryException;
|
||||
use App\Models\Audition;
|
||||
use App\Models\School;
|
||||
use App\Models\Student;
|
||||
use App\Models\User;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
|
||||
use function Pest\Laravel\actingAs;
|
||||
use function Pest\Laravel\get;
|
||||
|
||||
uses(RefreshDatabase::class);
|
||||
|
||||
it('denies a guest', function () {
|
||||
$request = get(route('doubler_request.index'));
|
||||
$request->assertRedirect(route('home'));
|
||||
});
|
||||
it('denies access to a user with no school', function () {
|
||||
// Arrange
|
||||
actAsNormal();
|
||||
// Act & Assert
|
||||
get(route('doubler_request.index'))
|
||||
->assertStatus(405);
|
||||
});
|
||||
it('allows access to a user with a school', function () {
|
||||
// Arrange
|
||||
$school = School::factory()->create();
|
||||
$user = User::factory()->create(['school_id' => $school->id]);
|
||||
actingAs($user);
|
||||
// Act & Assert
|
||||
get(route('doubler_request.index'))
|
||||
->assertOk()
|
||||
->assertSessionHasNoErrors();
|
||||
});
|
||||
it('includes a section for each event', function () {
|
||||
// Arrange
|
||||
$events = \App\Models\Event::factory()->count(3)->create();
|
||||
$school = School::factory()->create();
|
||||
$user = User::factory()->create(['school_id' => $school->id]);
|
||||
actingAs($user);
|
||||
// Act & Assert
|
||||
$response = get(route('doubler_request.index'));
|
||||
$response->assertOk()
|
||||
->assertViewHas('events', \App\Models\Event::all());
|
||||
foreach ($events as $event) {
|
||||
$response->assertSee($event->name);
|
||||
}
|
||||
});
|
||||
it(/**
|
||||
* @throws ManageEntryException
|
||||
*/ 'includes all doublers for the users school', function () {
|
||||
// Arrange
|
||||
$school = School::factory()->create();
|
||||
$user = User::factory()->create(['school_id' => $school->id]);
|
||||
$concert_event = \App\Models\Event::create(['name' => 'Concert Band']);
|
||||
$jazz_event = \App\Models\Event::create(['name' => 'Jazz Band']);
|
||||
$jas_audition = Audition::factory()->create([
|
||||
'name' => 'Jazz Alto Sax',
|
||||
'event_id' => $jazz_event->id,
|
||||
'minimum_grade' => 1,
|
||||
'maximum_grade' => 99,
|
||||
]);
|
||||
$jts_audition = Audition::factory()->create([
|
||||
'name' => 'Jazz Tenor Sax',
|
||||
'event_id' => $jazz_event->id,
|
||||
'minimum_grade' => 1,
|
||||
'maximum_grade' => 99,
|
||||
]);
|
||||
$cas_audition = Audition::factory()->create([
|
||||
'name' => 'Alto Sax',
|
||||
'event_id' => $concert_event->id,
|
||||
'minimum_grade' => 1,
|
||||
'maximum_grade' => 99,
|
||||
]);
|
||||
$cts_audition = Audition::factory()->create([
|
||||
'name' => 'Tenor Sax',
|
||||
'event_id' => $concert_event->id,
|
||||
'minimum_grade' => 1,
|
||||
'maximum_grade' => 99,
|
||||
]);
|
||||
$concert_student = Student::factory()->create(['school_id' => $school->id]);
|
||||
$jazz_student = Student::factory()->create(['school_id' => $school->id]);
|
||||
$singleton_student = Student::factory()->create(['school_id' => $school->id]);
|
||||
$other_school_student = Student::factory()->create();
|
||||
$entryCreator = new CreateEntry();
|
||||
$entryCreator->createEntry($concert_student, $cas_audition);
|
||||
$entryCreator->createEntry($singleton_student, $cas_audition);
|
||||
$entryCreator->createEntry($concert_student, $cts_audition);
|
||||
$entryCreator->createEntry($jazz_student, $jas_audition);
|
||||
$entryCreator->createEntry($jazz_student, $jts_audition);
|
||||
$entryCreator->createEntry($singleton_student, $jts_audition);
|
||||
$entryCreator->createEntry($other_school_student, $cas_audition);
|
||||
$entryCreator->createEntry($other_school_student, $cts_audition);
|
||||
$entryCreator->createEntry($other_school_student, $jts_audition);
|
||||
$entryCreator->createEntry($other_school_student, $jas_audition);
|
||||
actingAs($user);
|
||||
// Act
|
||||
$response = get(route('doubler_request.index'));
|
||||
// Assert
|
||||
$response->assertOk()
|
||||
->assertSee($concert_student->full_name())
|
||||
->assertSee($jazz_student->full_name())
|
||||
->assertDontSee($singleton_student->full_name())
|
||||
->assertDontSee($other_school_student->full_name());
|
||||
});
|
||||
Loading…
Reference in New Issue