Skip to content

Commit

Permalink
feat: student
Browse files Browse the repository at this point in the history
  • Loading branch information
iqbaleff214 committed Jun 4, 2024
1 parent 6200a76 commit 0808ecf
Show file tree
Hide file tree
Showing 19 changed files with 825 additions and 0 deletions.
23 changes: 23 additions & 0 deletions app/Enum/StudentStatus.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

namespace App\Enum;

enum StudentStatus: string
{
case CANDIDATE = 'candidate';
case ACTIVE = 'active';
case GRADUATED = 'graduated';
case EXPELLED = 'expelled';
case ON_LEAVE = 'on_leave';
case QUIT = 'quit';

public static function is(string $value, StudentStatus $status): bool
{
return self::tryFrom($value) === $status;
}

public static function isIncluded(?string $value, StudentStatus ...$status): bool
{
return in_array(self::tryFrom($value), $status);
}
}
149 changes: 149 additions & 0 deletions app/Http/Controllers/StudentController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
<?php

namespace App\Http\Controllers;

use App\Enum\Role;
use App\Http\Requests\StoreStudentRequest;
use App\Http\Requests\UpdateStudentRequest;
use App\Models\Student;
use App\Models\User;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
use Illuminate\View\View;
use Yajra\DataTables\Facades\DataTables;

class StudentController extends Controller
{
/**
* Display a listing of the resource.
*/
public function index(Request $request): View|JsonResponse
{
if ($request->ajax()) {
return DataTables::eloquent(Student::query())
->addColumn('action', function ($row) {
return '<div class="dropdown">
<button type="button"
class="btn p-0 dropdown-toggle hide-arrow"
data-bs-toggle="dropdown">
<i class="bx bx-dots-vertical-rounded"></i>
</button>
<div class="dropdown-menu">
<a class="dropdown-item" href="'.route('student.edit', $row).'"><i class="bx bx-edit-alt me-1"></i>'.__('label.edit').'</a>
<form action="'.route('student.destroy', $row).'" method="post" onsubmit="confirmSubmit(event, this)">
<input type="hidden" name="_method" value="DELETE">
<input type="hidden" name="_token" value="'.csrf_token().'" />
<button class="dropdown-item" type="submit"><i class="bx bx-trash me-1"></i>'.__('label.delete').'</button>
</form>
</div>
</div>';
})
->editColumn('image', fn ($row) => '<a data-fslightbox href="'.$row->image_url.'"><img src="'.$row->image_url.'" alt="user-avatar" class="d-block rounded" height="30" width="30"></a>')
->editColumn('gender', fn ($row) => __('label.'.$row->gender))
->editColumn('status', fn ($row) => __('label.'.$row->status))
->filterColumn('gender', fn ($query, $keyword) => $query->where('gender', $keyword))
->rawColumns(['action', 'image'])
->toJson();
}

return view('pages.student.index');
}

/**
* Show the form for creating a new resource.
*/
public function create(): View
{
return view('pages.student.create', [
'guardians' => User::role(Role::STUDENT_GUARDIAN)->get(),
]);
}

/**
* Store a newly created resource in storage.
*/
public function store(StoreStudentRequest $request): RedirectResponse
{
try {
$data = $request->validated();
if ($request->hasFile('image')) {
$data['image'] = time().random_int(0, PHP_INT_MAX).'.'.$request->file('image')->extension();
Storage::putFileAs('public', $request->file('image'), $data['image']);
}

Student::create($data);

return redirect()->route('student.index')->with('notification', $this->successNotification('notification.success_create', 'menu.student'));
} catch (\Throwable $throwable) {
Log::error($throwable->getMessage());

return back()->with('notification', $this->successNotification('notification.fail_create', 'menu.student'));
}
}

/**
* Display the specified resource.
*/
public function show(Student $student): View
{
return view('pages.student.show', [
'student' => $student,
]);
}

/**
* Show the form for editing the specified resource.
*/
public function edit(Student $student): View
{
return view('pages.student.edit', [
'student' => $student,
'guardians' => User::role(Role::STUDENT_GUARDIAN)->get(),
]);
}

/**
* Update the specified resource in storage.
*/
public function update(UpdateStudentRequest $request, Student $student): RedirectResponse
{
try {
$data = $request->validated();
if ($request->hasFile('image')) {
if ($student->image) {
Storage::delete("public/{$student->image}");
}

$data['image'] = time().random_int(0, PHP_INT_MAX).'.'.$request->file('image')->extension();
Storage::putFileAs('public', $request->file('image'), $data['image']);
}

$student->update($data);

return back()->with('notification', $this->successNotification('notification.success_update', 'menu.student'));
} catch (\Throwable $throwable) {
Log::error($throwable->getMessage());

return back()->with('notification', $this->successNotification('notification.fail_update', 'menu.student'));
}
}

/**
* Remove the specified resource from storage.
*/
public function destroy(Student $student): RedirectResponse
{
try {
$student->delete();

return back()->with('notification', $this->successNotification('notification.success_delete', 'menu.student'));
} catch (\Throwable $throwable) {
Log::error($throwable->getMessage());

return back()->with('notification', $this->successNotification('notification.fail_delete', 'menu.student'));
}
}
}
59 changes: 59 additions & 0 deletions app/Http/Requests/StoreStudentRequest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<?php

namespace App\Http\Requests;

use App\Enum\Role;
use App\Enum\StudentStatus;
use Illuminate\Contracts\Validation\ValidationRule;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;

class StoreStudentRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return auth()->user()->isRole(Role::OWNER) || auth()->user()->isRole(Role::HEADMASTER) || auth()->user()->isRole(Role::ADMINISTRATOR);
}

/**
* Get the validation rules that apply to the request.
*
* @return array<string, ValidationRule|array|string>
*/
public function rules(): array
{
return [
'student_id_number' => ['nullable', Rule::unique('students', 'student_id_number')],
'name' => ['required'],
'nickname' => ['nullable'],
'birthplace' => ['nullable'],
'birthdate' => ['required', 'date'],
'gender' => ['required'],
'status' => ['required'],
'admission_date' => ['required', 'date'],
'departure_date' => ['nullable', Rule::requiredIf(StudentStatus::isIncluded($this->status, StudentStatus::EXPELLED, StudentStatus::QUIT, StudentStatus::ON_LEAVE, StudentStatus::GRADUATED)), 'after:admission_date'],
'image' => ['nullable', 'image'],
'student_guardian_id' => ['required'],
];
}

public function attributes(): array
{
return [
'student_id_number' => __('field.student_id_number'),
'name' => __('field.name'),
'nickname' => __('field.nickname'),
'birthplace' => __('field.birthplace'),
'birthdate' => __('field.birthdate'),
'gender' => __('field.gender'),
'status' => __('field.status'),
'admission_date' => __('field.admission_date'),
'departure_date' => __('field.departure_date'),
'image' => __('field.image'),
'student_guardian_id' => __('field.student_guardian_id'),
];
}
}
59 changes: 59 additions & 0 deletions app/Http/Requests/UpdateStudentRequest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<?php

namespace App\Http\Requests;

use App\Enum\Role;
use App\Enum\StudentStatus;
use Illuminate\Contracts\Validation\ValidationRule;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;

class UpdateStudentRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return auth()->user()->isRole(Role::OWNER) || auth()->user()->isRole(Role::HEADMASTER) || auth()->user()->isRole(Role::ADMINISTRATOR);
}

/**
* Get the validation rules that apply to the request.
*
* @return array<string, ValidationRule|array|string>
*/
public function rules(): array
{
return [
'student_id_number' => ['nullable', Rule::unique('students', 'student_id_number')->ignore($this->id)],
'name' => ['required'],
'nickname' => ['nullable'],
'birthplace' => ['nullable'],
'birthdate' => ['required', 'date'],
'gender' => ['required'],
'status' => ['required'],
'admission_date' => ['required', 'date'],
'departure_date' => ['nullable', Rule::requiredIf(StudentStatus::isIncluded($this->status, StudentStatus::EXPELLED, StudentStatus::QUIT, StudentStatus::ON_LEAVE, StudentStatus::GRADUATED)), 'after:admission_date'],
'image' => ['nullable', 'image'],
'student_guardian_id' => ['required'],
];
}

public function attributes(): array
{
return [
'student_id_number' => __('field.student_id_number'),
'name' => __('field.name'),
'nickname' => __('field.nickname'),
'birthplace' => __('field.birthplace'),
'birthdate' => __('field.birthdate'),
'gender' => __('field.gender'),
'status' => __('field.status'),
'admission_date' => __('field.admission_date'),
'departure_date' => __('field.departure_date'),
'image' => __('field.image'),
'student_guardian_id' => __('field.student_guardian_id'),
];
}
}
51 changes: 51 additions & 0 deletions app/Models/Student.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Storage;

class Student extends Model
{
use HasFactory;

protected $fillable = [
'student_id_number', 'name', 'nickname', 'birthplace', 'birthdate',
'gender', 'status', 'admission_date', 'departure_date', 'image',
'student_guardian_id',
];

protected $appends = [
'image_url',
];

public function imageUrl(): Attribute
{
return new Attribute(
get: function () {
if ($this->image) {
return asset('storage/'.$this->image);
}

return asset('404_Black.jpg');
}
);
}

protected static function boot(): void
{
parent::boot();
static::creating(function (Student $student) {
if (is_null($student->student_id_number)) {
$student->student_id_number = time().random_int(0, 1000).time();
}
});
static::deleting(function (Student $student) {
if ($student->image) {
Storage::delete("public/$student->image");
}
});
}
}
36 changes: 36 additions & 0 deletions database/factories/StudentFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php

namespace Database\Factories;

use App\Enum\Gender;
use App\Enum\StudentStatus;
use App\Models\Student;
use Illuminate\Database\Eloquent\Factories\Factory;

/**
* @extends Factory<Student>
*/
class StudentFactory extends Factory
{
/**
* Define the model's default state.
*
* @return array<string, mixed>
*/
public function definition(): array
{
$gender = fake()->randomElement(Gender::cases());
$status = fake()->randomElement(StudentStatus::cases());

return [
'student_id_number' => fake()->unique()->creditCardNumber(),
'name' => fake()->name($gender->value),
'birthplace' => fake()->city(),
'birthdate' => fake()->dateTimeBetween('-12 years', '-6 years'),
'gender' => $gender,
'status' => $status,
'admission_date' => fake()->dateTimeBetween('-2 years', '-2 weeks'),
'departure_date' => $status === StudentStatus::ACTIVE ? null : fake()->dateTimeBetween('-2 weeks'),
];
}
}
Loading

0 comments on commit 0808ecf

Please sign in to comment.