mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-06-07 18:44:08 +00:00
441 lines
10 KiB
PHP
441 lines
10 KiB
PHP
<?php
|
|
|
|
/**
|
|
* 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.
|
|
*/
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace Chill\MainBundle\Entity\Workflow;
|
|
|
|
use Chill\MainBundle\Doctrine\Model\TrackCreationInterface;
|
|
use Chill\MainBundle\Doctrine\Model\TrackCreationTrait;
|
|
use Chill\MainBundle\Doctrine\Model\TrackUpdateInterface;
|
|
use Chill\MainBundle\Doctrine\Model\TrackUpdateTrait;
|
|
use Chill\MainBundle\Entity\User;
|
|
use Chill\MainBundle\Workflow\Validator\EntityWorkflowCreation;
|
|
use DateTimeInterface;
|
|
use Doctrine\Common\Collections\ArrayCollection;
|
|
use Doctrine\Common\Collections\Collection;
|
|
use Doctrine\ORM\Mapping as ORM;
|
|
use Iterator;
|
|
use RuntimeException;
|
|
use Symfony\Component\Serializer\Annotation as Serializer;
|
|
use function count;
|
|
|
|
/**
|
|
* @ORM\Entity
|
|
* @ORM\Table("chill_main_workflow_entity")
|
|
* @EntityWorkflowCreation(groups={"creation"})
|
|
* @Serializer\DiscriminatorMap(typeProperty="type", mapping={
|
|
* "entity_workflow": EntityWorkflow::class
|
|
* })
|
|
*/
|
|
class EntityWorkflow implements TrackCreationInterface, TrackUpdateInterface
|
|
{
|
|
use TrackCreationTrait;
|
|
|
|
use TrackUpdateTrait;
|
|
|
|
/**
|
|
* @ORM\OneToMany(targetEntity=EntityWorkflowComment::class, mappedBy="entityWorkflow", orphanRemoval=true)
|
|
*
|
|
* @var Collection|EntityWorkflowComment[]
|
|
*/
|
|
private Collection $comments;
|
|
|
|
/**
|
|
* @ORM\Id
|
|
* @ORM\GeneratedValue
|
|
* @ORM\Column(type="integer")
|
|
* @Serializer\Groups({"read"})
|
|
*/
|
|
private ?int $id = null;
|
|
|
|
/**
|
|
* @ORM\Column(type="string", length=255)
|
|
*/
|
|
private string $relatedEntityClass = '';
|
|
|
|
/**
|
|
* @ORM\Column(type="integer")
|
|
*/
|
|
private int $relatedEntityId;
|
|
|
|
/**
|
|
* @ORM\OneToMany(targetEntity=EntityWorkflowStep::class, mappedBy="entityWorkflow", orphanRemoval=true, cascade={"persist"})
|
|
* @ORM\OrderBy({"transitionAt": "ASC", "id": "ASC"})
|
|
*
|
|
* @var Collection|EntityWorkflowStep[]
|
|
*/
|
|
private Collection $steps;
|
|
|
|
/**
|
|
* @ORM\ManyToMany(targetEntity=User::class)
|
|
* @ORM\JoinTable(name="chill_main_workflow_entity_subscriber_to_final")
|
|
*
|
|
* @var Collection|User[]
|
|
*/
|
|
private Collection $subscriberToFinal;
|
|
|
|
/**
|
|
* @ORM\ManyToMany(targetEntity=User::class)
|
|
* @ORM\JoinTable(name="chill_main_workflow_entity_subscriber_to_step")
|
|
*
|
|
* @var Collection|User[]
|
|
*/
|
|
private Collection $subscriberToStep;
|
|
|
|
/**
|
|
* a step which will store all the transition data.
|
|
*/
|
|
private ?EntityWorkflowStep $transitionningStep = null;
|
|
|
|
/**
|
|
* @ORM\Column(type="text")
|
|
*/
|
|
private string $workflowName;
|
|
|
|
public function __construct()
|
|
{
|
|
$this->subscriberToFinal = new ArrayCollection();
|
|
$this->subscriberToStep = new ArrayCollection();
|
|
$this->comments = new ArrayCollection();
|
|
$this->steps = new ArrayCollection();
|
|
|
|
$initialStep = new EntityWorkflowStep();
|
|
$initialStep
|
|
->setCurrentStep('initial');
|
|
$this->addStep($initialStep);
|
|
}
|
|
|
|
public function addComment(EntityWorkflowComment $comment): self
|
|
{
|
|
if (!$this->comments->contains($comment)) {
|
|
$this->comments[] = $comment;
|
|
$comment->setEntityWorkflow($this);
|
|
}
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* @internal You should prepare a step and run a workflow transition instead of manually adding a step
|
|
*/
|
|
public function addStep(EntityWorkflowStep $step): self
|
|
{
|
|
if (!$this->steps->contains($step)) {
|
|
$this->steps[] = $step;
|
|
$step->setEntityWorkflow($this);
|
|
|
|
if ($this->isFinalize()) {
|
|
$step->setFinalizeAfter(true);
|
|
}
|
|
}
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function addSubscriberToFinal(User $user): self
|
|
{
|
|
if (!$this->subscriberToFinal->contains($user)) {
|
|
$this->subscriberToFinal[] = $user;
|
|
}
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function addSubscriberToStep(User $user): self
|
|
{
|
|
if (!$this->subscriberToStep->contains($user)) {
|
|
$this->subscriberToStep[] = $user;
|
|
}
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getComments(): Collection
|
|
{
|
|
return $this->comments;
|
|
}
|
|
|
|
public function getCurrentStep(): ?EntityWorkflowStep
|
|
{
|
|
$step = $this->steps->last();
|
|
|
|
if (false !== $step) {
|
|
return $step;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public function getCurrentStepCreatedAt(): ?DateTimeInterface
|
|
{
|
|
if (null !== $previous = $this->getPreviousStepIfAny()) {
|
|
return $previous->getTransitionAt();
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public function getCurrentStepCreatedBy(): ?User
|
|
{
|
|
if (null !== $previous = $this->getPreviousStepIfAny()) {
|
|
return $previous->getTransitionBy();
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public function getId(): ?int
|
|
{
|
|
return $this->id;
|
|
}
|
|
|
|
public function getRelatedEntityClass(): string
|
|
{
|
|
return $this->relatedEntityClass;
|
|
}
|
|
|
|
public function getRelatedEntityId(): int
|
|
{
|
|
return $this->relatedEntityId;
|
|
}
|
|
|
|
/**
|
|
* Method used by MarkingStore.
|
|
*
|
|
* get a string representation of the step
|
|
*/
|
|
public function getStep(): string
|
|
{
|
|
return $this->getCurrentStep()->getCurrentStep();
|
|
}
|
|
|
|
public function getStepAfter(EntityWorkflowStep $step): ?EntityWorkflowStep
|
|
{
|
|
$iterator = $this->steps->getIterator();
|
|
|
|
if ($iterator instanceof Iterator) {
|
|
$iterator->rewind();
|
|
|
|
while ($iterator->valid()) {
|
|
$curStep = $iterator->current();
|
|
|
|
if ($curStep === $step) {
|
|
$iterator->next();
|
|
|
|
if ($iterator->valid()) {
|
|
return $iterator->current();
|
|
}
|
|
|
|
return null;
|
|
}
|
|
$iterator->next();
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
throw new RuntimeException();
|
|
}
|
|
|
|
/**
|
|
* @return ArrayCollection|Collection
|
|
*/
|
|
public function getSteps()
|
|
{
|
|
return $this->steps;
|
|
}
|
|
|
|
public function getStepsChained(): array
|
|
{
|
|
$iterator = $this->steps->getIterator();
|
|
$previous = $next = $current = null;
|
|
$steps = [];
|
|
|
|
$iterator->rewind();
|
|
|
|
while ($iterator->valid()) {
|
|
$previous = $current;
|
|
$steps[] = $current = $iterator->current();
|
|
$current->setPrevious($previous);
|
|
|
|
$iterator->next();
|
|
|
|
if ($iterator->valid()) {
|
|
$next = $iterator->current();
|
|
} else {
|
|
$next = null;
|
|
}
|
|
|
|
$current->setNext($next);
|
|
}
|
|
|
|
return $steps;
|
|
}
|
|
|
|
/**
|
|
* @return ArrayCollection|Collection
|
|
*/
|
|
public function getSubscriberToFinal()
|
|
{
|
|
return $this->subscriberToFinal;
|
|
}
|
|
|
|
/**
|
|
* @return ArrayCollection|Collection
|
|
*/
|
|
public function getSubscriberToStep()
|
|
{
|
|
return $this->subscriberToStep;
|
|
}
|
|
|
|
/**
|
|
* get the step which is transitionning. Should be called only by event which will
|
|
* concern the transition.
|
|
*/
|
|
public function getTransitionningStep(): ?EntityWorkflowStep
|
|
{
|
|
return $this->transitionningStep;
|
|
}
|
|
|
|
public function getWorkflowName(): string
|
|
{
|
|
return $this->workflowName;
|
|
}
|
|
|
|
public function isFinalize(): bool
|
|
{
|
|
$steps = $this->getStepsChained();
|
|
|
|
if (1 === count($steps)) {
|
|
// the initial step cannot be finalized
|
|
return false;
|
|
}
|
|
|
|
/** @var EntityWorkflowStep $last */
|
|
$last = end($steps);
|
|
|
|
return $last->getPrevious()->isFinalizeAfter();
|
|
}
|
|
|
|
public function isFreeze(): bool
|
|
{
|
|
$steps = $this->getStepsChained();
|
|
|
|
if (1 === count($steps)) {
|
|
// the initial step cannot be finalized
|
|
return false;
|
|
}
|
|
|
|
/** @var EntityWorkflowStep $last */
|
|
$last = end($steps);
|
|
|
|
return $last->getPrevious()->isFreezeAfter();
|
|
}
|
|
|
|
public function isUserSubscribedToFinal(User $user): bool
|
|
{
|
|
return $this->subscriberToFinal->contains($user);
|
|
}
|
|
|
|
public function isUserSubscribedToStep(User $user): bool
|
|
{
|
|
return $this->subscriberToStep->contains($user);
|
|
}
|
|
|
|
public function prepareStepBeforeTransition(EntityWorkflowStep $step): self
|
|
{
|
|
$this->transitionningStep = $step;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function removeComment(EntityWorkflowComment $comment): self
|
|
{
|
|
if ($this->comments->removeElement($comment)) {
|
|
$comment->setEntityWorkflow(null);
|
|
}
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function removeStep(EntityWorkflowStep $step): self
|
|
{
|
|
if ($this->steps->removeElement($step)) {
|
|
$step->setEntityWorkflow(null);
|
|
}
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function removeSubscriberToFinal(User $user): self
|
|
{
|
|
$this->subscriberToFinal->removeElement($user);
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function removeSubscriberToStep(User $user): self
|
|
{
|
|
$this->subscriberToStep->removeElement($user);
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function setRelatedEntityClass(string $relatedEntityClass): EntityWorkflow
|
|
{
|
|
$this->relatedEntityClass = $relatedEntityClass;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function setRelatedEntityId(int $relatedEntityId): EntityWorkflow
|
|
{
|
|
$this->relatedEntityId = $relatedEntityId;
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Method use by marking store.
|
|
*
|
|
* @return $this
|
|
*/
|
|
public function setStep(string $step): self
|
|
{
|
|
$newStep = new EntityWorkflowStep();
|
|
$newStep->setCurrentStep($step);
|
|
|
|
// copy the freeze
|
|
if ($this->getCurrentStep()->isFreezeAfter()) {
|
|
$newStep->setFreezeAfter(true);
|
|
}
|
|
|
|
$this->addStep($newStep);
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function setWorkflowName(string $workflowName): EntityWorkflow
|
|
{
|
|
$this->workflowName = $workflowName;
|
|
|
|
return $this;
|
|
}
|
|
|
|
private function getPreviousStepIfAny(): ?EntityWorkflowStep
|
|
{
|
|
if (1 === count($this->steps)) {
|
|
return null;
|
|
}
|
|
|
|
return $this->steps->get($this->steps->count() - 2);
|
|
}
|
|
}
|