t1k3/laravel-calendar-event

View on GitHub
src/Models/TemplateCalendarEvent.php

Summary

Maintainability
A
1 hr
Test Coverage
<?php

namespace T1k3\LaravelCalendarEvent\Models;

use Carbon\Carbon;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Database\Query\Builder;
use T1k3\LaravelCalendarEvent\Enums\RecurringFrequenceType;
use T1k3\LaravelCalendarEvent\Interfaces\PlaceInterface;
use T1k3\LaravelCalendarEvent\Interfaces\TemplateCalendarEventInterface;
use T1k3\LaravelCalendarEvent\Interfaces\UserInterface;

/**
 * Class TemplateCalendarEvent
 * @package T1k3\LaravelCalendarEvent\Models
 */
class TemplateCalendarEvent extends AbstractModel implements TemplateCalendarEventInterface
{
    use SoftDeletes;

    /**
     * Fillable
     * @var array
     */
    protected $fillable = [
        'title',
        'start_datetime',
        'end_datetime',
        'description',
        'is_recurring',
        'end_of_recurring',
        'frequence_number_of_recurring',
        'frequence_type_of_recurring',
        'is_public',
    ];

    /**
     * Attribute Casting
     * @var array
     */
    protected $casts = [
        'is_recurring'     => 'boolean',
        'is_public'        => 'boolean',
        'start_datetime'   => 'datetime',
        'end_datetime'     => 'datetime',
        'end_of_recurring' => 'datetime',
    ];

    /**
     * TemplateCalendarEvent parent
     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
     */
    public function parent()
    {
        return $this->belongsTo(TemplateCalendarEvent::class, 'parent_id');
    }

    /**
     * CalendarEvent
     * @return \Illuminate\Database\Eloquent\Relations\HasMany
     */
    public function events()
    {
        return $this->hasMany(CalendarEvent::class, 'template_calendar_event_id');
    }

    /**
     * @param $query
     * @return Builder
     */
    public function scopeRecurring($query)
    {
        return $query->where('is_recurring', true);
    }

    /**
     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo|null
     */
    public function user()
    {
//        TODO If the config is null then you can not use ->user (LogicException), just ->user()
        $class = config('calendar-event.user.model');
        return $class ? $this->belongsTo($class) : null;
    }

    /**
     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo|null
     */
    public function place()
    {
//        TODO If the config is null then you can not use ->place (LogicException), just ->place()
        $class = config('calendar-event.place.model');
        return $class ? $this->belongsTo($class) : null;
    }

    /**
     * Create Calendar Event for TemplateCalendarEvent
     * @param \DateTimeInterface $startDateTime
     * @return \Illuminate\Database\Eloquent\Model
     */
    public function createCalendarEvent(\DateTimeInterface $startDate)
    {
        $diffInSeconds = $this->start_datetime->diffInSeconds($this->end_datetime);
        $endDate    = clone($startDate);
        $endDate    = $endDate->addSeconds($diffInSeconds);
        $calendarEvent = $this->events()->make([
            'start_datetime' => $startDate,
            'end_datetime'   => Carbon::parse( $endDate->format('Y-m-d') . ' ' . $this->end_datetime->format('H:i:s') ),
        ]);
        
        $calendarEvent->template()->associate($this);
        $calendarEvent->save();

        return $calendarEvent;
    }

    /**
     * Create or get calendar event
     * @param \DateTimeInterface $startDateTime
     * @return \Illuminate\Database\Eloquent\Model|null|static
     */
    public function createOrGetCalendarEvent(\DateTimeInterface $startDateTime)
    {
        $calendarEvent = $this->events()->where('start_datetime', $startDateTime)->first();
        if (!$calendarEvent) {
            $calendarEvent = $this->createCalendarEvent($startDateTime);
        }

        return $calendarEvent;
    }

