Files
Lesstime/src/Module/ProjectManagement/Infrastructure/Mcp/Tool/Project/CreateProjectTool.php
T
Matthieu d3abb584a9
Pull Request — Quality gate / Frontend (build) (pull_request) Successful in 38s
Pull Request — Quality gate / Backend (PHP CS + PHPUnit) (pull_request) Successful in 1m39s
fix(project) : permet de choisir un workflow à la création + filet par défaut
La création de projet échouait : `Project.workflow` est obligatoire mais
n'était jamais fourni (formulaire frontend, MCP create-project), tout POST
/api/projects partait en erreur de validation/contrainte NOT NULL.

- ProjectDefaultWorkflowListener (prePersist) : assigne le workflow par
  défaut quand aucun n'est fourni, couvrant API Platform, API brute et MCP.
- retrait de l'Assert\NotNull sur Project::workflow (la validation tournait
  avant le flush et empêchait le filet) ; la contrainte DB reste le garde-fou.
- CreateProjectTool (MCP) : paramètre optionnel workflowId.
- ProjectDrawer : sélecteur Workflow en création, pré-rempli sur le défaut,
  IRI envoyée dans le payload.
- tests fonctionnels : création avec et sans workflow.
2026-06-26 16:42:02 +02:00

73 lines
2.6 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Module\ProjectManagement\Infrastructure\Mcp\Tool\Project;
use App\Module\Directory\Domain\Repository\ClientRepositoryInterface;
use App\Module\ProjectManagement\Domain\Entity\Project;
use App\Module\ProjectManagement\Domain\Repository\WorkflowRepositoryInterface;
use App\Shared\Infrastructure\Mcp\Serializer;
use Doctrine\ORM\EntityManagerInterface;
use InvalidArgumentException;
use Mcp\Capability\Attribute\McpTool;
use Symfony\Bundle\SecurityBundle\Security;
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
use function sprintf;
#[McpTool(name: 'create-project', description: 'Create a new project. Code must be 2-10 uppercase letters. Optional workflowId selects the kanban workflow; the default workflow is used when omitted.')]
class CreateProjectTool
{
public function __construct(
private readonly EntityManagerInterface $entityManager,
private readonly ClientRepositoryInterface $clientRepository,
private readonly WorkflowRepositoryInterface $workflowRepository,
private readonly Security $security,
) {}
public function __invoke(
string $name,
string $code,
?string $description = null,
?string $color = null,
?int $clientId = null,
?int $workflowId = null,
): string {
if (!$this->security->isGranted('ROLE_USER')) {
throw new AccessDeniedException('Access denied: ROLE_USER required.');
}
$project = new Project();
$project->setName($name);
$project->setCode($code);
if (null !== $description) {
$project->setDescription($description);
}
if (null !== $color) {
$project->setColor($color);
}
if (null !== $clientId) {
$client = $this->clientRepository->findById($clientId);
if (null === $client) {
throw new InvalidArgumentException(sprintf('Client with ID %d not found.', $clientId));
}
$project->setClient($client);
}
if (null !== $workflowId) {
$workflow = $this->workflowRepository->findById($workflowId);
if (null === $workflow) {
throw new InvalidArgumentException(sprintf('Workflow with ID %d not found.', $workflowId));
}
$project->setWorkflow($workflow);
}
// When no workflow is supplied, ProjectDefaultWorkflowListener assigns the default at prePersist.
$this->entityManager->persist($project);
$this->entityManager->flush();
return json_encode(Serializer::project($project));
}
}