src/App/Entity/ConversationMessage/ConversationMessage.php line 45

Open in your IDE?
  1. <?php
  2. namespace App\Entity\ConversationMessage;
  3. use App\Entity\AutomatedConversationMessagesMailing;
  4. use App\Entity\DirectEmailCommunication\IncomingDirectEmailCommunicationMessage;
  5. use App\Entity\DirectEmailCommunication\OutgoingDirectEmailCommunicationMessage;
  6. use App\Entity\MobileApp\PushNotification;
  7. use App\Entity\MultiConversationMessagesMailing;
  8. use App\Entity\OccupationalField;
  9. use App\Entity\Profile;
  10. use App\Entity\Profile\JoboffererProfile;
  11. use App\Entity\Profile\JobseekerProfile;
  12. use App\Entity\RecurrentJob;
  13. use App\Entity\Search\AbstractSearchBlockDefinition;
  14. use App\Entity\WantedJob;
  15. use App\Utility\DateTimeUtility;
  16. use App\Utility\GuidUtility;
  17. use DateTime;
  18. use Doctrine\Common\Collections\ArrayCollection;
  19. use Doctrine\Common\Collections\Collection;
  20. use Doctrine\ORM\Mapping as ORM;
  21. use Exception;
  22. use InvalidArgumentException;
  23. use Symfony\Component\Validator\Constraints as Assert;
  24. /**
  25. * @ORM\Entity
  26. *
  27. * @ORM\Table(
  28. * name="conversation_messages",
  29. * indexes={
  30. *
  31. * @ORM\Index(name="jobseeker_profiles_id_sent_at_idx", columns={"jobseeker_profiles_id", "sent_at"}),
  32. * @ORM\Index(name="jobofferer_profiles_id_sent_at_idx", columns={"jobofferer_profiles_id", "sent_at"}),
  33. * @ORM\Index(name="is_deleted_by_receiver_is_read_sent_at_idx", columns={"is_deleted_by_receiver", "is_read", "sent_at"}),
  34. * @ORM\Index(name="jobseeker_profiles_id_unread_idx", columns={"jobseeker_profiles_id", "sender", "is_read", "is_deleted_by_receiver", "sent_at"}),
  35. * @ORM\Index(name="jobofferer_profiles_id_unread_idx", columns={"jobofferer_profiles_id", "sender", "is_read", "is_deleted_by_receiver", "sent_at"}),
  36. * @ORM\Index(name="read_deleted_by_receiver_automated_mailing_id_idx", columns={"is_read", "is_deleted_by_receiver", "automated_conversation_messages_mailings_id"})
  37. * }
  38. * )
  39. *
  40. * @ORM\Entity(repositoryClass="App\Repository\ConversationMessageRepository")
  41. */
  42. class ConversationMessage
  43. {
  44. public const SENDER_JOBSEEKER = 0;
  45. public const SENDER_JOBOFFERER = 1;
  46. public const ASSERTION_SUBJECT_MIN_LENGTH = 2;
  47. public const ASSERTION_SUBJECT_MAX_LENGTH = 255;
  48. public const ASSERTION_BODY_MIN_LENGTH = 2;
  49. public const ASSERTION_BODY_MAX_LENGTH = 8192;
  50. public const MESSAGE_TYPE_PROFILE_MESSAGE = 1;
  51. public const MESSAGE_TYPE_AUTOMATED_JOBRADAR_MESSAGE = 2;
  52. public const MESSAGE_TYPE_OTHER_AUTOMATED_MESSAGE = 3;
  53. public function __construct()
  54. {
  55. $this->subject = '';
  56. $this->body = '';
  57. $this->read = false;
  58. $this->deletedBySender = false;
  59. $this->deletedByReceiver = false;
  60. $this->deletedBySenderAt = null;
  61. $this->deletedByReceiverAt = null;
  62. $this->sentAt = DateTimeUtility::createDateTimeUtc();
  63. $this->isDirectApplied = false;
  64. $this->conversationMessageAttachmentFiles = new ArrayCollection();
  65. $this->occupationalField = null;
  66. $this->relatedConversationMessage = null;
  67. $this->relatedWantedJob = null;
  68. $this->relatedRecurrentJob = null;
  69. }
  70. /**
  71. * @var string
  72. *
  73. * @ORM\GeneratedValue(strategy="CUSTOM")
  74. *
  75. * @ORM\CustomIdGenerator(class="App\Utility\DatabaseIdGenerator")
  76. *
  77. * @ORM\Column(name="id", type="guid")
  78. *
  79. * @ORM\Id
  80. */
  81. private $id;
  82. public function setId(string $id)
  83. {
  84. GuidUtility::validOrThrow($id);
  85. $this->id = $id;
  86. }
  87. public function getId()
  88. {
  89. return $this->id;
  90. }
  91. /**
  92. * @var JobseekerProfile
  93. *
  94. * @ORM\ManyToOne(targetEntity="App\Entity\Profile\JobseekerProfile", inversedBy="conversationMessages", cascade={"persist"})
  95. *
  96. * @ORM\JoinColumn(name="jobseeker_profiles_id", referencedColumnName="id", nullable=false, onDelete="CASCADE")
  97. */
  98. private $jobseekerProfile;
  99. public function getJobseekerProfile(): JobseekerProfile
  100. {
  101. return $this->jobseekerProfile;
  102. }
  103. public function setJobseekerProfile(JobseekerProfile $jobseekerProfile)
  104. {
  105. $this->jobseekerProfile = $jobseekerProfile;
  106. }
  107. /**
  108. * @var JoboffererProfile
  109. *
  110. * @ORM\ManyToOne(targetEntity="App\Entity\Profile\JoboffererProfile", inversedBy="conversationMessages", cascade={"persist"})
  111. *
  112. * @ORM\JoinColumn(name="jobofferer_profiles_id", referencedColumnName="id", nullable=false, onDelete="CASCADE")
  113. */
  114. private $joboffererProfile;
  115. public function getJoboffererProfile(): JoboffererProfile
  116. {
  117. return $this->joboffererProfile;
  118. }
  119. public function setJoboffererProfile(JoboffererProfile $joboffererProfile)
  120. {
  121. $this->joboffererProfile = $joboffererProfile;
  122. }
  123. /**
  124. * @var int
  125. *
  126. * @ORM\Column(name="sender", type="smallint", nullable=false)
  127. */
  128. private $sender;
  129. public function getSender(): int
  130. {
  131. return $this->sender;
  132. }
  133. /** @throws Exception */
  134. public function setSender(int $sender): void
  135. {
  136. if (!($sender === self::SENDER_JOBSEEKER || $sender === self::SENDER_JOBOFFERER)) {
  137. throw new Exception('sender must be 0 or 1.');
  138. }
  139. $this->sender = $sender;
  140. }
  141. /**
  142. * @var AutomatedConversationMessagesMailing
  143. *
  144. * @ORM\ManyToOne(targetEntity="App\Entity\AutomatedConversationMessagesMailing", inversedBy="conversationMessages", cascade={"persist"}, fetch="EXTRA_LAZY")
  145. *
  146. * @ORM\JoinColumn(name="automated_conversation_messages_mailings_id", referencedColumnName="id", nullable=true, onDelete="CASCADE")
  147. */
  148. private $automatedConversationMessagesMailing;
  149. public function getAutomatedConversationMessagesMailing(): ?AutomatedConversationMessagesMailing
  150. {
  151. return $this->automatedConversationMessagesMailing;
  152. }
  153. public function setAutomatedConversationMessagesMailing(AutomatedConversationMessagesMailing $automatedConversationMessagesMailing): void
  154. {
  155. $this->automatedConversationMessagesMailing = $automatedConversationMessagesMailing;
  156. }
  157. /** @var string|null
  158. * @ORM\Column(name="block_in_which_acmm_found_result", type="string", length=255, nullable=true)
  159. */
  160. private $blockNameInWhichAutomatedConversationMessagesMailingFoundResult;
  161. public function getBlockNameInWhichAutomatedConversationMessagesMailingFoundResult(): ?string
  162. {
  163. return $this->blockNameInWhichAutomatedConversationMessagesMailingFoundResult;
  164. }
  165. public function setBlockNameInWhichAutomatedConversationMessagesMailingFoundResult(?AbstractSearchBlockDefinition $blockInWhichAutomatedConversationMessagesMailingFoundResult): void
  166. {
  167. if (!is_null($blockInWhichAutomatedConversationMessagesMailingFoundResult)) {
  168. $this->blockNameInWhichAutomatedConversationMessagesMailingFoundResult = $blockInWhichAutomatedConversationMessagesMailingFoundResult->getName(
  169. );
  170. }
  171. }
  172. /**
  173. * @var MultiConversationMessagesMailing
  174. *
  175. * @ORM\ManyToOne(targetEntity="App\Entity\MultiConversationMessagesMailing", inversedBy="conversationMessages", cascade={"persist"})
  176. *
  177. * @ORM\JoinColumn(name="multi_conversation_messages_mailings_id", referencedColumnName="id", nullable=true, onDelete="SET NULL")
  178. */
  179. private $multiConversationMessagesMailing;
  180. public function getMultiConversationMessagesMailing(): ?MultiConversationMessagesMailing
  181. {
  182. return $this->multiConversationMessagesMailing;
  183. }
  184. public function setMultiConversationMessagesMailing(MultiConversationMessagesMailing $multiConversationMessagesMailing): void
  185. {
  186. $this->multiConversationMessagesMailing = $multiConversationMessagesMailing;
  187. }
  188. /**
  189. * @var OutgoingDirectEmailCommunicationMessage|null
  190. *
  191. * @ORM\OneToOne(targetEntity="App\Entity\DirectEmailCommunication\OutgoingDirectEmailCommunicationMessage", mappedBy="conversationMessage", cascade={"persist", "remove"})
  192. */
  193. private $outgoingDirectEmailCommunicationMessage;
  194. public function getOutgoingDirectEmailCommunicationMessage(): ?OutgoingDirectEmailCommunicationMessage
  195. {
  196. return $this->outgoingDirectEmailCommunicationMessage;
  197. }
  198. public function setOutgoingDirectEmailCommunicationMessage(?OutgoingDirectEmailCommunicationMessage $outgoingDirectEmailCommunicationMessage): void
  199. {
  200. $this->outgoingDirectEmailCommunicationMessage = $outgoingDirectEmailCommunicationMessage;
  201. }
  202. /**
  203. * @var IncomingDirectEmailCommunicationMessage|null
  204. *
  205. * @ORM\OneToOne(targetEntity="App\Entity\DirectEmailCommunication\IncomingDirectEmailCommunicationMessage", mappedBy="conversationMessage", cascade={"persist", "remove"})
  206. */
  207. private $incomingDirectEmailCommunicationMessage;
  208. public function getIncomingDirectEmailCommunicationMessage(): ?IncomingDirectEmailCommunicationMessage
  209. {
  210. return $this->incomingDirectEmailCommunicationMessage;
  211. }
  212. public function setIncomingDirectEmailCommunicationMessage(?IncomingDirectEmailCommunicationMessage $incomingDirectEmailCommunicationMessage): void
  213. {
  214. $this->incomingDirectEmailCommunicationMessage = $incomingDirectEmailCommunicationMessage;
  215. }
  216. /**
  217. * @var string
  218. *
  219. * @ORM\Column(name="subject", type="string", length=255, nullable=false)
  220. *
  221. * @Assert\NotBlank()
  222. *
  223. * @Assert\Length(
  224. * min = ConversationMessage::ASSERTION_SUBJECT_MIN_LENGTH,
  225. * max = ConversationMessage::ASSERTION_SUBJECT_MAX_LENGTH,
  226. * )
  227. */
  228. private $subject;
  229. public function getSubject(): string
  230. {
  231. return $this->subject;
  232. }
  233. public function setSubject(string $subject): void
  234. {
  235. $this->subject = mb_substr($subject, 0, 255);
  236. }
  237. /**
  238. * @var string
  239. *
  240. * @ORM\Column(name="body", type="string", length=8192, nullable=false)
  241. *
  242. * @Assert\NotBlank()
  243. *
  244. * @Assert\Length(
  245. * min = ConversationMessage::ASSERTION_BODY_MIN_LENGTH,
  246. * max = ConversationMessage::ASSERTION_BODY_MAX_LENGTH,
  247. * )
  248. */
  249. private $body;
  250. public function getBody(): string
  251. {
  252. return $this->body;
  253. }
  254. public function setBody(string $body): void
  255. {
  256. $this->body = mb_substr($body, 0, 8192);
  257. }
  258. /**
  259. * @var bool
  260. *
  261. * @ORM\Column(name="is_read", type="boolean", nullable=false)
  262. */
  263. private $read;
  264. public function isRead(): bool
  265. {
  266. return $this->read;
  267. }
  268. public function setRead(bool $read): void
  269. {
  270. $this->read = $read;
  271. }
  272. /**
  273. * @var bool
  274. *
  275. * @ORM\Column(name="is_deleted_by_sender", type="boolean", nullable=false)
  276. */
  277. private $deletedBySender;
  278. public function isDeletedBySender(): bool
  279. {
  280. return $this->deletedBySender;
  281. }
  282. public function setDeletedBySender(bool $deletedBySender): void
  283. {
  284. $this->deletedBySender = $deletedBySender;
  285. if ($deletedBySender === true) {
  286. $this->deletedBySenderAt = DateTimeUtility::createDateTimeUtc();
  287. } else {
  288. $this->deletedBySenderAt = null;
  289. }
  290. }
  291. /**
  292. * @var bool
  293. *
  294. * @ORM\Column(name="is_deleted_by_receiver", type="boolean", nullable=false)
  295. */
  296. private $deletedByReceiver;
  297. public function isDeletedByReceiver(): bool
  298. {
  299. return $this->deletedByReceiver;
  300. }
  301. public function setDeletedByReceiver(bool $deletedByReceiver): void
  302. {
  303. $this->deletedByReceiver = $deletedByReceiver;
  304. if ($deletedByReceiver === true) {
  305. $this->deletedByReceiverAt = DateTimeUtility::createDateTimeUtc();
  306. } else {
  307. $this->deletedByReceiverAt = null;
  308. }
  309. }
  310. /**
  311. * @var ?DateTime
  312. *
  313. * @ORM\Column(name="deleted_by_sender_at", type="datetime", nullable=true)
  314. */
  315. private $deletedBySenderAt;
  316. public function getDeletedBySenderAt(): ?DateTime
  317. {
  318. return $this->deletedBySenderAt;
  319. }
  320. /**
  321. * @var ?DateTime
  322. *
  323. * @ORM\Column(name="deleted_by_receiver_at", type="datetime", nullable=true)
  324. */
  325. private $deletedByReceiverAt;
  326. public function getDeletedByReceiverAt(): ?DateTime
  327. {
  328. return $this->deletedByReceiverAt;
  329. }
  330. /** @throws Exception */
  331. public function isDeletedByOwner(Profile $ownerProfile): bool
  332. {
  333. if ($ownerProfile->isJobseeker() && ($this->jobseekerProfile->getId() !== $ownerProfile->getId())) {
  334. throw new Exception("Message {$this->getId()} does not belong to jobseeker {$ownerProfile->getId()}.");
  335. }
  336. if ($ownerProfile->isJobofferer() && ($this->joboffererProfile->getId() !== $ownerProfile->getId())) {
  337. throw new Exception("Message {$this->getId()} does not belong to jobofferer {$ownerProfile->getId()}.");
  338. }
  339. if ($this->isDeletedBySender() && $this->getSenderProfile()->getId() === $ownerProfile->getId()) {
  340. return true;
  341. }
  342. if ($this->isDeletedByReceiver() && $this->getReceiverProfile()->getId() === $ownerProfile->getId()) {
  343. return true;
  344. }
  345. return false;
  346. }
  347. /**
  348. * @var bool
  349. *
  350. * @ORM\Column(name="is_direct_applied", type="boolean", nullable=false)
  351. */
  352. private $isDirectApplied;
  353. public function isDirectApplied(): bool
  354. {
  355. return $this->isDirectApplied;
  356. }
  357. public function setIsDirectApplied(bool $isDirectApplied): void
  358. {
  359. $this->isDirectApplied = $isDirectApplied;
  360. }
  361. /**
  362. * @var DateTime
  363. *
  364. * @ORM\Column(name="sent_at", type="datetime", nullable=false)
  365. */
  366. private $sentAt;
  367. public function getSentAt(): DateTime
  368. {
  369. return $this->sentAt;
  370. }
  371. public function setSentAt(DateTime $sentAt): void
  372. {
  373. $this->sentAt = $sentAt;
  374. }
  375. /**
  376. * @var ConversationMessageAttachmentFile|Collection
  377. *
  378. * @ORM\ManyToMany(targetEntity="App\Entity\ConversationMessage\ConversationMessageAttachmentFile", mappedBy="conversationMessages", cascade={"persist"})
  379. */
  380. protected $conversationMessageAttachmentFiles;
  381. public function setConversationMessageAttachmentFiles(Collection $conversationMessageAttachmentFiles)
  382. {
  383. $this->conversationMessageAttachmentFiles = $conversationMessageAttachmentFiles;
  384. }
  385. public function getConversationMessageAttachmentFiles(): Collection
  386. {
  387. return $this->conversationMessageAttachmentFiles;
  388. }
  389. public function isSender(Profile $profile): bool
  390. {
  391. if (($this->sender === self::SENDER_JOBSEEKER
  392. && $profile instanceof JobseekerProfile
  393. && $profile->getId() === $this->getJobseekerProfile()->getId())
  394. || ($this->sender === self::SENDER_JOBOFFERER
  395. && $profile instanceof JoboffererProfile
  396. && $profile->getId() === $this->getJoboffererProfile()->getId())
  397. ) {
  398. return true;
  399. } else {
  400. return false;
  401. }
  402. }
  403. public function isReceiver(Profile $profile): bool
  404. {
  405. if (($this->sender === self::SENDER_JOBOFFERER
  406. && $profile instanceof JobseekerProfile
  407. && $profile->getId() === $this->getJobseekerProfile()->getId())
  408. || ($this->sender === self::SENDER_JOBSEEKER
  409. && $profile instanceof JoboffererProfile
  410. && $profile->getId() === $this->getJoboffererProfile()->getId())
  411. ) {
  412. return true;
  413. } else {
  414. return false;
  415. }
  416. }
  417. /** @throws Exception */
  418. public function getOppositeProfile(Profile $profile): Profile
  419. {
  420. if ($profile instanceof JobseekerProfile && $profile->getId() === $this->getJobseekerProfile()->getId()) {
  421. return $this->getJoboffererProfile();
  422. }
  423. if ($profile instanceof JoboffererProfile && $profile->getId() === $this->getJoboffererProfile()->getId()) {
  424. return $this->getJobseekerProfile();
  425. }
  426. throw new Exception('Profile id ' . $profile->getId() . ' does not belong to conversation message ' . $this->getId() . '.');
  427. }
  428. public function isAutomatedMessage(): bool
  429. {
  430. return !is_null($this->getAutomatedConversationMessagesMailing());
  431. }
  432. public function isAutomatedJobradarMessage(): bool
  433. {
  434. return $this->isAutomatedMessage() && $this->getAutomatedConversationMessagesMailing()->getMailingType() === AutomatedConversationMessagesMailing::MAILING_TYPE_JOBRADAR;
  435. }
  436. public function getSenderProfile(): Profile
  437. {
  438. if ($this->getSender() === self::SENDER_JOBOFFERER) {
  439. return $this->getJoboffererProfile();
  440. } else {
  441. return $this->getJobseekerProfile();
  442. }
  443. }
  444. public function getReceiverProfile(): Profile
  445. {
  446. if ($this->getSender() === self::SENDER_JOBOFFERER) {
  447. return $this->getJobseekerProfile();
  448. } else {
  449. return $this->getJoboffererProfile();
  450. }
  451. }
  452. public function canBeQuickReplied(): bool
  453. {
  454. return true;
  455. }
  456. public function isOutgoingDirectEmailCommunicationMessage(): bool
  457. {
  458. return !is_null($this->getOutgoingDirectEmailCommunicationMessage());
  459. }
  460. public function isIncomingDirectEmailCommunicationMessage(): bool
  461. {
  462. return !is_null($this->getIncomingDirectEmailCommunicationMessage());
  463. }
  464. public function __toString(): string
  465. {
  466. return (string)$this->getId();
  467. }
  468. /**
  469. * @var ?OccupationalField occupationalField
  470. *
  471. * @ORM\ManyToOne(targetEntity="App\Entity\OccupationalField", cascade={"persist"})
  472. *
  473. * @ORM\JoinColumn(name="occupational_field_id", referencedColumnName="id", onDelete="SET NULL")
  474. */
  475. private $occupationalField;
  476. public function getOccupationalField(): ?OccupationalField
  477. {
  478. return $this->occupationalField;
  479. }
  480. public function setOccupationalField(?OccupationalField $occupationalField)
  481. {
  482. $this->occupationalField = $occupationalField;
  483. }
  484. /**
  485. * @var ConversationMessage|null
  486. *
  487. * @ORM\ManyToOne(targetEntity="App\Entity\ConversationMessage\ConversationMessage", cascade={"persist"})
  488. *
  489. * @ORM\JoinColumn(name="related_conversation_messages_id", referencedColumnName="id", onDelete="SET NULL")
  490. */
  491. private $relatedConversationMessage;
  492. public function getRelatedConversationMessage(): ?ConversationMessage
  493. {
  494. return $this->relatedConversationMessage;
  495. }
  496. public function setRelatedConversationMessage(?ConversationMessage $relatedConversationMessage)
  497. {
  498. if (!is_null($relatedConversationMessage)) {
  499. if ($relatedConversationMessage->getId() === $this->id) {
  500. throw new InvalidArgumentException("Cannot set related conversation message for message {$this->id}, because the related message and this message are identical.");
  501. }
  502. }
  503. $this->relatedConversationMessage = $relatedConversationMessage;
  504. }
  505. /**
  506. * @var WantedJob|null
  507. *
  508. * @ORM\ManyToOne(targetEntity="App\Entity\WantedJob", cascade={"persist"})
  509. *
  510. * @ORM\JoinColumn(name="related_wanted_jobs_id", referencedColumnName="id", onDelete="SET NULL")
  511. */
  512. private $relatedWantedJob;
  513. public function getRelatedWantedJob(): ?WantedJob
  514. {
  515. if (is_null($this->relatedWantedJob)
  516. && is_null($this->getRelatedConversationMessage())
  517. && !is_null($this->getAutomatedConversationMessagesMailing())
  518. && !is_null($this->getJobseekerProfile())
  519. ) {
  520. foreach ($this->getAutomatedConversationMessagesMailing()->getWantedJobs() as $wantedJob) {
  521. if ($wantedJob->getJobseekerProfile()->getId() === $this->getJobseekerProfile()->getId()) {
  522. return $wantedJob;
  523. }
  524. }
  525. }
  526. if (is_null($this->relatedWantedJob)
  527. && !is_null($this->getRelatedConversationMessage())
  528. ) {
  529. return $this->getRelatedConversationMessage()->getRelatedWantedJob();
  530. }
  531. return $this->relatedWantedJob;
  532. }
  533. public function setRelatedWantedJob(?WantedJob $relatedWantedJob)
  534. {
  535. $this->relatedWantedJob = $relatedWantedJob;
  536. }
  537. /**
  538. * @var RecurrentJob|null
  539. *
  540. * @ORM\ManyToOne(targetEntity="App\Entity\RecurrentJob", cascade={"persist"})
  541. *
  542. * @ORM\JoinColumn(name="related_recurrent_jobs_id", referencedColumnName="id", onDelete="SET NULL")
  543. */
  544. private $relatedRecurrentJob;
  545. public function getRelatedRecurrentJob(): ?RecurrentJob
  546. {
  547. if (is_null($this->relatedRecurrentJob) && is_null($this->getRelatedConversationMessage()) && !is_null($this->getAutomatedConversationMessagesMailing())) {
  548. return $this->getAutomatedConversationMessagesMailing()->getRecurrentJob();
  549. }
  550. if (is_null($this->relatedRecurrentJob) && !is_null($this->getRelatedConversationMessage())) {
  551. return $this->getRelatedConversationMessage()->getRelatedRecurrentJob();
  552. }
  553. return $this->relatedRecurrentJob;
  554. }
  555. public function setRelatedRecurrentJob(?RecurrentJob $relatedRecurrentJob)
  556. {
  557. $this->relatedRecurrentJob = $relatedRecurrentJob;
  558. }
  559. /**
  560. * @var PushNotification[]|Collection
  561. *
  562. * @ORM\ManyToMany(targetEntity="App\Entity\MobileApp\PushNotification", mappedBy="relatedConversationMessages", cascade={"persist"})
  563. */
  564. private $relatedPushNotifications;
  565. }