Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

eggplantSword's avatar

Download multiple files Dompdf

I have a bunch of pdf reports I want to download all at once, right now there are as many as 150, I have a method for downloading a single pdf and that works ok, it's really slow to load. I want to know if there is a way I can download all of the reports at once.

This is the single download pdf function, this pdf report is for a survey results, so all the questions and a % of answered options.

public function pdf($id)
    {
        $survey = Survey::with('surveyQuestions.responseType',
            'surveyQuestions.surveyQuestionOption', 'surveyQuestions.answer')
            ->where('id', $id)->first();

        $Eid = Evaluation::where('survey_id', $id)->pluck('id')->first();
        $evaluators = EvaluationEvaluator::where('evaluation_id', $Eid)->with('group.users')->get();


        if ($evaluators[0]->user_id == null) {
            $tot = $evaluators[0]['group']['users']->count();
        } else {
            $tot = $evaluators->count();
        }


        $totalAnswered = SurveyAnswer::where('survey_id', $survey->id)->selectRaw('COUNT(DISTINCT user_id) as count')
            ->value('count');

        $result = [];


        foreach ($survey['surveyQuestions'] as $question) {
            $options = [];

            if ($question->response_type_id === 1) {
                foreach ($survey['answer'] as $answer) {
                    if ($question['pivot']->survey_id === $survey->id) {
                        if ($answer->survey_question_id === $question->id) {
                            $options[$answer->id] = [
                                'title' => $answer->answer,
                                'vote_count' => null
                            ];
                        }
                    }
                }
            }

            if ($question->response_type_id === 2) {

                $mul_total = SurveyAnswer::where('survey_id', $survey->id)->where('survey_question_id', $question->id)
                    ->selectRaw('COUNT(DISTINCT user_id) as count')
                    ->value('count');

                foreach ($question['surveyQuestionOption'] as $option) {

                    if ($question['pivot']->survey_id === $survey->id) {
                        $mul_count = 0;

                        foreach ($survey['answer'] as $answer) {
                            if ($answer->survey_question_id === $option->survey_question_id
                                && (int)$answer->answer === $option->id) {
                                $mul_count++;
                            }

                            $options[$option->id] = [
                                'title' => $option->option,
                                'vote_count' => $mul_count,
                                'total' => $mul_total
                            ];
                        }
                    }
                }
            }

            if ($question->response_type_id === 3) {
                if ($question->rank === 3) {
                    $question->opciones = [
                        ['id' => 1, 'option' => 1],
                        ['id' => 2, 'option' => 2],
                        ['id' => 3, 'option' => 3]
                    ];
                } else if ($question->rank === 4) {
                    $question->opciones = [
                        ['id' => 1, 'option' => 1],
                        ['id' => 2, 'option' => 2],
                        ['id' => 3, 'option' => 3],
                        ['id' => 4, 'option' => 4]
                    ];
                } else if ($question->rank === 5) {
                    $question->opciones = [
                        ['id' => 1, 'option' => 1],
                        ['id' => 2, 'option' => 2],
                        ['id' => 3, 'option' => 3],
                        ['id' => 4, 'option' => 4],
                        ['id' => 5, 'option' => 5]
                    ];
                } else {
                    $question->opciones = [
                        ['id' => 1, 'option' => 1],
                        ['id' => 2, 'option' => 2],
                        ['id' => 3, 'option' => 3],
                        ['id' => 4, 'option' => 4],
                        ['id' => 5, 'option' => 5],
                        ['id' => 6, 'option' => 6]
                    ];
                }

                $rk_total = SurveyAnswer::where('survey_id', $survey->id)->where('survey_question_id', $question->id)
                    ->selectRaw('COUNT(DISTINCT user_id) as count')
                    ->value('count');

                foreach ($question['opciones'] as $opt) {
                    $rk_count = 0;

                    foreach ($question['answer'] as $answer) {
                        if ($answer->survey_id === $survey->id) {
                            if ($answer->survey_question_id === $question->id && (int)$answer->answer === $opt['option']) {
                                $rk_count++;
                            }

                            $options[$opt['id']] = [
                                'title' => $opt['option'],
                                'vote_count' => $rk_count,
                                'total' => $rk_total
                            ];
                        }
                    }
                }
            }

            if ($question->response_type_id === 4) {
                $question->opciones = [
                    ['id' => 1, 'option' => 'Si'],
                    ['id' => 2, 'option' => 'No']
                ];

                $total = SurveyAnswer::where('survey_id', $survey->id)->where('survey_question_id', $question->id)
                    ->selectRaw('COUNT(DISTINCT user_id) as count')
                    ->value('count');

                foreach ($question['opciones'] as $opt) {

                    $count = 0;
                    foreach ($question['answer'] as $answer) {
                        if ($answer->survey_id === $survey->id) {
                            if ($answer->survey_question_id === $question->id && $answer->answer === $opt['option']) {
                                $count++;
                            }

                            $options[$opt['id']] = [
                                'title' => $opt['option'],
                                'vote_count' => $count,
                                'total' => $total
                            ];

                        }
                    }
                }
            }

            $result[] = [
                'id' => $question->id,
                'type' => $question->response_type_id,
                'rank' => $question->rank,
                'num' => $question->num,
                'title' => $question->question,
                'options' => $options
            ];
        }
        $data = Survey::with('surveyQuestions.responseType')->where('id', $id)->first();

        $data['result'] = $result;
        $data['totalSent'] = $tot;
        $data['totalAnswered'] = $totalAnswered;

        $pdf = App::make('dompdf.wrapper');
        $pdf->loadView('pdf.report', ['survey' => $data]);
        return $pdf->stream();
    }

