Julien Fastré 78a3dfd65e
Add validation for household metadata
A new validation group 'household_metadata' has been added to the Household form. The 'waitingForBirth' field now has a type check, and for the 'waitingForBirthDate' field, an expression is added to ensure it's set if 'waitingForBirth' is true. Simultaneously, error handling for form submission has been enriched in the HouseholdController to display specific error messages.
2024-04-08 14:52:40 +02:00

305 lines
11 KiB
PHP

<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\PersonBundle\Controller;
use Chill\MainBundle\Entity\Address;
use Chill\MainBundle\Form\Type\AddressDateType;
use Chill\PersonBundle\Entity\Household\Household;
use Chill\PersonBundle\Form\HouseholdType;
use Chill\PersonBundle\Repository\Household\PositionRepository;
use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter;
use Chill\PersonBundle\Security\Authorization\HouseholdVoter;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
use Symfony\Component\Serializer\SerializerInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
#[Route(path: '/{_locale}/person/household')]
class HouseholdController extends AbstractController
{
public function __construct(private readonly TranslatorInterface $translator, private readonly PositionRepository $positionRepository, private readonly SerializerInterface $serializer, private readonly Security $security, private readonly \Doctrine\Persistence\ManagerRegistry $managerRegistry) {}
/**
* @ParamConverter("household", options={"id": "household_id"})
*/
#[Route(path: '/{household_id}/accompanying-period', name: 'chill_person_household_accompanying_period', methods: ['GET', 'HEAD'])]
public function accompanyingPeriod(Request $request, Household $household)
{
$currentMembers = $household->getCurrentPersons();
$accompanyingPeriods = [];
foreach ($currentMembers as $p) {
$accompanyingPeriodsMember = $p->getCurrentAccompanyingPeriods();
foreach ($accompanyingPeriodsMember as $accompanyingPeriod) {
if (!$this->security->isGranted(AccompanyingPeriodVoter::SEE, $accompanyingPeriod)) {
continue;
}
$accompanyingPeriods[$accompanyingPeriod->getId()] = $accompanyingPeriod;
}
}
usort($accompanyingPeriods, static fn ($a, $b) => $b->getOpeningDate() <=> $a->getOpeningDate());
$oldMembers = $household->getNonCurrentMembers();
$accompanyingPeriodsOld = [];
foreach ($oldMembers as $m) {
$accompanyingPeriodsOldMember = $m->getPerson()->getAccompanyingPeriods();
foreach ($accompanyingPeriodsOldMember as $accompanyingPeriod) {
if (!$this->security->isGranted(AccompanyingPeriodVoter::SEE, $accompanyingPeriod)) {
continue;
}
$id = $accompanyingPeriod->getId();
if (!\array_key_exists($id, $accompanyingPeriodsOld) && !\array_key_exists($id, $accompanyingPeriods)) {
$accompanyingPeriodsOld[$id] = $accompanyingPeriod;
}
}
}
return $this->render(
'@ChillPerson/Household/accompanying_period.html.twig',
[
'household' => $household,
'accompanying_periods' => $accompanyingPeriods,
'accompanying_periods_old' => $accompanyingPeriodsOld,
]
);
}
/**
* @ParamConverter("household", options={"id": "household_id"})
*/
#[Route(path: '/{household_id}/address/edit', name: 'chill_person_household_address_edit', methods: ['GET', 'HEAD', 'POST'])]
public function addressEdit(Request $request, Household $household)
{
// TODO ACL
$address_id = $request->query->get('address_id');
$address = $this->managerRegistry->getManager()
->getRepository(Address::class)
->find($address_id);
return $this->render(
'@ChillPerson/Household/address_edit.html.twig',
[
'household' => $household,
'address' => $address,
]
);
}
/**
* @ParamConverter("household", options={"id": "household_id"})
*/
#[Route(path: '/{household_id}/addresses', name: 'chill_person_household_addresses', methods: ['GET', 'HEAD'])]
public function addresses(Request $request, Household $household)
{
// TODO ACL
// TODO put these lines into a validator constraint on household->getAddress
$addresses = $household->getAddresses();
$cond = true;
for ($i = 0; \count($addresses) - 1 > $i; ++$i) {
if ($addresses[$i]->getValidFrom() !== $addresses[$i + 1]->getValidTo()) {
$cond = false;
}
}
return $this->render(
'@ChillPerson/Household/addresses.html.twig',
[
'household' => $household,
]
);
}
/**
* @ParamConverter("household", options={"id": "household_id"})
*/
#[Route(path: '/{household_id}/address/move', name: 'chill_person_household_address_move', methods: ['GET', 'HEAD', 'POST'])]
public function addressMove(Request $request, Household $household)
{
// TODO ACL
return $this->render(
'@ChillPerson/Household/address_move.html.twig',
[
'household' => $household,
]
);
}
/**
* @ParamConverter("household", options={"id": "household_id"})
*/
#[Route(path: '/{household_id}/address/edit_valid_from', name: 'chill_person_household_address_valid_from_edit', methods: ['GET', 'HEAD', 'POST'])]
public function addressValidFromEdit(Request $request, Household $household)
{
$this->denyAccessUnlessGranted(HouseholdVoter::EDIT, $household);
if (!$request->query->has('address_id')) {
throw new BadRequestHttpException('parameter address_id is missing');
}
$address_id = $request->query->getInt('address_id');
// loop over adresses of the household, to be sure that the household is associated
// to the edited address
$address = null;
foreach ($household->getAddresses() as $householdAddress) {
if ($householdAddress->getId() === $address_id) {
$address = $householdAddress;
}
}
if (null === $address) {
throw new BadRequestHttpException('The edited address does not belongs to the household');
}
$form = $this->createForm(AddressDateType::class, $address, []);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$household->makeAddressConsistent();
$this->managerRegistry->getManager()->flush();
return $this->redirectToRoute('chill_person_household_addresses', [
'household_id' => $household->getId(),
]);
}
return $this->render(
'@ChillPerson/Household/address_valid_from_edit.html.twig',
[
'household' => $household,
'address' => $address,
'form' => $form->createView(),
]
);
}
/**
* @ParamConverter("household", options={"id": "household_id"})
*/
#[Route(path: '/{household_id}/members/metadata/edit', name: 'chill_person_household_members_metadata_edit', methods: ['GET', 'POST'])]
public function editHouseholdMetadata(Request $request, Household $household)
{
// TODO ACL
$form = $this->createMetadataForm($household);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$this->managerRegistry->getManager()->flush();
$this->addFlash('success', $this->translator->trans('household.data_saved'));
return $this->redirectToRoute('chill_person_household_summary', [
'household_id' => $household->getId(),
]);
}
if ($form->isSubmitted() && !$form->isValid()) {
$this->addFlash('error', $this->translator->trans('This form contains errors'));
foreach ($form->getErrors() as $error) {
$this->addFlash('error', $error->getMessage());
}
}
return $this->render('@ChillPerson/Household/edit_member_metadata.html.twig', [
'household' => $household,
'form' => $form->createView(),
]);
}
/**
* @ParamConverter("household", options={"id": "household_id"})
*/
#[Route(path: '/{household_id}/relationship', name: 'chill_person_household_relationship', methods: ['GET', 'HEAD'])]
public function showRelationship(Request $request, Household $household)
{
$jsonString = $this->serializer->serialize(
$household->getCurrentPersons(),
'json',
[AbstractNormalizer::GROUPS => ['read']]
);
return $this->render(
'@ChillPerson/Household/relationship.html.twig',
[
'household' => $household,
'persons' => $jsonString,
]
);
}
/**
* @ParamConverter("household", options={"id": "household_id"})
*/
#[Route(path: '/{household_id}/summary', name: 'chill_person_household_summary', methods: ['GET', 'HEAD'])]
public function summary(Request $request, Household $household)
{
// TODO ACL
$positions = $this->positionRepository
->findByActiveOrdered();
// little performance improvement:
// initialize members collection, which will avoid
// some queries
$household->getMembers()->initialize();
if ($request->query->has('edit')) {
$form = $this->createMetadataForm($household);
} else {
$form = null;
}
return $this->render(
'@ChillPerson/Household/summary.html.twig',
[
'household' => $household,
'positions' => $positions,
'form' => $form?->createView(),
]
);
}
private function createMetadataForm(Household $household): FormInterface
{
return $this->createForm(
HouseholdType::class,
$household,
[
'action' => $this->generateUrl(
'chill_person_household_members_metadata_edit',
[
'household_id' => $household->getId(),
]
),
]
);
}
}