fix canonicalization and show only active children in form

This commit is contained in:
Julien Fastré 2021-10-08 11:30:35 +02:00
parent 58ddbfb67b
commit 9a708ca618
6 changed files with 113 additions and 32 deletions

View File

@ -85,6 +85,10 @@ class ThirdParty implements TrackCreationInterface, TrackUpdateInterface
private ?string $nameCompany = ""; private ?string $nameCompany = "";
/** /**
* Canonicalized form composed of name, company name and acronym.
*
* This field is read-only, and is generated on database side.
*
* @ORM\Column(name="canonicalized", type="text", options={"default":""}) * @ORM\Column(name="canonicalized", type="text", options={"default":""})
*/ */
private ?string $canonicalized = ""; private ?string $canonicalized = "";
@ -116,7 +120,7 @@ class ThirdParty implements TrackCreationInterface, TrackUpdateInterface
/** /**
* Contact Persons: One Institutional ThirdParty has Many Contact Persons * Contact Persons: One Institutional ThirdParty has Many Contact Persons
* @ORM\OneToMany(targetEntity="Chill\ThirdPartyBundle\Entity\ThirdParty", mappedBy="parent", * @ORM\OneToMany(targetEntity="Chill\ThirdPartyBundle\Entity\ThirdParty", mappedBy="parent",
* cascade={"persist"}) * cascade={"persist"}, orphanRemoval=true)
* @var ThirdParty[]|Collection * @var ThirdParty[]|Collection
* @Assert\Valid(traverse=true) * @Assert\Valid(traverse=true)
*/ */
@ -319,6 +323,17 @@ class ThirdParty implements TrackCreationInterface, TrackUpdateInterface
return $this->email; return $this->email;
} }
public function isContactDataAnonymous(): bool
{
return $this->contactDataAnonymous;
}
public function setContactDataAnonymous(bool $contactDataAnonymous): ThirdParty
{
$this->contactDataAnonymous = $contactDataAnonymous;
return $this;
}
/** /**
* Set comment. * Set comment.
* *
@ -570,6 +585,47 @@ class ThirdParty implements TrackCreationInterface, TrackUpdateInterface
return $this->children; return $this->children;
} }
/**
* Get the children where active = true
*
* @return Collection
*/
public function getActiveChildren(): Collection
{
return $this->children->filter(fn (ThirdParty $tp) => $tp->getActive());
}
/**
* Add a child and set the child as active
*
* Method used in conjonction with getActiveChildren in form.
*
* @internal use the method addChild
* @param ThirdParty $child
* @return $this
*/
public function addActiveChild(ThirdParty $child): self
{
$child->setActive(true);
return $this->addChild($child);
}
/**
* mark the child as unactive, but keep the child existing in the
* database. To effectively remove the child, use removeChild instead.
*
* @param ThirdParty $child
* @return $this
*/
public function removeActiveChild(ThirdParty $child): self
{
$child->setActive(false);
return $this;
}
/** /**
* @param ThirdParty $child * @param ThirdParty $child
* @return $this * @return $this
@ -583,13 +639,17 @@ class ThirdParty implements TrackCreationInterface, TrackUpdateInterface
} }
/** /**
* Remove the child from the database.
*
* If you want to keep the child into the database
* but desactivate it, use removeActiveChildren instead.
*
* @param ThirdParty $child * @param ThirdParty $child
* @return $this * @return $this
*/ */
public function removeChild(ThirdParty $child): self public function removeChild(ThirdParty $child): self
{ {
$this->children->removeElement($child); $this->children->removeElement($child);
$child->setActive(false);
return $this; return $this;
} }

View File

