where('user_id', $account->id) ->withCount('qrCodes') ->withSum('qrCodes', 'scans_total') ->latest() ->paginate(20) ->withQueryString(); $base = Campaign::query()->where('user_id', $account->id); return view('qr.campaigns.index', [ 'campaigns' => $campaigns, 'campaignCount' => (clone $base)->count(), 'activeCount' => (clone $base)->where('status', Campaign::STATUS_ACTIVE)->count(), 'codesInCampaigns' => QrCode::query() ->where('user_id', $account->id) ->whereNotNull('campaign_id') ->count(), ]); } public function create(): View { return view('qr.campaigns.create', [ 'campaign' => new Campaign(['status' => Campaign::STATUS_ACTIVE]), ]); } public function store(Request $request): RedirectResponse { $account = ladill_account(); $data = $this->validated($request); $campaign = Campaign::create([ ...$data, 'user_id' => $account->id, ]); return redirect() ->route('qr.campaigns.show', $campaign) ->with('success', 'Campaign created.'); } public function show(Campaign $campaign): View { $this->authorizeCampaign($campaign); $codes = $campaign->qrCodes()->latest()->get(); $availableCodes = QrCode::query() ->where('user_id', $campaign->user_id) ->where(function ($q) use ($campaign) { $q->whereNull('campaign_id')->orWhere('campaign_id', $campaign->id); }) ->orderBy('label') ->get(); return view('qr.campaigns.show', [ 'campaign' => $campaign, 'codes' => $codes, 'availableCodes' => $availableCodes, 'totalScans' => (int) $codes->sum('scans_total'), ]); } public function update(Request $request, Campaign $campaign): RedirectResponse { $this->authorizeCampaign($campaign); $campaign->update($this->validated($request)); return back()->with('success', 'Campaign updated.'); } public function destroy(Campaign $campaign): RedirectResponse { $this->authorizeCampaign($campaign); QrCode::query() ->where('campaign_id', $campaign->id) ->update(['campaign_id' => null]); $campaign->delete(); return redirect() ->route('qr.campaigns.index') ->with('success', 'Campaign deleted. QR codes were unassigned, not removed.'); } public function attach(Request $request, Campaign $campaign): RedirectResponse { $this->authorizeCampaign($campaign); $validated = $request->validate([ 'qr_code_ids' => ['required', 'array', 'min:1'], 'qr_code_ids.*' => ['integer'], ]); QrCode::query() ->where('user_id', $campaign->user_id) ->whereIn('id', $validated['qr_code_ids']) ->update(['campaign_id' => $campaign->id]); return back()->with('success', 'QR codes added to campaign.'); } public function detach(Campaign $campaign, QrCode $qrCode): RedirectResponse { $this->authorizeCampaign($campaign); abort_unless((int) $qrCode->user_id === (int) $campaign->user_id, 403); abort_unless((int) $qrCode->campaign_id === (int) $campaign->id, 404); $qrCode->update(['campaign_id' => null]); return back()->with('success', 'QR code removed from campaign.'); } /** @return array */ private function validated(Request $request): array { $data = $request->validate([ 'name' => ['required', 'string', 'max:120'], 'description' => ['nullable', 'string', 'max:2000'], 'status' => ['required', Rule::in(array_keys(Campaign::STATUSES))], 'starts_at' => ['nullable', 'date'], 'ends_at' => ['nullable', 'date', 'after_or_equal:starts_at'], ]); $data['description'] = $data['description'] ?? null; $data['starts_at'] = $data['starts_at'] ?? null; $data['ends_at'] = $data['ends_at'] ?? null; return $data; } private function authorizeCampaign(Campaign $campaign): void { abort_unless((int) $campaign->user_id === (int) ladill_account()->id, 403); } }