🐥

barryvdh/laravel-dompdf 使用例

2021/05/09に公開

PDF保存処理例

  1. エントリポイント
  2. 非同期処理に対応できるようlaravelのjob機能で実行
  3. PDF作成、保存
    • ロケールを設定
    • PDFインスタンスの作成
    • PDFファイル保存

エントリポイント

https://github.com/bytefury/crater/blob/master/app/Http/Controllers/V1/Estimate/EstimatesController.php

    public function store(EstimatesRequest $request)
    {
        $estimate = Estimate::createEstimate($request);

        if ($request->has('estimateSend')) {
            $estimate->send($request->title, $request->body);
        }

        GenerateEstimatePdfJob::dispatch($estimate);

        return response()->json([
            'estimate' => $estimate
        ]);
    }
    
    public function update(EstimatesRequest $request, Estimate $estimate)
    {
        $estimate = $estimate->updateEstimate($request);

        GenerateEstimatePdfJob::dispatch($estimate, true);

        return response()->json([
            'estimate' => $estimate
        ]);
    }

非同期処理に対応できるようlaravelのjob機能で実行

https://github.com/bytefury/crater/blob/master/app/Jobs/GenerateEstimatePdfJob.php

<?php

namespace Crater\Jobs;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;

class GenerateEstimatePdfJob implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    public $estimate;
    public $deleteExistingFile;

    /**
     * Create a new job instance.
     *
     * @return void
     */
    public function __construct($estimate, $deleteExistingFile = false)
    {
        $this->estimate = $estimate;
        $this->deleteExistingFile = $deleteExistingFile;
    }

    /**
     * Execute the job.
     *
     * @return void
     */
    public function handle()
    {
        $this->estimate->generatePDF('estimate', $this->estimate->estimate_number, $this->deleteExistingFile);

        return 0;
    }
}

PDF作成、保存

https://github.com/bytefury/crater/blob/master/app/Traits/GeneratesPdfTrait.php

    public function generatePDF($collection_name, $file_name, $deleteExistingFile = false)
    {
        $save_pdf_to_disk = CompanySetting::getSetting('save_pdf_to_disk',  $this->company_id);

        // コンフィグ設定で保存が必要なければ保存しない
        if($save_pdf_to_disk == 'NO') {
            return 0;
        }

        $locale = CompanySetting::getSetting('language',  $this->company_id);

        // ロケール設定
        App::setLocale($locale);

        // PDFインスタンス作成
	// コードは下に記載
        $pdf = $this->getPDFData();

        // PDFファイル作成、保存
	// 後に正となるPDFファイルを保存するので、あくまで一時用
        \Storage::disk('local')->put('temp/'.$collection_name.'/'.$this->id.'/temp.pdf', $pdf->output());

        if($deleteExistingFile) {
	    // 自分自身(Illuminate\Database\Eloquent\Model)に紐づくメディアファイルの削除
	    // Spatie\MediaLibrary\InteractsWithMediaトレイトをくっつけて、
	    // clearMediaCollectionを実行すれば削除してくれる
            $this->clearMediaCollection($collection_name);
        }

        // Laravelのどのファイルシステムを利用するかを決定
        $file_disk = FileDisk::whereSetAsDefault(true)->first();

        if ($file_disk) {
	    // 決定したファイルシステムに設定
            $file_disk->setConfig();
        }

        // 保存
        $media = \Storage::disk('local')->path('temp/'.$collection_name.'/'.$this->id.'/temp.pdf');

        try {
	    // https://github.com/spatie/laravel-medialibrary
	    // を利用して正となるPDFファイルを保存
	    // こんな形で保存される
/*
*************************** 3. row ***************************
               id: 5
       model_type: Crater\Models\Payment
         model_id: 3
  collection_name: payment
             name: temp
        file_name: PAY-000001.pdf
        mime_type: application/pdf
             disk: temp_local
             size: 878396
    manipulations: []
custom_properties: {"file_disk_id":2}
responsive_images: []
     order_column: 3
       created_at: 2021-05-09 09:39:26
       updated_at: 2021-05-09 09:39:26
             uuid: 76abc923-bb32-4418-9817-470057890716
 conversions_disk: temp_local
*/
            $this->addMedia($media)
                ->withCustomProperties(['file_disk_id' => $file_disk->id])
                ->usingFileName($file_name.'.pdf')
                ->toMediaCollection($collection_name, config('filesystems.default'));

            // 一時用を削除
            \Storage::disk('local')->deleteDirectory('temp/'.$collection_name.'/'.$this->id);

            return true;
        } catch (\Exception $e) {
            return $e->getMessage();
        }
    }

app/Models/Estimate.php


    public function getPDFData()
    {
        $taxTypes = [];
        $taxes = [];
        $labels = [];

...

        // PDFのテンプレート情報を取得
	// テンプレートのビューコード
	// https://github.com/bytefury/crater/blob/master/resources/views/app/pdf/estimate/estimate1.blade.php
        $estimateTemplate = EstimateTemplate::find($this->estimate_template_id);

        $company = Company::find($this->company_id);
        $locale = CompanySetting::getSetting('language',  $company->id);

        // ここでもロケール設定している
        App::setLocale($locale);

        $logo = $company->logo_path;

        view()->share([
            'estimate' => $this,
            'logo' => $logo ?? null,
            'company_address' => $this->getCompanyAddress(),
            'shipping_address' => $this->getCustomerShippingAddress(),
            'billing_address' => $this->getCustomerBillingAddress(),
            'notes' => $this->getNotes(),
            'labels' => $labels,
            'taxes' => $taxes
        ]);

        // ここでbarryvdh/laravel-dompdfを利用している
        return PDF::loadView('app.pdf.estimate.' . $estimateTemplate->view);
	
	// おそらくこんな書き方でもいいのではないか
	// $var = [
	//    'estimate' => $this,
        //    'logo' => $logo ?? null,
        //    'company_address' => $this->getCompanyAddress(),
        //    'shipping_address' => $this->getCustomerShippingAddress(),
	//    ...
	//];
	// return PDF::loadView('app.pdf.estimate.' . $estimateTemplate->view, $var);
    }

PDF出力処理例

エントリポイント

app/Http/Controllers/V1/Invoice/InvoicePdfController.php

    /**
     * Handle the incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    public function __invoke(Invoice $invoice)
    {
        return $invoice->getGeneratedPDFOrStream('invoice');
    }

PDF出力

app/Traits/GeneratesPdfTrait.php

<?php

namespace Crater\Traits;

use Carbon\Carbon;
use Crater\Models\Address;
use Crater\Models\FileDisk;
use Crater\Models\CompanySetting;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\App;

trait GeneratesPdfTrait
{
    public function getGeneratedPDFOrStream($collection_name)
    {
	// 値があればこのよな値が$pdfに入る
	// ["path" => "\/var\/www\/storage\/app\/Invoices\/INV-000001.pdf","file_name" => "INV-000001.pdf"]
        $pdf = $this->getGeneratedPDF($collection_name);
        if($pdf && file_exists($pdf['path'])) {
            return response()->make(file_get_contents($pdf['path']), 200, [
                'Content-Type'        => 'application/pdf',
                'Content-Disposition' => 'inline; filename="'.$pdf['file_name'].'.pdf"'
            ]);
        }

        $locale = CompanySetting::getSetting('language',  $this->company_id);

        App::setLocale($locale);

        $pdf = $this->getPDFData();

        return response()->make($pdf->stream(), 200, [
            'Content-Type'        => 'application/pdf',
            'Content-Disposition' => 'inline; filename="'.$this[$collection_name.'_number'].'.pdf"'
        ]);
    }

    public function getGeneratedPDF($collection_name)
    {
        try {
	    // vendor/spatie/laravel-medialibrary/src/InteractsWithMedia.php
	    /* @var $media おそらく vendor/spatie/laravel-medialibrary/src/MediaCollections/Models/Media.php */
            $media = $this->getMedia($collection_name)->first();

            if ($media) {
	        // custom_propertiesに{"file_disk_id":2}このような値が入っている
		// file_disk_idとはfilesystem.phpのdisksの設定を格納しているテーブルのid
                $file_disk = FileDisk::find($media->custom_properties['file_disk_id']);

                if (!$file_disk) {
                    return false;
                }

                $file_disk->setConfig();

                $path = null;

                if($file_disk->driver == 'local'){
                    $path = $media->getPath();
                } else {
                    $path = $media->getTemporaryUrl(Carbon::now()->addMinutes(5));
                }

                return collect([
                    'path' => $path,
                    'file_name' => $media->file_name
                ]);
            }
        } catch(\Exception $e){
            return false;
        }

        return false;
    }

Discussion