A idea for a Expression action that have the ability to call a method directly on the resource.
Limitations:
- Superficial implementation, requires
id
in the url, and no composite identifiers - No idea if this is a good idea or not :D might save some time when you need custom actions but don't feel like adding more classes than nessecary
- strugling to provide defaults (null) to Action variables
How to use it:
new Action(
name: 'addPartnershipArea',
input: CreateAreaDto::class,
output: Partnership::class,
expression: 'object.createArea(input.county)',
uriTemplate: '/partnerships/{id}/create_area',
uriVariables: [],
security: "is_granted('ROLE_SKIL')",
securityPostDenormalize: null,
),
(I wish Output, uriVariables, security and securityPostDenormalize could be null
)
The Action:
<?php
namespace App\Service\ApiPlatform;
use ApiPlatform\Metadata\HttpOperation;
use App\Controller\ActionController;
#[\Attribute(\Attribute::TARGET_CLASS | \Attribute::IS_REPEATABLE)]
class Action extends HttpOperation
{
private string $expression;
public function __construct(
string $name,
string $input,
string|false|null $output,
string $expression,
string $uriTemplate,
string|array|null $uriVariables,
string $security,
string|null $securityPostDenormalize,
) {
parent::__construct(
method: 'POST',
uriTemplate: $uriTemplate,
uriVariables: $uriVariables,
controller: ActionController::class,
security: $security,
securityPostDenormalize: $securityPostDenormalize,
input: $input,
output: $output,
name: $name
);
$this->expression = $expression;
}
public function getExpression(): string
{
return $this->expression;
}
}
The magic:
<?php
namespace App\Controller;
use ApiPlatform\State\CallableProvider;
use App\Service\ApiPlatform\Action;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
use Symfony\Component\HttpFoundation\Request;
class ActionController
{
private CallableProvider $provider;
public function __construct(
#[AutoWire(service: 'api_platform.state_provider')] CallableProvider $provider,
private EntityManagerInterface $em,
) {
$this->provider = $provider;
}
public function __invoke(Request $request, $data)
{
$attributes = $request->attributes;
/** @var Action $operation */
$operation = $attributes->get('_api_operation');
$className = $attributes->get('_api_resource_class');
$id = $attributes->get('id');
if (!$id || !$className || !$operation) {
throw new \RuntimeException('Invalid action, missing operation, className or id');
}
$repo = $this->em->getRepository($className);
$object = $repo->find($id);
$expressionLanguage = new ExpressionLanguage();
return $expressionLanguage->evaluate($operation->getExpression(), ['object' => $object, 'input' => $data]);
}
}