@ -16,6 +16,7 @@ use Doctrine\ORM\QueryBuilder;
use Doctrine\Persistence\ObjectManager; use Doctrine\Persistence\ObjectManager;
use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\CallbackTransformer; use Symfony\Component\Form\CallbackTransformer;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormInterface; use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\FormView; use Symfony\Component\Form\FormView;
@ -138,6 +139,10 @@ class ThirdPartyType extends AbstractType
'placeholder' => 'thirdparty.choose profession', 'placeholder' => 'thirdparty.choose profession',
'required' => false 'required' => false
]) ])
->add('contactDataAnonymous', CheckboxType::class, [
'required' => false,
'label' => 'thirdparty.Contact data are confidential'
])
; ;
// Institutional ThirdParty (parent) // Institutional ThirdParty (parent)
@ -165,12 +170,13 @@ class ThirdPartyType extends AbstractType
'multiple' => true, 'multiple' => true,
'attr' => ['class' => 'select2'] 'attr' => ['class' => 'select2']
]) ])
->add('children', ChillCollectionType::class, [ ->add('activeChildren', ChillCollectionType::class, [
'entry_type' => ThirdPartyType::class, 'entry_type' => ThirdPartyType::class,
'entry_options' => [ 'entry_options' => [
'is_child' => true, 'is_child' => true,
'block_name' => 'children' 'block_name' => 'children'
], ],
'block_name' => 'active_children',
'allow_add' => true, 'allow_add' => true,
'allow_delete' => true, 'allow_delete' => true,
'by_reference' => false, 'by_reference' => false,

View File

@ -23,7 +23,7 @@
{{ form_row(form.email) }} {{ form_row(form.email) }}
<h2>{{ 'Contacts'|trans }}</h2> <h2>{{ 'Contacts'|trans }}</h2>
{{ form_widget(form.children) }} {{ form_widget(form.activeChildren) }}
<div class="mb-3 row"> <div class="mb-3 row">
{{ form_label(form.address) }} {{ form_label(form.address) }}

View File

@ -1,5 +1,5 @@
{% block _third_party_children_entry_widget %} {% block _third_party_active_children_entry_widget %}
<div class="container"> <div class="container">
<div class="row"> <div class="row">
<div class="form-group col-md-3 mb-3"> <div class="form-group col-md-3 mb-3">
@ -19,16 +19,27 @@
</div> </div>
</div> </div>
<div class="row"> <div class="row">
<div class="form-group col-md-6 mb-3"> <div class="form-group col-md-5 mb-3">
{{ form_widget(form.telephone) }} {{ form_widget(form.telephone) }}
{{ form_errors(form.telephone) }} {{ form_errors(form.telephone) }}
{{ form_label(form.telephone) }} {{ form_label(form.telephone) }}
</div> </div>
<div class="form-group col-md-6 mb-3"> <div class="form-group col-md-5 mb-3">
{{ form_widget(form.email) }} {{ form_widget(form.email) }}
{{ form_errors(form.email) }} {{ form_errors(form.email) }}
{{ form_label(form.email) }} {{ form_label(form.email) }}
</div> </div>
<div class="form-group col-md-2 mb-3">
{{ form_widget(form.contactDataAnonymous) }}
{{ form_label(form.contactDataAnonymous) }}
{{ form_errors(form.contactDataAnonymous) }}
</div>
</div>
<div class="row">
<div class="form-group col-md-12 mb-3">
{{ form_widget(form.comment) }}
{{ form_errors(form.comment) }}
</div>
</div> </div>
</div> </div>
{% endblock %} {% endblock %}

View File

@ -20,9 +20,14 @@ class ThirdPartyApiSearch implements SearchApiInterface
return (new SearchApiQuery) return (new SearchApiQuery)
->setSelectKey('tparty') ->setSelectKey('tparty')
->setSelectJsonbMetadata("jsonb_build_object('id', tparty.id)") ->setSelectJsonbMetadata("jsonb_build_object('id', tparty.id)")
->setSelectPertinence("SIMILARITY(?, LOWER(UNACCENT(tparty.name)))", [ $pattern ]) ->setSelectPertinence("GREATEST(".
"STRICT_WORD_SIMILARITY(LOWER(UNACCENT(?)), tparty.canonicalized),".
"(tparty.canonicalized LIKE '%' || LOWER(UNACCENT(?)) || '%')::int".
")", [ $pattern, $pattern ])
->setFromClause('chill_3party.third_party AS tparty') ->setFromClause('chill_3party.third_party AS tparty')
->setWhereClause('SIMILARITY(LOWER(UNACCENT(?)), LOWER(UNACCENT(tparty.name))) > 0.20', [ $pattern ]) ->setWhereClause("tparty.active IS TRUE ".
"AND (LOWER(UNACCENT(?)) <<% tparty.canonicalized OR ".
"tparty.canonicalized LIKE '%' || LOWER(UNACCENT(?)) || '%')", [ $pattern, $pattern ])
; ;
} }
@ -33,7 +38,7 @@ class ThirdPartyApiSearch implements SearchApiInterface
public function prepare(array $metadatas): void public function prepare(array $metadatas): void
{ {
} }
public function supportsResult(string $key, array $metadatas): bool public function supportsResult(string $key, array $metadatas): bool

View File

@ -22,19 +22,15 @@ final class Version20211007165001 extends AbstractMigration
$this->addSql(" $this->addSql("
UPDATE chill_3party.third_party UPDATE chill_3party.third_party
SET canonicalized = SET canonicalized =
COALESCE( UNACCENT(
UNACCENT( LOWER(
LOWER( name ||
name || CASE WHEN COALESCE(name_company, '') <> '' THEN ' ' ELSE '' END ||
CASE WHEN name <> '' THEN ' ' ELSE '' END || COALESCE(name_company, '') ||
name_company || CASE WHEN COALESCE(acronym, '') <> '' THEN ' ' ELSE '' END ||
CASE WHEN name_company <> '' THEN ' ' ELSE '' END || COALESCE(acronym, '')
acronym || )
CASE WHEN acronym <> '' THEN ' ' ELSE '' END )
)
),
''
)
"); ");
$this->addSql(" $this->addSql("
CREATE OR REPLACE FUNCTION chill_3party.canonicalize() RETURNS TRIGGER CREATE OR REPLACE FUNCTION chill_3party.canonicalize() RETURNS TRIGGER
@ -42,14 +38,17 @@ final class Version20211007165001 extends AbstractMigration
AS AS
$$ $$
BEGIN BEGIN
NEW.canonicalized = UNACCENT(LOWER( NEW.canonicalized =
NEW.name || UNACCENT(
CASE WHEN NEW.name <> '' THEN ' ' ELSE '' END || LOWER(
NEW.name_company || name ||
CASE WHEN NEW.name_company <> '' THEN ' ' ELSE '' END || CASE WHEN COALESCE(name_company, '') <> '' THEN ' ' ELSE '' END ||
NEW.acronym || COALESCE(name_company, '') ||
CASE WHEN NEW.acronym <> '' THEN ' ' ELSE '' END CASE WHEN COALESCE(acronym, '') <> '' THEN ' ' ELSE '' END ||
)); COALESCE(acronym, '')
)
)
;
return NEW; return NEW;
END END
@ -71,7 +70,7 @@ final class Version20211007165001 extends AbstractMigration
"); ");
$this->addSql(" $this->addSql("
CREATE INDEX chill_custom_canonicalized_trgm_idx_gist CREATE INDEX chill_custom_canonicalized_trgm_idx_gist
ON chill_3party.third_party USING GIST (canonicalized gist_trgm_ops) ON chill_3party.third_party USING GIST (canonicalized gist_trgm_ops) WHERE active IS TRUE
"); ");
} }