diff --git a/app/Http/Controllers/admin/Catalog/Direction/EntranceExaminationController.php b/app/Http/Controllers/admin/Catalog/Direction/EntranceExaminationController.php new file mode 100644 index 0000000..fba5a32 --- /dev/null +++ b/app/Http/Controllers/admin/Catalog/Direction/EntranceExaminationController.php @@ -0,0 +1,105 @@ +validated(); + + $entranceExamination = new EntranceExamination(); + $entranceExamination->examination_type_id = $validated['examination_type_id']; + $entranceExamination->direction_id = $validated['direction_id']; + $entranceExamination->subject_id = $validated['subject_id']; + $entranceExamination->scores = $validated['scores']; + $entranceExamination->position = $validated['position']; + $entranceExamination->subject_type_id = $validated['subject_type_id']; + $entranceExamination->save(); + + return redirect()->route('entrance_examinations.index'); + } + + public function show(EntranceExamination $entranceExamination): View + { + return view( + 'admin.catalog.direction.entrance_examination.show', + compact('entranceExamination') + ); + } + + public function edit(EntranceExamination $entranceExamination): View + { + $directions = Direction::pluck('name', 'id'); + $examination_types = ExaminationType::pluck('name', 'id'); + $subjects = Subject::pluck('name', 'id'); + $subjectTypes = SubjectType::pluck('name', 'id'); + return view( + 'admin.catalog.direction.entrance_examination.edit', + compact( + 'entranceExamination', + 'directions', + 'examination_types', + 'subjects', + 'subjectTypes', + ) + ); + } + + public function update( + UpdateEntranceExaminationRequest $request, + EntranceExamination $entranceExamination + ): RedirectResponse { + $validated = $request->validated(); + + $entranceExamination->examination_type_id = $validated['examination_type_id']; + $entranceExamination->direction_id = $validated['direction_id']; + $entranceExamination->subject_id = $validated['subject_id']; + $entranceExamination->scores = $validated['scores']; + $entranceExamination->position = $validated['position']; + $entranceExamination->subject_type_id = $validated['subject_type_id']; + $entranceExamination->save(); + + return redirect()->route('entrance_examinations.index'); + } + + public function destroy(EntranceExamination $entranceExamination): RedirectResponse + { + $entranceExamination->delete(); + return redirect()->route('entrance_examinations.index'); + } +} diff --git a/app/Http/Controllers/admin/Catalog/DirectionController.php b/app/Http/Controllers/admin/Catalog/DirectionController.php index 0672c4a..c8c0fd3 100644 --- a/app/Http/Controllers/admin/Catalog/DirectionController.php +++ b/app/Http/Controllers/admin/Catalog/DirectionController.php @@ -93,6 +93,9 @@ class DirectionController extends Controller public function destroy(Direction $direction): RedirectResponse { + if ($direction->entranceExaminations()->exists()) { + return back(); + } $direction->delete(); return redirect()->route('directions.index'); } diff --git a/app/Http/Requests/admin/Catalog/Direction/StoreEntranceExaminationRequest.php b/app/Http/Requests/admin/Catalog/Direction/StoreEntranceExaminationRequest.php new file mode 100644 index 0000000..f4e535d --- /dev/null +++ b/app/Http/Requests/admin/Catalog/Direction/StoreEntranceExaminationRequest.php @@ -0,0 +1,25 @@ + 'required|numeric|int|max:1000', + 'examination_type_id' => 'required|numeric|int|max:1000', + 'subject_id' => 'required|numeric|int|max:1000', + 'subject_type_id' => 'required|numeric|int|max:1000', + 'scores' => 'required|numeric|int|max:1000', + 'position' => 'required|numeric|int|max:1000', + ]; + } +} diff --git a/app/Http/Requests/admin/Catalog/Direction/UpdateEntranceExaminationRequest.php b/app/Http/Requests/admin/Catalog/Direction/UpdateEntranceExaminationRequest.php index fef7be6..883f336 100644 --- a/app/Http/Requests/admin/Catalog/Direction/UpdateEntranceExaminationRequest.php +++ b/app/Http/Requests/admin/Catalog/Direction/UpdateEntranceExaminationRequest.php @@ -8,13 +8,18 @@ class UpdateEntranceExaminationRequest extends FormRequest { public function authorize(): bool { - return false; + return true; } public function rules(): array { return [ - + 'direction_id' => 'required|numeric|int|max:1000', + 'examination_type_id' => 'required|numeric|int|max:1000', + 'subject_id' => 'required|numeric|int|max:1000', + 'subject_type_id' => 'required|numeric|int|max:1000', + 'scores' => 'required|numeric|int|max:1000', + 'position' => 'required|numeric|int|max:1000', ]; } } diff --git a/app/Models/Direction.php b/app/Models/Direction.php index 34df08c..3a302fd 100644 --- a/app/Models/Direction.php +++ b/app/Models/Direction.php @@ -5,6 +5,7 @@ namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; +use Illuminate\Database\Eloquent\Relations\HasMany; class Direction extends Model { @@ -33,4 +34,9 @@ class Direction extends Model { return $this->belongsTo(EducationForm::class); } + + public function entranceExaminations(): HasMany + { + return $this->hasMany('App\Models\EntranceExamination', 'direction_id'); + } } diff --git a/app/Models/EntranceExamination.php b/app/Models/EntranceExamination.php new file mode 100644 index 0000000..9ca35cc --- /dev/null +++ b/app/Models/EntranceExamination.php @@ -0,0 +1,42 @@ +belongsTo(ExaminationType::class); + } + + public function direction(): BelongsTo + { + return $this->belongsTo(Direction::class); + } + + public function subject(): BelongsTo + { + return $this->belongsTo(Subject::class); + } + + public function subjectType(): BelongsTo + { + return $this->belongsTo(SubjectType::class); + } +} diff --git a/database/factories/EntranceExaminationFactory.php b/database/factories/EntranceExaminationFactory.php new file mode 100644 index 0000000..2e6c13c --- /dev/null +++ b/database/factories/EntranceExaminationFactory.php @@ -0,0 +1,20 @@ + 1, + 'direction_id' => 1, + 'subject_id' => 1, + 'scores' => 45, + 'position' => fake()->randomDigit(), + 'subject_type_id' => 1, + ]; + } +} diff --git a/database/migrations/2024_02_15_075913_create_entrance_examinations_table.php b/database/migrations/2024_02_15_075913_create_entrance_examinations_table.php new file mode 100644 index 0000000..a18dcf1 --- /dev/null +++ b/database/migrations/2024_02_15_075913_create_entrance_examinations_table.php @@ -0,0 +1,27 @@ +id(); + $table->foreignId('examination_type_id')->constrained('examination_types'); + $table->foreignId('direction_id')->constrained('directions'); + $table->foreignId('subject_id')->constrained('subjects'); + $table->integer('scores'); + $table->integer('position'); + $table->foreignId('subject_type_id')->constrained('subject_types'); + $table->timestamps(); + }); + } + + public function down(): void + { + Schema::dropIfExists('entrance_examinations'); + } +}; diff --git a/database/seeders/DatabaseSeeder.php b/database/seeders/DatabaseSeeder.php index 6437501..e3e6d62 100644 --- a/database/seeders/DatabaseSeeder.php +++ b/database/seeders/DatabaseSeeder.php @@ -31,6 +31,7 @@ class DatabaseSeeder extends Seeder SubjectSeeder::class, SubjectTypeSeeder::class, DirectionSeeder::class, + EntranceExaminationSeeder::class, ]); $this->call([ diff --git a/database/seeders/EntranceExaminationSeeder.php b/database/seeders/EntranceExaminationSeeder.php new file mode 100644 index 0000000..ec50ae6 --- /dev/null +++ b/database/seeders/EntranceExaminationSeeder.php @@ -0,0 +1,40 @@ +insert([ + [ + 'examination_type_id' => 1, + 'direction_id' => 1, + 'subject_id' => 1, + 'scores' => 40, + 'position' => 1, + 'subject_type_id' => 1, + ], + [ + 'examination_type_id' => 1, + 'direction_id' => 1, + 'subject_id' => 2, + 'scores' => 45, + 'position' => 2, + 'subject_type_id' => 1, + ], + [ + 'examination_type_id' => 1, + 'direction_id' => 1, + 'subject_id' => 3, + 'scores' => 50, + 'position' => 3, + 'subject_type_id' => 1, + ], + ]); + } +} diff --git a/resources/views/admin/catalog/direction/entrance_examination/create.blade.php b/resources/views/admin/catalog/direction/entrance_examination/create.blade.php new file mode 100644 index 0000000..8165235 --- /dev/null +++ b/resources/views/admin/catalog/direction/entrance_examination/create.blade.php @@ -0,0 +1,90 @@ +@extends('layouts.admin_layout') +@section('content') + @auth() +
+
+

Создать вступительный экзамен

+ {{ Form::open(['url' => route('entrance_examinations.store'), 'method' => 'POST', 'class' => '']) }} +
+
+ {{ Form::label('direction_id', 'Направление подготовки') }} +
+
+ {{ Form::select('direction_id', $directions, null, ['class' => 'form-select']) }} +
+
+ @if ($errors->any()) + {{ $errors->first('direction_id') }} + @endif +
+ +
+ {{ Form::label('examination_type_id', 'Тип экзамена') }} +
+
+ {{ Form::select('examination_type_id', $examination_types, null, ['class' => 'form-select']) }} +
+
+ @if ($errors->any()) + {{ $errors->first('examination_type_id') }} + @endif +
+ +
+ {{ Form::label('subject_id', 'Предмет') }} +
+
+ {{ Form::select('subject_id', $subjects, null, ['class' => 'form-select']) }} +
+
+ @if ($errors->any()) + {{ $errors->first('subject_id') }} + @endif +
+ +
+ {{ Form::label('subject_type_id', 'Тип предмета') }} +
+
+ {{ Form::select('subject_type_id', $subjectTypes, null, ['class' => 'form-select']) }} +
+
+ @if ($errors->any()) + {{ $errors->first('subject_type_id') }} + @endif +
+ + +
+ {{ Form::label('scores', 'Кол-во баллов') }} +
+
+ {{ Form::text('scores', '', ['class' => 'form-control']) }} +
+
+ @if ($errors->any()) + {{ $errors->first('scores') }} + @endif +
+ +
+ {{ Form::label('position', 'Позиция') }} +
+
+ {{ Form::text('position', '', ['class' => 'form-control']) }} +
+
+ @if ($errors->any()) + {{ $errors->first('position') }} + @endif +
+ +
+ {{ Form::submit('Создать', ['class' => 'btn btn-primary']) }} +
+
+ {{ Form::close() }} +
+
+ @endauth +@endsection diff --git a/resources/views/admin/catalog/direction/entrance_examination/edit.blade.php b/resources/views/admin/catalog/direction/entrance_examination/edit.blade.php new file mode 100644 index 0000000..fb2c11b --- /dev/null +++ b/resources/views/admin/catalog/direction/entrance_examination/edit.blade.php @@ -0,0 +1,90 @@ +@extends('layouts.admin_layout') +@section('content') + @auth() +
+
+

Изменить вступительный экзамен

+ {{ Form::open(['url' => route('entrance_examinations.update', $entranceExamination), 'method' => 'PATCH', 'class' => '']) }} +
+
+ {{ Form::label('direction_id', 'Направление подготовки') }} +
+
+ {{ Form::select('direction_id', $directions, $entranceExamination->direction->id, ['class' => 'form-select']) }} +
+
+ @if ($errors->any()) + {{ $errors->first('direction_id') }} + @endif +
+ +
+ {{ Form::label('examination_type_id', 'Тип экзамена') }} +
+
+ {{ Form::select('examination_type_id', $examination_types, $entranceExamination->examinationType->id, ['class' => 'form-select']) }} +
+
+ @if ($errors->any()) + {{ $errors->first('examination_type_id') }} + @endif +
+ +
+ {{ Form::label('subject_id', 'Предмет') }} +
+
+ {{ Form::select('subject_id', $subjects, $entranceExamination->subject->id, ['class' => 'form-select']) }} +
+
+ @if ($errors->any()) + {{ $errors->first('subject_id') }} + @endif +
+ +
+ {{ Form::label('subject_type_id', 'Тип предмета') }} +
+
+ {{ Form::select('subject_type_id', $subjectTypes, $entranceExamination->subjectType->id, ['class' => 'form-select']) }} +
+
+ @if ($errors->any()) + {{ $errors->first('subject_type_id') }} + @endif +
+ + +
+ {{ Form::label('scores', 'Кол-во баллов') }} +
+
+ {{ Form::text('scores', $entranceExamination->scores, ['class' => 'form-control']) }} +
+
+ @if ($errors->any()) + {{ $errors->first('scores') }} + @endif +
+ +
+ {{ Form::label('position', 'Позиция') }} +
+
+ {{ Form::text('position', $entranceExamination->position, ['class' => 'form-control']) }} +
+
+ @if ($errors->any()) + {{ $errors->first('position') }} + @endif +
+ +
+ {{ Form::submit('Изменить', ['class' => 'btn btn-primary']) }} +
+
+ {{ Form::close() }} +
+
+ @endauth +@endsection diff --git a/resources/views/admin/catalog/direction/entrance_examination/index.blade.php b/resources/views/admin/catalog/direction/entrance_examination/index.blade.php new file mode 100644 index 0000000..bc04285 --- /dev/null +++ b/resources/views/admin/catalog/direction/entrance_examination/index.blade.php @@ -0,0 +1,47 @@ +@extends('layouts.admin_layout') +@section('content') +
+

Вступительные экзамены

+
+ Создать вступительный экзамен +
+
+ + + + + + + + + + + + + + + @foreach($entranceExaminations as $entranceExamination) + + + + + + + + + + @endforeach + +
Направление подготовкиТип экзаменаПредметТип предметаКол-во балловПозициядействия
{{ $entranceExamination->direction->name }}{{ $entranceExamination->examinationType->name }}{{ $entranceExamination->subject->name }}{{ $entranceExamination->subjectType->name }}{{ $entranceExamination->scores }}{{ $entranceExamination->position }} + посмотреть + редактировать + удалить +
+
+
+
+@endsection diff --git a/resources/views/admin/catalog/direction/entrance_examination/show.blade.php b/resources/views/admin/catalog/direction/entrance_examination/show.blade.php new file mode 100644 index 0000000..cea7b15 --- /dev/null +++ b/resources/views/admin/catalog/direction/entrance_examination/show.blade.php @@ -0,0 +1,19 @@ +@extends('layouts.admin_layout') +@section('content') + @auth() +
+

Направление подготовки

+

{{ $entranceExamination->direction->name }}

+

Тип экзамена

+

{{ $entranceExamination->examinationType->name }}

+

Предмет

+

{{ $entranceExamination->subject->name }}

+

Тип предмета

+

{{ $entranceExamination->subjectType->name }}

+

Мин. кол-во баллов

+

{{ $entranceExamination->scores }}

+

Позиция

+

{{ $entranceExamination->position }}

