After watching the Laravell Nova presentation I wanted to create similar functionality to Lenses in my own app.
I have the following concepts:
- Entity: Standard Doctrine Entity
- Resource: A class that describes a resource including the target entity and available lenses.
- Lens: Has an method
apply(Request $request, QueryBuilder $qb)
that allow you to modify theQueryBuilder
based on theRequest
.
The goal is to define all Lenses
as a service and then somehow assign them to a Resource
. This is the problem I'm trying to solve.
Attempt 1: Directly inject the Lenses
into the resource
ProjectResource.php
<?php
class ProjectResource
{
protected $lenses = [];
public function __construct(
ProjectRepository $repository,
LensInterface $activeProjectLens,
LensInterface $starredProjectLens
) {
$this->lenses = [
$activeProjectLens,
$starredProjectLens
];
}
public function getLenses() {
return $this->lenses;
}
}
The downside of this is that each Lens
service is instantiated and needs to be defined manually
Attempt 2: Inject tagged Lenses
into the resource
In my services.yaml
tag the services and assign them as an argument to the resource:
App\Lens\ActiveProjectLens:
tags: ['resource.project.lens']
App\Lens\StarredProjectLens:
tags: ['resource.project.lens']
App\Resource\ProjectResource:
arguments:
$lenses: !tagged resource.project.lens
ProjectResource.php
<?php
class ProjectResource
{
protected $lenses = [];
public function __construct(
ProjectRepository $repository,
iterable $lenses
) {
$this->lenses = $lenses;
}
public function getLenses() {
return $this->lenses;
}
}
The downside of this approach is every Lens
service and Resource must be tagged and cannot be an auto-configured service.
**Attempt 3: Add a compiler pass **
I attempted to add the process()
method to the Kernel
but I didn't get too far with that.
My goal is to define a list of services somehow in the Resource
and have them injected. Is there any established pattern for this?
Your approach with the tags seems good. Symfony provides a way to automatically add tags to classes that implement a certain interface: Interface-based service configuration.
To use that you have to do the following:
App\Lens\LensInterface
) and let your lens classes implement the interface.services.yaml
file add this config:Then every class implementing your LensInterface would be injected into the ProjectResource without having to explicitly configure every single lens.