<?php
namespace App\Domain\Entity;
use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\Delete;
use ApiPlatform\Metadata\Get;
use ApiPlatform\Metadata\GetCollection;
use ApiPlatform\Metadata\Patch;
use ApiPlatform\Metadata\Post;
use ApiPlatform\Metadata\Put;
use App\Application\ApiResources\State\Provider\Trip\TripGetCollectionProvider;
use App\Application\Controller\Api\Trip\DuplicateAction;
use App\Application\Controller\Api\Trip\ExportAction;
use App\Application\Controller\Api\Trip\GetPdfAction;
use App\Application\Controller\Api\Trip\GetTripAction;
use App\Application\Controller\Api\Trip\PostAction;
use App\Application\Controller\Api\Trip\PutAction;
use App\Application\Controller\Api\Trip\SearchAction;
use App\Application\Controller\Api\Trip\ShareTripAction;
use App\Application\Controller\Api\Trip\UserShareTripAction;
use App\Application\Dto\Trip\ShareTripInput;
use App\Application\Dto\Trip\TripSearchOutput;
use App\Application\Dto\Trip\UserShareTripInput;
use App\Domain\Entity\Behavior\Identifiable;
use App\Domain\Entity\Comment\TripComment;
use App\Domain\Entity\Enumeration\TripAttribute;
use App\Domain\Entity\Translation\TripTranslation;
use DateTime;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Gedmo\SoftDeleteable\Traits\SoftDeleteable;
use Gedmo\Timestampable\Traits\Timestampable;
use Locastic\ApiPlatformTranslationBundle\Model\AbstractTranslatable;
use Locastic\ApiPlatformTranslationBundle\Model\TranslationInterface;
/**
* Class Trip
*
* @package App\Domain\Entity
*/
#[ApiResource(
operations: [
new Delete(security: "is_granted('ROLE_ADMIN') or object.author.id == user.id"),
new GetCollection(
uriTemplate: '/trips/export',
controller: ExportAction::class,
),
new GetCollection(
normalizationContext: ['groups' => ['trip_list']],
filters: [
'trip.query_filter',
'trip.order_filter',
'trip.search_filter',
'trip.range_filter',
'soft_delete_filter',
],
provider: TripGetCollectionProvider::class,
),
new GetCollection(
uriTemplate: '/trips/limited_infos',
normalizationContext: ['groups' => ['trip_limited_infos']],
filters: [
'trip.query_filter',
'trip.order_filter',
'trip.search_filter',
'trip.range_filter',
'soft_delete_filter',
],
),
new GetCollection(
uriTemplate: '/trips/elastic',
controller: SearchAction::class,
output: TripSearchOutput::class,
name: 'elastic_trips',
),
new GetCollection(
uriTemplate: '/trips/list',
security: "is_granted('ROLE_ADMIN') or object.author.id == user.id",
normalizationContext: ['groups' => ['trip_board_list']],
filters: [
'trip.query_filter',
'trip.order_filter',
'trip.search_filter',
'trip.range_filter',
'soft_delete_filter',
],
),
new Get(uriTemplate: '/trips/{uuid}', controller: GetTripAction::class),
new Get(uriTemplate: '/trips/{uuid}/pdf', controller: GetPdfAction::class),
new Get(uriTemplate: '/trips/{uuid}/duplicate', controller: DuplicateAction::class),
new Put(
uriTemplate: '/trips/{uuid}/share',
controller: UserShareTripAction::class,
security: "is_granted('ROLE_USER')",
input: UserShareTripInput::class,
),
new Post(
uriTemplate: '/trips/share',
controller: ShareTripAction::class,
security: "is_granted('ROLE_USER')",
input: ShareTripInput::class
),
new Post(controller: PostAction::class, security: "is_granted('ROLE_USER')"),
new Put(
controller: PutAction::class,
security: "is_granted('ROLE_ADMIN') or object.author.id == user.id"
),
new Patch(
security: "is_granted('ROLE_ADMIN') or object.author.id == user.id"
)
],
normalizationContext: ['groups' => ['trip_read']],
denormalizationContext: ['groups' => ['trip_write']]
)]
class Trip extends AbstractTranslatable
{
use Identifiable;
use Timestampable;
use SoftDeleteable;
public Collection $attributes;
public Collection $categories;
public Collection $clonedTrips;
public Collection $comments;
public Collection $countries;
/**
* @var Collection<TripMapPoint>
*/
public Collection $directionWaypoints;
public function __construct(
public float $totalDistance,
public int $totalDuration,
public int $stepsCount,
public bool $avoidHighways = false,
public bool $avoidTolls = false,
public bool $topTrip = false,
public bool $favorite = false,
public bool $shared = false,
public ?string $description = null,
public ?TripMapPoint $directionStart = null,
public ?Media $photo = null,
public ?Media $banner = null,
public ?TripMapPoint $directionEnd = null,
public ?DateTime $startDate = null,
public ?DateTime $endDate = null,
public ?Person $author = null,
public ?string $authorName = null,
public ?Trip $clonedFrom = null,
public float $averageNotation = 0,
public ?string $remoteId = null,
public ?string $recoRemoteId = null,
public ?DateTime $zhistUpdate = null,
public array $polylines = [],
/** for dto only */
public ?string $nextTrip = null,
public ?string $previousTrip = null,
) {
parent::__construct();
$this->attributes = new ArrayCollection();
$this->directionWaypoints = new ArrayCollection();
$this->clonedTrips = new ArrayCollection();
$this->comments = new ArrayCollection();
$this->countries = new ArrayCollection();
$this->categories = new ArrayCollection();
}
public function __clone()
{
$this->id = null;
$this->remoteId = null;
$this->recoRemoteId = null;
$this->uuid = null;
$this->createdAt = null;
$this->updatedAt = null;
$this->topTrip = false;
$this->favorite = false;
$this->shared = false;
$this->comments = new ArrayCollection();
if ($this->directionStart) {
$this->directionStart = clone $this->directionStart;
}
if ($this->directionEnd) {
$this->directionEnd = clone $this->directionEnd;
}
$waypoints = $this->directionWaypoints->toArray();
$this->directionWaypoints = new ArrayCollection();
foreach ($waypoints as $waypoint) {
$this->addDirectionWaypoint(clone $waypoint);
}
$attributes = $this->attributes->toArray();
$this->attributes = new ArrayCollection();
foreach ($attributes as $attribute) {
$this->addTripAttribute(clone $attribute);
}
$translations = $this->translations->toArray();
$this->translations = new ArrayCollection();
foreach ($translations as $translation) {
$this->addTranslation(clone $translation);
}
return $this;
}
public function hasDirectionWaypoint(TripMapPoint $mapPoint): bool
{
return $this->directionWaypoints->contains($mapPoint);
}
public function addDirectionWaypoint(TripMapPoint $mapPoint): void
{
if ($this->directionWaypoints->contains($mapPoint)) {
return;
}
$this->directionWaypoints->add($mapPoint);
}
public function addTripAttribute(TripAttribute $tripAttribute): void
{
if (!$this->attributes->contains($tripAttribute)) {
$this->attributes->add($tripAttribute);
}
}
public function getClonedFromId(): int | null
{
return $this->clonedFrom?->id;
}
public function removeDirectionWaypoint(TripMapPoint $mapPoint): void
{
if ($this->directionWaypoints->contains($mapPoint)) {
$this->directionWaypoints->removeElement($mapPoint);
}
}
public function removeTripAttribute(TripAttribute $tripAttribute): void
{
if ($this->attributes->contains($tripAttribute)) {
$this->attributes->removeElement($tripAttribute);
}
}
public function getName(?string $locale = null): ?string
{
return $this->getTranslationType($locale)->getName();
}
public function getTranslationType(?string $locale = null): TripTranslation
{
return $this->getTranslation($locale);
}
public function setName(?string $name): void
{
$this->getTranslationType()->setName($name);
}
public function createTranslation(): TranslationInterface
{
return new TripTranslation();
}
public function getClonedCount(): int
{
return $this->clonedTrips->filter(fn($trip) => $trip->deletedAt === null)->count();
}
public function getClonedTrips(): Collection
{
return $this->clonedTrips;
}
public function setClonedTrips(Collection $clonedTrips): void
{
$this->clonedTrips = $clonedTrips;
}
public function addClonedTrip(Trip $trip): void
{
if ($this->clonedTrips->contains($trip)) {
return;
}
$this->clonedTrips->add($trip);
}
public function removeClonedTrip(Trip $trip): void
{
if (!$this->clonedTrips->contains($trip)) {
return;
}
$this->clonedTrips->removeElement($trip);
}
/**
* @return Collection
*/
public function getCountries(): Collection
{
return $this->countries;
}
/**
* @param Collection $countries
*/
public function setCountries(Collection $countries): void
{
$this->countries = $countries;
}
/**
* @param TripCountry $country
*/
public function addCountry(TripCountry $country): void
{
if ($this->countries->contains($country)) {
return;
}
$this->countries->add($country);
}
/**
* @param TripCountry $country
*/
public function removeCountry(TripCountry $country): void
{
if (!$this->countries->contains($country)) {
return;
}
$this->countries->removeElement($country);
}
/**
* @return Collection
*/
public function getCategories(): Collection
{
return $this->categories;
}
/**
* @param Collection $categories
*/
public function setCategories(Collection $categories): void
{
$this->categories = $categories;
}
/**
* @param TripCategory $category
*/
public function addCategory(TripCategory $category): void
{
if ($this->categories->contains($category)) {
return;
}
$this->categories->add($category);
}
/**
* @param TripCategory $category
*/
public function removeCategory(TripCategory $category): void
{
if (!$this->categories->contains($category)) {
return;
}
$this->categories->removeElement($category);
}
/**
* @return Collection
*/
public function getComments(): Collection
{
return $this->comments->filter(static fn(TripComment $c) => !$c->isDeleted());
}
/**
* @param Collection $comments
*/
public function setComments(Collection $comments): void
{
$this->comments = $comments;
}
/**
* @param TripComment $comment
*/
public function addComment(TripComment $comment): void
{
if ($this->comments->contains($comment)) {
return;
}
$this->comments->add($comment);
}
/**
* @param TripComment $comment
*/
public function removeComment(TripComment $comment): void
{
if (!$this->comments->contains($comment)) {
return;
}
$this->comments->removeElement($comment);
}
public function getAuthorName(): ?string
{
if ($this->authorName) {
return $this->authorName;
}
return $this->author?->getFullname();
}
public function getDurationString(): string
{
if ($this->totalDuration <= 0) {
return '0h 0min';
}
$timeInMinutes = floor($this->totalDuration / 60);
$hours = floor($timeInMinutes / 60);
$minutes = $timeInMinutes % 60;
return "{$hours}h {$minutes}min";
}
public function isElasticSearchPublishable(): bool
{
return $this->deletedAt === null;
}
}