kolumna SQL (z aliasów joinów). */ private const SORTABLE = [ 'name' => 'w.WLL_NAME', 'bariera' => 'w.WLL_BARIERA', 'pump' => 'p.PUP_NAME', 'pump_no' => 'drp.DRS_SN_NUMBER', 'engine' => 'e.ENG_NAME', 'engine_no' => 'dre.DRS_SN_NUMBER', 'type' => 'w.WLL_SYS_TYPE', ]; public function index(Request $request) { $department = $request->session()->get('department'); $search = trim((string) $request->query('q', '')); // Domyślny status jak w legacy: 3. $status = (int) $request->query('status', 3); [$sort, $dir] = $this->sortParams($request); // Słownik statusów do filtra (legacy: DIC_ID in (3,4) + "Wszystkie"). $statusOptions = DB::connection('softspm') ->table('sys_dictionaries') ->whereIn('DIC_ID', [3, 4]) ->orderBy('DIC_ID') ->pluck('DIC_POL_LANG', 'DIC_ID'); $wells = $this->buildQuery($department, $search, $status) ->when($sort !== null, fn ($q) => $q->orderBy(self::SORTABLE[$sort], $dir)) ->orderBy('w.WLL_NAME') ->paginate(20) ->withQueryString(); // Stan ON/OFF dociągany tylko dla 20 widocznych studni (wydajność). $this->attachOnOff($wells); return view('wells.index', [ 'wells' => $wells, 'search' => $search, 'status' => $status, 'statusOptions' => $statusOptions, 'statusAll' => self::STATUS_ALL, 'department' => $department, ]); } /** Formularz dodania studni (migracja site301A). */ public function create(WellService $wells) { return view('wells.create', $this->formLookups($wells)); } /** Zapis nowej studni (migracja site301inf, dodawanie). */ public function store(Request $request, WellService $wells) { $data = $this->validateWell($request, $wells, isUpdate: false); $drsId = $wells->create( $data, $request->session()->get('department'), $request->user()->USR_USERNAME, ); return redirect() ->route('wells.index') ->with('status', 'Dodano studnię „'.$data['name'].'".'); } /** Formularz edycji studni (migracja site301E). */ public function edit(int $drs, WellService $wells) { $well = $wells->findForEdit($drs); abort_if(! $well, 404); return view('wells.edit', $this->formLookups($wells) + ['well' => $well]); } /** Aktualizacja studni (migracja site301inf, subWellUpdate). */ public function update(int $drs, Request $request, WellService $wells) { $data = $this->validateWell($request, $wells, isUpdate: true); $wells->update($drs, $data, $request->user()->USR_USERNAME); return redirect() ->route('wells.index') ->with('status', 'Zaktualizowano studnię „'.$data['name'].'".'); } /** Wspólne słowniki do formularzy dodania/edycji. */ private function formLookups(WellService $wells): array { return [ 'waterIntakes' => $wells->waterIntakes(), 'categories' => $wells->dictionaryById('WELL_CATEGORY'), 'statuses' => $wells->dictionaryById('WELL_STATUS_LEVEL_01'), 'sysTypes' => $wells->dictionaryDescriptions('WELL_SYSTEM_TYPE'), 'statusDescs' => $wells->dictionaryDescriptions('WELL_STATUS_LEVEL_03'), ]; } /** Walidacja + normalizacja pól formularza studni. */ private function validateWell(Request $request, WellService $wells, bool $isUpdate): array { $rules = [ 'name' => ['required', 'string', 'max:255'], 'water_intake_id' => ['required', 'integer', 'min:1'], 'category_id' => ['required', 'integer'], 'status_id' => ['required', 'integer'], 'latitude' => ['nullable', 'string', 'max:128'], 'longitude' => ['nullable', 'string', 'max:128'], ]; if ($isUpdate) { $rules += [ 'well_id' => ['required', 'integer'], 'pp_scope' => ['nullable', 'string', 'max:16'], 'hh_scope' => ['nullable', 'string', 'max:16'], 'sys_type' => ['nullable', 'string', 'max:128'], 'status_desc' => ['nullable', 'string', 'max:255'], 'available_date' => ['nullable', 'date'], 'build_time' => ['nullable', 'string', 'max:8'], 'wle_id' => ['nullable', 'integer'], ]; } else { $rules += [ 'diameter' => ['nullable', 'string', 'max:32'], 'depth' => ['nullable', 'string', 'max:32'], 'supply' => ['nullable', 'string', 'max:32'], 'available_date' => ['nullable', 'date'], ]; } $data = $request->validate($rules); // Unikalność nazwy tylko przy dodawaniu (legacy sprawdzał przed INSERT). if (! $isUpdate && $wells->nameExists($data['name'])) { throw ValidationException::withMessages(['name' => 'Studnia o tej nazwie już istnieje.']); } return $data; } /** * Jedno zapytanie zastępujące listę + N+1 z legacy. * * Mapowanie tabel legacy: * seg_wells (w) - studnia * seg_dev_resources (drw,12) - zasób studni: DRS_ID, dział, status * seg_well_pump_eng_rel (rel) - powiązanie studnia↔pompa/silnik * seg_dev_resources (drp,10) - zasób pompy (nr seryjny) * seg_pumps (p) - pompa (nazwa) * seg_dev_resources (dre,11) - zasób silnika (nr seryjny) * seg_engines (e) - silnik (nazwa) * seg_dev_resources_det (507) - ostatni stan ON/OFF */ private function buildQuery(?string $department, string $search, int $status) { // UWAGA WYDAJNOŚĆ: NIE łączymy tu ostatniego ON/OFF z seg_dev_resources_det // (tabela ma miliardy wierszy) - agregacja po całości była wąskim gardłem // paginacji. Stan ON/OFF dociągamy osobno tylko dla bieżącej strony // w attachOnOff(). return DB::connection('softspm') ->table('seg_wells as w') ->leftJoin('seg_dev_resources as drw', function ($j) { $j->on('drw.DRS_DEV_ID', '=', 'w.WLL_ID')->where('drw.DRS_DEV_TYPE', '=', 12); }) ->leftJoin('seg_well_pump_eng_rel as rel', 'rel.RWL_WLL_ID', '=', 'w.WLL_ID') // Pompa ->leftJoin('seg_dev_resources as drp', function ($j) { $j->on('drp.DRS_ID', '=', 'rel.RWL_PUP_DRS_ID')->where('drp.DRS_DEV_TYPE', '=', 10); }) ->leftJoin('seg_pumps as p', 'p.PUP_ID', '=', 'drp.DRS_DEV_ID') // Silnik ->leftJoin('seg_dev_resources as dre', function ($j) { $j->on('dre.DRS_ID', '=', 'rel.RWL_ENG_DRS_ID')->where('dre.DRS_DEV_TYPE', '=', 11); }) ->leftJoin('seg_engines as e', 'e.ENG_ID', '=', 'dre.DRS_DEV_ID') ->where('drw.DRS_DEPARTMENT', $department) ->when($search !== '', fn ($q) => $q->where('w.WLL_NAME', 'like', '%'.$search.'%')) ->when($status !== self::STATUS_ALL, fn ($q) => $q->where('drw.DRS_STATUS_DIC_ID', $status)) ->select([ 'drw.DRS_ID as drs_id', 'w.WLL_NAME as well_name', 'w.WLL_BARIERA as bariera', 'p.PUP_NAME as pump_name', 'drp.DRS_SN_NUMBER as pump_no', 'e.ENG_NAME as engine_name', 'dre.DRS_SN_NUMBER as engine_no', 'w.WLL_SYS_TYPE as sys_type', ]); } /** * Dociąga ostatni stan ON/OFF (DRD_RESOURCES_DETAIL=507) TYLKO dla studni * z bieżącej strony - po ich DRS_ID (indeks INX_DRD_DRS_DETAIL_DATE), * zamiast agregować całą tabelę seg_dev_resources_det. Każdy wiersz * dostaje właściwość ->onoff. */ private function attachOnOff(\Illuminate\Contracts\Pagination\LengthAwarePaginator $wells): void { $ids = collect($wells->items())->pluck('drs_id')->filter()->values()->all(); $values = empty($ids) ? collect() : DB::connection('softspm') ->table('seg_dev_resources_det as d') ->joinSub( DB::connection('softspm') ->table('seg_dev_resources_det') ->select('DRD_DRS_ID', DB::raw('MAX(DRD_ID) as max_id')) ->where('DRD_RESOURCES_DETAIL', 507) ->whereIn('DRD_DRS_ID', $ids) ->groupBy('DRD_DRS_ID'), 'mx', 'mx.max_id', '=', 'd.DRD_ID' ) ->pluck('d.DRD_VALUE', 'd.DRD_DRS_ID'); foreach ($wells->items() as $row) { $row->onoff = $values[$row->drs_id] ?? null; } } }