Quale motivo di progettazione usare per rendere pagine PDF per una proposta PDF?

1

Voglio creare un set di pattern da utilizzare per il rendering di una proposta PDF (un insieme di pagine PDF). Ho fatto una buona partenza, ma non sono chiaro su come procedere quando è coinvolta una maggiore complessità.

Sto usando la libreria di API TCPDF per il rendering PDF (libreria di terze parti). Inizialmente il modo di generare la proposta PDF era in un unico file con comandi PDF ed era un disastro. Ora ho iniziato a costruire modelli su questo.

L'idea principale che ho è di avere una serie di modelli di PDF che dicano a TCPDF cosa fare e di fornire i dati a questi modelli separatamente. Per mantenere i modelli coerenti, ho aggiunto un'interfaccia di seguito:

interface PdfTemplateInterface
{
    function runPdfTemplate(TcpdfFpdi $pdf, array $data = null): void;
}

Esempio concreto:

class TechnicalPageTemplate implements PdfTemplateInterface
{
    function runPdfTemplate(TcpdfFpdi $pdf, array $data = null): void
    {
        //TCPDF commands that draw on PDF
        $pdf->SetFont('Helvetica', '', 9);
        $pdf->writeHTMLCell(33, '', '', 0, $data['html']);
        $pdf->Image($data['fname'], 3, 5, 33, 0, '', '', 'B', true, 300);
    }
}

Poi ho un renderer, il cui scopo è quello di incapsulare e gestire l'oggetto TCPDF e di eseguire qualsiasi modello passato a esso, fornendo loro l'istanza e i dati di TCPDF.

class PdfRenderer
{
    /** @var TcpdfFpdi */
    protected $pdf;

    function __construct(TcpdfFpdi $pdf)
    {
        $this->pdf = $pdf;
    }

    function renderTemplate(PdfTemplateInterface $template, array $data = null): void
    {
        $template->runPdfTemplate($this->pdf, $data);
    }
}

Per inizializzare:

$pdf = new TCPDF(...);
$pdfRenderer = new PdfRenderer($pdf);

Da un controller / azione / gestore puoi impostare $ pdfRenderer come servizio e chiamare il modello sopra in questo modo:

    $this->pdfRenderer->renderTemplate(new TechnicalPageTemplate(), array(
        'html' => $this->repository->getData($id),
        'fname' => $fname
    ));

I modelli sono essenzialmente come le istruzioni di snippet di pagina su TCPDF e puoi inserire i dati in essi. Sono abbastanza contento di come è andata a finire.

Da qui le cose diventano un po 'meno chiare.

Ho bisogno di generare un PDF con più pagine, in cui ogni pagina logica potrebbe aver bisogno dei propri dati e dipendenze e le esigenze del repository.

Come posso configurarlo?

Attualmente ricevo qualcosa di simile da Controller / RequestHandler ...

//function Controller::page1()
$this->pdfRenderer->renderTemplate(new HeaderTemplate(), $header);
$this->pdfRenderer->renderTemplate(new Page1Template(), array(
    'html' => $this->repository->getData($id)
));
$this->pdfRenderer->renderTemplate(new FooterTemplate(), $footer);


//function Controller::page2()
$this->pdfRenderer->renderTemplate(new HeaderTemplate(), $header);
$this->pdfRenderer->renderTemplate(new Page2Template(), array(
    'data' => $this->repository2->getData($id),
    'image' => $this->repository3->getData($id),
));
$this->pdfRenderer->renderTemplate(new FooterTemplate(), $footer);


//function Controller::page3()
$this->pdfRenderer->renderTemplate(new HeaderTemplate(), $header);
$this->pdfRenderer->renderTemplate(new Page3TemplatePart1(), array(
    'html' => $this->repository4->getData($id)
));
$this->pdfRenderer->renderTemplate(new Page3TemplatePart2(), array(
    'html' => $this->dependency5->getData()
));
$this->pdfRenderer->renderTemplate(new FooterTemplate(), $footer);

È disordinato e mi sembra che ci sia un buon modello in attesa di emergere ...
ma non lo vedo.

Una cosa che vedo sopra il codice "disordinato" dipende troppo da pdfRenderer. Un pensiero che ho è di spostare il codice da singoli metodi pageX () di Controller in metodi PdfRenderer.

vale a dire.

Direzione 1: estendi PdfRenderer

class Proposal extends PdfRenderer
{
    function page1()
    {
        parent::renderTemplate(new HeaderTemplate(), $header);
        parent::renderTemplate(new Page1Template(), array(
            'html' => $this->repository->getData($id)
        ));
        parent::renderTemplate(new FooterTemplate(), $footer);
    }
}

//Then call from Controller
$this->proposal = new Proposal(Repository(), Other20Dependencies());
$this->proposal->page1();
$this->proposal->page2();

Ciò che non mi piace di questo schema è che unisce le preoccupazioni. Preoccupazione di PdfRenderer con esigenze aziendali della proposta.

Direzione 2: organizza il codice in Pages

Potrei definire ... blocchi di controller supplementari, chiamarli Pages .

Inizia con un'interfaccia:

interface PdfPageInterface
{
    function runPdfTemplates(PdfRenderer $pdfRenderer);
}

Pagina 1 diventa

class Page1 extends PdfPageInterface
{
    function __construct(... Dependencies for Page1 only ...)
    {}

    function renderPage(PdfRenderer $pdfRenderer)
    {
        $pdfRenderer->renderTemplate(new HeaderTemplate(), $header);
        $pdfRenderer->renderTemplate(new Page1Template(), array(
            'html' => $this->repository->getData($id)
        ));
        $pdfRenderer->renderTemplate(new FooterTemplate(), $footer);
    }
}

//from within Controller:
$page1 = new Page1(...deps...);
$this->pdfRenderer->renderPage($page1);

dove PdfRenderer :: renderPage sarà così:

function renderPage(PdfPageInterface $page): void
{
    $page->runPdfTemplates($this);
}

In questo modo ogni pagina è il proprio mini-controller che è inizializzato con e può gestire le proprie dipendenze.

Il problema che vedo in questa direzione è che ... ho già il controller di stile MVC principale. Mi sembra che Pages sarà una derivazione del controller, che non serve in realtà come uno schema particolare, ma più che altro viene utilizzato per l'organizzazione del codice. Cioè, non un vero modello separato ma più come faranno parte del Controller, ma in file diversi. Questa divisione del controller è ciò che mi dà fastidio.

Mi sento come se ci fosse un modello migliore disponibile. Ma non riesco a vederlo. Il mio obiettivo qui è trovare quel modello. C'è qualcosa che consiglieresti?

    
posta Dennis 20.08.2018 - 22:21
fonte

0 risposte