From f6135d75d8324afc58f8c91c22ef7143dbdd2264 Mon Sep 17 00:00:00 2001 From: Piotr Jura Date: Tue, 30 May 2023 16:41:40 +0200 Subject: [PATCH] Job applications with only salary --- job-board/app/Exceptions/Handler.php | 6 +- .../Controllers/JobApplicationController.php | 29 ++++++++ .../app/Http/Controllers/JobController.php | 2 +- job-board/app/Models/Job.php | 15 ++++ job-board/app/Models/JobApplication.php | 24 +++++++ job-board/app/Models/User.php | 6 ++ job-board/app/Policies/JobPolicy.php | 72 +++++++++++++++++++ .../app/Providers/AuthServiceProvider.php | 4 +- .../factories/JobApplicationFactory.php | 23 ++++++ ...9_111115_create_job_applications_table.php | 33 +++++++++ job-board/database/seeders/DatabaseSeeder.php | 10 ++- .../views/components/layout.blade.php | 22 +++++- job-board/resources/views/job/show.blade.php | 12 ++++ .../views/job_application/create.blade.php | 26 +++++++ job-board/routes/web.php | 8 ++- 15 files changed, 284 insertions(+), 8 deletions(-) create mode 100644 job-board/app/Http/Controllers/JobApplicationController.php create mode 100644 job-board/app/Models/JobApplication.php create mode 100644 job-board/app/Policies/JobPolicy.php create mode 100644 job-board/database/factories/JobApplicationFactory.php create mode 100644 job-board/database/migrations/2023_05_29_111115_create_job_applications_table.php create mode 100644 job-board/resources/views/job_application/create.blade.php diff --git a/job-board/app/Exceptions/Handler.php b/job-board/app/Exceptions/Handler.php index 56af264..570fac7 100644 --- a/job-board/app/Exceptions/Handler.php +++ b/job-board/app/Exceptions/Handler.php @@ -2,7 +2,9 @@ namespace App\Exceptions; +use Illuminate\Auth\Access\AuthorizationException; use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler; +use Illuminate\Http\Request; use Throwable; class Handler extends ExceptionHandler @@ -24,7 +26,7 @@ class Handler extends ExceptionHandler public function register(): void { $this->reportable(function (Throwable $e) { - // + }); } -} +} \ No newline at end of file diff --git a/job-board/app/Http/Controllers/JobApplicationController.php b/job-board/app/Http/Controllers/JobApplicationController.php new file mode 100644 index 0000000..39cd790 --- /dev/null +++ b/job-board/app/Http/Controllers/JobApplicationController.php @@ -0,0 +1,29 @@ +authorize('apply', $job); + return view('job_application.create', ['job' => $job]); + } + + public function store(Job $job, Request $request) + { + $this->authorize('apply', $job); + $job->jobApplications()->create([ + ...$request->validate([ + 'expected_salary' => 'required|min:1|max:1000000' + ]), + 'user_id' => auth()->user()->id + ]); + + return redirect()->route('jobs.show', $job) + ->with('success', 'Job application submitted.'); + } +} \ No newline at end of file diff --git a/job-board/app/Http/Controllers/JobController.php b/job-board/app/Http/Controllers/JobController.php index 7e398c1..ff6d5f2 100644 --- a/job-board/app/Http/Controllers/JobController.php +++ b/job-board/app/Http/Controllers/JobController.php @@ -49,7 +49,7 @@ public function show(Job $job) { return view( 'job.show', - ['job' => $job->load('employer.jobs')] + ['job' => $job->load('employer.jobs')->loadCount('jobApplications')] ); } diff --git a/job-board/app/Models/Job.php b/job-board/app/Models/Job.php index 64e6290..c2e743e 100644 --- a/job-board/app/Models/Job.php +++ b/job-board/app/Models/Job.php @@ -2,10 +2,12 @@ namespace App\Models; +use Illuminate\Auth\Authenticatable; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Relations\BelongsTo; +use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Query\Builder as QueryBuilder; class Job extends Model @@ -25,6 +27,11 @@ public function employer(): BelongsTo return $this->belongsTo(Employer::class); } + public function jobApplications(): HasMany + { + return $this->hasMany(JobApplication::class); + } + public function scopeFilter(Builder|QueryBuilder $query, array $filters): Builder|QueryBuilder { return $query->when($filters['search'] ?? null, function ($query, $search) { @@ -45,4 +52,12 @@ public function scopeFilter(Builder|QueryBuilder $query, array $filters): Builde $query->where('category', $category); }); } + + public function hasUserApplied(Authenticatable|User|int $user): bool + { + return $this->where('id', $this->id)->whereHas( + 'jobApplications', + fn($query) => $query->where('user_id', '=', $user->id ?? $user) + )->exists(); + } } \ No newline at end of file diff --git a/job-board/app/Models/JobApplication.php b/job-board/app/Models/JobApplication.php new file mode 100644 index 0000000..9609ea2 --- /dev/null +++ b/job-board/app/Models/JobApplication.php @@ -0,0 +1,24 @@ +belongsTo(Job::class); + } + + public function user(): BelongsTo + { + return $this->belongsTo(User::class); + } +} \ No newline at end of file diff --git a/job-board/app/Models/User.php b/job-board/app/Models/User.php index ad6f3a7..810680f 100644 --- a/job-board/app/Models/User.php +++ b/job-board/app/Models/User.php @@ -4,6 +4,7 @@ // use Illuminate\Contracts\Auth\MustVerifyEmail; use Illuminate\Database\Eloquent\Factories\HasFactory; +use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\Relations\HasOne; use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; @@ -47,4 +48,9 @@ public function employer(): HasOne { return $this->hasOne(Employer::class); } + + public function jobApplications(): HasMany + { + return $this->hasMany(JobApplication::class); + } } \ No newline at end of file diff --git a/job-board/app/Policies/JobPolicy.php b/job-board/app/Policies/JobPolicy.php new file mode 100644 index 0000000..2c4d00e --- /dev/null +++ b/job-board/app/Policies/JobPolicy.php @@ -0,0 +1,72 @@ +hasUserApplied($user); + } +} \ No newline at end of file diff --git a/job-board/app/Providers/AuthServiceProvider.php b/job-board/app/Providers/AuthServiceProvider.php index 54756cd..b9aeac1 100644 --- a/job-board/app/Providers/AuthServiceProvider.php +++ b/job-board/app/Providers/AuthServiceProvider.php @@ -13,7 +13,7 @@ class AuthServiceProvider extends ServiceProvider * @var array */ protected $policies = [ - // + ]; /** @@ -23,4 +23,4 @@ public function boot(): void { // } -} +} \ No newline at end of file diff --git a/job-board/database/factories/JobApplicationFactory.php b/job-board/database/factories/JobApplicationFactory.php new file mode 100644 index 0000000..2b8a486 --- /dev/null +++ b/job-board/database/factories/JobApplicationFactory.php @@ -0,0 +1,23 @@ + + */ +class JobApplicationFactory extends Factory +{ + /** + * Define the model's default state. + * + * @return array + */ + public function definition(): array + { + return [ + 'expected_salary' => $this->faker->numberBetween(4000, 170000), + ]; + } +} \ No newline at end of file diff --git a/job-board/database/migrations/2023_05_29_111115_create_job_applications_table.php b/job-board/database/migrations/2023_05_29_111115_create_job_applications_table.php new file mode 100644 index 0000000..c0ad931 --- /dev/null +++ b/job-board/database/migrations/2023_05_29_111115_create_job_applications_table.php @@ -0,0 +1,33 @@ +id(); + $table->foreignIdFor(User::class); + $table->foreignIdFor(Job::class); + + $table->unsignedInteger('expected_salary'); + + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('job_applications'); + } +}; \ No newline at end of file diff --git a/job-board/database/seeders/DatabaseSeeder.php b/job-board/database/seeders/DatabaseSeeder.php index 7d3746f..d72fca8 100644 --- a/job-board/database/seeders/DatabaseSeeder.php +++ b/job-board/database/seeders/DatabaseSeeder.php @@ -34,6 +34,14 @@ public function run(): void ]); } - // \App\Models\User::factory(10)->create(); + foreach ($users as $user) { + $jobs = \App\Models\Job::inRandomOrder()->take(rand(0, 4))->get(); + foreach ($jobs as $job) { + \App\Models\JobApplication::factory()->create([ + 'job_id' => $job->id, + 'user_id' => $user->id, + ]); + } + } } } \ No newline at end of file diff --git a/job-board/resources/views/components/layout.blade.php b/job-board/resources/views/components/layout.blade.php index caaeaf8..374330b 100644 --- a/job-board/resources/views/components/layout.blade.php +++ b/job-board/resources/views/components/layout.blade.php @@ -18,11 +18,14 @@ class="from-10% via-30% to-90% mx-auto mt-10 max-w-2xl bg-gradient-to-r from-ind -
    +
      @auth
    • {{ auth()->user()->name ?? 'Anynomus' }}
    • +
    • + Applications +
    • @csrf @@ -37,6 +40,23 @@ class="from-10% via-30% to-90% mx-auto mt-10 max-w-2xl bg-gradient-to-r from-ind @endauth
    + @if (session('error')) + + @endif + @if (session('success')) + + @endif + {{ $slot }} diff --git a/job-board/resources/views/job/show.blade.php b/job-board/resources/views/job/show.blade.php index 36ddc3e..c794f53 100644 --- a/job-board/resources/views/job/show.blade.php +++ b/job-board/resources/views/job/show.blade.php @@ -5,6 +5,18 @@

    {!! nl2br(e($job->description)) !!}

    + + @can('apply', $job) +
    + + Apply + +
    {{ $job->job_applications_count }} other + {{ Str::plural('applicant', $job->job_applications_count) }}
    +
    + @else +
    You already applied to this job
    + @endcan diff --git a/job-board/resources/views/job_application/create.blade.php b/job-board/resources/views/job_application/create.blade.php new file mode 100644 index 0000000..59c5614 --- /dev/null +++ b/job-board/resources/views/job_application/create.blade.php @@ -0,0 +1,26 @@ + + + + + + +

    Your Job Application

    + + + @csrf +
    + + +
    + + Apply + +
    +
    diff --git a/job-board/routes/web.php b/job-board/routes/web.php index 23b793c..b3fbeda 100644 --- a/job-board/routes/web.php +++ b/job-board/routes/web.php @@ -1,6 +1,7 @@ only(['create', 'store']); Route::delete('logout', fn() => to_route('auth.destroy'))->name('logout'); Route::delete('auth', [AuthController::class, 'destroy']) - ->name('auth.destroy'); \ No newline at end of file + ->name('auth.destroy'); + +Route::middleware('auth')->group(function () { + Route::resource('job.application', JobApplicationController::class) + ->only(['create', 'store'])->scoped(); +}); \ No newline at end of file