This is what I tried for the downloadAll function, however understandably this give me a timeout error, a single pdf takes about 15-20 seconds to load which is incredibly slow.

public function downloadAllReports()
    {
        $now = Carbon::now();

        $report = Evaluation::with('survey')
            ->whereDate('report_date', '<=', $now)
            ->whereDate('report_end', '>=', $now)
            ->where('report', true)
            ->whereHas('survey', function ($query) {
                $query->where('user_id', auth()->user()->id);
            })
            ->orderBy('updated_at', 'desc')
            ->get();


        foreach ($report as $item) {
            $this->pdf($item->id);
        }
    }

What is my best option here to download all the reports, also if there is a way to make the pdf load faster that would be great.

0 likes
7 replies
Brian Kidd's avatar

@msslgomez There are several ways to do this and I'll suggest what I'd probably do. There are others on the forum that may have other ideas.

For certain, I wouldn't try to do this all at one time. It would be better to do something like this asynchronously and not make the user wait.

First of all, I assume the logic you are using to get the data needed from the database is optimized - I had a difficult time trying to follow it. If you are not sure, I would use something like the Debugbar to see what is taking so long. My guess is that it's querying the data and not so much for the PDF creation.

Here's what I would do:

Loop through the parameters needed for each report and create a separate job for each. You'll want to have some way to keep track of the jobs and if they have all completed. You could use job chaining (https://laravel.com/docs/7.x/queues#job-chaining) or Mohammed Said just did a talk this week on queues and using Laravel Bus that you may find helpful: https://youtu.be/ajDwUNfKu48?t=2451

At a high level, create a job for each report and save the report in a new directory. After the pdf jobs are complete, dispatch another job that is responsible for creating a zip file of all of the reports. If you do this on a public directory or an S3 bucket (if the S3 resource is private, you'll need to use a signed url), you can then email a link to the zip file to the user where they can download it.

Hope this helps.

eggplantSword's avatar

@briankidd I think I'm doing to use Excel reports only based on the speed but would I go about doing the download part the same way?

Brian Kidd's avatar

@msslgomez I'm sorry, I don't know what you are asking. When you have multiple files, you'll need to either download a zip or give them a link where they can download themselves. With a slow process, I'd prefer sending a link once it is finish but that's a personal decision.

eggplantSword's avatar

@briankidd I mean does it make a difference between the type of file (pdf of excel) that is being added to the zip for download?

eggplantSword's avatar

@briankidd I've decided to use the Jobs approach, I just have a question. In the Job Class in the handle method what exactly should I put in there? I have the code to create the excel file and save it to the server, should all of that logic go in the handle method?

Brian Kidd's avatar

@msslgomez put as much logic in the handle method that makes sense but you can always add other methods to the job if needed for clarity.

1 like

Please or to participate in this conversation.