Skip to content

Instantly share code, notes, and snippets.

@BonBonSlick
Last active April 5, 2024 16:32
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save BonBonSlick/c815b67e3a9b86281b10482ba0a22ff3 to your computer and use it in GitHub Desktop.
Save BonBonSlick/c815b67e3a9b86281b10482ba0a22ff3 to your computer and use it in GitHub Desktop.
DateTime_$airAt
<?php
/*
* (c) BonBonSlick - 7/27/21, 1:51 PM
*/
// my own project snippet to create dynamic statuses based on nested content dates
declare(strict_types=1);
/*
* Created by BonBonSlick
*/
namespace App\Domain\Core\Traits;
use App\Domain\AnimePack\Anime\Model\Anime;
use App\Domain\Core\DTO\DataTypes\BooleanValue;
use App\Domain\Core\ValueObjects\Time\AiredAt;
use App\Domain\EpisodePack\Episode\Model\Episode;
use App\Domain\StoryElement\Model\StoryElement;
use App\Infrastructure\Traits\Helper\TimeHelperTrait;
use DateTimeImmutable;
use DateTimeInterface;
/**
* In case AnimeComponent is being CANCELLED, element must be removed
*
* @todo - maybe in future add cancelledAt date, but do we keep cancelled anime / episodes / seasons??
*/
trait EntityAiredAtTrait
{
protected AiredAt $airAt;
use TimeHelperTrait;
final public function air(DateTimeImmutable $time = null): void
{
if (true === $this->airAt->isAnnounced()->isNegative()) {
$this->announce();
}
$this->airAt = new AiredAt(airAt: $time ?? new DateTimeImmutable());
}
/**
* air date is usually unknown
*/
public function announce(DateTimeImmutable $time = null): void
{
$now = new DateTimeImmutable();
if (false === $time instanceof DateTimeInterface || $time > $now) {
$time = $now;
}
$this->airAt = new AiredAt(announcedAt: $time);
if (true === $this instanceof Episode) {
// this is used to prevent infinite RECURSION!
return;
}
$announceNestedModels = static function (StoryElement|Episode $model) use ($time): void {
// WARNING!! Possible infinite RECURSION!!
$model->announce($time);
};
if (true === $this instanceof Anime || true === $this instanceof StoryElement) {
$this->storyElements()->forAll(
static function (StoryElement $model) use ($announceNestedModels) {
$announceNestedModels($model);
}
);
// episodes must be updated the last, because its the deepest tree root element
$this->episodes()->forAll(
static function (Episode $model) use ($announceNestedModels): void {
$announceNestedModels($model);
}
);
}
}
public function isAired(): BooleanValue
{
if (true === $this instanceof Episode && true === $this->airAt->airAt() instanceof DateTimeInterface) {
return new BooleanValue(
$this->airAt->airAt() <= new DateTimeImmutable()
);
}
$returnValue = new BooleanValue();
$now = new DateTimeImmutable();
$isModelAired = static function (Anime|StoryElement|Episode $model) use (&$returnValue, $now): void {
$returnValue = new BooleanValue(true === $model->airAt->airAt() <= $now);
};
if (true === $this instanceof Anime || true === $this instanceof StoryElement) {
$this->episodes()->forAll(
static function (Episode $model) use ($isModelAired): void {
$isModelAired($model);
}
);
$this->storyElements()->forAll(
static function (StoryElement $model) use ($isModelAired) {
$isModelAired($model);
}
);
}
return $returnValue;
}
public function isOngoing(): BooleanValue
{
$returnValue = new BooleanValue();
if (true === $this instanceof Episode) {
return $returnValue;
}
$self = $this;
$isModelToBeAired = static function (Anime|StoryElement|Episode $model) use (&$returnValue, $self): void {
$isExtraContentWillBeReleasedInFuture = true === $model->airAt->isAnnounced()->isPositive() ||
true === $model->airAt->isUpcoming()->isPositive();
$returnValue = new BooleanValue(
true === $self->airAt->isUpcoming()->isPositive() && true === $isExtraContentWillBeReleasedInFuture
);
};
if (true === $this instanceof Anime || true === $this instanceof StoryElement) {
$this->storyElements()->forAll(
static function (StoryElement $model) use ($isModelToBeAired) {
$isModelToBeAired($model);
}
);
$this->episodes()->forAll(
static function (Episode $model) use ($isModelToBeAired): void {
$isModelToBeAired($model);
}
);
}
return $returnValue;
}
}
<?php
/*
* (c) BonBonSlick
*/
declare(strict_types=1);
namespace App\Domain\Core\ValueObjects\Time;
use App\Domain\Core\DTO\DataTypes\BooleanValue;
use App\Domain\Core\ValueObjects\Abstracts\AbstractVO;
use DateTimeImmutable;
use DateTimeInterface;
use function get_object_vars;
/**
*
* Anime - announced - upcoming - ongoing - released
* StoryElement - announced - upcoming - ongoing - released
* Episode - announced - upcoming - released
*
*
* These are dynamic statuses which we get from just using airAt and announceAt timestamps
* This system design is used to sync dynamic statuses automaticcally between related hierarchy of related domain models
* Yes, this is very bad design if you have large set of data, but in context of anime, data sets usually small
* the rest edge cases can be optimized using cache
*
* case UPCOMING = 'upcoming';
* case ONGOING = 'ongoing';
* case RELEASED = 'released';
* case CANCELLED = 'cancelled';
*/
final class AiredAt extends AbstractVO
{
public function __construct(
// - when element is going to be shown to the public, the date is known
protected readonly ?DateTimeImmutable $airAt = null,
// - the date when something was announced to be aired, the date is unknown yet
protected readonly ?DateTimeImmutable $announcedAt = null,
) {
}
public function isAnnounced(): BooleanValue
{
return new BooleanValue(true === $this->announcedAt() instanceof DateTimeImmutable);
}
public function announcedAt(): ?DateTimeImmutable
{
return $this->announcedAt;
}
public function isUpcoming(): BooleanValue
{
$returnValue = new BooleanValue();
if (true === $this->airAt() instanceof DateTimeInterface) {
$returnValue = new BooleanValue(
$this->airAt() > new DateTimeImmutable()
);
}
return $returnValue;
}
public function airAt(): ?DateTimeImmutable
{
return $this->airAt;
}
public function value(): object
{
return (object)get_object_vars($this);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment