refactoring Document and Admission resource
Tests & Lint & Deploy to Railway / deploy (push) Blocked by required conditions Details
Tests & Lint & Deploy to Railway / build (2.6.6, 20.x, 8.3) (push) Has been cancelled Details

This commit is contained in:
aslan 2024-02-14 09:44:53 +03:00
parent fffd4c3310
commit 4b094339f0
15 changed files with 300 additions and 239 deletions

View File

@ -1,105 +0,0 @@
<?php
namespace App\Http\Controllers;
use App\Http\Requests\StoreFileRequest;
use App\Http\Requests\StoreReceptionScreenRequest;
use App\Http\Requests\UpdateFileRequest;
use App\Http\Requests\UpdateReceptionScreenRequest;
use App\Models\Documents;
use App\Models\ReceptionScreen;
use App\Services\WorkWithFiles;
use Carbon\Carbon;
use Illuminate\Contracts\View\Factory;
use Illuminate\Contracts\View\View;
use Illuminate\Foundation\Application;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Storage;
class DocumentController extends Controller
{
public function index(): View|Application|Factory|\Illuminate\Contracts\Foundation\Application
{
abort_if(Auth::guest(), 403);
$files = Documents::all()->sortBy('position');
return view('documents.index', compact('files'));
}
public function create($idReceptionScreen = null): View
{
abort_if(Auth::guest(), 403);
$receptionScreens = ReceptionScreen::pluck('name', 'id');
$files = Documents::where('reception_screen_id', '=', $idReceptionScreen)->get();
return view('files.create', compact('receptionScreens', 'idReceptionScreen', 'files'));
}
public function store(StoreFileRequest $request)
{
abort_if(Auth::guest(), 403);
$nameFile = $request->file('url')->getClientOriginalName();
$name = Storage::put('public', $request->file('url'));
$validated = $request->validated();
$file = new Documents();
$file->name = $validated['name'];
$file->file_name = $nameFile;
$file->url = Storage::url($name);
$file->position = $validated['position'];
$file->reception_screen_id = $validated['idReceptionScreen'];
$file->save();
return redirect()->route('files.index');
}
public function download($id)
{
$file = (new Documents())->find($id);
return Storage::url($file->url);
}
public function edit(int $idFile)
{
abort_if(Auth::guest(), 403);
$file = (new Documents())->find($idFile);
$files = Documents::where('reception_screen_id', '=', $file->reception_screen_id)->get();
$receptionScreens = ReceptionScreen::pluck('name', 'id');
$idsReceptionScreens = $receptionScreens->keys()->toArray();
$idReceptionScreen = $file->reception_screen_id;
return view(
'files.edit',
compact(
'receptionScreens',
'idsReceptionScreens',
'idReceptionScreen',
'files',
'file'
)
);
}
public function update(UpdateFileRequest $request, Documents $file)
{
abort_if(Auth::guest(), 403);
$validated = $request->validated();
$file->name = $validated['name'];
$file->position = $validated['position'];
$file->reception_screen_id = $validated['idReceptionScreen'];
$file->save();
return redirect()->route('admin-reception-screen.index');
}
public function destroy($idFile)
{
abort_if(Auth::guest(), 403);
$file = (new Documents())->find($idFile);
$file->delete();
return redirect()->route('admin-reception-screen.index');
}
}

View File

@ -0,0 +1,25 @@
<?php
namespace App\Http\Requests\admin;
use Illuminate\Contracts\Validation\ValidationRule;
use Illuminate\Foundation\Http\FormRequest;
class StoreDocumentRequest extends FormRequest
{
public function authorize(): bool
{
return true;
}
public function rules(): array
{
return [
'name' => 'required|max:255',
'description' => 'string',
'position' => 'required|int|max:255',
'document' => 'required|file',
'admission_id' => 'required|int|max:255',
];
}
}

View File

@ -0,0 +1,28 @@
<?php
namespace App\Http\Requests\admin;
use Illuminate\Foundation\Http\FormRequest;
class UpdateDocumentRequest extends FormRequest
{
public function authorize(): bool
{
return true;
}
public function rules(): array
{
return [
'name' => [
'required',
'string',
'max:255',
"unique:documents,name,{$this->document->id}",
],
'position' => 'required|int|max:255',
'description' => 'string',
'admission_id' => 'required|int|max:255',
];
}
}

View File

@ -6,7 +6,7 @@ use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class File extends Model
class Document extends Model
{
use HasFactory;
@ -18,8 +18,8 @@ class File extends Model
'description',
];
public function receptionScreen(): BelongsTo
public function admission(): BelongsTo
{
return $this->belongsTo(ReceptionScreen::class);
return $this->belongsTo(Admission::class);
}
}

View File

@ -0,0 +1,20 @@
<?php
namespace Database\Factories;
use Illuminate\Database\Eloquent\Factories\Factory;
class DocumentFactory extends Factory
{
public function definition(): array
{
return [
'name' => fake()->name(),
'file_name' => fake()->name(),
'description' => fake()->text(),
'url' => fake()->url(),
'position' => fake()->randomDigit(),
'admission_id' => 1,
];
}
}

View File

@ -11,14 +11,14 @@ return new class extends Migration
*/
public function up(): void
{
Schema::create('files', function (Blueprint $table) {
Schema::create('documents', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('file_name')->nullable();
$table->string('description')->nullable();
$table->text('description')->nullable();
$table->string('url');
$table->integer('position');
$table->foreignId('reception_screen_id')->constrained('reception_screens');
$table->foreignId('admission_id')->constrained('admissions');
$table->timestamps();
});
}
@ -28,6 +28,6 @@ return new class extends Migration
*/
public function down(): void
{
Schema::dropIfExists('files');
Schema::dropIfExists('documents');
}
};

View File

@ -2,42 +2,43 @@
namespace Database\Seeders;
use Carbon\Carbon;
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;
class FileSeeder extends Seeder
class DocumentSeeder extends Seeder
{
/**
* Run the database seeds.
*/
public function run(): void
{
DB::table('files')->insert([
DB::table('documents')->insert([
[
'name' => 'файл 1',
'file_name' => 'file1',
'description' => 'description1',
'url' => 'url/url1',
'position' => 2,
'reception_screen_id' => 1,
'created_at' => Carbon::now(),
'admission_id' => 1,
'created_at' => now(),
],
[
'name' => 'файл 2',
'description' => 'description2',
'file_name' => 'file2',
'url' => 'url/url2',
'position' => 3,
'reception_screen_id' => 1,
'created_at' => Carbon::now(),
'admission_id' => 1,
'created_at' => now(),
],
[
'name' => 'файл 3',
'file_name' => 'file3',
'description' => 'description3',
'url' => 'url/url3',
'reception_screen_id' => 1,
'admission_id' => 1,
'position' => 1,
'created_at' => Carbon::now(),
'created_at' => now(),
]
]);
}

View File

@ -1,32 +0,0 @@
#-------------------------------------------------------------------------------#
# Qodana analysis is configured by qodana.yaml file #
# https://www.jetbrains.com/help/qodana/qodana-yaml.html #
#-------------------------------------------------------------------------------#
version: "1.0"
#Specify inspection profile for code analysis
profile:
name: qodana.starter
#Enable inspections
#include:
# - name: <SomeEnabledInspectionId>
#Disable inspections
#exclude:
# - name: <SomeDisabledInspectionId>
# paths:
# - <path/where/not/run/inspection>
php:
version: 8.3 #(Applied in CI/CD pipeline)
#Execute shell command before Qodana execution (Applied in CI/CD pipeline)
#bootstrap: sh ./prepare-qodana.sh
#Install IDE plugins before Qodana execution (Applied in CI/CD pipeline)
#plugins:
# - id: <plugin.id> #(plugin id can be found at https://plugins.jetbrains.com)
#Specify Qodana linter for analysis (Applied in CI/CD pipeline)
linter: jetbrains/qodana-php:latest

View File

@ -21,7 +21,7 @@
<th scope="row">{{ $admission->position }}</th>
<td><a href="{{ route('admissions.show', $admission) }}">{{ $admission->name }}
@if(count($admission->documents) !== 0)
-- {{ count($admission->documents) }} файла(ов)
({{ count($admission->documents) }} файла)
@endif</a></td>
<td>
<a href="{{ route("admissions.edit", $admission) }}"
@ -72,7 +72,7 @@
<tr class=" border border-white border-bottom-0 border-start-0 border-end-0">
<td colspan="4">
<div class="mb-2">
<a href="{{ route('documents_create_with_admission', $admission) }}"
<a href="{{ route('document_create_from_admission', $admission) }}"
class="btn btn-primary">
Добавить файл
</a>
@ -93,7 +93,7 @@
<tr>
<td colspan="4">
<a href="{{ route('documents_create_with_admission', $admission) }}"
<a href="{{ route('document_create_from_admission', $admission) }}"
class="btn btn-primary">Добавить
файл</a>
</td>

View File

@ -0,0 +1,59 @@
@extends('layouts.admin_layout')
@section('content')
@auth()
<div class="row">
<div class="col">
<h1 class="">Изменить документ</h1>
{{ Form::open(['url' => route('documents.update', $document), 'method' => 'PATCH', 'files'=>'true']) }}
<div class="mt-2">
{{ Form::label('name', 'Имя документа') }}
</div>
<div class="mt-2">
{{ Form::text('name', $document->name, ['class' => 'form-control']) }}
</div>
<div>
@if ($errors->any())
{{ $errors->first('name') }}
@endif
</div>
<div class="mt-2">
{{ Form::label('description', 'Описание') }}
</div>
<div class="mt-2">
{{ Form::text('description', $document->description, ['class' => 'form-control']) }}
</div>
<div>
@if ($errors->any())
{{ $errors->first('description') }}
@endif
</div>
<div class="mt-2">
{{ Form::label('position', 'Позиция') }}
</div>
<div class="mt-2">
{{ Form::text('position', $document->position, ['class' => 'form-control']) }}
</div>
<div>
@if ($errors->any())
{{ $errors->first('name') }}
@endif
</div>
<div class="mt-2">
{{ Form::label('admission_id', 'Пункт экрана приема') }}
</div>
<div class="mt-2">
{{ Form::select('admission_id', $admissions, $document->admission_id, ['class' => 'form-select']) }}
</div>
<div>
@if ($errors->any())
{{ $errors->first('admission_id') }}
@endif
</div>
<div class="mt-4">
{{ Form::submit('Изменить', ['class' => 'btn btn-primary'])}}
</div>
{{Form::close()}}
</div>
</div>
@endauth
@endsection

View File

@ -1,30 +1,28 @@
@extends('layouts.admin-layout')
@extends('layouts.admin_layout')
@section('content')
<div class="container">
<h2>Файлы</h2>
<h2>Документы</h2>
<br>
<a href="{{ route('files.create') }}" class="btn btn-primary">Добавить файл</a>
<a href="{{ route('documents.create') }}" class="btn btn-primary">Добавить документ</a>
<br>
<br>
<table class="table">
<thead class="border-b-2 border-solid border-black text-left" style="text-align: left">
<tr>
{{-- <th scope="col">Позиция</th>--}}
<th scope="col">Экран Приема</th>
<th scope="col">Название файла</th>
<th scope="col">Название документа</th>
<th scope="col">действия</th>
<th scope="col">действия</th>
</tr>
</thead>
<tbody>
@foreach($files as $file)
@foreach($documents as $document)
<tr>
<td>{{ $file->receptionScreen->name }}</td>
{{-- <th scope="row">{{ $file->position }}</th>--}}
<td>{{ $file->name }}</td>
<td><a href="{{ route("files.edit", $file) }}" class="btn btn-secondary">редактировать</a>
<td>{{ $document->admission->name }}</td>
<td><a href="{{ route('documents.show', $document) }}">{{ $document->name }}</a></td>
<td><a href="{{ route("documents.edit", $document) }}" class="btn btn-secondary">редактировать</a>
<a rel="nofollow" data-method="delete" data-confirm="Вы действительно хотите удалить?"
href="{{ route('files.destroy', $file) }}" class="btn btn-danger">
href="{{ route('documents.destroy', $document) }}" class="btn btn-danger">
удалить
</a>
</td>

View File

@ -1,68 +0,0 @@
@extends('layouts.admin-layout')
@section('content')
@auth()
<div class="row">
<div class="col">
<h1 class="">Изменить файл</h1>
{{ Form::open(array('url' => route('files.update', $file), 'method' => 'PATCH', 'files'=>'true')) }}
<div class="mt-2">
{{ Form::label('name', 'Имя файла') }}
</div>
<div class="mt-2">
{{ Form::text('name', $file->name, ['class' => 'form-control']) }}
</div>
<div>
@if ($errors->any())
{{ $errors->first('name') }}
@endif
</div>
<div class="mt-2">
{{ Form::label('position', 'Позиция') }}
</div>
<div class="mt-2">
{{ Form::text('position', $file->position, ['class' => 'form-control']) }}
</div>
<div>
@if ($errors->any())
{{ $errors->first('name') }}
@endif
</div>
<div class="mt-2">
{{ Form::label('idReceptionScreen', 'Пункт экрана приема') }}
</div>
<div class="mt-2">
{{ Form::select('idReceptionScreen', $receptionScreens, $idReceptionScreen, $idsReceptionScreens,['class' => 'form-select']) }}
</div>
<div>
@if ($errors->any())
{{ $errors->first('idReceptionScreen') }}
@endif
</div>
<div class="mt-4">
{{ Form::submit('Загрузить файл', ['class' => 'btn btn-primary'])}}
</div>
{{Form::close()}}
</div>
@if($idReceptionScreen !== null)
<div class="col">
<h2>Файлы пункта Экрана Приема: {{ $receptionScreens[$idReceptionScreen] }}</h2>
<table class="table">
<thead class="border-b-2 border-solid border-black text-left" style="text-align: left">
<tr>
<th scope="col">Позиция</th>
<th scope="col">Название</th>
</tr>
</thead>
<tbody>
@foreach($files as $file)
<tr>
<th scope="row">{{ $file->position }}</th>
<th scope="row">{{ $file->name }}</th>
@endforeach
</tbody>
</table>
</div>
@endif
</div>
@endauth
@endsection

View File

@ -13,8 +13,9 @@ Route::middleware(['auth', 'verified'])->prefix('admin')->group(function () {
return view('admin');
})->name('dashboard');
Route::get('/documents/create/{admission}', [DocumentController::class, 'createFromAdmission'])->name('documents_create_with_admission');
Route::get('/documents/download/{Document}', [DocumentController::class, 'download'])->name('documents_download');
Route::get('/documents/create/{admission}', [DocumentController::class, 'createFromAdmission'])->name('document_create_from_admission');
Route::post('/documents/store_from_admission', [DocumentController::class, 'storeFromAdmission'])->name('document_store_from_admission');
Route::get('/documents/download/{document}', [DocumentController::class, 'download'])->name('document_download');
Route::resource('/educational_institutions', EducationalInstitutionController::class)
->scoped(['educational_institution' => 'slug']);

View File

@ -0,0 +1,134 @@
<?php
namespace Tests\Feature\admin;
use App\Models\Admission;
use App\Models\Document;
use App\Models\User;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Storage;
use Tests\TestCase;
class DocumentTest extends TestCase
{
private User $user;
private array $data;
private Document $document;
private Admission $admission;
protected function setUp(): void
{
parent::setUp();
$this->admission = Admission::factory()->create();
$this->document = Document::factory()->create();
$this->data = Document::factory()->make()->only([
'position',
'name',
'description',
'admission_id',
]);
$this->user = User::factory()->create([
'name' => 'admin',
'email' => 'test@example.com',
'password' => 123456
]);
}
public function testIndexDocumentsPage(): void
{
$response = $this->actingAs($this->user)
->withSession(['banned' => false])
->get(route('documents.index'));
$response->assertOk();
}
public function testCreateDocumentPage(): void
{
$response = $this->actingAs($this->user)
->withSession(['banned' => false])
->get(route('documents.create'));
$response->assertOk();
}
public function testStoreDocument(): void
{
Storage::fake('fake');
$file = UploadedFile::fake()->create('fake.pdf', 100, 'application/pdf');
$response = $this->actingAs($this->user)
->withSession(['banned' => false])
->post(route('documents.store'), [...$this->data, 'document' => $file]);
$response->assertRedirect(route('documents.index'));
$this->assertDatabaseHas('documents', $this->data);
}
public function testShowDocumentPage(): void
{
$response = $this->actingAs($this->user)
->withSession(['banned' => false])
->get(route('documents.show', $this->document));
$response->assertOk();
}
public function testEditDocumentPage(): void
{
$response = $this->actingAs($this->user)
->withSession(['banned' => false])
->get(route('documents.edit', $this->document));
$response->assertOk();
}
public function testUpdateDocument(): void
{
$response = $this->actingAs($this->user)
->withSession(['banned' => false])
->patch(route('documents.update', $this->document), $this->data);
$response->assertRedirect(route('documents.index'));
$this->assertDatabaseHas('documents', $this->data);
}
public function testDestroyDocument(): void
{
$response = $this->actingAs($this->user)
->withSession(['banned' => false])
->delete(route('documents.destroy', $this->document));
$response->assertRedirect(route('documents.index'));
$this->assertDatabaseMissing('documents', $this->document->toArray());
}
public function testCreateDocumentFromAdmissionPage(): void
{
$response = $this->actingAs($this->user)
->withSession(['banned' => false])
->get(route('document_create_from_admission', $this->admission));
$response->assertOk();
}
public function testStoreFromAdmissionDocument(): void
{
Storage::fake('fake');
$file = UploadedFile::fake()->create('fake.pdf', 100, 'application/pdf');
$response = $this->actingAs($this->user)
->withSession(['banned' => false])
->post(route('document_store_from_admission'), [...$this->data, 'document' => $file]);
$response->assertRedirect(route('admissions.index'));
$this->assertDatabaseHas('documents', $this->data);
}
}