src/App/Entity/User.php line 29

Open in your IDE?
  1. <?php declare(strict_types=1);
  2. namespace App\Entity;
  3. use App\Contract\Payments\PaymentUserInterface;
  4. use App\Entity\Message\MessageBid;
  5. use App\Model\UserLoginInfoInterface;
  6. use DateTime;
  7. use DateTimeZone;
  8. use Doctrine\Common\Collections\ArrayCollection;
  9. use Doctrine\Common\Collections\Collection;
  10. use Doctrine\Common\Collections\Criteria;
  11. use Doctrine\ORM\Mapping as ORM;
  12. use Gedmo\Mapping\Annotation as Gedmo;
  13. use JMS\Serializer\Annotation as JMS;
  14. use RuntimeException;
  15. use Symfony\Component\Security\Core\User\EquatableInterface;
  16. use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
  17. use Symfony\Component\Security\Core\User\UserInterface;
  18. use Symfony\Contracts\Service\ResetInterface;
  19. use function count;
  20. /**
  21.  * @ORM\Entity(repositoryClass="App\Repository\UserRepository")
  22.  * @ORM\Cache(usage="NONSTRICT_READ_WRITE", region="cache_user")
  23.  * @JMS\ReadOnlyProperty()
  24.  * @JMS\ExclusionPolicy(policy="all")
  25.  */
  26. class User implements UserInterfaceUserLoginInfoInterfacePaymentUserInterfacePasswordAuthenticatedUserInterfaceResetInterfaceEquatableInterface
  27. {
  28.     public const ROLE_VERIFIED 'ROLE_VERIFIED';
  29.     public const ROLE_HAS_MEMBERSHIP 'ROLE_HAS_MEMBERSHIP';
  30.     private const PREVIOUS_EMAILS 'previous_emails';
  31.     private const NOTIFIED_CC_EXPIRATION 'notified_cc';
  32.     /**
  33.      * @ORM\Id()
  34.      * @ORM\GeneratedValue()
  35.      * @ORM\Column(type="integer", options={"unsigned": true})
  36.      */
  37.     private ?int $id null;
  38.     /**
  39.      * @ORM\Column(type="string", unique=true, length=255)
  40.      */
  41.     private string $email '';
  42.     /**
  43.      * @ORM\Column(type="string", length=400)
  44.      */
  45.     private string $nickname '';
  46.     /**
  47.      * @ORM\Column(type="string", length=255)
  48.      */
  49.     private string $password '';
  50.     private ?string $plainPassword null;
  51.     /**
  52.      * @ORM\Column(type="boolean")
  53.      */
  54.     private bool $locked false;
  55.     /**
  56.      * @ORM\Column(type="boolean")
  57.      */
  58.     private bool $verified false;
  59.     private bool $justVerified false;
  60.     /**
  61.      * @ORM\Column(type="boolean")
  62.      */
  63.     private bool $verificationLock false;
  64.     /**
  65.      * @ORM\Column(type="boolean")
  66.      */
  67.     private bool $agreedNewsletter false;
  68.     /**
  69.      * @ORM\Column(type="string", nullable=true)
  70.      */
  71.     private ?string $lastIp;
  72.     /**
  73.      * @ORM\Column(type="datetime", nullable=true)
  74.      */
  75.     private ?DateTime $lastLogin;
  76.     /**
  77.      * @ORM\Column(type="datetime")
  78.      * @Gedmo\Timestampable(on="update")
  79.      */
  80.     private DateTime $lastModified;
  81.     /**
  82.      * @ORM\Column(type="datetime")
  83.      */
  84.     private DateTime $createdAt;
  85.     /**
  86.      * @ORM\Column(type="integer")
  87.      */
  88.     private int $credits 0;
  89.     /**
  90.      * @var DealBid[]|Collection<int, DealBid>
  91.      * @ORM\OneToMany(targetEntity="DealBid", mappedBy="user")
  92.      */
  93.     private Collection $dealBids;
  94.     /**
  95.      * @var ProjectBid[]|Collection<int, ProjectBid>
  96.      * @ORM\OneToMany(targetEntity="ProjectBid", mappedBy="user")
  97.      */
  98.     private Collection $projectBids;
  99.     /**
  100.      * @var ShopVideoBid[]|Collection<int, ShopVideoBid>
  101.      * @ORM\OneToMany(targetEntity="ShopVideoBid", mappedBy="user")
  102.      */
  103.     private Collection $shopVideoBids;
  104.     /**
  105.      * @var Collection<int, MessageBid>
  106.      * @ORM\OneToMany(targetEntity="App\Entity\Message\MessageBid", mappedBy="user")
  107.      */
  108.     private Collection $messageBids;
  109.     /**
  110.      * @var Collection<int, Favorite>
  111.      * @ORM\OneToMany(targetEntity="App\Entity\Favorite", mappedBy="user")
  112.      */
  113.     private Collection $favorites;
  114.     /**
  115.      * @var Referral[]|Collection<int, Referral>
  116.      * @ORM\OneToMany(targetEntity="App\Entity\Referral", mappedBy="user", cascade={"persist"})
  117.      */
  118.     private Collection $referrals;
  119.     /**
  120.      * @var CreditsMembership[]|Collection<int, CreditsMembership>
  121.      * @ORM\OneToMany(targetEntity="CreditsMembership", mappedBy="user", cascade={"refresh"})
  122.      * @ORM\Cache(usage="NONSTRICT_READ_WRITE", region="cache_user")
  123.      */
  124.     private Collection $creditsMemberships;
  125.     private ?CreditsMembership $activeCreditsMembership null;
  126.     /**
  127.      * @ORM\Column(type="string", length=255)
  128.      */
  129.     private string $baseTransactionId '';
  130.     /**
  131.      * @var string[]
  132.      */
  133.     private array $defaultRoles = ['ROLE_USER'];
  134.     /**
  135.      * @var string[]
  136.      */
  137.     private array $roles;
  138.     /**
  139.      * @var array<mixed>
  140.      * @ORM\Column(type="json")
  141.      */
  142.     private array $metaInfo = [];
  143.     public function __construct()
  144.     {
  145.         $this->createdAt = new DateTime();
  146.         $this->lastLogin = new DateTime();
  147.         $this->dealBids = new ArrayCollection();
  148.         $this->projectBids = new ArrayCollection();
  149.         $this->shopVideoBids = new ArrayCollection();
  150.         $this->messageBids = new ArrayCollection();
  151.         $this->referrals = new ArrayCollection();
  152.         $this->creditsMemberships = new ArrayCollection();
  153.         $this->favorites = new ArrayCollection();
  154.     }
  155.     public function getId(): ?int
  156.     {
  157.         return $this->id;
  158.     }
  159.     public function setId(int $id): void
  160.     {
  161.         $this->id $id;
  162.     }
  163.     public function getEmail(): ?string
  164.     {
  165.         return $this->email;
  166.     }
  167.     public function setEmail(?string $email): self
  168.     {
  169.         if (null === $email) {
  170.             $email '';
  171.         }
  172.         if ('' !== $email && '' !== $this->email) {
  173.             if (!isset($this->metaInfo[self::PREVIOUS_EMAILS]) || !is_array($this->metaInfo[self::PREVIOUS_EMAILS])) {
  174.                 $this->metaInfo[self::PREVIOUS_EMAILS] = [];
  175.             }
  176.             $this->metaInfo[self::PREVIOUS_EMAILS][] = $this->email;
  177.         }
  178.         $this->email $email;
  179.         return $this;
  180.     }
  181.     public function getNickname(): string
  182.     {
  183.         return $this->nickname;
  184.     }
  185.     public function setNickname(?string $nickname): User
  186.     {
  187.         if (null === $nickname) {
  188.             $nickname '';
  189.         }
  190.         $this->nickname $nickname;
  191.         return $this;
  192.     }
  193.     /**
  194.      * @return string[]
  195.      */
  196.     public function getRoles(): array
  197.     {
  198.         if (isset($this->roles)) {
  199.             return $this->roles;
  200.         }
  201.         $roles $this->defaultRoles;
  202.         if ($this->isVerified()) {
  203.             $roles[] = self::ROLE_VERIFIED;
  204.         }
  205.         if (null !== $this->getActiveCreditsMembership()) {
  206.             $roles[] = self::ROLE_HAS_MEMBERSHIP;
  207.         }
  208.         return $this->roles $roles;
  209.     }
  210.     public function resetRoles(): void
  211.     {
  212.         unset($this->roles);
  213.     }
  214.     public function getSalt(): string
  215.     {
  216.         return '';
  217.     }
  218.     public function getUsername(): string
  219.     {
  220.         return $this->email;
  221.     }
  222.     public function getUserIdentifier(): string
  223.     {
  224.         return $this->email;
  225.     }
  226.     public function eraseCredentials(): void
  227.     {
  228.         $this->plainPassword null;
  229.     }
  230.     public function getPassword(): string
  231.     {
  232.         return $this->password;
  233.     }
  234.     public function isEqualTo(UserInterface $user): bool
  235.     {
  236.         if (!$user instanceof self) {
  237.             return false;
  238.         }
  239.         if ($this->getPassword() !== $user->getPassword()) {
  240.             return false;
  241.         }
  242.         if ($this->getSalt() !== $user->getSalt()) {
  243.             return false;
  244.         }
  245.         $currentRoles $this->getRoles();
  246.         $newRoles $user->getRoles();
  247.         $rolesChanged count($currentRoles) !== count($newRoles) || count($currentRoles) !== count(array_intersect($currentRoles$newRoles));
  248.         if ($rolesChanged) {
  249.             return false;
  250.         }
  251.         if ($this->getUserIdentifier() !== $user->getUserIdentifier()) {
  252.             return false;
  253.         }
  254.         if ($this->isLocked() !== $user->isLocked()) {
  255.             return false;
  256.         }
  257.         return $this->isVerificationLock() === $user->isVerificationLock();
  258.     }
  259.     public function setPassword(string $password): User
  260.     {
  261.         $this->password $password;
  262.         return $this;
  263.     }
  264.     public function getPlainPassword(): ?string
  265.     {
  266.         return $this->plainPassword;
  267.     }
  268.     public function setPlainPassword(?string $plainPassword): User
  269.     {
  270.         $this->plainPassword $plainPassword;
  271.         return $this;
  272.     }
  273.     public function isLocked(): bool
  274.     {
  275.         return $this->locked;
  276.     }
  277.     public function setLocked(bool $locked): User
  278.     {
  279.         $this->locked $locked;
  280.         return $this;
  281.     }
  282.     public function isVerified(): bool
  283.     {
  284.         return $this->verified;
  285.     }
  286.     public function setVerified(bool $verified): User
  287.     {
  288.         if (!$this->verified && $verified) {
  289.             $this->justVerified true;
  290.         }
  291.         $this->verified $verified;
  292.         return $this;
  293.     }
  294.     public function isJustVerified(): bool
  295.     {
  296.         return $this->justVerified;
  297.     }
  298.     public function isVerificationLock(): bool
  299.     {
  300.         return $this->verificationLock;
  301.     }
  302.     public function setVerificationLock(bool $verificationLock): User
  303.     {
  304.         $this->verificationLock $verificationLock;
  305.         return $this;
  306.     }
  307.     public function isAgreedNewsletter(): bool
  308.     {
  309.         return $this->agreedNewsletter;
  310.     }
  311.     public function setAgreedNewsletter(bool $agreedNewsletter): User
  312.     {
  313.         $this->agreedNewsletter $agreedNewsletter;
  314.         return $this;
  315.     }
  316.     public function getLastIp(): ?string
  317.     {
  318.         return $this->lastIp;
  319.     }
  320.     public function setLastIp(?string $lastIp): User
  321.     {
  322.         $this->lastIp $lastIp;
  323.         return $this;
  324.     }
  325.     public function getLastLogin(): ?DateTime
  326.     {
  327.         return $this->lastLogin;
  328.     }
  329.     public function setLastLogin(?DateTime $lastLogin): User
  330.     {
  331.         $this->lastLogin $lastLogin;
  332.         return $this;
  333.     }
  334.     public function getLastModified(): DateTime
  335.     {
  336.         return $this->lastModified;
  337.     }
  338.     public function setLastModified(DateTime $lastModified): User
  339.     {
  340.         $this->lastModified $lastModified;
  341.         return $this;
  342.     }
  343.     public function getCreatedAt(): DateTime
  344.     {
  345.         return $this->createdAt;
  346.     }
  347.     public function setCreatedAt(DateTime $createdAt): User
  348.     {
  349.         $this->createdAt $createdAt;
  350.         return $this;
  351.     }
  352.     public function getCredits(): int
  353.     {
  354.         return $this->credits;
  355.     }
  356.     public function getCreditsFloat(): float
  357.     {
  358.         return $this->credits 100;
  359.     }
  360. //    Only getter. Use UserRepository::addCredits
  361. //    private function setCredits(int $credits): void
  362. //    {
  363. //        $this->credits = $credits;
  364. //    }
  365.     /**
  366.      * @return DealBid[]|Collection<int, DealBid>
  367.      */
  368.     public function getDealBids(): Collection
  369.     {
  370.         return $this->dealBids;
  371.     }
  372.     public function addDealBid(DealBid $bid): void
  373.     {
  374.         if ($this->dealBids->contains($bid)) {
  375.             return;
  376.         }
  377.         $this->dealBids->add($bid);
  378.     }
  379.     /**
  380.      * @return ProjectBid[]|Collection<int, ProjectBid>
  381.      */
  382.     public function getProjectBids(): Collection
  383.     {
  384.         return $this->projectBids;
  385.     }
  386.     public function addProjectBid(ProjectBid $bid): void
  387.     {
  388.         if ($this->projectBids->contains($bid)) {
  389.             return;
  390.         }
  391.         $this->projectBids->add($bid);
  392.     }
  393.     /**
  394.      * @return ShopVideoBid[]|Collection<int, ShopVideoBid>
  395.      */
  396.     public function getShopVideoBids()
  397.     {
  398.         return $this->shopVideoBids;
  399.     }
  400.     public function addShopVideoBid(ShopVideoBid $bid): void
  401.     {
  402.         if ($this->shopVideoBids->contains($bid)) {
  403.             return;
  404.         }
  405.         $this->shopVideoBids->add($bid);
  406.     }
  407.     /**
  408.      * @return Collection<int, MessageBid>
  409.      */
  410.     public function getMessageBids(): Collection
  411.     {
  412.         return $this->messageBids;
  413.     }
  414.     public function addMessageBid(MessageBid $bid): void
  415.     {
  416.         if ($this->messageBids->contains($bid)) {
  417.             return;
  418.         }
  419.         $this->messageBids->add($bid);
  420.     }
  421.     /**
  422.      * @return Collection<int, Favorite>
  423.      */
  424.     public function getFavorites(): Collection
  425.     {
  426.         return $this->favorites;
  427.     }
  428.     public function addFavorite(Favorite $favorite): void
  429.     {
  430.         if ($this->favorites->contains($favorite)) {
  431.             return;
  432.         }
  433.         $this->favorites->add($favorite);
  434.     }
  435.     public function removeFavorite(Favorite $favorite): void
  436.     {
  437.         $this->favorites->removeElement($favorite);
  438.     }
  439.     public function getReferral(): ?Referral
  440.     {
  441.         $referrals $this->referrals->filter(static function (Referral $referral): bool {
  442.             return !$referral->isCancelled();
  443.         });
  444.         if (=== count($referrals)) {
  445.             return null;
  446.         }
  447.         return $referrals->first() !== false $referrals->first() : null;
  448.     }
  449.     public function addReferral(User $referredBy): Referral
  450.     {
  451.         if (null !== $this->getReferral()) {
  452.             throw new RuntimeException('Referral for user ' $this->getId() . ' already exists.');
  453.         }
  454.         $referral = new Referral($this$referredBy);
  455.         $this->referrals->add($referral);
  456.         return $referral;
  457.     }
  458.     /**
  459.      * @return Collection<int, CreditsMembership>
  460.      */
  461.     public function getCreditsMemberships(): Collection
  462.     {
  463.         return $this->creditsMemberships;
  464.     }
  465.     /**
  466.      * @throws RuntimeException
  467.      */
  468.     public function getActiveCreditsMembership(): ?CreditsMembership
  469.     {
  470.         if (null === $this->activeCreditsMembership) {
  471.             $nowUTC = new DateTime('now', new DateTimeZone('UTC'));
  472.             // Because CreditsMemberships are hydrated they have a local default timezone set.
  473.             // We need to grab a time string of UTC and apply local timezone to properly compare dates.
  474.             $nowDefaultTimezone = new DateTime($nowUTC->format('Y-m-d H:i:s'));
  475.             // Force PersistentCollection to load. This will allow to a greater cache hits, as it does not use
  476.             // time-sensitive query. If we'd have a problem with hydrating CreditMemberships (> 50? 100?) then its
  477.             // luxury problem, and should be solved by shifting old statistical data to statistical database.
  478.             $this->creditsMemberships->first();
  479.             $activeCreditsMembership $this->creditsMemberships->matching(
  480.                 Criteria::create()
  481.                     ->where(Criteria::expr()->lte('lastTransactionAt'$nowDefaultTimezone))
  482.                     ->andWhere(Criteria::expr()->neq('lastSuccessfulTransactionAt'null))
  483.                     ->andWhere(Criteria::expr()->lte('lastSuccessfulTransactionAt'$nowDefaultTimezone))
  484.                     // need to leave a gap for users who are currently browsing a website and should be re-billed
  485.                     ->andWhere(Criteria::expr()->gte('nextTransactionAt', (clone $nowDefaultTimezone)->modify('-10 minutes')))
  486.                     ->andWhere(Criteria::expr()->eq('cancelled'false))
  487.             );
  488.             if ($activeCreditsMembership->count() > 1) {
  489.                 throw new RuntimeException('User has too many active credits loader.');
  490.             }
  491.             if ($activeCreditsMembership->count() === && false !== $activeCreditsMembership->first()) {
  492.                 $this->activeCreditsMembership $activeCreditsMembership->first();
  493.             }
  494.         }
  495.         return $this->activeCreditsMembership;
  496.     }
  497.     public function isActiveCreditsMembershipConfirmed(): bool
  498.     {
  499.         $activeCreditsMembership $this->getActiveCreditsMembership();
  500.         if (null === $activeCreditsMembership || null === $activeCreditsMembership->getLastSuccessfulTransactionAt()) {
  501.             return false;
  502.         }
  503.         return $activeCreditsMembership->isConfirmedActiveCreditsMembership();
  504.     }
  505.     public function getBaseTransactionId(): ?string
  506.     {
  507.         return $this->baseTransactionId === '' null $this->baseTransactionId;
  508.     }
  509.     public function setBaseTransactionId(string $baseTransactionId): void
  510.     {
  511.         $this->baseTransactionId $baseTransactionId;
  512.     }
  513.     public function getTrackingHash(): ?string
  514.     {
  515.         if (isset($this->metaInfo['tracking_hash'])) {
  516.             if (!is_string($this->metaInfo['tracking_hash'])) {
  517.                 unset($this->metaInfo['tracking_hash']);
  518.                 return null;
  519.             }
  520.             return $this->metaInfo['tracking_hash'];
  521.         }
  522.         return null;
  523.     }
  524.     public function setTrackingHash(string $trackingHash): void
  525.     {
  526.         if ($trackingHash === '') {
  527.             return;
  528.         }
  529.         $this->metaInfo['tracking_hash'] = $trackingHash;
  530.     }
  531.     public function setNotifyProject(bool $notifyProject): void
  532.     {
  533.         $this->metaInfo['notify_project'] = $notifyProject;
  534.     }
  535.     public function isNotifyProject(): bool
  536.     {
  537.         return !isset($this->metaInfo['notify_project']) || !is_bool($this->metaInfo['notify_project']) ? true $this->metaInfo['notify_project'];
  538.     }
  539.     public function setNotifyDeal(bool $notifyDeal): void
  540.     {
  541.         $this->metaInfo['notify_deal'] = $notifyDeal;
  542.     }
  543.     public function isNotifyDeal(): bool
  544.     {
  545.         return !isset($this->metaInfo['notify_deal']) || !is_bool($this->metaInfo['notify_deal']) ? true $this->metaInfo['notify_deal'];
  546.     }
  547.     public function setNotifyMessage(bool $notifyMessage): void
  548.     {
  549.         $this->metaInfo['notify_message'] = $notifyMessage;
  550.     }
  551.     public function isNotifyMessage(): bool
  552.     {
  553.         return !isset($this->metaInfo['notify_message']) || !is_bool($this->metaInfo['notify_message']) ? true $this->metaInfo['notify_message'];
  554.     }
  555.     /**
  556.      * @param Project|Deal $proposal
  557.      */
  558.     public function shouldNotifyProposal(Proposal $proposal): bool
  559.     {
  560.         if ($proposal instanceof Project && $this->isNotifyProject()) {
  561.             return true;
  562.         }
  563.         return $proposal instanceof Deal && $this->isNotifyDeal();
  564.     }
  565.     public function getPreferredTheme(): ?string
  566.     {
  567.         if (!isset($this->metaInfo['theme']) || !is_string($this->metaInfo['theme'])) {
  568.             return null;
  569.         }
  570.         return $this->metaInfo['theme'];
  571.     }
  572.     public function setPreferredTheme(string $theme): void
  573.     {
  574.         if ($theme === '') {
  575.             return;
  576.         }
  577.         $this->metaInfo['theme'] = $theme;
  578.     }
  579.     /**
  580.      * @return mixed[]
  581.      */
  582.     public function getMetaInfo(): array
  583.     {
  584.         return $this->metaInfo;
  585.     }
  586.     /**
  587.      * @param mixed[] $metaInfo
  588.      */
  589.     public function setMetaInfo(array $metaInfo): User
  590.     {
  591.         $this->metaInfo $metaInfo;
  592.         return $this;
  593.     }
  594.     /**
  595.      * @return array<int, string>
  596.      */
  597.     public function getPreviousEmails(): array
  598.     {
  599.         if (!isset($this->metaInfo[self::PREVIOUS_EMAILS]) || !is_array($this->metaInfo[self::PREVIOUS_EMAILS])) {
  600.             return [];
  601.         }
  602.         return $this->metaInfo[self::PREVIOUS_EMAILS];
  603.     }
  604.     public function getNotifiedExpiration(int $transactionId): bool
  605.     {
  606.         if (!isset($this->metaInfo[self::NOTIFIED_CC_EXPIRATION])
  607.             || !is_array($this->metaInfo[self::NOTIFIED_CC_EXPIRATION])
  608.             || !isset($this->metaInfo[self::NOTIFIED_CC_EXPIRATION][$transactionId])
  609.             || !is_bool($this->metaInfo[self::NOTIFIED_CC_EXPIRATION][$transactionId])
  610.         ) {
  611.             return false;
  612.         }
  613.         return $this->metaInfo[self::NOTIFIED_CC_EXPIRATION][$transactionId];
  614.     }
  615.     public function setEmailNotifiedExpiration(int $transactionId): void
  616.     {
  617.         if (!isset($this->metaInfo[self::NOTIFIED_CC_EXPIRATION])
  618.             || !is_array($this->metaInfo[self::NOTIFIED_CC_EXPIRATION])
  619.         ) {
  620.             $this->metaInfo[self::NOTIFIED_CC_EXPIRATION] = [];
  621.         }
  622.         $this->metaInfo[self::NOTIFIED_CC_EXPIRATION][$transactionId] = true;
  623.     }
  624.     public function __toString(): string
  625.     {
  626.         return $this->nickname !== '' $this->nickname 'user-' $this->getId();
  627.     }
  628.     /**
  629.      * @return mixed[]
  630.      */
  631.     public function __serialize(): array
  632.     {
  633.         if (!isset($this->roles)) {
  634.             $this->roles = [];
  635.         }
  636.         return [
  637.             $this->id,
  638.             $this->password,
  639.             $this->email,
  640.             $this->locked,
  641.             $this->roles,
  642.             $this->verified,
  643.             $this->verificationLock,
  644.         ];
  645.     }
  646.     /**
  647.      * @param mixed[] $data
  648.      */
  649.     public function __unserialize(array $data): void
  650.     {
  651.         [
  652.             $this->id// @phpstan-ignore-line
  653.             $this->password// @phpstan-ignore-line
  654.             $this->email// @phpstan-ignore-line
  655.             $this->locked// @phpstan-ignore-line
  656.             $this->roles// @phpstan-ignore-line
  657.             $this->verified// @phpstan-ignore-line
  658.             $this->verificationLock// @phpstan-ignore-line
  659.         ] = $data;
  660.     }
  661.     public function reset(): void
  662.     {
  663.         $this->activeCreditsMembership null;
  664.         $this->resetRoles();
  665.     }
  666. }