ebs/src/Entity/ServiceRequest.php
2023-12-21 08:49:38 +01:00

334 lines
8.8 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Entity;
use ApiPlatform\Metadata\ApiProperty;
use App\Doctrine\Behavior\TimestampableEntity;
use App\Enum\ServiceRequest\ServiceRequestStatus;
use App\Form\Type\ServiceRequest\CreateServiceRequestType;
use App\Repository\ServiceRequestRepository;
use App\Validator as AppAssert;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\IdGenerator\UuidGenerator;
use Symfony\Component\Uid\Uuid;
use Symfony\Component\Validator\Constraints as Assert;
#[ORM\Entity(repositoryClass: ServiceRequestRepository::class)]
#[ORM\Table]
#[ORM\Index(columns: ['owner_id', 'recipient_id'])]
#[AppAssert\Constraints\ServiceRequest\ProductAvailabilityNoOverlap(
groups: [CreateServiceRequestType::class]
)]
class ServiceRequest implements \Stringable
{
use TimestampableEntity;
/**
* Generates a V6 uuid.
*/
#[ORM\Id]
#[ORM\Column(type: 'uuid', unique: true)]
#[ORM\GeneratedValue(strategy: 'CUSTOM')]
#[ORM\CustomIdGenerator(class: UuidGenerator::class)]
#[ApiProperty(identifier: true)]
protected Uuid $id;
/**
* Lender/provider, it's the user that owns the product (object/service). It
* can be retrieved from the product, but it's easier to have here for the admin
* pages (filtering).
*/
#[ORM\ManyToOne(targetEntity: User::class)]
#[ORM\JoinColumn(nullable: false, onDelete: 'CASCADE')] // if the lender is deleted then the service will also be deleted without constraint error
#[Assert\NotBlank]
protected User $owner;
/**
* Related product or service.
*/
#[ORM\ManyToOne(targetEntity: Product::class, inversedBy: 'serviceRequests')]
#[ORM\JoinColumn(nullable: false, onDelete: 'CASCADE')] // if the product is deleted then the service will also be deleted without constraint error
#[Assert\NotBlank]
protected Product $product;
/**
* Recipient, it's the borrower of the object or the recipient of the service.
*/
#[ORM\ManyToOne(targetEntity: User::class)]
#[ORM\JoinColumn(nullable: false, onDelete: 'CASCADE')] // if the borrower is deleted then the service will also be deleted without constraint error
#[Assert\NotBlank]
protected User $recipient;
/**
* Status of the request service, see full documentation in the enumeration class.
* This status field is managed by a workflow and shouldn't never be changed
* manually.
*/
#[ORM\Column(type: 'string', nullable: false, enumType: ServiceRequestStatus::class)]
#[Assert\NotBlank]
protected ServiceRequestStatus $status = ServiceRequestStatus::NEW;
/**
* Planned starting date for the request service. When the user will retrieve
* the object or date of the rendez-vous for the service.
*/
#[ORM\Column(type: Types::DATETIME_IMMUTABLE)]
protected \DateTimeImmutable $startAt;
/**
* Planned ending date for the request service: when the borrower will bring
* back the oject of the owner or end date of the service. For a service, the
* end date will be generally equal to the start at.
* eg: the service is for one hour.
*
* @todo endDate should be >= startDate (can be the same day)
*/
#[ORM\Column(type: Types::DATETIME_IMMUTABLE)]
protected \DateTimeImmutable $endAt;
/**
* Virtual field for the request service creation. This is the optional message
* sent by the borrower to the lender a the request service creation. It is stored
* in the conversation thread of the request service not the request service itself.
*/
protected ?string $message = null;
/**
* @var Collection<int, Message> $messages
*/
#[ORM\OneToMany(mappedBy: 'serviceRequest', targetEntity: Message::class, cascade: ['persist', 'remove', 'detach'])]
#[ORM\OrderBy(['createdAt' => 'ASC'])]
private Collection $messages;
public function __construct()
{
$this->messages = new ArrayCollection();
}
public function __toString()
{
return (string) $this->id;
}
public function getId(): Uuid
{
return $this->id;
}
public function setId(Uuid $id): ServiceRequest
{
$this->id = $id;
return $this;
}
public function getOwner(): User
{
return $this->owner;
}
public function isOwner(?User $user): bool
{
return $this->owner === $user;
}
public function setOwner(User $owner): ServiceRequest
{
$this->owner = $owner;
return $this;
}
public function getProduct(): Product
{
return $this->product;
}
public function setProduct(Product $product): ServiceRequest
{
$this->product = $product;
return $this;
}
public function getRecipient(): User
{
return $this->recipient;
}
public function isRecipient(User $user): bool
{
return $this->recipient === $user;
}
public function setRecipient(User $recipient): ServiceRequest
{
$this->recipient = $recipient;
return $this;
}
public function getStatus(): ServiceRequestStatus
{
return $this->status;
}
/**
* This function should never be called manually, except in tests.
*
* @internal
*/
public function setStatus(ServiceRequestStatus $status): ServiceRequest
{
$this->status = $status;
return $this;
}
/**
* This is needed for the workflow that works with strings, not enum.
*
* @internal
*/
public function getStatusRaw(): string
{
return $this->status->value;
}
/**
* This function should never be called manually.
*
* @internal
*/
public function setStatusRaw(string $status): ServiceRequest
{
$this->status = ServiceRequestStatus::from($status);
return $this;
}
public function getStartAt(): \DateTimeImmutable
{
return $this->startAt;
}
public function setStartAt(\DateTimeImmutable $startAt): ServiceRequest
{
$this->startAt = $startAt;
return $this;
}
public function getEndAt(): \DateTimeImmutable
{
return $this->endAt;
}
public function setEndAt(\DateTimeImmutable $endAt): ServiceRequest
{
$this->endAt = $endAt;
return $this;
}
public function getMessage(): ?string
{
return $this->message;
}
public function setMessage(?string $message): ServiceRequest
{
$this->message = $message;
return $this;
}
/**
* @return Collection<int, Message>
*/
public function getMessages(): Collection
{
return $this->messages;
}
/**
* @param Collection<int, Message> $messages
*/
public function setMessages(Collection $messages): self
{
$this->messages = $messages;
return $this;
}
public function addMessage(Message $message): self
{
if (!$this->messages->contains($message)) {
$this->messages->add($message);
$message->setServiceRequest($this);
}
return $this;
}
public function removeMessage(Message $item): self
{
$this->messages->removeElement($item);
return $this;
}
public function messagesCount(): int
{
return $this->messages->count();
}
public function isLoan(): bool
{
return $this->product->getType()->isObject();
}
public function isService(): bool
{
return $this->product->getType()->isService();
}
// end of basic 'etters ————————————————————————————————————————————————————
public function isOwnerOrRecipient(User $user): bool
{
return $this->owner === $user || $this->recipient === $user;
}
public function hasUnreadMessages(User $user): bool
{
$messages = $this->messages->filter(
fn (Message $message) => $this->isOwner($user) ?
!$message->isOwnerRead() :
!$message->isRecipientRead());
return !$messages->isEmpty();
}
/**
* The the other user involved in the service request.
*/
public function getOtherUser(?User $actor): User
{
return $this->isOwner($actor) ? $this->getRecipient() : $this->getOwner();
}
/**
* Get the finalized date from the end date. We consider a service request is
* finished the day just after the end date to avoid overlap problems.
*/
public function getFinalizedAt(): \DateTimeImmutable
{
return $this->getEndAt()->modify('tomorrow midnight');
}
}