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 { // 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 { $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(); } }