Skip to content

Instantly share code, notes, and snippets.

@ggagosh
Created March 31, 2023 13:28
Show Gist options
  • Save ggagosh/c27b3fa805c2a2519225031d9505565e to your computer and use it in GitHub Desktop.
Save ggagosh/c27b3fa805c2a2519225031d9505565e to your computer and use it in GitHub Desktop.
/**
* Validates the entity against its configuration.
*
* @return bool
* TRUE if the entity is valid, FALSE otherwise.
*/
public function validate(): bool {
foreach ($this->config as $type => $type_config) {
// Check if the entity has any days of this type.
if (!isset($this->data[$type])) {
continue;
}
// Check if the entity has too many days of this type.
$count = count($this->data[$type]);
if ($count > $type_config['have']) {
return FALSE;
}
// Check if the entity has too many days of this type in a row.
$max = $type_config['max'];
$current = 0;
foreach ($this->data[$type] as $day) {
if (isset($prev) && ($day->getTimestamp() - $prev->getTimestamp()) / (60 * 60 * 24) <= 1) {
$current++;
if ($current > $max) {
return FALSE;
}
}
else {
$current = 1;
}
$prev = $day;
}
// Check if the entity has enough days between days of this type.
$between = $type_config['between'];
foreach ($this->data[$type] as $key => $day) {
if (isset($prev) && ($day->getTimestamp() - $prev->getTimestamp()) / (60 * 60 * 24) < $between) {
return FALSE;
}
$prev = $day;
}
// Check if the entity has enough days before the first day of this type.
$pre = $type_config['pre'];
$first_day = min($this->data[$type]);
$days_since_first_day = ($first_day->getTimestamp() - $this->start_date->getTimestamp()) / (60 * 60 * 24);
if ($days_since_first_day < $pre) {
return FALSE;
}
// Check if the entity has days of this type that are forced to be in a row.
if ($type_config['force_together']) {
foreach ($this->data[$type] as $key => $day) {
if (isset($prev) && ($day->getTimestamp() - $prev->getTimestamp()) / (60 * 60 * 24) > 1) {
return FALSE;
}
$prev = $day;
}
}
}
return TRUE;
}
/**
* Calculate number of days between two dates, excluding weekends.
*
* @param \DateTime $start_date
* The start date.
* @param \DateTime $end_date
* The end date.
*
* @return int
* The number of days.
*/
protected function calculateDays(\DateTime $start_date, \DateTime $end_date) {
$days = 0;
$interval = new \DateInterval('P1D');
$period = new \DatePeriod($start_date, $interval, $end_date->modify('+1 day'));
foreach ($period as $date) {
if ($date->format('N') < 6) {
$days++;
}
}
return $days;
}
use Drupal\Core\Entity\EntityChangedTrait;
use Drupal\Core\Entity\ContentEntityBase;
class MyEntity extends ContentEntityBase implements MyEntityInterface {
use EntityChangedTrait;
public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
$fields = parent::baseFieldDefinitions($entity_type);
$fields['start_date'] = BaseFieldDefinition::create('datetime')
->setLabel(t('Start Date'))
->setRequired(TRUE);
$fields['end_date'] = BaseFieldDefinition::create('datetime')
->setLabel(t('End Date'))
->setRequired(TRUE);
return $fields;
}
public function getStartDate() {
return $this->get('start_date')->date;
}
public function setStartDate(\DateTime $start_date) {
$this->set('start_date', $start_date);
return $this;
}
public function getEndDate() {
return $this->get('end_date')->date;
}
public function setEndDate(\DateTime $end_date) {
$this->set('end_date', $end_date);
return $this;
}
public function validate(MyEntityInterface $entity) {
$errors = [];
// Check if start date is before end date.
if ($entity->getStartDate() >= $entity->getEndDate()) {
$errors[] = t('Start date must be before end date.');
}
// Check if entity is within allowed maximum number of days.
$entity_type = $entity->getEntityType()->id();
$days = $this->calculateDays($entity->getStartDate(), $entity->getEndDate());
if ($entity_type == 'vacation' && $days > 10) {
$errors[] = t('Vacation cannot exceed 10 days.');
}
elseif ($entity_type == 'day_off' && $days > 1) {
$errors[] = t('Day off cannot exceed 1 day.');
}
elseif ($entity_type == 'sick_leave' && $days > 1) {
$errors[] = t('Sick leave cannot exceed 1 day.');
}
elseif ($entity_type == 'medical_leave' && $days > 60) {
$errors[] = t('Medical leave cannot exceed 60 days.');
}
elseif ($entity_type == 'unpaid_leave' && $days > 10) {
$errors[] = t('Unpaid leave cannot exceed 10 days.');
}
// Check if entity is within allowed minimum number of days.
$pre_days = $this->calculateDays($this->getPreviousEndDate(), $entity->getStartDate());
if ($entity_type == 'vacation' && $pre_days < 10) {
$errors[] = t('There must be at least 10 working days between vacation periods.');
}
elseif ($entity_type == 'day_off' && $pre_days < 1) {
$errors[] = t('There must be at least 1 day between day off periods.');
}
// Check if entity is allowed to be forced together.
if ($entity_type == 'vacation' && !$entity->isForceTogether()) {
$errors[] = t('Vacation days must be taken in a row.');
}
elseif ($entity_type == 'unpaid_leave' && !$entity->isForceTogether()) {
$errors[] = t('Unpaid leave days must be taken in a row.');
}
return $errors;
use Drupal\Core\Entity\ContentEntityInterface;
interface MyEntityInterface extends ContentEntityInterface {
/**
* Gets the start date of the entity.
*
* @return \DateTime
* The start date of the entity.
*/
public function getStartDate();
/**
* Sets the start date of the entity.
*
* @param \DateTime $start_date
* The start date of the entity.
*
* @return $this
*/
public function setStartDate(\DateTime $start_date);
/**
* Gets the end date of the entity.
*
* @return \DateTime
* The end date of the entity.
*/
public function getEndDate();
/**
* Sets the end date of the entity.
*
* @param \DateTime $end_date
* The end date of the entity.
*
* @return $this
*/
public function setEndDate(\DateTime $end_date);
/**
* Validates the entity.
*
* @param \Drupal\my_module\Entity\MyEntityInterface $entity
* The entity to validate.
*
* @return array
* An array of error messages.
*/
public function validate(MyEntityInterface $entity);
/**
* Checks if the entity is valid.
*
* @return bool
* TRUE if the entity is valid, FALSE otherwise.
*/
public function isValid();
/**
* Checks if the entity is a vacation day.
*
* @return bool
* TRUE if the entity is a vacation day, FALSE otherwise.
*/
public function isVacation();
/**
* Checks if the entity is a day off.
*
* @return bool
* TRUE if the entity is a day off, FALSE otherwise.
*/
public function isDayOff();
/**
* Checks if the entity is a sick leave day.
*
* @return bool
* TRUE if the entity is a sick leave day, FALSE otherwise.
*/
public function isSickLeave();
/**
* Checks if the entity is a medical leave day.
*
* @return bool
* TRUE if the entity is a medical leave day, FALSE otherwise.
*/
public function isMedicalLeave();
/**
* Checks if the entity is an unpaid leave day.
*
* @return bool
* TRUE if the entity is an unpaid leave day, FALSE otherwise.
*/
public function isUnpaidLeave();
/**
* Checks if the entity is a working day.
*
* @return bool
* TRUE if the entity is a working day, FALSE otherwise.
*/
public function isWorkingDay();
}
@ggagosh
Copy link
Author

ggagosh commented Mar 31, 2023

<?php

use PHPUnit\Framework\TestCase;
use MyEntity\MyEntity;
use DateTime;
use Spatie\Period\Period;

class MyEntityTest extends TestCase
{
    private $myEntity;

    public function setUp(): void
    {
        $config = [
            "vacation" => [
                "have" => "24 wd",
                "max" => "10 wd",
                "between" => "10 wd",
                "pre" => "10 d",
                "force_together" => true
            ],
            "day_off" => [
                "have" => "5 wd",
                "max" => "1 wd",
                "between" => "1 d",
                "pre" => "0 d"
            ],
            "sick_leave" => [
                "have" => "5 wd",
                "max" => "1 wd",
                "between" => "0 d",
                "pre" => "0 d"
            ],
            "medical_leave" => [
                "have" => "60 d",
                "max" => "60 d",
                "between" => "0 d",
                "pre" => "0 d",
                "force_together" => true
            ],
            "unpaid_leave" => [
                "have" => "10 d",
                "max" => "10 d",
                "between" => "10 d",
                "pre" => "10 d",
                "force_together" => true
            ]
        ];
        $this->myEntity = new MyEntity($config);
    }

    public function testIsValidDayType()
    {
        $this->assertTrue($this->myEntity->isValidDayType("vacation"));
        $this->assertTrue($this->myEntity->isValidDayType("day_off"));
        $this->assertFalse($this->myEntity->isValidDayType("invalid_type"));
    }

    public function testIsValidDate()
    {
        $valid_date = new DateTime('now +20 days');
        $this->assertTrue($this->myEntity->isValidDate($valid_date));
        $invalid_date = new DateTime('now +365 days');
        $this->assertFalse($this->myEntity->isValidDate($invalid_date));
    }

    public function testIsValidPeriod()
    {
        $valid_start_date = new DateTime('now +20 days');
        $valid_end_date = new DateTime('now +25 days');
        $valid_period = new Period($valid_start_date, $valid_end_date);
        $this->assertTrue($this->myEntity->isValidPeriod("vacation", $valid_period));
        
        $invalid_start_date = new DateTime('now +10 days');
        $invalid_end_date = new DateTime('now +30 days');
        $invalid_period = new Period($invalid_start_date, $invalid_end_date);
        $this->assertFalse($this->myEntity->isValidPeriod("vacation", $invalid_period));
    }

    public function testCalculateDays()
    {
        $valid_start_date = new DateTime('now +20 days');
        $valid_end_date = new DateTime('now +25 days');
        $valid_period = new Period($valid_start_date, $valid_end_date);
        $this->assertEquals(5, $this->myEntity->calculateDays("vacation", $valid_period));
        
        $invalid_start_date = new DateTime('now +10 days');
        $invalid_end_date = new DateTime('now +30 days');
        $invalid_period = new Period($invalid_start_date, $invalid_end_date);
        $this->expectException(InvalidArgumentException::class);
       

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment