add model + form to handle alt names

This commit is contained in:
Julien Fastré 2020-01-30 15:51:39 +01:00
parent 7b7ce4a604
commit 426458811c
14 changed files with 482 additions and 2 deletions

View File

@ -0,0 +1,70 @@
<?php
namespace Chill\PersonBundle\Config;
/**
* Give help to interact with the config for alt names
*
*
*/
class ConfigPersonAltNamesHelper
{
/**
* the raw config, directly from the container parameter
*
* @var array
*/
private $config = [];
public function __construct($config)
{
$this->config = $config;
}
/**
* Return true if at least one alt name is configured
*
* @return bool
*/
public function hasAltNames(): bool
{
return count($this->config) > 0;
}
/**
* get the choices as key => values
*
* @return array
*/
public function getChoices(): array
{
$choices = [];
foreach ($this->config as $entry) {
$labels = $entry['labels'];
$lang = false;
$label = false;
$cur = reset($labels);
while ($cur) {
if (key($labels) === 'lang') {
$lang = current($labels);
}
if (key($labels) === 'label') {
$label = current($labels);
}
if ($lang !== FALSE && $label !== FALSE) {
$choices[$entry['key']][$lang] = $label;
$lang = false;
$label = false;
}
$cur = next($labels);
}
}
return $choices;
}
}

View File

@ -65,6 +65,7 @@ class ChillPersonExtension extends Extension implements PrependExtensionInterfac
$loader->load('services/form.yml'); $loader->load('services/form.yml');
$loader->load('services/repository.yml'); $loader->load('services/repository.yml');
$loader->load('services/templating.yml'); $loader->load('services/templating.yml');
$loader->load('services/alt_names.yml');
// load service advanced search only if configure // load service advanced search only if configure
if ($config['search']['search_by_phone'] != 'never') { if ($config['search']['search_by_phone'] != 'never') {

View File

@ -78,6 +78,26 @@ class Configuration implements ConfigurationInterface
->append($this->addFieldNode('address')) ->append($this->addFieldNode('address'))
->append($this->addFieldNode('accompanying_period')) ->append($this->addFieldNode('accompanying_period'))
->append($this->addFieldNode('memo')) ->append($this->addFieldNode('memo'))
->arrayNode('alt_names')
->defaultValue([])
->arrayPrototype()
->children()
->scalarNode('key')
->isRequired()->cannotBeEmpty()
->end()
->arrayNode('labels')->isRequired()->cannotBeEmpty()
->children()
->scalarNode('lang')->isRequired()->cannotBeEmpty()
->example('fr')
->end()
->scalarNode('label')->isRequired()->cannotBeEmpty()
->example('Nom de jeune fille')
->end()
->end()
->end()
->end()
->end()
->end()
->end() //children for 'person_fields', parent = array 'person_fields' ->end() //children for 'person_fields', parent = array 'person_fields'
->end() // person_fields, parent = children of root ->end() // person_fields, parent = children of root
->arrayNode('accompanying_periods_fields') ->arrayNode('accompanying_periods_fields')

View File

@ -43,6 +43,12 @@ class Person implements HasCenterInterface {
/** @var string The person's last name */ /** @var string The person's last name */
private $lastName; private $lastName;
/**
*
* @var \Doctrine\Common\Collections\Collection
*/
private $altNames;
/** @var \DateTime The person's birthdate */ /** @var \DateTime The person's birthdate */
private $birthdate; //to change in birthdate private $birthdate; //to change in birthdate
@ -123,6 +129,7 @@ class Person implements HasCenterInterface {
$this->accompanyingPeriods = new ArrayCollection(); $this->accompanyingPeriods = new ArrayCollection();
$this->spokenLanguages = new ArrayCollection(); $this->spokenLanguages = new ArrayCollection();
$this->addresses = new ArrayCollection(); $this->addresses = new ArrayCollection();
$this->altNames = new ArrayCollection();
if ($opening === null) { if ($opening === null) {
$opening = new \DateTime(); $opening = new \DateTime();
@ -325,6 +332,38 @@ class Person implements HasCenterInterface {
return $this->lastName; return $this->lastName;
} }
public function getAltNames(): \Doctrine\Common\Collections\Collection
{
return $this->altNames;
}
public function setAltNames(\Doctrine\Common\Collections\Collection $altNames)
{
$this->altNames = $altNames;
return $this;
}
public function addAltName(PersonAltName $altName)
{
if (FALSE === $this->altNames->contains($altName)) {
$this->altNames->add($altName);
$altName->setPerson($this);
}
return $this;
}
public function removeAltName(PersonAltName $altName)
{
if ($this->altNames->contains($altName)) {
$altName->setPerson(null);
$this->altNames->removeElement($altName);
}
return $this;
}
/** /**
* Set birthdate * Set birthdate
* *

118
Entity/PersonAltName.php Normal file
View File

@ -0,0 +1,118 @@
<?php
namespace Chill\PersonBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* PersonAltName
*
* @ORM\Table(name="person_alt_name")
* @ORM\Entity(repositoryClass="Chill\PersonBundle\Repository\PersonAltNameRepository")
*/
class PersonAltName
{
/**
* @var int
*
* @ORM\Column(name="id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @var string
*
* @ORM\Column(name="key", type="string", length=255)
*/
private $key;
/**
* @var string
*
* @ORM\Column(name="label", type="text")
*/
private $label;
/**
*
* @var Person
* @ORM\OneToMany(
* targetEntity="Chill\PersonBundle\Entity\Person",
* mappedBy="altNames"
* )
*/
private $person;
/**
* Get id.
*
* @return int
*/
public function getId()
{
return $this->id;
}
/**
* Set key.
*
* @param string $key
*
* @return PersonAltName
*/
public function setKey($key)
{
$this->key = $key;
return $this;
}
/**
* Get key.
*
* @return string
*/
public function getKey()
{
return $this->key;
}
/**
* Set label.
*
* @param string $label
*
* @return PersonAltName
*/
public function setLabel($label)
{
$this->label = $label;
return $this;
}
/**
* Get label.
*
* @return string
*/
public function getLabel()
{
return $this->label;
}
public function getPerson(): Person
{
return $this->person;
}
public function setPerson(?Person $person = null)
{
$this->person = $person;
return $this;
}
}

View File

@ -0,0 +1,73 @@
<?php
namespace Chill\PersonBundle\Form\DataMapper;
use Symfony\Component\Form\DataMapperInterface;
use Doctrine\Common\Collections\Collection;
use Symfony\Component\Form\Exception\UnexpectedTypeException;
use Chill\PersonBundle\Entity\PersonAltName;
/**
*
*
*/
class PersonAltNameDataMapper implements DataMapperInterface
{
public function mapDataToForms($viewData, $forms)
{
if (null === $viewData) {
return;
}
if (!$viewData instanceof Collection) {
throw new UnexpectedTypeException($viewData, Collection::class);
}
$mapIndexToKey = [];
foreach ($viewData->getIterator() as $key => $altName) {
/** @var PersonAltName $altName */
$mapIndexToKey[$altName->getKey()] = $key;
}
foreach ($forms as $key => $form) {
if (\array_key_exists($key, $mapIndexToKey)) {
$form->setData($viewData->get($mapIndexToKey[$key])->getLabel());
}
}
}
/**
*
* @param FormInterface[] $forms
* @param Collection $viewData
*/
public function mapFormsToData($forms, &$viewData)
{
$mapIndexToKey = [];
foreach ($viewData->getIterator() as $key => $altName) {
/** @var PersonAltName $altName */
$mapIndexToKey[$altName->getKey()] = $key;
}
foreach ($forms as $key => $form) {
$isEmpty = empty($form->getData());
if (\array_key_exists($key, $mapIndexToKey)) {
if ($isEmpty) {
$viewData->remove($mapIndexToKey[$key]);
} else {
$viewData->get($mapIndexToKey[$key])->setLabel($form->getData());
}
} else {
if (!$isEmpty) {
$altName = (new PersonAltName())
->setKey($key)
->setLabel($form->getData())
;
$viewData->add($altName);
}
}
}
}
}

View File

@ -34,6 +34,8 @@ use Chill\MainBundle\Form\Type\Select2CountryType;
use Chill\MainBundle\Form\Type\Select2LanguageType; use Chill\MainBundle\Form\Type\Select2LanguageType;
use Chill\CustomFieldsBundle\Form\Type\CustomFieldType; use Chill\CustomFieldsBundle\Form\Type\CustomFieldType;
use Chill\PersonBundle\Form\Type\Select2MaritalStatusType; use Chill\PersonBundle\Form\Type\Select2MaritalStatusType;
use Chill\PersonBundle\Config\ConfigPersonAltNamesHelper;
use Chill\PersonBundle\Form\Type\PersonAltNameType;
class PersonType extends AbstractType class PersonType extends AbstractType
{ {
@ -47,13 +49,22 @@ class PersonType extends AbstractType
*/ */
protected $config = array(); protected $config = array();
/**
*
* @var ConfigPersonAltNamesHelper
*/
protected $configAltNamesHelper;
/** /**
* *
* @param string[] $personFieldsConfiguration configuration of visibility of some fields * @param string[] $personFieldsConfiguration configuration of visibility of some fields
*/ */
public function __construct(array $personFieldsConfiguration) public function __construct(
{ array $personFieldsConfiguration,
ConfigPersonAltNamesHelper $configAltNamesHelper
) {
$this->config = $personFieldsConfiguration; $this->config = $personFieldsConfiguration;
$this->configAltNamesHelper = $configAltNamesHelper;
} }
/** /**
@ -70,6 +81,12 @@ class PersonType extends AbstractType
'required' => true 'required' => true
)); ));
if ($this->configAltNamesHelper->hasAltNames()) {
$builder->add('altNames', PersonAltNameType::class, [
'by_reference' => false
]);
}
if ($this->config['memo'] === 'visible') { if ($this->config['memo'] === 'visible') {
$builder $builder
->add('memo', TextareaType::class, array('required' => false)) ->add('memo', TextareaType::class, array('required' => false))

View File

@ -0,0 +1,70 @@
<?php
namespace Chill\PersonBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Chill\PersonBundle\Config\ConfigPersonAltNamesHelper;
use Chill\MainBundle\Templating\TranslatableStringHelper;
/**
*
*
*/
class PersonAltNameType extends AbstractType
{
/**
*
* @var ConfigPersonAltNamesHelper
*/
private $configHelper;
/**
*
* @var TranslatableStringHelper
*/
private $translatableStringHelper;
public function __construct(
ConfigPersonAltNamesHelper $configHelper,
TranslatableStringHelper $translatableStringHelper
) {
$this->configHelper = $configHelper;
$this->translatableStringHelper = $translatableStringHelper;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
foreach ($this->getKeyChoices() as $label => $key) {
$builder->add($key, TextType::class, [
'label' => $label,
'required' => false
]);
}
$builder->setDataMapper(new \Chill\PersonBundle\Form\DataMapper\PersonAltNameDataMapper());
}
protected function getKeyChoices()
{
$choices = $this->configHelper->getChoices();
$translatedChoices = [];
foreach ($choices as $key => $labels) {
$label = $this->translatableStringHelper->localize($labels);
$translatedChoices[$label] = $key;
}
return $translatedChoices;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver
->setDefault('class', \Chill\PersonBundle\Entity\PersonAltName::class)
;
}
}

View File

@ -74,6 +74,11 @@ Chill\PersonBundle\Entity\Person:
targetEntity: AccompanyingPeriod targetEntity: AccompanyingPeriod
mappedBy: person mappedBy: person
cascade: [persist, remove, merge, detach] cascade: [persist, remove, merge, detach]
altNames:
targetEntity: PersonAltName
mappedBy: person
cascade: [persist, remove, merge, detach]
orphanRemoval: true
manyToMany: manyToMany:
spokenLanguages: spokenLanguages:
targetEntity: Chill\MainBundle\Entity\Language targetEntity: Chill\MainBundle\Entity\Language

View File

@ -0,0 +1,21 @@
Chill\PersonBundle\Entity\PersonAltName:
type: entity
table: chill_person_alt_name
repositoryClass: Chill\PersonBundle\Repository\PersonAltNameRepository
id:
id:
type: integer
id: true
generator:
strategy: AUTO
fields:
key:
type: string
length: 255
label:
type: text
manyToOne:
person:
targetEntity: Person
inversedBy: altNames

View File

@ -0,0 +1,4 @@
services:
Chill\PersonBundle\Config\ConfigPersonAltNamesHelper:
arguments:
$config: '%chill_person.person_fields.alt_names%'

View File

@ -3,6 +3,7 @@ services:
class: Chill\PersonBundle\Form\PersonType class: Chill\PersonBundle\Form\PersonType
arguments: arguments:
- "%chill_person.person_fields%" - "%chill_person.person_fields%"
- '@Chill\PersonBundle\Config\ConfigPersonAltNamesHelper'
tags: tags:
- { name: form.type } - { name: form.type }
@ -29,3 +30,10 @@ services:
- '@Symfony\Component\Translation\TranslatorInterface' - '@Symfony\Component\Translation\TranslatorInterface'
tags: tags:
- { name: form.type } - { name: form.type }
Chill\PersonBundle\Form\Type\PersonAltNameType:
arguments:
$configHelper: '@Chill\PersonBundle\Config\ConfigPersonAltNamesHelper'
$translatableStringHelper: '@chill.main.helper.translatable_string'
tags:
- { name: form.type }

View File

@ -0,0 +1,31 @@
<?php declare(strict_types=1);
namespace Application\Migrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Add person alt name table
*/
final class Version20200128084445 extends AbstractMigration
{
public function up(Schema $schema) : void
{
$this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'postgresql', 'Migration can only be executed safely on \'postgresql\'.');
$this->addSql("CREATE SEQUENCE chill_person_alt_name_id_seq INCREMENT BY 1 MINVALUE 1 START 1");
$this->addSql("CREATE TABLE chill_person_alt_name (id INT NOT NULL, person_id INT DEFAULT NULL, key VARCHAR(255) NOT NULL, label TEXT NOT NULL, PRIMARY KEY(id))");
$this->addSql("CREATE INDEX IDX_2628668E217BBB47 ON chill_person_alt_name (person_id)");
$this->addSql("ALTER TABLE chill_person_alt_name ADD CONSTRAINT FK_2628668E217BBB47 FOREIGN KEY (person_id) REFERENCES chill_person_person (id) NOT DEFERRABLE INITIALLY IMMEDIATE");
}
public function down(Schema $schema) : void
{
$this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'postgresql', 'Migration can only be executed safely on \'postgresql\'.');
$this->addSql("DROP INDEX IDX_2628668E217BBB47");
$this->addSql("DROP TABLE chill_person_alt_name");
$this->addSql("DROP SEQUENCE chill_person_alt_name_id_seq");
}
}

View File

@ -39,6 +39,9 @@
<legend><h2>{{ 'General information'|trans }}</h2></legend> <legend><h2>{{ 'General information'|trans }}</h2></legend>
{{ form_row(form.firstName, {'label' : 'First name'}) }} {{ form_row(form.firstName, {'label' : 'First name'}) }}
{{ form_row(form.lastName, {'label' : 'Last name'}) }} {{ form_row(form.lastName, {'label' : 'Last name'}) }}
{% if form.altNames is defined %}
{{ form_widget(form.altNames, { 'label': 'Alternative names'}) }}
{% endif %}
{{ form_row(form.gender, {'label' : 'Gender'}) }} {{ form_row(form.gender, {'label' : 'Gender'}) }}
</fieldset> </fieldset>