migrants-nt-sec/app/Http/Controllers/PublicSearchController.php

291 lines
8.8 KiB
PHP

<?php
namespace App\Http\Controllers;
use App\Http\Resources\PersonCollection;
use App\Models\Person;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Exception;
class PublicSearchController extends Controller
{
/**
* Search for persons with various filters
*
* @param Request $request
* @return JsonResponse
*/
public function search(Request $request): JsonResponse
{
try {
$query = $this->buildSearchQuery($request);
$persons = $this->paginateAndLoadRelations($query, $request);
return response()->json([
'success' => true,
'data' => new PersonCollection($persons),
'message' => 'Public search results retrieved successfully'
]);
} catch (Exception $e) {
return response()->json([
'success' => false,
'message' => 'Failed to retrieve search results',
'error' => $e->getMessage()
], 500);
}
}
/**
* Build the search query with all filters applied
*
* @param Request $request
* @return Builder
*/
private function buildSearchQuery(Request $request): Builder
{
$query = Person::query();
$useOrConditions = $this->shouldUseOrConditions($request);
// Apply basic filters (firstName, lastName, etc.)
$this->applyBasicFilters($query, $request, $useOrConditions);
// Apply relation-based filters
$this->applyYearOfArrivalFilter($query, $request);
$this->applyAgeAtMigrationFilter($query, $request);
$this->applySettlementLocationFilter($query, $request);
return $query;
}
/**
* Determine if OR logic should be used between filters
*
* @param Request $request
* @return bool
*/
private function shouldUseOrConditions(Request $request): bool
{
$exactMatch = $request->boolean('exactMatch');
$useOrLogic = $request->boolean('useOrLogic');
return $useOrLogic || $exactMatch;
}
/**
* Apply filters for fields in the Person table
*
* @param Builder $query
* @param Request $request
* @param bool $useOrConditions
* @return void
*/
private function applyBasicFilters(Builder $query, Request $request, bool $useOrConditions): void
{
$filters = $this->collectBasicFilters($request);
if (empty($filters)) {
return;
}
if ($useOrConditions) {
$this->applyFiltersWithOrLogic($query, $filters);
} else {
$this->applyFiltersWithAndLogic($query, $filters);
}
}
/**
* Collect basic filters from the request
*
* @param Request $request
* @return array
*/
private function collectBasicFilters(Request $request): array
{
$exactMatch = $request->boolean('exactMatch');
$filters = [];
$filterFields = [
'id_card_no' => 'id_card_no',
'firstName' => 'christian_name',
'lastName' => 'surname',
'regionOfOrigin' => 'place_of_birth'
];
foreach ($filterFields as $requestKey => $dbField) {
if ($request->has($requestKey) && $request->input($requestKey) !== 'all') {
$value = $request->input($requestKey);
if ($exactMatch) {
// For exact matching, use raw query with BINARY for strict case-sensitive matching
$filters[] = [$dbField, 'raw', $value];
} else {
// For partial matching, use standard LIKE with wildcards
$filters[] = [$dbField, 'LIKE', "%{$value}%"];
}
}
}
return $filters;
}
/**
* Apply filters using OR logic
*
* @param Builder $query
* @param array $filters
* @return void
*/
private function applyFiltersWithOrLogic(Builder $query, array $filters): void
{
$query->where(function ($q) use ($filters) {
foreach ($filters as $index => $filter) {
$method = $index === 0 ? 'where' : 'orWhere';
if ($filter[1] === 'raw') {
// Handle raw exact matching (case-sensitive)
$field = $filter[0];
$value = $filter[2];
$q->whereRaw("BINARY {$field} = ?", [$value]);
} else {
// Handle standard operators
$q->{$method}($filter[0], $filter[1], $filter[2]);
}
}
});
}
/**
* Apply filters using AND logic
*
* @param Builder $query
* @param array $filters
* @return void
*/
private function applyFiltersWithAndLogic(Builder $query, array $filters): void
{
foreach ($filters as $filter) {
if ($filter[1] === 'raw') {
// Handle raw exact matching (case-sensitive)
$field = $filter[0];
$value = $filter[2];
$query->whereRaw("BINARY {$field} = ?", [$value]);
} else {
// Handle standard operators
$query->where($filter[0], $filter[1], $filter[2]);
}
}
}
/**
* Filter by Year of Arrival
*
* @param Builder $query
* @param Request $request
* @return void
*/
private function applyYearOfArrivalFilter(Builder $query, Request $request): void
{
if ($request->has('yearOfArrival') && $request->yearOfArrival !== 'all') {
$year = $request->yearOfArrival;
$query->whereHas('migration', function (Builder $subQuery) use ($year) {
$subQuery->whereYear('date_of_arrival_aus', $year)
->orWhereYear('date_of_arrival_nt', $year);
});
}
}
/**
* Filter by Age at Migration
*
* @param Builder $query
* @param Request $request
* @return void
*/
private function applyAgeAtMigrationFilter(Builder $query, Request $request): void
{
if ($request->has('ageAtMigration') && $request->ageAtMigration !== 'all') {
$ageAtMigration = (int) $request->ageAtMigration;
$query->whereHas('migration', function (Builder $subQuery) use ($ageAtMigration) {
$subQuery->whereRaw('YEAR(date_of_arrival_aus) - YEAR(person.date_of_birth) = ?', [$ageAtMigration])
->orWhereRaw('YEAR(date_of_arrival_nt) - YEAR(person.date_of_birth) = ?', [$ageAtMigration]);
});
}
}
/**
* Filter by Settlement Location
*
* @param Builder $query
* @param Request $request
* @return void
*/
private function applySettlementLocationFilter(Builder $query, Request $request): void
{
if ($request->has('settlementLocation') && $request->settlementLocation !== 'all') {
$location = $request->settlementLocation;
$query->whereHas('residence', function (Builder $subQuery) use ($location) {
$subQuery->where('town_or_city', 'LIKE', "%{$location}%");
});
}
}
/**
* Paginate results and load related models
*
* @param Builder $query
* @param Request $request
* @return \Illuminate\Pagination\LengthAwarePaginator
*/
private function paginateAndLoadRelations(Builder $query, Request $request): \Illuminate\Pagination\LengthAwarePaginator
{
$perPage = $request->input('per_page', 10);
$persons = $query->paginate($perPage);
// Eager load related models
$persons->getCollection()->each->load([
'migration',
'naturalization',
'residence',
'family',
'internment'
]);
return $persons;
}
/**
* Get a specific migrant record by ID
*
* @param mixed $id
* @return JsonResponse
*/
public function getRecord($id): JsonResponse
{
try {
$person = Person::with([
'migration',
'naturalization',
'residence',
'family',
'internment'
])->findOrFail($id);
return response()->json([
'success' => true,
'data' => $person,
'message' => 'Record retrieved successfully'
]);
} catch (Exception $e) {
return response()->json([
'success' => false,
'message' => 'Record not found',
'error' => $e->getMessage()
], 404);
}
}
}