diff --git a/Actions/ActionEvent.php b/Actions/ActionEvent.php
new file mode 100644
index 000000000..5ac3055cb
--- /dev/null
+++ b/Actions/ActionEvent.php
@@ -0,0 +1,143 @@
+
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+namespace Chill\PersonBundle\Actions;
+
+use Symfony\Component\EventDispatcher\Event;
+
+/**
+ * Event triggered when an entity attached to a person is removed.
+ *
+ *
+ */
+class ActionEvent extends Event
+{
+ const DELETE = 'CHILL_PERSON.DELETE_ASSOCIATED_ENTITY';
+ const MOVE = 'CHILL_PERSON.MOVE_ASSOCIATED_ENTITY';
+
+ /**
+ *
+ * @var int
+ */
+ protected $personId;
+
+ /**
+ * the FQDN class name as recorded in doctrine
+ *
+ * @var string
+ */
+ protected $entity;
+
+ /**
+ * an array of key value data to describe the movement
+ *
+ * @var array
+ */
+ protected $metadata;
+
+ /**
+ * the sql statement
+ *
+ * @var string
+ */
+ protected $sqlStatement;
+
+ /**
+ *
+ * @var string[]
+ */
+ protected $preSql = [];
+
+ /**
+ *
+ * @var string[]
+ */
+ protected $postSql = [];
+
+ public function __construct($personId, $entity, $sqlStatement, $metadata = [])
+ {
+ $this->personId = $personId;
+ $this->entity = $entity;
+ $this->sqlStatement = $sqlStatement;
+ $this->metadata = $metadata;
+ }
+
+ /**
+ *
+ * @return string[]
+ */
+ public function getPreSql(): array
+ {
+ return $this->preSql;
+ }
+
+ /**
+ *
+ * @return string[]
+ */
+ public function getPostSql(): array
+ {
+ return $this->postSql;
+ }
+
+ /*
+ * Add Sql which will be executed **before** the delete statement
+ */
+ public function addPreSql(string $preSql)
+ {
+ $this->preSql[] = $preSql;
+ return $this;
+ }
+
+ /**
+ * Add Sql which will be executed **after** the delete statement
+ *
+ * @param type $postSql
+ * @return $this
+ */
+ public function addPostSql(string $postSql)
+ {
+ $this->postSql[] = $postSql;
+ return $this;
+ }
+
+ public function getPersonId(): int
+ {
+ return $this->personId;
+ }
+
+ /**
+ * get the entity name, as recorded in doctrine
+ *
+ * @return string
+ */
+ public function getEntity(): string
+ {
+ return $this->entity;
+ }
+
+ public function getSqlStatement()
+ {
+ return $this->sqlStatement;
+ }
+
+ public function getMetadata()
+ {
+ return $this->metadata;
+ }
+
+}
diff --git a/Actions/Remove/PersonMove.php b/Actions/Remove/PersonMove.php
index 112d8daa4..fb7e6b9b4 100644
--- a/Actions/Remove/PersonMove.php
+++ b/Actions/Remove/PersonMove.php
@@ -1,6 +1,6 @@
+ * Copyright (C) 2016-2019 Champs-Libres
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
@@ -21,9 +21,16 @@ use Doctrine\ORM\EntityManagerInterface;
use Chill\PersonBundle\Entity\Person;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Doctrine\ORM\Mapping\ClassMetadata;
+use Symfony\Component\EventDispatcher\EventDispatcherInterface;
+use Chill\PersonBundle\Actions\ActionEvent;
/**
- * Move all person to a new one, and delete the old record.
+ * Move or delete entities associated to a person to a new one, and delete the
+ * old person. The data associated to a person (birthdate, name, ...) are left
+ * untouched on the "new one".
+ *
+ * See `getSql` for details.
+ *
*
*/
class PersonMove
@@ -34,14 +41,51 @@ class PersonMove
*/
protected $em;
- public function __construct(EntityManagerInterface $em)
- {
+ /**
+ *
+ * @var EventDispatcherInterface
+ */
+ protected $eventDispatcher;
+
+ public function __construct(
+ EntityManagerInterface $em,
+ EventDispatcherInterface $eventDispatcher
+ ) {
$this->em = $em;
+ $this->eventDispatcher = $eventDispatcher;
}
- public function getSQL(Person $from, Person $to)
+ /**
+ * Return the sql used to move or delete entities associated to a person to
+ * a new one, and delete the old person. The data associated to a person
+ * (birthdate, name, ...) are left untouched on the "new one".
+ *
+ * The accompanying periods associated to a person are always removed. The other
+ * associated entity are updated: the new person id is associated to the entity.
+ *
+ * Optionnaly, you can ask for removing entity by passing them in $deleteEntities
+ * parameters.
+ *
+ * The following events are triggered:
+ * - `'CHILL_PERSON.DELETE_ASSOCIATED_ENTITY'` is triggered when an entity
+ * will be removed ;
+ * - `'CHILL_PERSON.MOVE_ASSOCIATED_ENTITY'` is triggered when an entity
+ * will be moved ;
+ *
+ * Those events have the following metadata:
+ *
+ * - 'original_action' : always 'move' ;
+ * - 'to': the person id to move ;
+ *
+ * @param Person $from
+ * @param Person $to
+ * @param array $deleteEntities
+ * @return type
+ */
+ public function getSQL(Person $from, Person $to, array $deleteEntities = [])
{
$sqls = [];
+ $toDelete = \array_merge($deleteEntities, $this->getDeleteEntities());
foreach ($this->em->getMetadataFactory()->getAllMetadata() as $metadata) {
if ($metadata->isMappedSuperclass) {
@@ -50,11 +94,21 @@ class PersonMove
foreach ($metadata->getAssociationMappings() as $field => $mapping) {
if ($mapping['targetEntity'] === Person::class) {
- if (\in_array($metadata->getName(), $this->deleteEntities())) {
- $sqls[] = $this->createDeleteSQL($metadata, $from, $field);
+
+ if (\in_array($metadata->getName(), $toDelete)) {
+ $sql = $this->createDeleteSQL($metadata, $from, $field);
+ $event = new ActionEvent($from->getId(), $metadata->getName(), $sql,
+ ['to' => $to->getId(), 'original_action' => 'move']);
+ $this->eventDispatcher->dispatch(ActionEvent::DELETE, $event);
+
} else {
- $sqls[] = $this->createMoveSQL($metadata, $from, $to, $field);
+ $sql = $this->createMoveSQL($metadata, $from, $to, $field);
+ $event = new ActionEvent($from->getId(), $metadata->getName(), $sql,
+ ['to' => $to->getId(), 'original_action' => 'move']);
+ $this->eventDispatcher->dispatch(ActionEvent::MOVE, $event);
}
+
+ $sqls = \array_merge($sqls, $event->getPreSql(), [$event->getSqlStatement()], $event->getPostSql());
}
}
}
@@ -110,7 +164,7 @@ class PersonMove
*
* @return array
*/
- protected function deleteEntities(): array
+ protected function getDeleteEntities(): array
{
return [
AccompanyingPeriod::class
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 298b375a6..3b1d70e17 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -47,3 +47,4 @@ Branche master
- fix error on macro renderPerson / withLink not taken into account
- add a link between accompanying person and user
- add an icon when the file is opened / closed in result list, and in person rendering macro
+- improve command to move person and all data: allow to delete some entities during move and add events
diff --git a/Command/ChillPersonMoveCommand.php b/Command/ChillPersonMoveCommand.php
index 6ab2c8979..4f2b2098a 100644
--- a/Command/ChillPersonMoveCommand.php
+++ b/Command/ChillPersonMoveCommand.php
@@ -1,4 +1,20 @@
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
namespace Chill\PersonBundle\Command;
@@ -11,6 +27,7 @@ use Chill\PersonBundle\Actions\Remove\PersonMove;
use Doctrine\ORM\EntityManagerInterface;
use Chill\PersonBundle\Entity\Person;
use Symfony\Component\Console\Exception\RuntimeException;
+use Psr\Log\LoggerInterface;
class ChillPersonMoveCommand extends ContainerAwareCommand
{
@@ -26,14 +43,22 @@ class ChillPersonMoveCommand extends ContainerAwareCommand
*/
protected $em;
+ /**
+ *
+ * @var LoggerInterface
+ */
+ protected $chillLogger;
+
public function __construct(
PersonMove $mover,
- EntityManagerInterface $em
+ EntityManagerInterface $em,
+ LoggerInterface $chillLogger
) {
parent::__construct('chill:person:move');
$this->mover = $mover;
$this->em = $em;
+ $this->chillLogger = $chillLogger;
}
protected function configure()
@@ -45,12 +70,13 @@ class ChillPersonMoveCommand extends ContainerAwareCommand
->addOption('to', 't', InputOption::VALUE_REQUIRED, "The person id which will received data")
->addOption('dump-sql', null, InputOption::VALUE_NONE, "dump sql to stdout")
->addOption('force', null, InputOption::VALUE_NONE, "execute sql instead of dumping it")
+ ->addOption('delete-entity', null, InputOption::VALUE_REQUIRED|InputOption::VALUE_IS_ARRAY, "entity to delete", [])
;
}
protected function interact(InputInterface $input, OutputInterface $output)
{
- if (FALSE === ($input->hasOption('dump-sql') || $input->hasOption('force'))) {
+ if (FALSE === $input->hasOption('dump-sql') && FALSE === $input->hasOption('force')) {
$msg = "You must use \"--dump-sql\" or \"--force\"";
throw new RuntimeException($msg);
}
@@ -72,6 +98,7 @@ class ChillPersonMoveCommand extends ContainerAwareCommand
$repository = $this->em->getRepository(Person::class);
$from = $repository->find($input->getOption('from'));
$to = $repository->find($input->getOption('to'));
+ $deleteEntities = $input->getOption('delete-entity');
if ($from === NULL) {
throw new RuntimeException(sprintf("Person \"from\" with id %d not found", $input->getOption('from')));
@@ -80,13 +107,15 @@ class ChillPersonMoveCommand extends ContainerAwareCommand
throw new RuntimeException(sprintf("Person \"to\" with id %d not found", $input->getOption('to')));
}
- $sqls = $this->mover->getSQL($from, $to);
+ $sqls = $this->mover->getSQL($from, $to, $deleteEntities);
if ($input->getOption('dump-sql')) {
foreach($sqls as $sql) {
$output->writeln($sql);
}
} else {
+ $ctxt = $this->buildLoggingContext($from, $to, $deleteEntities, $sqls);
+ $this->chillLogger->notice("Trying to move a person from command line", $ctxt);
$connection = $this->em->getConnection();
$connection->beginTransaction();
foreach($sqls as $sql) {
@@ -97,7 +126,25 @@ class ChillPersonMoveCommand extends ContainerAwareCommand
}
$connection->commit();
+ $this->chillLogger->notice("Move a person from command line succeeded", $ctxt);
}
}
+
+ protected function buildLoggingContext(Person $from, Person $to, $deleteEntities, $sqls)
+ {
+ $ctxt = [
+ 'from' => $from->getId(),
+ 'to' => $to->getId()
+ ];
+
+ foreach ($deleteEntities as $key => $de) {
+ $ctxt['delete_entity_'.$key] = $de;
+ }
+ foreach ($sqls as $key => $sql) {
+ $ctxt['sql_'.$key] = $sql;
+ }
+
+ return $ctxt;
+ }
}
diff --git a/Resources/config/services/actions.yml b/Resources/config/services/actions.yml
index 22b50b1f8..e4c6c6621 100644
--- a/Resources/config/services/actions.yml
+++ b/Resources/config/services/actions.yml
@@ -1,4 +1,5 @@
services:
Chill\PersonBundle\Actions\Remove\PersonMove:
arguments:
- $em: '@Doctrine\ORM\EntityManagerInterface'
\ No newline at end of file
+ $em: '@Doctrine\ORM\EntityManagerInterface'
+ $eventDispatcher: '@Symfony\Component\EventDispatcher\EventDispatcherInterface'
\ No newline at end of file
diff --git a/Resources/config/services/command.yml b/Resources/config/services/command.yml
index fec8bdfd4..59a2f984a 100644
--- a/Resources/config/services/command.yml
+++ b/Resources/config/services/command.yml
@@ -13,5 +13,6 @@ services:
arguments:
$em: '@Doctrine\ORM\EntityManagerInterface'
$mover: '@Chill\PersonBundle\Actions\Remove\PersonMove'
+ $chillLogger: '@chill.main.logger'
tags:
- { name: console.command }