src_legacy/Tionvel/WorkflowBundle/Entity/WorkflowProcess.php line 22

Open in your IDE?
  1. <?php
  2. namespace Tionvel\WorkflowBundle\Entity;
  3. use App\Entity\User;
  4. use DateTime;
  5. use Doctrine\Common\Collections\ArrayCollection;
  6. use Doctrine\Common\Collections\Collection;
  7. use Doctrine\Common\Collections\Criteria;
  8. use Doctrine\ORM\Mapping as ORM;
  9. use Exception;
  10. use Symfony\Component\Security\Core\User\UserInterface;
  11. use Tionvel\WorkflowBundle\Utils\AttributeResolver;
  12. use Tionvel\WorkflowBundle\Utils\Dot;
  13. /**
  14. * Class WorkflowProcess
  15. * @package Tionvel\WorkflowBundle\Entity
  16. * @ORM\Entity(repositoryClass="Tionvel\WorkflowBundle\Repository\WorkflowProcessRepository")
  17. * @ORM\Table(name="workflow_processes")
  18. */
  19. class WorkflowProcess
  20. {
  21. /**
  22. * @var int
  23. * @ORM\Id @ORM\Column(type="integer")
  24. * @ORM\GeneratedValue(strategy="AUTO")
  25. */
  26. private $id;
  27. /**
  28. * @ORM\Column(type="string", nullable=true)
  29. */
  30. private $uuid;
  31. /**
  32. * @var string
  33. * @ORM\Column(type="string")
  34. */
  35. private $workflow;
  36. /**
  37. * @var string
  38. * @ORM\Column(type="string", nullable=true)
  39. */
  40. private $subWorkflow;
  41. /**
  42. * @var User
  43. * @ORM\ManyToOne(targetEntity="App\Entity\User")
  44. * @ORM\JoinColumn(onDelete="CASCADE")
  45. */
  46. private $owner;
  47. /**
  48. * @var ArrayCollection
  49. * @ORM\OneToMany(
  50. * targetEntity="Tionvel\WorkflowBundle\Entity\WorkflowState",
  51. * mappedBy="process",
  52. * fetch="LAZY",
  53. * cascade={"persist"},
  54. * orphanRemoval=false
  55. * )
  56. */
  57. private $states;
  58. /**
  59. * @var ArrayCollection
  60. * @ORM\OneToMany(
  61. * targetEntity="Tionvel\WorkflowBundle\Entity\WorkflowAttribute",
  62. * mappedBy="process",
  63. * fetch="LAZY",
  64. * cascade={"persist"},
  65. * orphanRemoval=false
  66. * )
  67. */
  68. private $attributes;
  69. /**
  70. * @var Dot
  71. */
  72. private $resolvedAttrs;
  73. /**
  74. * WorkflowProcess constructor.
  75. * @throws Exception
  76. */
  77. public function __construct()
  78. {
  79. $this->attributes = new ArrayCollection;
  80. $this->states = new ArrayCollection;
  81. $this->resolvedAttrs = new Dot;
  82. }
  83. /**
  84. * @param $workflowId
  85. * @param User $user
  86. * @return WorkflowProcess
  87. * @throws Exception
  88. */
  89. public static function init($workflowId, User $user)
  90. {
  91. list (
  92. $workflowId,
  93. $subWorkflowId
  94. ) = explode('/', $workflowId);
  95. $self = new self;
  96. $self->setOwner($user);
  97. $self->setWorkflow($workflowId);
  98. $self->setSubWorkflow($subWorkflowId);
  99. $self->addState(WorkflowState::start($self, $user));
  100. $self->addAssignee('owner', $user);
  101. return $self;
  102. }
  103. /**
  104. * @param $state
  105. * @param User $user
  106. * @param string $transition
  107. * @throws Exception
  108. */
  109. public function transition($state, $user, $transition)
  110. {
  111. $newState = WorkflowState::start($this, $user, $state, $transition);
  112. $currentState = $this->getCurrentState();
  113. $currentState->finish($newState, $user, $transition);
  114. $newState->setPrev($currentState);
  115. $this->addState($newState);
  116. }
  117. /**
  118. * @return string
  119. */
  120. public function getId()
  121. {
  122. return $this->id;
  123. }
  124. public function getUuid()
  125. {
  126. return $this->uuid;
  127. }
  128. public function setUuid($uuid)
  129. {
  130. $this->uuid = $uuid;
  131. }
  132. /**
  133. * @return string
  134. */
  135. public function getWorkflow()
  136. {
  137. return $this->workflow;
  138. }
  139. /**
  140. * @param string $workflow
  141. */
  142. public function setWorkflow(string $workflow)
  143. {
  144. $this->workflow = $workflow;
  145. }
  146. /**
  147. * @return User
  148. */
  149. public function getOwner()
  150. {
  151. return $this->owner;
  152. }
  153. /**
  154. * @param User $owner
  155. */
  156. public function setOwner(User $owner)
  157. {
  158. $this->owner = $owner;
  159. }
  160. /**
  161. * @return ArrayCollection
  162. */
  163. public function getStates()
  164. {
  165. return $this->states;
  166. }
  167. /**
  168. * @param ArrayCollection $states
  169. */
  170. public function setStates($states)
  171. {
  172. $this->states = $states;
  173. }
  174. /**
  175. * @param WorkflowState $state
  176. */
  177. public function addState(WorkflowState $state)
  178. {
  179. if (!$this->states->contains($state)) {
  180. $this->states[] = $state;
  181. $state->setProcess($this);
  182. }
  183. }
  184. /**
  185. * @param string $key
  186. * @return bool
  187. */
  188. public function hasStateByKey(string $key)
  189. {
  190. return (bool) !$this->states->filter(function (WorkflowState $state) use ($key) {
  191. return $state->isState($key);
  192. })->isEmpty();
  193. }
  194. /**
  195. * @param string $key
  196. * @return ArrayCollection|Collection
  197. */
  198. public function getStateByKey(string $key)
  199. {
  200. return $this->states->filter(function (WorkflowState $state) use ($key) {
  201. return $state->isState($key);
  202. })->last();
  203. }
  204. /**
  205. * @return WorkflowState
  206. * @throws Exception
  207. */
  208. public function getCurrentState()
  209. {
  210. if ($state = $this->states->last()) {
  211. return $state;
  212. }
  213. return WorkflowState::start($this, $this->owner);
  214. }
  215. /**
  216. * @return string
  217. * @throws Exception
  218. */
  219. public function getState()
  220. {
  221. return $this->getCurrentState()->getState();
  222. }
  223. /**
  224. * @param $state
  225. * @return bool
  226. */
  227. public function inState($state)
  228. {
  229. /** @var WorkflowState $current */
  230. if (!$current = $this->states->last()) {
  231. return false;
  232. }
  233. return $current->isState($state);
  234. }
  235. /**
  236. * @return bool
  237. */
  238. public function isStarted()
  239. {
  240. return !$this->inState(WorkflowState::START_STATE);
  241. }
  242. /**
  243. * @return DateTime
  244. * @throws Exception
  245. */
  246. public function getStartedAt()
  247. {
  248. if ($state = $this->getStates()->first()) {
  249. return $state->getStartedAt();
  250. }
  251. return WorkflowState::start($this, $this->owner)->getStartedAt();
  252. }
  253. /**
  254. * @return DateTime
  255. * @throws Exception
  256. */
  257. public function getStartCompletedAt(): ?DateTime
  258. {
  259. if ($state = $this->getStates()->first()) {
  260. return $state->getCompletedAt();
  261. }
  262. return null;
  263. }
  264. /**
  265. * @return ArrayCollection
  266. */
  267. public function getAttributes()
  268. {
  269. return $this->attributes;
  270. }
  271. /**
  272. * @param ArrayCollection $attributes
  273. */
  274. public function setAttributes(ArrayCollection $attributes)
  275. {
  276. $this->attributes = $attributes;
  277. }
  278. /**
  279. * @return array
  280. */
  281. public function getArrayAttributes()
  282. {
  283. $attributes = [];
  284. /** @var WorkflowAttribute $attribute */
  285. foreach ($this->attributes as $attribute) {
  286. $attributes[$attribute->getPath()] = $attribute->toArray();
  287. }
  288. return $attributes;
  289. }
  290. /**
  291. * @param array $attributes
  292. */
  293. public function setResolvedAttributes(array $attributes = [])
  294. {
  295. $this->resolvedAttrs = Dot::create($attributes);
  296. }
  297. /**
  298. * @return Dot
  299. */
  300. public function getResolvedAttributes()
  301. {
  302. return $this->resolvedAttrs;
  303. }
  304. /**
  305. * @return array
  306. */
  307. public function attrs()
  308. {
  309. return $this->resolvedAttrs->all();
  310. }
  311. /**
  312. * @param $path
  313. * @param $default
  314. * @return mixed
  315. */
  316. public function attr($path, $default = null)
  317. {
  318. return $this->resolvedAttrs->get($path, $default);
  319. }
  320. /**
  321. * @param $path
  322. * @return array
  323. */
  324. public function arrayAttr($path)
  325. {
  326. $attr = $this->attr($path, []);
  327. if ((is_array($attr) && isset($attr['created_at']) || !is_array($attr))) {
  328. return [$attr];
  329. }
  330. return $attr;
  331. }
  332. /**
  333. * @param $path
  334. * @return array
  335. */
  336. public function arrayAttrList($path)
  337. {
  338. $attr = $this->attr($path, []);
  339. if (count($attr) > 1 ) {
  340. return $attr;
  341. }
  342. return [$attr];
  343. }
  344. /**
  345. * @param $path
  346. * @return bool
  347. */
  348. public function has($path)
  349. {
  350. return $this->resolvedAttrs->has($path);
  351. }
  352. /**
  353. * @param $path
  354. * @param null $default
  355. * @return mixed
  356. */
  357. public function first($path, $default = null)
  358. {
  359. $attr = $this->attr($path, $default);
  360. return is_array($attr) ? reset($attr) : $attr;
  361. }
  362. /**
  363. * @param $path
  364. * @param null $default
  365. * @return mixed
  366. */
  367. public function last($path, $default = null)
  368. {
  369. $attr = $this->attr($path, $default);
  370. return is_array($attr) ? end($attr) : $attr;
  371. }
  372. /**
  373. * @param WorkflowAttribute $attribute
  374. */
  375. public function addAttribute(WorkflowAttribute $attribute)
  376. {
  377. if (!$this->attributes->contains($attribute)) {
  378. $this->attributes->add($attribute);
  379. $attribute->setProcess($this);
  380. }
  381. }
  382. public function removeAttr($key)
  383. {
  384. $this->attributes->matching(
  385. Criteria::create()->where(Criteria::expr()->eq('key0', $key))
  386. )->map(function (WorkflowAttribute $attribute) {
  387. $this->attributes->removeElement($attribute);
  388. $attribute->setProcess(null);
  389. });
  390. }
  391. public function removeAttrByPath($path)
  392. {
  393. $keys = explode('.',$path);
  394. $criteria = Criteria::create();
  395. foreach ($keys as $i => $key) {
  396. $expr = Criteria::expr()->eq('key'.$i, $key);
  397. $i == 0 ? $criteria->where($expr) : $criteria->andWhere($expr);
  398. }
  399. $this->attributes->matching($criteria)->map(function (WorkflowAttribute $attribute) {
  400. $this->attributes->removeElement($attribute);
  401. $attribute->setProcess(null);
  402. });
  403. }
  404. /**
  405. * @param User $user
  406. * @return bool
  407. * @throws Exception
  408. */
  409. public function userIsAssignee(User $user)
  410. {
  411. return $user === $this->owner || $this->hasAssignee($user);
  412. }
  413. /**
  414. * @param User $user
  415. * @param $key
  416. * @return bool
  417. * @throws Exception
  418. */
  419. public function userIs(UserInterface $user, $key)
  420. {
  421. return !$this->getCurrentState()
  422. ->getAssignees()
  423. ->filter(function (WorkflowAssignee $assignee) use ($user, $key) {
  424. return $assignee->isUser($user) && $assignee->isType($key);
  425. })
  426. ->isEmpty()
  427. ;
  428. }
  429. /**
  430. *
  431. * @param User $user
  432. * @return array
  433. * @throws Exception
  434. */
  435. public function getUserRole(UserInterface $user)
  436. {
  437. $roles = $this->getCurrentState()
  438. ->getAssignees()
  439. ->filter(function (WorkflowAssignee $assignee) use ($user) {
  440. return $assignee->isUser($user);
  441. })
  442. ->map(function (WorkflowAssignee $assignee) {
  443. return $assignee->getName();
  444. })
  445. ->toArray()
  446. ;
  447. return array_unique($roles);
  448. }
  449. /**
  450. * @param User $user
  451. * @return bool
  452. * @throws Exception
  453. */
  454. public function hasAssignee(User $user)
  455. {
  456. return !$this->getCurrentState()
  457. ->getAssignees()
  458. ->filter(function (WorkflowAssignee $assignee) use ($user) {
  459. return $assignee->isUser($user);
  460. })
  461. ->isEmpty()
  462. ;
  463. }
  464. /**
  465. * @param $key
  466. * @param User $user
  467. * @throws Exception
  468. */
  469. public function addAssignee($key, User $user)
  470. {
  471. $this->getCurrentState()->addAssignee(WorkflowAssignee::create($user, $key));
  472. }
  473. public function removeAssigneeAsRole(User $user, $role)
  474. {
  475. foreach ($this->getAssignees() as $assignee) {
  476. if ($assignee->isType($role) && $assignee->isUser($user)) {
  477. $this->getAssignees()->removeElement($assignee);
  478. }
  479. }
  480. }
  481. /**
  482. * @param $key
  483. * @throws Exception
  484. */
  485. public function removeAssigneeByKey($key)
  486. {
  487. $this->getCurrentState()->removeAssigneeByKey($key);
  488. }
  489. /**
  490. * @return ArrayCollection|WorkflowAssignee[]
  491. * @throws Exception
  492. */
  493. public function getAssignees()
  494. {
  495. return $this->getCurrentState()->getAssignees();
  496. }
  497. public function getOldAssigneesArray() {
  498. $assignees = [];
  499. $states = $this->getStates()->filter(function(WorkflowState $state) {
  500. return !$state->isCurrent();
  501. });
  502. /** @var WorkflowState $state */
  503. foreach ($states as $state) {
  504. foreach ($state->getAssignees() as $assignee) {
  505. $assignees[] = [
  506. 'username' => $assignee->getUser()->getUserIdentifier(),
  507. 'user' => $assignee->getUser()->getId(),
  508. 'role' => $assignee->getName()
  509. ];
  510. }
  511. }
  512. return array_unique($assignees, SORT_REGULAR);
  513. }
  514. public function getCurrentAssigneesArray() {
  515. $assignees = [];
  516. $currentAssignees = $this->getCurrentState()->getAssignees();
  517. foreach($currentAssignees as $assignee) {
  518. $assignees[] = [
  519. 'username' => $assignee->getUser()->getUserIdentifier(),
  520. 'user' => $assignee->getUser()->getId(),
  521. 'role' => $assignee->getName()
  522. ];
  523. }
  524. return $assignees;
  525. }
  526. public function getDiffCurrentAssignees() {
  527. $currentAssignees = $this->getCurrentAssigneesArray();
  528. $oldAssignees = $this->getOldAssigneesArray();
  529. foreach ($currentAssignees as $key => $a) {
  530. foreach($oldAssignees as $b) {
  531. if($a['role'] == $b['role'] && $a['user'] == $b['user']) {
  532. unset($currentAssignees[$key]);
  533. }
  534. }
  535. }
  536. return $currentAssignees;
  537. }
  538. /**
  539. * @return array
  540. * @throws Exception
  541. */
  542. public function getRolesMap()
  543. {
  544. $rolesMap = [];
  545. /** @var WorkflowAssignee $assignee */
  546. foreach ($this->getAssignees() as $assignee) {
  547. $role = $assignee->getName();
  548. $user = $assignee->getUser();
  549. if (!isset($rolesMap[$role])) {
  550. $rolesMap[$role] = [];
  551. }
  552. $rolesMap[$role][] = $user;
  553. }
  554. return array_map(function ($i) {
  555. return count($i) === 1 ? $i[0] : $i;
  556. }, $rolesMap);
  557. }
  558. /**
  559. * @param DateTime $timeout
  560. * @throws Exception
  561. */
  562. public function setTimeout(DateTime $timeout)
  563. {
  564. $this->getCurrentState()->setDeadline($timeout);
  565. }
  566. /**
  567. * @return string
  568. */
  569. public function getSubWorkflow()
  570. {
  571. return $this->subWorkflow;
  572. }
  573. /**
  574. * @param string $subWorkflow
  575. */
  576. public function setSubWorkflow($subWorkflow)
  577. {
  578. $this->subWorkflow = $subWorkflow;
  579. }
  580. }