    /**
     * Edit calendar event | Exist or not | Check data
     * @param \DateTimeInterface $startDateTime
     * @param array $attributes
     * @param UserInterface|null $user
     * @param PlaceInterface|null $place
     * @return null|CalendarEvent
     */
    public function editCalendarEvent(\DateTimeInterface $startDateTime, array $attributes, UserInterface $user = null, PlaceInterface $place = null)
    {
        $calendarEvent = $this->createOrGetCalendarEvent($startDateTime);
        return $calendarEvent->editCalendarEvent($attributes, $user, $place);
    }

    /**
     * Edit calendar event | Exist or not | Do not check data
     * @param \DateTimeInterface $startDateTime
     * @param array $attributes
     * @param UserInterface|null $user
     * @param PlaceInterface|null $place
     * @return mixed
     */
    public function updateCalendarEvent(\DateTimeInterface $startDateTime, array $attributes, UserInterface $user = null, PlaceInterface $place = null)
    {
        $calendarEvent = $this->createOrGetCalendarEvent($startDateTime);
        return $calendarEvent->updateCalendarEvent($attributes, $user, $place);
    }

    /**
     * Delete calendar event | Exist or not
     * @param \DateTimeInterface $startDateTime
     * @param bool|null $isRecurring
     * @return bool|null
     */
    public function deleteCalendarEvent(\DateTimeInterface $startDateTime, bool $isRecurring = null)
    {
        if ($isRecurring === null) $isRecurring = $this->is_recurring;

        $calendarEvent = $this->events()->where('start_datetime', $startDateTime)->first();
        if (!$calendarEvent) {
            $calendarEvent = $this->createCalendarEvent($startDateTime);
        }

        return $calendarEvent->deleteCalendarEvent($isRecurring);
    }

    /**
     * Generate next calendar event to template
     * @param \DateTimeInterface $now
     * @return \Illuminate\Database\Eloquent\Model|null
     */
    public function generateNextCalendarEvent(\DateTimeInterface $now)
    {
        if (!$this->is_recurring
            || ($this->end_of_recurring !== null
                && $this->end_of_recurring <= $now)
        ) {
            return null;
        }

        $calendarEvent = $this->events()->withTrashed()->orderBy('start_datetime', 'desc')->first();
        $startDateTime     = $this->getNextCalendarEventStartDateTime($calendarEvent->start_datetime);
        if ($startDateTime === null) {
            return null;
        }

        return $this->createCalendarEvent($startDateTime);
    }

    /**
     * Get next calendar event start date
     * @param \DateTimeInterface $startDateTime
     * @return \DateTimeInterface|null
     */
    public function getNextCalendarEventStartDateTime(\DateTimeInterface $startDateTime)
    {
        if (!$this->is_recurring) {
            return null;
        }

//        TODO Refactor: OCP, Strategy
        switch ($this->frequence_type_of_recurring) {
            case RecurringFrequenceType::DAY:
                $startDateTime->addDays($this->frequence_number_of_recurring);
                break;
            case RecurringFrequenceType::WEEK:
                $startDateTime->addWeeks($this->frequence_number_of_recurring);
                break;
            case RecurringFrequenceType::MONTH:
                $startDateTime->addMonths($this->frequence_number_of_recurring);
                break;
            case RecurringFrequenceType::YEAR:
                $startDateTime->addYears($this->frequence_number_of_recurring);
                break;
            case RecurringFrequenceType::NTHWEEKDAY:
                $nextMonth = $startDateTime->copy()->addMonths($this->frequence_number_of_recurring);
                    $weekdays = getWeekdaysInMonth(
                    $startDateTime->format('l'),
                    $nextMonth
                );
                $startDateTime = $weekdays[$startDateTime->weekOfMonth - 1];
        }

        if (($this->end_of_recurring !== null
            && $this->end_of_recurring < $startDateTime)
//            || $this->events()->withTrashed()->where('start_datetime', $startDateTime)->whereNotNull('deleted_at')->first()
        ) {
            return null;
        }

        return $startDateTime;
    }
}