mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-06-07 18:44:08 +00:00
Updated entity properties to use Collection&Selectable for better type safety and interoperability. This change affects User, Household, Calendar, Person, AccompanyingPeriod, and EntityWorkflow classes.
617 lines
15 KiB
PHP
617 lines
15 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
/*
|
|
* 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.
|
|
*/
|
|
|
|
namespace Chill\MainBundle\Entity;
|
|
|
|
use Chill\MainBundle\Entity\User\UserJobHistory;
|
|
use Chill\MainBundle\Entity\User\UserScopeHistory;
|
|
use Doctrine\Common\Collections\ArrayCollection;
|
|
use Doctrine\Common\Collections\Collection;
|
|
use Doctrine\Common\Collections\Criteria;
|
|
use Doctrine\Common\Collections\Selectable;
|
|
use Doctrine\ORM\Mapping as ORM;
|
|
use libphonenumber\PhoneNumber;
|
|
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
|
|
use Symfony\Component\Security\Core\User\UserInterface;
|
|
use Symfony\Component\Serializer\Annotation as Serializer;
|
|
use Symfony\Component\Validator\Context\ExecutionContextInterface;
|
|
use Chill\MainBundle\Validation\Constraint\PhonenumberConstraint;
|
|
|
|
/**
|
|
* User.
|
|
*/
|
|
#[Serializer\DiscriminatorMap(typeProperty: 'type', mapping: ['user' => User::class])]
|
|
#[ORM\Entity]
|
|
#[ORM\Cache(usage: 'NONSTRICT_READ_WRITE', region: 'acl_cache_region')]
|
|
#[ORM\Table(name: 'users')]
|
|
class User implements UserInterface, \Stringable, PasswordAuthenticatedUserInterface
|
|
{
|
|
#[ORM\Id]
|
|
#[ORM\Column(name: 'id', type: \Doctrine\DBAL\Types\Types::INTEGER)]
|
|
#[ORM\GeneratedValue(strategy: 'AUTO')]
|
|
protected ?int $id = null;
|
|
|
|
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::DATETIME_IMMUTABLE, nullable: true)]
|
|
private ?\DateTimeImmutable $absenceStart = null;
|
|
|
|
/**
|
|
* Array where SAML attributes's data are stored.
|
|
*/
|
|
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::JSON, nullable: false)]
|
|
private array $attributes = [];
|
|
|
|
#[ORM\ManyToOne(targetEntity: Civility::class)]
|
|
private ?Civility $civility = null;
|
|
|
|
#[ORM\ManyToOne(targetEntity: Location::class)]
|
|
private ?Location $currentLocation = null;
|
|
|
|
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::STRING, length: 150, nullable: true)]
|
|
private ?string $email = null;
|
|
|
|
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::STRING, length: 150, nullable: true, unique: true)]
|
|
private ?string $emailCanonical = null;
|
|
|
|
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::BOOLEAN)]
|
|
private bool $enabled = true;
|
|
|
|
/**
|
|
* @var Collection<int, \Chill\MainBundle\Entity\GroupCenter>
|
|
*/
|
|
#[ORM\ManyToMany(targetEntity: GroupCenter::class, inversedBy: 'users')]
|
|
#[ORM\Cache(usage: 'NONSTRICT_READ_WRITE')]
|
|
private Collection $groupCenters;
|
|
|
|
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::STRING, length: 200)]
|
|
private string $label = '';
|
|
|
|
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::BOOLEAN)] // sf4 check: in yml was false by default !?
|
|
private bool $locked = true;
|
|
|
|
#[ORM\ManyToOne(targetEntity: Center::class)]
|
|
private ?Center $mainCenter = null;
|
|
|
|
#[ORM\ManyToOne(targetEntity: Location::class)]
|
|
private ?Location $mainLocation = null;
|
|
|
|
/**
|
|
* @var \Doctrine\Common\Collections\Collection<int, \Chill\MainBundle\Entity\User\UserScopeHistory>&Selectable
|
|
*/
|
|
#[ORM\OneToMany(mappedBy: 'user', targetEntity: UserScopeHistory::class, cascade: ['persist', 'remove'], orphanRemoval: true)]
|
|
private Collection&Selectable $scopeHistories;
|
|
|
|
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::STRING, length: 255)]
|
|
private string $password = '';
|
|
|
|
/**
|
|
* @internal must be set to null if we use bcrypt
|
|
*/
|
|
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::STRING, length: 255, nullable: true)]
|
|
private ?string $salt = null;
|
|
|
|
/**
|
|
* @var \Doctrine\Common\Collections\Collection<int, \Chill\MainBundle\Entity\User\UserJobHistory>&Selectable
|
|
*/
|
|
#[ORM\OneToMany(mappedBy: 'user', targetEntity: UserJobHistory::class, cascade: ['persist', 'remove'], orphanRemoval: true)]
|
|
private Collection&Selectable $jobHistories;
|
|
|
|
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::STRING, length: 80)]
|
|
private string $username = '';
|
|
|
|
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::STRING, length: 80, unique: true, nullable: true)]
|
|
private ?string $usernameCanonical = null;
|
|
|
|
/**
|
|
* The user's mobile phone number.
|
|
*/
|
|
#[ORM\Column(type: 'phone_number', nullable: true)]
|
|
#[PhonenumberConstraint]
|
|
private ?PhoneNumber $phonenumber = null;
|
|
|
|
/**
|
|
* User constructor.
|
|
*/
|
|
public function __construct()
|
|
{
|
|
$this->groupCenters = new ArrayCollection();
|
|
$this->scopeHistories = new ArrayCollection();
|
|
$this->jobHistories = new ArrayCollection();
|
|
}
|
|
|
|
public function __toString(): string
|
|
{
|
|
return $this->getLabel();
|
|
}
|
|
|
|
/**
|
|
* @return User
|
|
*/
|
|
public function addGroupCenter(GroupCenter $groupCenter)
|
|
{
|
|
$this->groupCenters->add($groupCenter);
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function eraseCredentials() {}
|
|
|
|
public function getAbsenceStart(): ?\DateTimeImmutable
|
|
{
|
|
return $this->absenceStart;
|
|
}
|
|
|
|
/**
|
|
* Get attributes.
|
|
*
|
|
* @return array
|
|
*/
|
|
public function getAttributes()
|
|
{
|
|
if (null === $this->attributes) {
|
|
$this->attributes = [];
|
|
}
|
|
|
|
return $this->attributes;
|
|
}
|
|
|
|
public function getCivility(): ?Civility
|
|
{
|
|
return $this->civility;
|
|
}
|
|
|
|
public function getCurrentLocation(): ?Location
|
|
{
|
|
return $this->currentLocation;
|
|
}
|
|
|
|
public function getEmail(): ?string
|
|
{
|
|
return $this->email;
|
|
}
|
|
|
|
/**
|
|
* @return string
|
|
*/
|
|
public function getEmailCanonical()
|
|
{
|
|
return $this->emailCanonical;
|
|
}
|
|
|
|
/**
|
|
* @return Collection<GroupCenter>
|
|
*/
|
|
public function getGroupCenters(): Collection
|
|
{
|
|
return $this->groupCenters;
|
|
}
|
|
|
|
/**
|
|
* Get id.
|
|
*/
|
|
public function getId(): ?int
|
|
{
|
|
return $this->id;
|
|
}
|
|
|
|
public function getLabel(): string
|
|
{
|
|
return $this->label;
|
|
}
|
|
|
|
public function getMainCenter(): ?Center
|
|
{
|
|
return $this->mainCenter;
|
|
}
|
|
|
|
public function getMainLocation(): ?Location
|
|
{
|
|
return $this->mainLocation;
|
|
}
|
|
|
|
public function getMainScope(?\DateTimeImmutable $atDate = null): ?Scope
|
|
{
|
|
$atDate ??= new \DateTimeImmutable('now');
|
|
|
|
foreach ($this->scopeHistories as $scopeHistory) {
|
|
if ($atDate >= $scopeHistory->getStartDate() && (
|
|
null === $scopeHistory->getEndDate() || $atDate < $scopeHistory->getEndDate()
|
|
)) {
|
|
return $scopeHistory->getScope();
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public function getMainScopeHistories(): Collection
|
|
{
|
|
return $this->scopeHistories;
|
|
}
|
|
|
|
/**
|
|
* @return ArrayCollection|UserScopeHistory[]
|
|
*/
|
|
public function getMainScopeHistoriesOrdered(): ArrayCollection
|
|
{
|
|
$scopeHistories = $this->getMainScopeHistories();
|
|
|
|
$sortedScopeHistories = $scopeHistories->toArray();
|
|
|
|
usort($sortedScopeHistories, fn ($a, $b) => $a->getStartDate() < $b->getStartDate() ? 1 : -1);
|
|
|
|
return new ArrayCollection($sortedScopeHistories);
|
|
}
|
|
|
|
public function getPassword(): ?string
|
|
{
|
|
return $this->password;
|
|
}
|
|
|
|
public function getRoles(): array
|
|
{
|
|
return ['ROLE_USER'];
|
|
}
|
|
|
|
public function getSalt(): ?string
|
|
{
|
|
return $this->salt;
|
|
}
|
|
|
|
public function getUserJob(?\DateTimeImmutable $atDate = null): ?UserJob
|
|
{
|
|
$atDate ??= new \DateTimeImmutable('now');
|
|
|
|
foreach ($this->jobHistories as $jobHistory) {
|
|
if ($atDate >= $jobHistory->getStartDate() && (
|
|
null === $jobHistory->getEndDate() || $atDate < $jobHistory->getEndDate()
|
|
)) {
|
|
return $jobHistory->getJob();
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public function getUserJobHistories(): Collection
|
|
{
|
|
return $this->jobHistories;
|
|
}
|
|
|
|
public function getUserScopeHistories(): Collection
|
|
{
|
|
return $this->scopeHistories;
|
|
}
|
|
|
|
/**
|
|
* @return ArrayCollection|UserJobHistory[]
|
|
*/
|
|
public function getUserJobHistoriesOrdered(): ArrayCollection
|
|
{
|
|
$jobHistories = $this->getUserJobHistories();
|
|
|
|
$sortedJobHistories = $jobHistories->toArray();
|
|
|
|
usort($sortedJobHistories, fn ($a, $b) => $a->getStartDate() < $b->getStartDate() ? 1 : -1);
|
|
|
|
return new ArrayCollection($sortedJobHistories);
|
|
}
|
|
|
|
/**
|
|
* @return string
|
|
*/
|
|
public function getUsername()
|
|
{
|
|
return $this->username;
|
|
}
|
|
|
|
public function getUserIdentifier(): string
|
|
{
|
|
return $this->username;
|
|
}
|
|
|
|
/**
|
|
* @return string
|
|
*/
|
|
public function getUsernameCanonical()
|
|
{
|
|
return $this->usernameCanonical;
|
|
}
|
|
|
|
public function isAbsent(): bool
|
|
{
|
|
return null !== $this->getAbsenceStart() && $this->getAbsenceStart() <= new \DateTimeImmutable('now');
|
|
}
|
|
|
|
/**
|
|
* @return bool
|
|
*/
|
|
public function isAccountNonExpired()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* @return bool
|
|
*/
|
|
public function isAccountNonLocked()
|
|
{
|
|
return $this->locked;
|
|
}
|
|
|
|
/**
|
|
* @return bool
|
|
*/
|
|
public function isCredentialsNonExpired()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* @return bool
|
|
*/
|
|
public function isEnabled()
|
|
{
|
|
return $this->enabled;
|
|
}
|
|
|
|
/**
|
|
* This function check that groupCenter are present only once. The validator
|
|
* use this function to avoid a user to be associated to the same groupCenter
|
|
* more than once.
|
|
*/
|
|
public function isGroupCenterPresentOnce(ExecutionContextInterface $context)
|
|
{
|
|
$groupCentersIds = [];
|
|
|
|
foreach ($this->getGroupCenters() as $groupCenter) {
|
|
if (\in_array($groupCenter->getId(), $groupCentersIds, true)) {
|
|
$context->buildViolation('The user has already those permissions')
|
|
->addViolation();
|
|
} else {
|
|
$groupCentersIds[] = $groupCenter->getId();
|
|
}
|
|
}
|
|
}
|
|
|
|
public function getPhonenumber(): ?PhoneNumber
|
|
{
|
|
return $this->phonenumber;
|
|
}
|
|
|
|
/**
|
|
* @throws \RuntimeException if the groupCenter is not in the collection
|
|
*/
|
|
public function removeGroupCenter(GroupCenter $groupCenter)
|
|
{
|
|
if (false === $this->groupCenters->removeElement($groupCenter)) {
|
|
throw new \RuntimeException('The groupCenter could not be removed, it seems not to be associated with the user. Aborting.');
|
|
}
|
|
}
|
|
|
|
public function setAbsenceStart(?\DateTimeImmutable $absenceStart): void
|
|
{
|
|
$this->absenceStart = $absenceStart;
|
|
}
|
|
|
|
public function setAttributeByDomain(string $domain, string $key, $value): self
|
|
{
|
|
$this->attributes[$domain][$key] = $value;
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Merge the attributes with existing attributes.
|
|
*
|
|
* Only the key provided will be created or updated. For a two-level array, use @see{User::setAttributeByDomain}
|
|
*/
|
|
public function setAttributes(array $attributes): self
|
|
{
|
|
$this->attributes = array_merge($this->attributes, $attributes);
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function setCivility(?Civility $civility): User
|
|
{
|
|
$this->civility = $civility;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function setCurrentLocation(?Location $currentLocation): User
|
|
{
|
|
$this->currentLocation = $currentLocation;
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* @return $this
|
|
*/
|
|
public function setEmail(?string $email)
|
|
{
|
|
$this->email = $email;
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* @return $this
|
|
*/
|
|
public function setEmailCanonical(?string $emailCanonical)
|
|
{
|
|
$this->emailCanonical = $emailCanonical;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function setEnabled(bool $enabled)
|
|
{
|
|
$this->enabled = $enabled;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function setLabel(string $label): User
|
|
{
|
|
$this->label = $label;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function setMainCenter(?Center $mainCenter): User
|
|
{
|
|
$this->mainCenter = $mainCenter;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function setMainLocation(?Location $mainLocation): User
|
|
{
|
|
$this->mainLocation = $mainLocation;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function setMainScope(?Scope $mainScope): User
|
|
{
|
|
if ($mainScope === $this->getMainScope()) {
|
|
return $this;
|
|
}
|
|
|
|
$newScope = new UserScopeHistory();
|
|
|
|
$newScope
|
|
->setStartDate(new \DateTimeImmutable('now'))
|
|
->setScope($mainScope)
|
|
->setUser($this);
|
|
|
|
$this->scopeHistories[] = $newScope;
|
|
|
|
$criteria = new Criteria();
|
|
$criteria->orderBy(['startDate' => 'ASC', 'id' => 'ASC']);
|
|
|
|
/** @var \Iterator $scopes */
|
|
$scopes = $this->scopeHistories->matching($criteria)->getIterator();
|
|
$scopes->rewind();
|
|
|
|
do {
|
|
/** @var UserScopeHistory $current */
|
|
$current = $scopes->current();
|
|
$scopes->next();
|
|
|
|
if ($scopes->valid()) {
|
|
$next = $scopes->current();
|
|
$current->setEndDate($next->getStartDate());
|
|
}
|
|
} while ($scopes->valid());
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* @return $this
|
|
*/
|
|
public function setPassword(string $password)
|
|
{
|
|
$this->password = $password;
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* @return $this
|
|
*/
|
|
public function setSalt(?string $salt)
|
|
{
|
|
$this->salt = $salt;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function setUserJob(?UserJob $userJob): User
|
|
{
|
|
if ($userJob === $this->getUserJob()) {
|
|
return $this;
|
|
}
|
|
|
|
$newJob = new UserJobHistory();
|
|
|
|
$newJob
|
|
->setStartDate(new \DateTimeImmutable('now'))
|
|
->setJob($userJob)
|
|
->setUser($this);
|
|
|
|
$this->jobHistories[] = $newJob;
|
|
|
|
$criteria = new Criteria();
|
|
$criteria->orderBy(['startDate' => \Doctrine\Common\Collections\Order::Ascending, 'id' => \Doctrine\Common\Collections\Order::Ascending]);
|
|
|
|
/** @var \Iterator $jobs */
|
|
$jobs = $this->jobHistories->matching($criteria)->getIterator();
|
|
$jobs->rewind();
|
|
|
|
do {
|
|
/** @var UserJobHistory $current */
|
|
$current = $jobs->current();
|
|
$jobs->next();
|
|
|
|
if ($jobs->valid()) {
|
|
$next = $jobs->current();
|
|
$current->setEndDate($next->getStartDate());
|
|
}
|
|
} while ($jobs->valid());
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Set username.
|
|
*
|
|
* @return User
|
|
*/
|
|
public function setUsername(?string $name)
|
|
{
|
|
$this->username = (string) $name;
|
|
|
|
if ('' === trim($this->getLabel())) {
|
|
$this->setLabel($name);
|
|
}
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* @return $this
|
|
*/
|
|
public function setUsernameCanonical(?string $usernameCanonical)
|
|
{
|
|
$this->usernameCanonical = $usernameCanonical;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function unsetAttribute($key): self
|
|
{
|
|
unset($this->attributes[$key]);
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function setPhonenumber(?PhoneNumber $phonenumber): self
|
|
{
|
|
$this->phonenumber = $phonenumber;
|
|
|
|
return $this;
|
|
}
|
|
}
|