I am trying to learn the correct usage of the IoC in Laravel. In my experiment, I have an interface that has multiple implementations. I am trying to Bind these implementations using a Contextual Binding. What I have written, i thought would work however I am clearly misunderstanding something, or just doing it plain wrong...
In my AppServiceProvider.php
I have defined my bindings as such
use ...;
// Bind the Document Writer Interface to its Implementations
$this->app->bind(DocumentWriter::class, function ($app, DocumentType $document_type) {
return match ($document_type) {
DocumentType::Pdf => PdfWriter::class,
DocumentType::Doc => DocWriter::class,
DocumentType::Xls => XlsWriter::class,
};
});
$this->app->when(ManagementReport::class)
->needs(DocumentWriter::class)
->give(function ($app, DocumentType $document_type) {
return match ($document_type) {
DocumentType::Pdf => PdfWriter::class,
DocumentType::Doc => DocWriter::class,
DocumentType::Xls => XlsWriter::class,
};
});
I am trying to resolve the DocumentWriter based on a passed Enum.
In my command I am running this; where $management_report->getType()
returns an Enum correctly.
// Initialise the Document Writer Service
use ...;
$document_writer = App::makeWith(
DocumentWriter::class,
['document_type' => $management_report->getType()]
);
$management_report_service = App::makeWith(
ManagementReport::class,
['document_type' => $management_report->getType()]
);
Both of these fail with the same error
AppServiceProvider: Argument #2 ($document_type) must be of type App\Enums\DocumentWriter\Type, array given
Am I misunderstanding the behavior here and thus doing something wrong? Is this even possible? I want the Resolving of this interface and Service to fail if the Document Type is not a valid value of the Enum.
I should also add that my ManagementReport
Service is expecting an implementation of the DocumentWriter
Interface.
My Enum
namespace App\Enums\DocumentWriter;
enum Type: string
{
case Xls = 'xls';
case Doc = 'doc';
case Pdf = 'pdf';
}
My ManagementReport
Service Class
namespace App\Services\Building;
use ...;
class ManagementReport
{
public function __construct(protected DocumentWriter $writer) {}
}
My Interface
namespace App\Contracts;
interface DocumentWriter
{
public function setTemplate();
public function getTemplate();
}
And my 3 implementations of the Interface
PdfWriter.php
, DocWriter.php
and XlsWriter.php
namespace App\Services\DocumentWriter;
use App\Contracts\DocumentWriter;
class PdfWriter implements DocumentWriter
{
public function getTemplate()
{
// TODO: Implement getTemplate() method.
}
public function setTemplate()
{
// TODO: Implement setTemplate() method.
}
}
I was able to get this working as I desired with the following adjustment to my
AppServiceProvider
.I further improved my solution by binding my service to the Contextual Binding of the Interface.
Added a little error handling to give it some meaning and works well.
It should be noted that the
makeWith
function from the container sends the second parameter as is; directly to the Binding.makeWith
does not work this way with the containerswhen()->needs()
notation apparently.