0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

foreach内でクエリは発行しちゃダメですね

Posted at

PDFファイルを一括でダウンロードできる機能を作成しましたが、高負荷なクエリがあった部分をリファクタリングしたので、その記録を残しておきます。

よくやってしまいがち(私だけ?)ですが、foreachの中でクエリを発行してしまっておりました。

    public function downloadMultiplePDFs(Request $request)
    {
        $invoiceIds = $request->input('invoice_ids');
        $zip = new ZipArchive;
        $zipFileName = 'invoice.zip';

        if (empty($invoiceIds)) {
            return redirect()
                ->route('invoice.index')
                ->with('message', 'ダウンロードできる請求がありません');
        }

        if ($zip->open(storage_path($zipFileName), ZipArchive::CREATE) === TRUE) {
            foreach ($invoiceIds as $invoice_id) {
                // ここでクエリを発行しているのが良くない
                $invoice = $this->viewListInvoiceService->findByOne($invoice_id);
                $data['endOfMonth'] = $this->invoiceDownloadPDFService->getEndOfMonth($invoice);
                $data += $this->invoiceDownloadPDFService->getTotalPriceWithTax($invoice);
                $fileName = $this->invoiceDownloadPDFService->generateFilename($invoice);
                $pdf = PDF::loadView('pdf.invoice', compact('invoice', 'data'));

                // 一時ファイルにPDFを保存
                $tempPath = storage_path('app/temp/' . $fileName);
                $pdf->save($tempPath);

                // ZIPファイルに追加
                $zip->addFile($tempPath, $fileName);
            }
            $zip->close();
        }

        return response()->download(storage_path($zipFileName))->deleteFileAfterSend(true);

上記だとforeachの中で毎回データを取りにいってるので、量が増えると高負荷になってしまいますね…

なのでforeachの前でwhereInでデータを一回で全て取得してから、foreachで取得したデータ分をループさせるという方法にすればよさそうです。

    public function downloadMultiplePDFs(Request $request)
    {
        $invoiceIds = $request->input('invoice_ids');
        $zip = new ZipArchive;
        $zipFileName = 'invoice.zip';

        if (empty($invoiceIds)) {
            return redirect()
                ->route('invoice.index')
                ->with('message', 'ダウンロードできる請求がありません');
        }
        // ここで一回で取得する
        $invoices = $this->viewListInvoiceService->findByIds($invoiceIds)->get();
        if ($zip->open(storage_path($zipFileName), ZipArchive::CREATE) === TRUE) {
            foreach ($invoices as $invoice) {
                $data['endOfMonth'] = $this->invoiceDownloadPDFService->getEndOfMonth($invoice);
                $data += $this->invoiceDownloadPDFService->getTotalPriceWithTax($invoice);
                $fileName = $this->invoiceDownloadPDFService->generateFilename($invoice);
                $pdf = PDF::loadView('pdf.invoice', compact('invoice', 'data'));

                // 一時ファイルにPDFを保存
                $tempPath = storage_path('app/temp/' . $fileName);
                $pdf->save($tempPath);

                // ZIPファイルに追加
                $zip->addFile($tempPath, $fileName);
            }
            $zip->close();
        }

        return response()->download(storage_path($zipFileName))->deleteFileAfterSend(true);
    }

$this->viewListInvoiceService->findByIds($invoiceIds)の部分でデータ取得してます。
service層は省略するとして、repositoryでデータベースから取得は下記のように書きました。

    public function findByIds(array $invoiceIds, array $relations)
    {
        return $this->invoice
            ->with($relations)
            ->whereIn('id', $invoiceIds);
    }

これで何回もデータベースにアクセスしなくてもよさそうです。
以上です。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?