requireAuth()) { return $redirect; } $request = Request::capture(); $search = (string) ($request->input('search') ?? ''); $territoryId = (string) ($request->input('territory_id') ?? ''); $doNotCall = (string) ($request->input('do_not_call') ?? ''); $page = max(1, (int) ($request->input('page') ?? 1)); $perPage = 20; $repo = new HouseholdRepository(database()); $total = $repo->countAll($search, $territoryId, $doNotCall); $pagination = new Pagination($total, $page, $perPage); $households = $repo->findPaged($page, $perPage, $search, $territoryId, $doNotCall); $territories = (new TerritoryRepository(database()))->allOrdered(); return $this->view('households.index', [ 'pageTitle' => 'Households', 'households' => $households, 'territories' => $territories, 'search' => $search, 'territoryId' => $territoryId, 'doNotCall' => $doNotCall, 'pagination' => $pagination, ]); } public function show(int|string $id): Response { if ($redirect = $this->requireAuth()) { return $redirect; } $household = (new HouseholdRepository(database()))->findWithTerritory((int) $id); if (!$household) { return Response::notFound('Household not found.'); } $names = (new HouseholderNameRepository(database()))->findAllByHousehold((int) $id); $maps = maps_config(); return $this->view('households.show', [ 'pageTitle' => e($household['address']), 'household' => $household, 'names' => $names, 'maps' => $maps, ]); } public function create(): Response { if ($redirect = $this->requireAuth()) { return $redirect; } $request = Request::capture(); $territories = (new TerritoryRepository(database()))->allOrdered(); return $this->view('households.create', [ 'pageTitle' => 'New Household', 'territories' => $territories, 'defaultTerritoryId' => (string) ($request->input('territory_id') ?? ''), 'errors' => [], 'old' => [], ]); } public function store(): Response { if ($redirect = $this->requireAuth()) { return $redirect; } $request = Request::capture(); $territories = (new TerritoryRepository(database()))->allOrdered(); if (!verify_csrf_token($request->input('_token'))) { return $this->view('households.create', [ 'pageTitle' => 'New Household', 'territories' => $territories, 'defaultTerritoryId' => '', 'errors' => ['_token' => ['Invalid request. Please try again.']], 'old' => $request->all(), ]); } $fields = $this->extractFields($request); $validator = (new Validator()) ->required('territory_id', $fields['territory_id'], 'Territory is required.') ->required('address', $fields['address'], 'Address is required.') ->maxLength('address', $fields['address'], 255) ->optionalNumeric('street_number', $fields['street_number']) ->optionalNumeric('latitude', $fields['latitude'], 'Latitude must be a number.') ->optionalNumeric('longitude', $fields['longitude'], 'Longitude must be a number.'); if ($validator->fails()) { return $this->view('households.create', [ 'pageTitle' => 'New Household', 'territories' => $territories, 'defaultTerritoryId' => $fields['territory_id'], 'errors' => $validator->errors(), 'old' => $request->all(), ]); } $now = date('Y-m-d H:i:s'); database()->execute( 'INSERT INTO households (territory_id, address, street_number, street_name, latitude, longitude, is_business, do_not_call, do_not_call_date, do_not_call_notes, do_not_call_private_notes, created_at, updated_at) VALUES (:territory_id, :address, :street_number, :street_name, :latitude, :longitude, :is_business, :do_not_call, :do_not_call_date, :do_not_call_notes, :do_not_call_private_notes, :now, :now)', array_merge($fields, ['now' => $now]) ); flash('success', 'Household created.'); return $this->redirect('/households'); } public function edit(int|string $id): Response { if ($redirect = $this->requireAuth()) { return $redirect; } $household = (new HouseholdRepository(database()))->find((int) $id); if (!$household) { return Response::notFound('Household not found.'); } $territories = (new TerritoryRepository(database()))->allOrdered(); return $this->view('households.edit', [ 'pageTitle' => 'Edit Household', 'household' => $household, 'territories' => $territories, 'errors' => [], ]); } public function update(int|string $id): Response { if ($redirect = $this->requireAuth()) { return $redirect; } $request = Request::capture(); $household = (new HouseholdRepository(database()))->find((int) $id); if (!$household) { return Response::notFound('Household not found.'); } if (!verify_csrf_token($request->input('_token'))) { flash('error', 'Invalid request.'); return $this->redirect('/households/' . $id . '/edit'); } $fields = $this->extractFields($request); $validator = (new Validator()) ->required('territory_id', $fields['territory_id'], 'Territory is required.') ->required('address', $fields['address'], 'Address is required.') ->maxLength('address', $fields['address'], 255) ->optionalNumeric('street_number', $fields['street_number']) ->optionalNumeric('latitude', $fields['latitude'], 'Latitude must be a number.') ->optionalNumeric('longitude', $fields['longitude'], 'Longitude must be a number.'); $territories = (new TerritoryRepository(database()))->allOrdered(); if ($validator->fails()) { return $this->view('households.edit', [ 'pageTitle' => 'Edit Household', 'household' => array_merge($household, $fields), 'territories' => $territories, 'errors' => $validator->errors(), ]); } database()->execute( 'UPDATE households SET territory_id = :territory_id, address = :address, street_number = :street_number, street_name = :street_name, latitude = :latitude, longitude = :longitude, is_business = :is_business, do_not_call = :do_not_call, do_not_call_date = :do_not_call_date, do_not_call_notes = :do_not_call_notes, do_not_call_private_notes = :do_not_call_private_notes, updated_at = :now WHERE id = :id', array_merge($fields, ['now' => date('Y-m-d H:i:s'), 'id' => $id]) ); flash('success', 'Household updated.'); return $this->redirect('/households/' . $id); } public function delete(int|string $id): Response { if ($redirect = $this->requireAuth()) { return $redirect; } $request = Request::capture(); $household = (new HouseholdRepository(database()))->find((int) $id); if (!$household) { return Response::notFound('Household not found.'); } if (!verify_csrf_token($request->input('_token'))) { flash('error', 'Invalid request.'); return $this->redirect('/households'); } database()->execute('DELETE FROM householder_names WHERE household_id = :id', ['id' => $id]); database()->execute('DELETE FROM households WHERE id = :id', ['id' => $id]); flash('success', 'Household deleted.'); return $this->redirect('/households'); } /** @return array */ private function extractFields(Request $request): array { $doNotCall = (bool) $request->input('do_not_call'); return [ 'territory_id' => (string) ($request->input('territory_id') ?? ''), 'address' => trim((string) ($request->input('address') ?? '')), 'street_number' => $request->input('street_number') !== '' ? $request->input('street_number') : null, 'street_name' => trim((string) ($request->input('street_name') ?? '')), 'latitude' => $request->input('latitude') !== '' ? $request->input('latitude') : null, 'longitude' => $request->input('longitude') !== '' ? $request->input('longitude') : null, 'is_business' => (int) (bool) $request->input('is_business'), 'do_not_call' => (int) $doNotCall, 'do_not_call_date' => $doNotCall ? (trim((string) ($request->input('do_not_call_date') ?? '')) ?: null) : null, 'do_not_call_notes' => trim((string) ($request->input('do_not_call_notes') ?? '')), 'do_not_call_private_notes' => trim((string) ($request->input('do_not_call_private_notes') ?? '')), ]; } }