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

haakym's avatar

Confusion about constructors in abstract classes

Hi everyone,

I'm having trouble understanding how Jeffrey implements (what I see as) a wrapper around the mail class's mail->send method in one of the Larabook videos where he uses an abstract class. I've done something similar in a project I'm working on for sending mail and it works just fine, now I wanted to apply the same logic for rendering a view (which I'll later ouput into a pdf using dompdf, but one step at a time) and it doesn't seem to work!

Here's my implementation of the mailer that I used in a project which works fine and it's pretty much the same as Jeffrey's:

Mailer.php

<?php namespace Reports\Mailers;

use Illuminate\Mail\Mailer as Mail;

abstract class Mailer{

    private $mail;

    function __construct(Mail $mail)
    {
        $this->mail = $mail;
    }

    public function sendTo($to, $bcc, $subject, $view, $data)
    {
        $this->mail->send($view, $data, function($message) use ($to, $bcc, $subject) 
        {
            $message
                ->to($to)
                ->bcc($bcc)
                ->subject($subject);
        });
    }

}

then the above class is extended with LinkMailer.php

<?php namespace Reports\Mailers;

class LinkMailer extends Mailer {

    public function sendMassLinkTo($to, $data)
    {
        $subject = 'my subject';
        $view = 'my.view';
        $bcc = 'my@email.com';

        return $this->sendTo($to, $bcc, $subject, $view, $data);
    }

}

Then I call the method in my controller with no worries like this:

$this->mailer->sendMassLinkTo($to, $data);

So now I'm trying to write an abstract class to render a report using view->make->render. Please ignore that it's called PdfReport as I'm going to implement the Pdf part later.

PdfReport.php

<?php namespace Reports\PdfReports;

use Illuminate\View\View as View;

abstract class PdfReport {

    private $view;

    function __construct(View $view)
    {
        $this->view = $view;
    }

    public function render($reportView, $report)
    {
        $this->view->make('report.pdf.' . $reportView, ['report' => $report])->render();
    }

}

So the above class looks pretty much the same as the Mailer class in terms of it's set up and reusing Laravel's illuminate components. So here's my class that extends the abstract class: EslPdfReport.php

<?php namespace Reports\PdfReports;

class EslPdfReport extends PdfReport {

    public function renderReport($report)
    {
        return $this->render('esl', $report);
    }

}

Now I've ran this in my routes file as so:

use Reports\PdfReports\EslPdfReport;

Route::get('pdftest', array(    
    'as' => 'pdftest',
        function(){
            $eslReport = new EslPdfReport();
            $eslReport->renderReport(EslReport::find(1));
        }
));

and I get this error: Argument 1 passed to Reports\PdfReports\PdfReport::__construct() must be an instance of Illuminate\View\View, none given, called in C:\wamp\www\reports\app\routes.php on line 357 and defined

Which I understand why it's throwing the error, so I don't get why it doesn't throw the error if I do the following implementation in a controller:

<?php

use Reports\PdfReports\EslPdfReport;

class HomeController extends BaseController {

    private $eslPdfReport;

    function __construct(EslPdfReport $eslPdfReport)
    {
        $this->eslPdfReport = $eslPdfReport;
    }

    public function pdftest()
    {
    $this->eslPdfReport->renderReport(EslReport::find(1));
  }     

}

This then throws the following error which I'm guessing is a different issue: Illuminate \ Container \ BindingResolutionException Target [Illuminate\View\Engines\EngineInterface] is not instantiable.

Any help or advice would be appreciated. I could be going about this completely in the wrong way anyway, but hopefully I'll learn something. Thanks if you read this far!

0 likes
3 replies
JoshWilley's avatar

Change this:

use Reports\PdfReports\EslPdfReport;

Route::get('pdftest', array(    
    'as' => 'pdftest',
        function(){
            $eslReport = new EslPdfReport();
            $eslReport->renderReport(EslReport::find(1));
        }
));

To this:

use Reports\PdfReports\EslPdfReport;

Route::get('pdftest', array(    
    'as' => 'pdftest',
        function(){
            $eslReport = App::make('Reports\PdfReports\EslPdfReport');
            $eslReport->renderReport(EslReport::find(1));
        }
));

Because your EslPdfReport class is extending a class that requires parameters in the constructor, those have to get passed through.

So when you new up a class:

$eslReport = new EslPdfReport(new View()); // dependencies have to be passed through

Luckily, Laravel provides a container that will automatically resolve classes for you, hence:

App::make('Reports\PdfReports\EslPdfReport');

Let me know if this helps!

1 like
haakym's avatar

@JoshWilley, thanks so much for your reply!

I changed this:

use Reports\PdfReports\EslPdfReport;

Route::get('pdftest', array(    
    'as' => 'pdftest',
        function(){
            $eslReport = new EslPdfReport();
            $eslReport->renderReport(EslReport::find(1));
        }
));

to this:

use Reports\PdfReports\EslPdfReport;

Route::get('pdftest', array(    
    'as' => 'pdftest',
        function(){
            $eslReport = App::make('Reports\PdfReports\EslPdfReport');
            $eslReport->renderReport(EslReport::find(1));
        }
));

and get this error: Illuminate \ Container \ BindingResolutionException Target [Illuminate\View\Engines\EngineInterface] is not instantiable.

Any ideas? Thank you.

nolros's avatar

Also, you don't have a return on your render() method. $this->view->make('report.pdf.' . $reportView, ['report' => $report])->render();

Please or to participate in this conversation.