+
+ @endauth +@endsection diff --git a/resources/views/layouts/admin_layout.blade.php b/resources/views/layouts/admin_layout.blade.php index 042ae30..b17d99b 100644 --- a/resources/views/layouts/admin_layout.blade.php +++ b/resources/views/layouts/admin_layout.blade.php @@ -56,6 +56,7 @@
  • Факультеты
  • Кафедры
  • Направления
  • +
  • Вступ. экзамены
  • Уровни образования
  • Формы образования
  • Типы Экзаменов
  • diff --git a/routes/admin.php b/routes/admin.php index abd8ea8..57cb228 100644 --- a/routes/admin.php +++ b/routes/admin.php @@ -4,6 +4,7 @@ use App\Http\Controllers\admin\AdmissionController; use App\Http\Controllers\admin\Catalog\DepartmentController; use App\Http\Controllers\admin\Catalog\Direction\EducationFormController; use App\Http\Controllers\admin\Catalog\Direction\EducationLevelController; +use App\Http\Controllers\admin\Catalog\Direction\EntranceExaminationController; use App\Http\Controllers\admin\Catalog\Direction\ExaminationTypeController; use App\Http\Controllers\admin\Catalog\Direction\SubjectController; use App\Http\Controllers\admin\Catalog\Direction\SubjectTypeController; @@ -53,6 +54,8 @@ Route::middleware(['auth', 'verified'])->prefix('admin')->group(function () { Route::resource('/subject_types', SubjectTypeController::class) ->scoped(['subject_type' => 'slug']); + Route::resource('/entrance_examinations', EntranceExaminationController::class); + Route::resources([ '/documents' => DocumentController::class, '/users' => UserController::class, diff --git a/tests/Feature/admin/catalog/DirectionTest.php b/tests/Feature/admin/catalog/DirectionTest.php index cd5425e..7625bc8 100644 --- a/tests/Feature/admin/catalog/DirectionTest.php +++ b/tests/Feature/admin/catalog/DirectionTest.php @@ -7,7 +7,11 @@ use App\Models\Direction; use App\Models\EducationalInstitution; use App\Models\EducationForm; use App\Models\EducationLevel; +use App\Models\EntranceExamination; +use App\Models\ExaminationType; use App\Models\Faculty; +use App\Models\Subject; +use App\Models\SubjectType; use App\Models\User; use Tests\TestCase; @@ -15,7 +19,9 @@ class DirectionTest extends TestCase { private User $user; private Direction $direction; + private EntranceExamination $entranceExamination; private array $data; + protected function setUp(): void { parent::setUp(); @@ -26,6 +32,12 @@ class DirectionTest extends TestCase EducationForm::factory()->create(); $this->direction = Direction::factory()->create(); + + ExaminationType::factory()->create(); + Subject::factory()->create(); + SubjectType::factory()->create(); + $this->entranceExamination = EntranceExamination::factory()->create(); + $this->data = Direction::factory()->make()->only([ 'position', 'name', @@ -104,6 +116,7 @@ class DirectionTest extends TestCase public function testDestroyDirection(): void { + $this->entranceExamination->delete(); $response = $this->actingAs($this->user) ->withSession(['banned' => false]) ->delete(route('directions.destroy', $this->direction)); @@ -112,4 +125,27 @@ class DirectionTest extends TestCase $this->assertDatabaseMissing('directions', $this->direction->toArray()); } + + public function testNotDestroyDirectionWithEntranceExamination(): void + { + $response = $this->actingAs($this->user) + ->withSession(['banned' => false]) + ->delete(route('directions.destroy', $this->direction)); + + $response->assertStatus(302); + + $this->assertDatabaseHas( + 'directions', + $this->direction->only([ + 'position', + 'name', + 'description', + 'code', + 'slug', + 'education_level_id', + 'education_form_id', + 'department_id', + ]) + ); + } } diff --git a/tests/Feature/admin/catalog/direction/EntranceExaminationTest.php b/tests/Feature/admin/catalog/direction/EntranceExaminationTest.php new file mode 100644 index 0000000..ba4804b --- /dev/null +++ b/tests/Feature/admin/catalog/direction/EntranceExaminationTest.php @@ -0,0 +1,123 @@ +create(); + Faculty::factory()->create(); + Department::factory()->create(); + EducationLevel::factory()->create(); + EducationForm::factory()->create(); + Direction::factory()->create(); + ExaminationType::factory()->create(); + Subject::factory()->create(); + SubjectType::factory()->create(); + + $this->entranceExamination = EntranceExamination::factory()->create(); + + + $this->data = EntranceExamination::factory()->make()->only([ + 'direction_id', + 'examination_type_id', + 'subject_id', + 'subject_type_id', + 'scores', + 'position', + ]); + + $this->user = User::factory()->create([ + 'name' => 'admin', + 'email' => 'test@example.com', + 'password' => 123456 + ]); + } + + public function testIndexEntranceExaminationsPage(): void + { + $response = $this->actingAs($this->user) + ->withSession(['banned' => false]) + ->get(route('entrance_examinations.index')); + + $response->assertOk(); + } + + public function testCreateEntranceExaminationPage(): void + { + $response = $this->actingAs($this->user) + ->withSession(['banned' => false]) + ->get(route('entrance_examinations.create')); + + $response->assertOk(); + } + + public function testStoreEntranceExamination(): void + { + $response = $this->actingAs($this->user) + ->withSession(['banned' => false]) + ->post(route('entrance_examinations.store', $this->data)); + + $response->assertRedirect(route('entrance_examinations.index')); + + $this->assertDatabaseHas('entrance_examinations', $this->data); + } + + public function testShowEntranceExaminationPage(): void + { + $response = $this->actingAs($this->user) + ->withSession(['banned' => false]) + ->get(route('entrance_examinations.show', $this->entranceExamination)); + + $response->assertOk(); + } + + public function testEditEntranceExaminationPage(): void + { + $response = $this->actingAs($this->user) + ->withSession(['banned' => false]) + ->get(route('entrance_examinations.edit', $this->entranceExamination)); + + $response->assertOk(); + } + + public function testUpdateEntranceExamination(): void + { + $response = $this->actingAs($this->user) + ->withSession(['banned' => false]) + ->patch(route('entrance_examinations.update', $this->entranceExamination), $this->data); + + $response->assertRedirect(route('entrance_examinations.index')); + + $this->assertDatabaseHas('entrance_examinations', $this->data); + } + + public function testEntranceExaminationDirection(): void + { + $response = $this->actingAs($this->user) + ->withSession(['banned' => false]) + ->delete(route('entrance_examinations.destroy', $this->entranceExamination)); + + $response->assertRedirect(route('entrance_examinations.index')); + + $this->assertDatabaseMissing('entrance_examinations', $this->entranceExamination->toArray()); + } +}