*/ public function header(): array { $identity = $this->poll->anonymous ? [] : [ __('Voted'), __('Changed'), __('Profile type'), __('Profile'), __('Submitted by'), ]; return array_merge($identity, $this->questions()->map(fn ($question) => $question->labelFor($this->locale))->all()); } /** * Rows in chunks, so large polls are not loaded into memory at once. * * @return \Generator> */ public function rows(): \Generator { $questions = $this->questions(); $query = $this->poll->responses()->with(['answers', 'voter', 'user']); if ($this->poll->anonymous) { // Shuffled, so the order does not reveal who voted when foreach ($query->inRandomOrder()->get() as $response) { yield $this->row($response, $questions); } return; } foreach ($query->orderBy('submitted_at')->orderBy('id')->lazy(200) as $response) { yield $this->row($response, $questions); } } private function row(PollResponse $response, Collection $questions): array { $answers = $response->answers->keyBy('poll_question_id'); $cells = []; if (!$this->poll->anonymous) { $changed = $response->updated_at && $response->updated_at->gt($response->submitted_at->copy()->addSecond()); $cells = [ $response->submitted_at->format('Y-m-d H:i'), $changed ? $response->updated_at->format('Y-m-d H:i') : '', __(class_basename($response->voter_type)), $response->voter?->name ?? __('Deleted profile'), $response->user?->name ?? '', ]; } foreach ($questions as $question) { $answer = $answers->get($question->id); $cells[] = $answer ? $question->questionType()->answerToText($answer->value, $answer->text, $question->settings ?? [], $this->locale) : ''; } return array_map([self::class, 'safeCell'], $cells); } private function questions(): Collection { return $this->poll->questions()->get()->filter( fn ($question) => app(QuestionTypeRegistry::class)->has($question->type) )->values(); } /** * Prevent spreadsheet formula injection: a cell starting with = + - @ (or a tab / carriage return) * would be run as a formula when the file is opened in Excel or LibreOffice. */ public static function safeCell(string $cell): string { return $cell !== '' && str_contains("=+-@\t\r", $cell[0]) ? "'" . $cell : $cell; } }