223 lines
6.5 KiB
PHP
223 lines
6.5 KiB
PHP
<?php
|
|
|
|
namespace App\Repositories;
|
|
|
|
use App\Models\Job;
|
|
use App\Services\CacheService;
|
|
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
|
|
use Illuminate\Database\Eloquent\Collection;
|
|
|
|
class JobRepository
|
|
{
|
|
public function __construct(
|
|
private CacheService $cacheService
|
|
) {}
|
|
/**
|
|
* Get all active jobs with pagination
|
|
*/
|
|
public function getActiveJobs(int $perPage = 15): LengthAwarePaginator
|
|
{
|
|
return Job::with(['source', 'category', 'jobLocation'])
|
|
->active()
|
|
->latest('posted_at')
|
|
->paginate($perPage);
|
|
}
|
|
|
|
/**
|
|
* Find a job by ID
|
|
*/
|
|
public function find(int $id): ?Job
|
|
{
|
|
return Job::with(['source', 'category', 'jobLocation'])->find($id);
|
|
}
|
|
|
|
/**
|
|
* Get jobs by category
|
|
*/
|
|
public function getByCategory(int $categoryId, int $perPage = 15): LengthAwarePaginator
|
|
{
|
|
return Job::with(['source', 'category', 'jobLocation'])
|
|
->active()
|
|
->where('job_category_id', $categoryId)
|
|
->latest('posted_at')
|
|
->paginate($perPage);
|
|
}
|
|
|
|
/**
|
|
* Get jobs by location
|
|
*/
|
|
public function getByLocation(int $locationId, int $perPage = 15): LengthAwarePaginator
|
|
{
|
|
return Job::with(['source', 'category', 'jobLocation'])
|
|
->active()
|
|
->where('job_location_id', $locationId)
|
|
->latest('posted_at')
|
|
->paginate($perPage);
|
|
}
|
|
|
|
/**
|
|
* Get remote jobs
|
|
*/
|
|
public function getRemoteJobs(int $perPage = 15): LengthAwarePaginator
|
|
{
|
|
return Job::with(['source', 'category', 'jobLocation'])
|
|
->active()
|
|
->remote()
|
|
->latest('posted_at')
|
|
->paginate($perPage);
|
|
}
|
|
|
|
/**
|
|
* Get recent jobs (last N days)
|
|
*/
|
|
public function getRecentJobs(int $days = 7, int $perPage = 15): LengthAwarePaginator
|
|
{
|
|
return Job::with(['source', 'category', 'jobLocation'])
|
|
->active()
|
|
->where(function ($query) use ($days) {
|
|
$query->where('posted_at', '>=', now()->subDays($days))
|
|
->orWhere(function ($q) use ($days) {
|
|
$q->whereNull('posted_at')
|
|
->where('created_at', '>=', now()->subDays($days));
|
|
});
|
|
})
|
|
->latest('posted_at')
|
|
->paginate($perPage);
|
|
}
|
|
|
|
/**
|
|
* Get jobs by experience level
|
|
*/
|
|
public function getByExperienceLevel(string $level, int $perPage = 15): LengthAwarePaginator
|
|
{
|
|
return Job::with(['source', 'category', 'jobLocation'])
|
|
->active()
|
|
->experienceLevel($level)
|
|
->latest('posted_at')
|
|
->paginate($perPage);
|
|
}
|
|
|
|
/**
|
|
* Get jobs by type
|
|
*/
|
|
public function getByJobType(string $type, int $perPage = 15): LengthAwarePaginator
|
|
{
|
|
return Job::with(['source', 'category', 'jobLocation'])
|
|
->active()
|
|
->ofType($type)
|
|
->latest('posted_at')
|
|
->paginate($perPage);
|
|
}
|
|
|
|
/**
|
|
* Get similar jobs (same category, exclude current)
|
|
*/
|
|
public function getSimilarJobs(Job $job, int $limit = 5): Collection
|
|
{
|
|
return Job::with(['source', 'category', 'jobLocation'])
|
|
->active()
|
|
->where('id', '!=', $job->id)
|
|
->where('job_category_id', $job->job_category_id)
|
|
->latest('posted_at')
|
|
->limit($limit)
|
|
->get();
|
|
}
|
|
|
|
/**
|
|
* Get featured jobs (most recent from diverse sources)
|
|
*/
|
|
public function getFeaturedJobs(int $limit = 10): Collection
|
|
{
|
|
$cacheKey = $this->cacheService->featuredJobsKey($limit);
|
|
|
|
return $this->cacheService->rememberWithTags(['jobs'], $cacheKey, CacheService::CACHE_FEATURED_JOBS, function () use ($limit) {
|
|
// First, build a subquery with window function
|
|
$subQuery = \DB::table('jobs')
|
|
->selectRaw('jobs.*, ROW_NUMBER() OVER (PARTITION BY job_source_id ORDER BY posted_at DESC) as rn')
|
|
->whereNull('deleted_at')
|
|
->where(function ($query) {
|
|
$query->whereNull('expires_at')
|
|
->orWhere('expires_at', '>', now());
|
|
});
|
|
|
|
// Then query from that subquery filtering by rn
|
|
// Use withoutGlobalScopes to prevent SoftDeletes from adding WHERE clause
|
|
return Job::withoutGlobalScopes()
|
|
->with(['source', 'category', 'jobLocation'])
|
|
->fromSub($subQuery, 'ranked_jobs')
|
|
->whereRaw('ranked_jobs.rn <= 2')
|
|
->orderBy('posted_at', 'desc')
|
|
->limit($limit)
|
|
->get();
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Get job statistics
|
|
*/
|
|
public function getStatistics(): array
|
|
{
|
|
$cacheKey = $this->cacheService->statisticsKey();
|
|
|
|
return $this->cacheService->remember($cacheKey, CacheService::CACHE_STATISTICS, function () {
|
|
$total = Job::count();
|
|
$active = Job::active()->count();
|
|
$remote = Job::active()->remote()->count();
|
|
$recentlyAdded = Job::where('created_at', '>=', now()->subDays(7))->count();
|
|
|
|
$topCategories = Job::active()
|
|
->select('job_category_id', \DB::raw('COUNT(*) as count'))
|
|
->whereNotNull('job_category_id')
|
|
->groupBy('job_category_id')
|
|
->orderBy('count', 'desc')
|
|
->limit(5)
|
|
->with('category')
|
|
->get();
|
|
|
|
return [
|
|
'total_jobs' => $total,
|
|
'active_jobs' => $active,
|
|
'remote_jobs' => $remote,
|
|
'recently_added' => $recentlyAdded,
|
|
'top_categories' => $topCategories,
|
|
];
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Create a new job
|
|
*/
|
|
public function create(array $data): Job
|
|
{
|
|
return Job::create($data);
|
|
}
|
|
|
|
/**
|
|
* Update a job
|
|
*/
|
|
public function update(Job $job, array $data): bool
|
|
{
|
|
return $job->update($data);
|
|
}
|
|
|
|
/**
|
|
* Delete a job
|
|
*/
|
|
public function delete(Job $job): bool
|
|
{
|
|
return $job->delete();
|
|
}
|
|
|
|
/**
|
|
* Get jobs count by source
|
|
*/
|
|
public function getCountBySource(): Collection
|
|
{
|
|
return Job::with('source')
|
|
->select('job_source_id', \DB::raw('COUNT(*) as count'))
|
|
->groupBy('job_source_id')
|
|
->orderBy('count', 'desc')
|
|
->get();
|
|
}
|
|
}
|