HEX
Server: Microsoft-IIS/8.5
System: Windows NT YDAWBH120 6.3 build 9600 (Windows Server 2012 R2 Standard Edition) AMD64
User: tentjecom_web (0)
PHP: 7.4.14
Disabled: NONE
Upload Files
File: D:/HostingSpaces/SBogers10/blije-gasten.komma.pro/app/Komma/Shop/Invoicing/InvoiceService.php
<?php declare(strict_types=1);


namespace App\Komma\Shop\Invoicing;


use App\Komma\Globalization\RegionInfo;
use App\Komma\Globalization\RegionInfoInterface;
use App\Komma\Shop\Orders\Models\Order;
use Barryvdh\DomPDF\Facade as PDF;
use Illuminate\Contracts\Filesystem\Filesystem;
use Illuminate\Support\Facades\Storage;
use Illuminate\View\View;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
use Symfony\Component\HttpFoundation\StreamedResponse;

/**
 * Class InvoiceService
 *
 * @package App\Komma\Shop\Invoicing
 */
class InvoiceService
{
    /** @var string the filesystem disk we use for storing invoices */
    private const DISK = 'local';

    /** @var string the directory on the disk that contains the invoices */
    private const DIRECTORY = 'order_documents'.DIRECTORY_SEPARATOR.'invoices'.DIRECTORY_SEPARATOR;

    /** @var string The name of the view that represents an invoice */
    private const VIEWNAME = 'shop.pages.invoice.invoiceShow';

    /**
     * InvoiceService constructor.
     */
    public function __construct()
    {
        $this->initializeDisk();
    }

    /**
     * @param Order $order
     * @return View
     */
    public function getInvoiceForOrderAsView(Order $order): View
    {
        if(!$this->getDisk()->exists($this->getInvoicePDFPathFromOrder($order))) {
            //Not yet created. Generate a pdf.
            $this->generatePdfForOrder($order);
        }

        return view(self::VIEWNAME, $this->getInvoiceViewData($order));
    }

    /**
     * Returns a response that makes the browser open up the PDF so that it is displayed in the browser
     *
     * @param Order $order
     * @return BinaryFileResponse
     */
    public function getInvoiceForOrderAsPDF(Order $order): BinaryFileResponse
    {
        if(!$this->getDisk()->exists($this->getInvoicePDFPathFromOrder($order))) {
            //Not yet created. Generate a pdf.
            $this->generatePdfForOrder($order);
        }

        return response()->file($this->getDisk()->path($this->getInvoicePDFPathFromOrder($order)));
    }

    /**
     * Returns a response that forces the browser to download the PDF for the order given
     *
     * @param Order $order
     * @return StreamedResponse
     */
    public function getInvoiceForOrderAsPDFDownload(Order $order): StreamedResponse
    {
        if(!$this->getDisk()->exists($this->getInvoicePDFPathFromOrder($order))) {
            //Not yet created. Generate a pdf.
            $this->generatePdfForOrder($order);
        }

        return Storage::download($this->getInvoicePDFPathFromOrder($order), $order->invoice_number.'.pdf');
    }

    /**
     * @param Order $order
     */
    private function generatePdfForOrder(Order $order): void
    {
        $this->deleteOldInvoicesForOrder($order);
        $pdf = PDF::loadView(self::VIEWNAME, $this->getInvoiceViewData($order));
        if(!$this->getDisk()->put(self::getInvoicePDFPathFromOrder($order), $pdf->output())) throw new \RuntimeException('Could not save PDF for order with id: '.$order->id);
    }

    /**
     * Returns the path to an invoice's pdf, that is relative to the disks directory
     *
     * @param Order $order
     * @param bool $generatePdfIfNeeded
     * @return string
     */
    public function getInvoicePDFPathFromOrder(Order $order, $generatePdfIfNeeded = false): string
    {
        if($generatePdfIfNeeded && !$this->getDisk()->exists($this->getInvoicePDFPathFromOrder($order))) {
            $this->generatePdfForOrder($order);
        }
        return self::DIRECTORY.$order->invoice_number.'_'.$order->updated_at->timestamp.'.pdf';
    }

    /**
     * Deletes old invoices for order, if there are any.
     *
     * @param Order $order
     */
    private function deleteOldInvoicesForOrder(Order $order)
    {
        $oldInvoices = $this->findOldInvoicesForOrder($order);
        foreach($oldInvoices as $oldInvoice) {
            $oldInvoicePath = self::DIRECTORY.DIRECTORY_SEPARATOR.$oldInvoice;
            if(!$this->getDisk()->delete(self::DIRECTORY.DIRECTORY_SEPARATOR.$oldInvoice))
                throw new \InvalidArgumentException('Could not delete old invoice: '.$oldInvoicePath);
        }
    }

    /**
     * Returns an array containing paths to old invoices
     *
     * @param Order $order
     * @return array
     */
    private function findOldInvoicesForOrder(Order $order): array
    {
        $allFiles = $this->getDisk()->allFiles(self::DIRECTORY);
        $oldInvoices = [];
        foreach($allFiles as $file)
        {
            $filename = basename($file);
            //The preg_match returns 1 if the filename starts with the invoice number but is not followed by the current updated_at timestamp. And finaly ending with .pdf.
            if(preg_match('/^'.preg_quote($order->invoice_number).'.+(?<!preg_quote('.$order->updated_at->timestamp.'))\.pdf$/', $filename) === 1) {
                $oldInvoices[] = $filename;
            }
        }

        return $oldInvoices;
    }



    /**
     * @param Order $order
     * @return array
     */
    private function getInvoiceViewData(Order $order):array
    {
        //Make sure everything is loaded for what we'd like to display in the view
        $order->load([
            'customer',
            'orderedProducts.product.translations',
            'orderedGroups.productGroup.translations',
            'orderedProductComposites.orderedGroups.productGroup.translations',
            'orderedProductComposites.productComposite.translations',
        ]);

        $viewData = [];
        $viewData['rootUrl'] = request()->root();
        $viewData['order'] = $order;
        $viewData['showTotal'] = true;
        $viewData['regionInfo'] = app(RegionInfoInterface::class);

        return $viewData;
    }

    /**
     * @return Filesystem
     */
    private function getDisk(): FileSystem
    {
        return Storage::disk(self::DISK);
    }

    /**
     * Initialize storage disk. Making sure there is a directory where to save invoices
     */
    private function initializeDisk(): void
    {
        if(!$this->getDisk()->exists(self::DIRECTORY)) {
            if (!$this->getDisk()->makeDirectory(self::DIRECTORY)) {
                throw new \RuntimeException('Could not create the document directory.');
            }
        }
    }
}