├── .gitignore ├── .travis.yml ├── README.md ├── composer.json ├── phpmd.xml ├── phpunit.xml ├── src ├── Console │ └── Commands │ │ └── GenerateCalendarEvent.php ├── Enums │ └── RecurringFrequenceType.php ├── Exceptions │ └── CalendarEventException.php ├── Interfaces │ ├── CalendarEventInterface.php │ ├── PlaceInterface.php │ ├── TemplateCalendarEventInterface.php │ └── UserInterface.php ├── Models │ ├── AbstractModel.php │ ├── CalendarEvent.php │ └── TemplateCalendarEvent.php ├── ServiceProvider.php ├── Support │ └── helpers.php ├── Traits │ ├── CalendarEventPlaceTrait.php │ └── CalendarEventUserTrait.php ├── config │ └── calendar-event.php └── database │ ├── factories │ ├── CalendarEventFactory.php │ └── TemplateCalendarEventFactory.php │ └── migrations │ ├── 2017_08_22_082938_create_template_calendar_events_table.php │ └── 2017_08_22_093248_create_calendar_events_table.php └── tests ├── TestCase.php ├── Unit ├── Console │ └── Command │ │ └── GenerateCalendarEventTest.php ├── Models │ ├── CalendarEventTest.php │ └── TemplateCalendarEventTest.php ├── Traits │ ├── CalendarEventPlaceTraitTest.php │ └── CalendarEventUserTraitTest.php └── config │ └── ConfigTest.php └── fixtures ├── Models ├── Place.php └── User.php └── database ├── factories ├── PlaceFactory.php └── UserFactory.php └── migrations ├── 2017_09_08_064317_create_places_table.php └── 2017_09_08_064448_create_users_table.php /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | 3 | build 4 | vendor 5 | composer.lock -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | php: 4 | - 7.1 5 | 6 | before_install: 7 | - travis_retry composer self-update 8 | 9 | install: 10 | - travis_retry composer install --no-interaction --prefer-dist 11 | 12 | script: 13 | - vendor/bin/phpunit --verbose 14 | - vendor/bin/phpmd src text phpmd.xml --exclude src/database 15 | 16 | after_success: 17 | - bash <(curl -s https://codecov.io/bash) 18 | 19 | matrix: 20 | fast_finish: true 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Laravel Calendar Event 2 | 3 | [![Latest Stable Version](https://poser.pugx.org/t1k3/laravel-calendar-event/v/stable)](https://packagist.org/packages/t1k3/laravel-calendar-event) 4 | [![Total Downloads](https://poser.pugx.org/t1k3/laravel-calendar-event/downloads)](https://packagist.org/packages/t1k3/laravel-calendar-event) 5 | [![License](https://poser.pugx.org/t1k3/laravel-calendar-event/license)](https://packagist.org/packages/t1k3/laravel-calendar-event) 6 | [![Build Status](https://travis-ci.org/t1k3/laravel-calendar-event.svg?branch=master)](https://travis-ci.org/t1k3/laravel-calendar-event) 7 | [![codecov](https://codecov.io/gh/t1k3/laravel-calendar-event/branch/master/graph/badge.svg)](https://codecov.io/gh/t1k3/laravel-calendar-event) 8 | [![Maintainability](https://api.codeclimate.com/v1/badges/c226ae53c829f256ec58/maintainability)](https://codeclimate.com/github/t1k3/laravel-calendar-event/maintainability) 9 | 10 | ## Installation 11 | ```bash 12 | composer require t1k3/laravel-calendar-event 13 | ``` 14 | 15 | After updating composer, add the ServiceProvider to the providers array in `config/app.php` 16 | ```php 17 | T1k3\LaravelCalendarEvent\ServiceProvider::class, 18 | ``` 19 | 20 | You need publish to the config. 21 | ```bash 22 | php artisan vendor:publish --provider="T1k3\LaravelCalendarEvent\ServiceProvider" 23 | ``` 24 | 25 | You need to run the migrations for this package. 26 | ```bash 27 | php artisan migrate 28 | ``` 29 | 30 | ## Usage 31 | 32 | #### Recurring options 33 | - DAY 34 | - WEEK 35 | - MONTH 36 | - YEAR 37 | - NTHWEEKDAY: nth weekday per month, example 2nd Monday 38 | 39 | #### Create CalendarEvent 40 | If you like to attach `User` and/or `Place` then must have: 41 | * configurate `config/calendar-event.php` 42 | * implements `UserInterface`, `PlaceInterface` on your Models 43 | * you can use `CalendarEventUserTrait`, `CalendarEventPlaceTrait` in Models 44 | 45 | ```php 46 | use T1k3\LaravelCalendarEvent\Interfaces\PlaceInterface; 47 | use T1k3\LaravelCalendarEvent\Traits\CalendarEventPlaceTrait; 48 | 49 | class Place extends Model implements PlaceInterface 50 | { 51 | use CalendarEventPlaceTrait; 52 | } 53 | ``` 54 | 55 | ```php 56 | use T1k3\LaravelCalendarEvent\Models\CalendarEvent; 57 | use T1k3\LaravelCalendarEvent\Enums\RecurringFrequenceType; 58 | 59 | $calendarEvent = new CalendarEvent(); 60 | $calendarEvent = $calendarEvent->createCalendarEvent([ 61 | 'title' => 'Lorem ipsum', 62 | 'start_datetime' => Carbon::parse('2017-08-25 16:00:00'), 63 | 'end_datetime' => Carbon::parse('2017-08-25 17:30:00'), 64 | 'description' => 'Lorem ipsum dolor sit amet', 65 | 'is_recurring' => true, 66 | 'frequence_number_of_recurring' => 1, 67 | 'frequence_type_of_recurring' => RecurringFrequenceType::WEEK, 68 | 'is_public' => true, 69 | 'end_of_recurring' => Carbon::parse('2017-09-08') 70 | ], $user = null, $place = null); 71 | ``` 72 | 73 | #### Edit and Update CalendarEvent 74 | ```php 75 | $calendarEvent = CalendarEvent::find($id); 76 | $calendarEventUpdated = $calendarEvent->editCalendarEvent([ 77 | 'start_datetime' => Carbon::parse('2017-08-26'), 78 | 'is_recurring' => false, 79 | ], $user = null, $place = null); 80 | 81 | // $calendarEventUpdated === null ? dd('NOT_MODIFIED') : dd('MODIFIED', $calendarEventUpdated); 82 | ``` 83 | 84 | #### Update CalendarEvent (without data check) 85 | ```php 86 | $calendarEvent = CalendarEvent::find($id); 87 | $calendarEventUpdated = $calendarEvent->updateCalendarEvent([ 88 | 'start_datetime' => Carbon::parse('2017-08-26'), 89 | 'is_recurring' => false, 90 | ], $user = null, $place = null); 91 | ``` 92 | 93 | #### Delete CalendarEvent 94 | ```php 95 | $calendarEvent = CalendarEvent::find($id); 96 | $isDeleted = $calendarEvent->deleteCalendarEvent($isRecurring = null); 97 | ``` 98 | 99 | #### Edit and Update not existing CalendarEvent 100 | ```php 101 | use T1k3\LaravelCalendarEvent\Models\TemplateCalendarEvent; 102 | 103 | $templateCalendarEvent = TemplateCalendarEvent::find($id); 104 | $calendarEventUpdated = $templateCalendarEvent->editCalendarEvent(Carbon::parse('2017-08-30'), [ 105 | 'description' => 'Foo Bar' 106 | ], $user = null, $place = null); 107 | 108 | // $calendarEventUpdated === null ? dd('NOT_MODIFIED') : dd('MODIFIED', $calendarEventUpdated); 109 | ``` 110 | 111 | #### Update not existing CalendarEvent (without data check) 112 | ```php 113 | use T1k3\LaravelCalendarEvent\Models\TemplateCalendarEvent; 114 | 115 | $templateCalendarEvent = TemplateCalendarEvent::find($id); 116 | $calendarEventUpdated = $templateCalendarEvent->updateCalendarEvent(Carbon::parse('2017-08-30'), [ 117 | 'description' => 'Foo Bar' 118 | ], $user = null, $place = null); 119 | ``` 120 | 121 | #### Delete not existing CalendarEvent 122 | ```php 123 | $templateCalendarEvent = TemplateCalendarEvent::find($id); 124 | $isDeleted = $templateCalendarEvent->deleteCalendarEvent(Carbon::parse('2017-08-30'), $isRecurring = null); 125 | ``` 126 | 127 | #### Get (potential) CalendarEvent(s) of month 128 | If the CalendarEvent is not exist then it is append `is_not_exists` attribute with `true` value 129 | ```php 130 | $calendarEvents = CalendarEvent::showPotentialCalendarEventsOfMonth(Carbon::parse('2017-08')); 131 | ``` 132 | 133 | #### Generate next CalendarEvent(s) from Console 134 | Do NOT forget the [Laravel Task Scheduling](https://laravel.com/docs/master/scheduling) 135 | - The command run at hourly in schedule 136 | ```bash 137 | * * * * * php /path-to-your-project/artisan schedule:run >> /dev/null 2>&1 138 | # OR manually 139 | php artisan generate:calendar-event 140 | ``` 141 | 142 | ### Validation 143 | Do NOT forget the [validation](https://laravel.com/docs/master/validation) 144 | - start_datetime 145 | - end_datetime 146 | 147 | ### How to upgrade Carbon 148 | ```bash 149 | $ docker run -it --rm -v $PWD:/app -w /app epcallan/php7-testing-phpunit:7.2-phpunit7 bash 150 | $ composer install 151 | $ ./vendor/bin/upgrade-carbon 152 | ``` 153 | 154 | ### How to Testing 155 | ```bash 156 | $ docker run -it --rm -v $PWD:/app -w /app epcallan/php7-testing-phpunit:7.2-phpunit7 bash 157 | $ composer install 158 | $ ./vendor/bin/phpunit 159 | ``` 160 | 161 | ### TODO 162 | - [ ] OCP 163 | - [ ] Name conventions, example: `TemplateCalendarEvent::events()` to `TemplateCalendarEvent::calendarEvents()` 164 | - [ ] [Custom validation rule](https://laravel.com/docs/master/validation#custom-validation-rules) to date/time diff 165 | 166 | ### Special thanks 167 | - [Bit and Pixel](https://bitandpixel.hu) 168 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "t1k3/laravel-calendar-event", 3 | "description": "Laravel Calendar Event", 4 | "keywords": [ 5 | "laravel-calendar-event", 6 | "laravel", 7 | "calendar", 8 | "event" 9 | ], 10 | "license": "MIT", 11 | "minimum-stability": "dev", 12 | "prefer-stable": true, 13 | "require": { 14 | "php": ">=7.1.3", 15 | "illuminate/database": "~5", 16 | "illuminate/support": "~5" 17 | }, 18 | "require-dev": { 19 | "mockery/mockery": "^1.1", 20 | "orchestra/testbench": "~3.0|~3.6|~3.7|~3.8", 21 | "phpmd/phpmd": "^2.6", 22 | "phpunit/phpunit": "^7.3", 23 | "composer/composer": "^1.9" 24 | }, 25 | "autoload": { 26 | "classmap": [ 27 | "src/database" 28 | ], 29 | "psr-4": { 30 | "T1k3\\LaravelCalendarEvent\\": "src" 31 | } 32 | }, 33 | "autoload-dev": { 34 | "psr-4": { 35 | "T1k3\\LaravelCalendarEvent\\Tests\\": "tests", 36 | "T1k3\\LaravelCalendarEvent\\Tests\\Fixtures\\": "tests/fixtures" 37 | } 38 | }, 39 | "extra": { 40 | "laravel": { 41 | "providers": [ 42 | "T1k3\\LaravelCalendarEvent\\ServiceProvider" 43 | ] 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /phpmd.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | Laravel Calendar Event Code Check 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 13 | tests/Unit 14 | 15 | 16 | 17 | 18 | src 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /src/Console/Commands/GenerateCalendarEvent.php: -------------------------------------------------------------------------------- 1 | templateCalendarEvent = $templateCalendarEvent; 46 | } 47 | 48 | /** 49 | * Handle 50 | */ 51 | public function handle() 52 | { 53 | $date = $this->option('date') ? Carbon::parse($this->option('date')) : Carbon::now(); 54 | $templateCalendarEvents = $this->templateCalendarEvent 55 | ->where('is_recurring', true) 56 | ->where(function ($q) use ($date) { 57 | $q->whereNull('end_of_recurring') 58 | ->orWhere('end_of_recurring', '>=', $date); 59 | }) 60 | ->get(); 61 | 62 | $count = 0; 63 | foreach ($templateCalendarEvents as $templateCalendarEvent) { 64 | $dateNext = null; 65 | $endOfRecurring = (clone $date)->addWeek(); 66 | // TODO Refactor: OCP, Strategy 67 | switch ($templateCalendarEvent->frequence_type_of_recurring) { 68 | case RecurringFrequenceType::DAY: 69 | $startDateTime = $templateCalendarEvent->start_datetime; 70 | $endOfRecurring = (clone $date)->hour($startDateTime->hour)->minute($startDateTime->minute)->second($startDateTime->second); 71 | $diff = $templateCalendarEvent->start_datetime->diffInDays($endOfRecurring); 72 | $dateNext = $templateCalendarEvent->start_datetime->addDays($diff); 73 | 74 | if ($templateCalendarEvent->start_datetime->diffInDays($dateNext) % (int)$templateCalendarEvent->frequence_number_of_recurring === 0) { 75 | $dateNext->addDays((int)$templateCalendarEvent->frequence_number_of_recurring - 1); 76 | $endOfRecurring->addDays((int)$templateCalendarEvent->frequence_number_of_recurring - 1); 77 | } 78 | break; 79 | case RecurringFrequenceType::WEEK: 80 | // $endOfRecurring = (clone $date)->addWeek(); 81 | $diff = $templateCalendarEvent->start_datetime->diffInWeeks($endOfRecurring); 82 | $dateNext = $templateCalendarEvent->start_datetime->addWeeks($diff); 83 | break; 84 | case RecurringFrequenceType::MONTH: 85 | // $endOfRecurring = (clone $date)->addMonth(); 86 | $diff = $templateCalendarEvent->start_datetime->diffInMonths($endOfRecurring); 87 | $dateNext = $templateCalendarEvent->start_datetime->addMonths($diff); 88 | break; 89 | case RecurringFrequenceType::YEAR: 90 | $startDateTime = $templateCalendarEvent->start_datetime; 91 | $endOfRecurring = (clone $date)->addYear()->hour($startDateTime->hour)->minute($startDateTime->minute)->second($startDateTime->second); 92 | $diff = $templateCalendarEvent->start_datetime->diffInYears($endOfRecurring); 93 | $dateNext = $templateCalendarEvent->start_datetime->addYears($diff); 94 | break; 95 | case RecurringFrequenceType::NTHWEEKDAY: 96 | $diff = $date->firstOfMonth()->diffInMonths($templateCalendarEvent->start_datetime); 97 | 98 | $nextMonth = $templateCalendarEvent->start_datetime->addMonths($diff); 99 | $weekdays = getWeekdaysInMonth( 100 | $templateCalendarEvent->start_datetime->format('l'), 101 | $nextMonth 102 | ); 103 | 104 | $dateNext = $weekdays[$templateCalendarEvent->start_datetime->weekOfMonth - 1]; 105 | } 106 | 107 | if ($dateNext !== null 108 | && ($templateCalendarEvent->end_of_recurring === null 109 | || $dateNext <= $templateCalendarEvent->end_of_recurring 110 | ) 111 | && $dateNext >= $date 112 | && $dateNext <= $endOfRecurring 113 | && !$templateCalendarEvent->events()->withTrashed()->where('start_datetime', $dateNext)->first() 114 | ) { 115 | $calendarEvent = $templateCalendarEvent->createCalendarEvent($dateNext); 116 | $count++; 117 | 118 | $this->closure($calendarEvent); 119 | 120 | Log::info( 121 | sprintf(' 122 | Generated CalendarEvent from Console: 123 | template_calendar_event_id: %s, 124 | calendar_event_id: %s, 125 | start_datetime: %s, 126 | end_datetime: %s', 127 | $calendarEvent->template_calendar_event_id, 128 | $calendarEvent->id, 129 | $calendarEvent->start_datetime->format('Y-m-d'), 130 | $calendarEvent->end_datetime->format('Y-m-d') 131 | ) 132 | ); 133 | } 134 | } 135 | 136 | $this->info(sprintf('Generated CalendarEvent from Console | Summary: %s.', $count)); 137 | } 138 | 139 | /** 140 | * TODO Name convention: callback() 141 | * Callback for calendar event created 142 | * @param CalendarEventInterface $calendarEvent 143 | * @return CalendarEventInterface 144 | */ 145 | protected function closure(CalendarEventInterface $calendarEvent) 146 | { 147 | return $calendarEvent; 148 | } 149 | } -------------------------------------------------------------------------------- /src/Enums/RecurringFrequenceType.php: -------------------------------------------------------------------------------- 1 | where('is_public', true); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Models/CalendarEvent.php: -------------------------------------------------------------------------------- 1 | 'datetime', 36 | 'end_datetime' => 'datetime', 37 | ]; 38 | 39 | /** 40 | * TemplateCalendarEvent 41 | * @return \Illuminate\Database\Eloquent\Relations\BelongsTo 42 | */ 43 | public function template() 44 | { 45 | return $this->belongsTo(TemplateCalendarEvent::class, 'template_calendar_event_id'); 46 | } 47 | 48 | /** 49 | * Calendar event (template) data - input is different 50 | * @param array $attributes 51 | * @return bool 52 | */ 53 | public function dataIsDifferent(array $attributes): bool 54 | { 55 | if (isset($attributes['start_datetime'])) { 56 | // CalendarEvent data check 57 | if ($this->start_datetime->format('Y-m-d') !== $attributes['start_datetime']->format('Y-m-d')) { 58 | return true; 59 | } 60 | unset($attributes['start_datetime']); 61 | } 62 | 63 | // TemplateCalendarEvent data check | Skip start_datetime from template 64 | return !arrayIsEqualWithDB($attributes, $this->template, ['start_datetime']); 65 | } 66 | 67 | /** 68 | * Create CalendarEvent with Template, User, Place 69 | * @param array $attributes 70 | * @param UserInterface|null $user 71 | * @param PlaceInterface|null $place 72 | * @return \Illuminate\Database\Eloquent\Model 73 | */ 74 | public function createCalendarEvent(array $attributes, UserInterface $user = null, PlaceInterface $place = null) 75 | { 76 | DB::transaction(function () use ($attributes, $user, $place, &$calendarEvent) { 77 | $templateCalendarEvent = $this->template()->make($attributes); 78 | 79 | if ($templateCalendarEvent->user() !== null && $user !== null) { 80 | $templateCalendarEvent->user()->associate($user); 81 | } 82 | if ($templateCalendarEvent->place() !== null && $place !== null) { 83 | $templateCalendarEvent->place()->associate($place); 84 | } 85 | 86 | $templateCalendarEvent->save(); 87 | 88 | $calendarEvent = $this->make($attributes); 89 | $calendarEvent->template()->associate($templateCalendarEvent); 90 | $calendarEvent->save(); 91 | }); 92 | 93 | return $calendarEvent; 94 | } 95 | 96 | /** 97 | * Update CalendarEvent 98 | * @param array $attributes 99 | * @param UserInterface|null $user 100 | * @param PlaceInterface|null $place 101 | * @return mixed 102 | */ 103 | public function updateCalendarEvent(array $attributes, UserInterface $user = null, PlaceInterface $place = null) 104 | { 105 | DB::transaction(function () use ($attributes, $user, $place, &$calendarEventNew) { 106 | $calendarEventNew = $this->createCalendarEvent( 107 | array_merge( 108 | $this->template->toArray(), 109 | [ 110 | 'start_datetime' => $this->start_datetime, 111 | 'end_datetime' => $this->end_datetime, 112 | ], 113 | $attributes 114 | ), 115 | $user, 116 | $place 117 | ); 118 | 119 | $templateCalendarEvent = $calendarEventNew->template->parent()->associate($this->template); 120 | $templateCalendarEvent->save(); 121 | 122 | if ($this->template->is_recurring && $calendarEventNew->template->is_recurring) { 123 | $this->template->update([ 124 | 'end_of_recurring' => $this->start_datetime, 125 | ]); 126 | } else { 127 | $calendarEventNew->template->update([ 128 | 'end_of_recurring' => null, 129 | ]); 130 | } 131 | // $this->delete(); 132 | $this->deleteCalendarEvent($this->template->is_recurring && $templateCalendarEvent->is_recurring); 133 | }); 134 | 135 | return $calendarEventNew; 136 | } 137 | 138 | /** 139 | * Edit\Update calendar event with data check 140 | * @param array $attributes 141 | * @param UserInterface|null $user 142 | * @param PlaceInterface|null $place 143 | * @return null|CalendarEvent 144 | */ 145 | public function editCalendarEvent(array $attributes, UserInterface $user = null, PlaceInterface $place = null) 146 | { 147 | if ($this->dataIsDifferent($attributes) 148 | || ($user ? $user->id : null) != $this->template->user_id 149 | || ($place ? $place->id : null) != $this->template->place_id 150 | ) { 151 | return $this->updateCalendarEvent($attributes, $user, $place); 152 | } 153 | return null; 154 | } 155 | 156 | /** 157 | * Delete calendar event 158 | * @param bool|null $isRecurring 159 | * @return bool|null 160 | */ 161 | public function deleteCalendarEvent(bool $isRecurring = null) 162 | { 163 | DB::transaction(function () use ($isRecurring, &$isDeleted) { 164 | if ($isRecurring === null) { 165 | $isRecurring = $this->template->is_recurring; 166 | } 167 | 168 | if ($this->template->is_recurring && $isRecurring) { 169 | $this->template->update(['end_of_recurring' => $this->start_datetime]); 170 | 171 | $nextCalendarEvents = $this->template->events()->where('start_datetime', '>', $this->start_datetime)->get(); 172 | foreach ($nextCalendarEvents as $nextCalendarEvent) { 173 | $nextCalendarEvent->delete(); 174 | } 175 | 176 | if ($this->template->start_datetime == $this->start_datetime) { 177 | $this->template->delete(); 178 | } 179 | } 180 | 181 | if (!$this->template->is_recurring) { 182 | $this->template->delete(); 183 | } 184 | 185 | $isDeleted = $this->delete(); 186 | }); 187 | return $isDeleted; 188 | } 189 | 190 | /** 191 | * Show (potential) CalendarEvent of the month 192 | * @param \DateTimeInterface $date 193 | * @return Collection|static 194 | */ 195 | public static function showPotentialCalendarEventsOfMonth(\DateTimeInterface $date) 196 | { 197 | $endOfRecurring = $date->lastOfMonth()->hour(23)->minute(59)->second(59); 198 | $month = str_pad($endOfRecurring->month, 2, '0', STR_PAD_LEFT); 199 | $templateCalendarEvents = self::getMonthlyEvents($endOfRecurring); 200 | 201 | $calendarEvents = collect(); 202 | foreach ($templateCalendarEvents as $templateCalendarEvent) { 203 | $calendarEvents = $calendarEvents->merge( 204 | $templateCalendarEvent->events()->whereMonth('start_datetime', $month)->get() 205 | ); 206 | 207 | $calendarEventTmpLast = $templateCalendarEvent->events()->orderBy('start_datetime', 'desc')->first(); 208 | $dateNext = self::getNextDateFromTemplate($templateCalendarEvent, $calendarEventTmpLast, $date); 209 | 210 | while ($dateNext !== null && $dateNext->year == $date->year && $dateNext->month <= (int)$month) { 211 | $diffInDays = $templateCalendarEvent->start_datetime->diffInDays($templateCalendarEvent->end_datetime); 212 | $dateNextEnd = clone($dateNext); 213 | $dateNextEnd = $dateNextEnd->addDays($diffInDays); 214 | 215 | $calendarEventNotExists = (new CalendarEvent())->make([ 216 | 'start_datetime' => $dateNext, 217 | 'end_datetime' => $dateNextEnd, 218 | ]); 219 | $calendarEventNotExists->is_not_exists = true; 220 | $calendarEventNotExists->template()->associate($templateCalendarEvent); 221 | 222 | if ($calendarEventNotExists->start_datetime->month === (int)$month && !$templateCalendarEvent->events()->where('start_datetime', $dateNext)->first()) { 223 | $calendarEvents = $calendarEvents->merge(collect([$calendarEventNotExists])); 224 | } 225 | 226 | $dateNext = $templateCalendarEvent->getNextCalendarEventStartDateTime($calendarEventNotExists->start_datetime); 227 | } 228 | } 229 | 230 | return $calendarEvents; 231 | } 232 | 233 | private static function getNextDateFromTemplate(TemplateCalendarEvent $templateCalendarEvent, CalendarEvent $calendarEventTmpLast, \DateTimeInterface $date) 234 | { 235 | $dateNext = null; 236 | // TODO Refactor: OCP, Strategy 237 | if ($calendarEventTmpLast) { 238 | switch ($templateCalendarEvent->frequence_type_of_recurring) { 239 | case RecurringFrequenceType::DAY: 240 | $diff = $calendarEventTmpLast->template->frequence_number_of_recurring; 241 | $dateNext = $calendarEventTmpLast->start_datetime->addDays($diff); 242 | break; 243 | case RecurringFrequenceType::WEEK: 244 | $diff = $date->firstOfMonth()->diffInWeeks($calendarEventTmpLast->start_datetime); 245 | $dateNext = $calendarEventTmpLast->start_datetime->addWeeks($diff); 246 | break; 247 | case RecurringFrequenceType::MONTH: 248 | $diff = $date->firstOfMonth()->diffInMonths($calendarEventTmpLast->start_datetime); 249 | $dateNext = $calendarEventTmpLast->start_datetime->addMonths($diff); 250 | break; 251 | case RecurringFrequenceType::YEAR: 252 | $diff = $date->firstOfMonth()->diffInYears($calendarEventTmpLast->start_datetime); 253 | $dateNext = $calendarEventTmpLast->start_datetime->addYears($diff); 254 | break; 255 | case RecurringFrequenceType::NTHWEEKDAY: 256 | $diff = $date->firstOfMonth()->diffInMonths($calendarEventTmpLast->start_datetime); 257 | $nextMonth = $calendarEventTmpLast->start_datetime->addMonths($diff); 258 | 259 | $weekdays = getWeekdaysInMonth( 260 | $calendarEventTmpLast->start_datetime->format('l'), 261 | $nextMonth 262 | ); 263 | $dateNext = $weekdays[$calendarEventTmpLast->start_datetime->weekOfMonth - 1]; 264 | break; 265 | } 266 | } 267 | 268 | return $dateNext; 269 | } 270 | 271 | private static function getMonthlyEvents(\DateTimeInterface $endOfRecurring): Collection 272 | { 273 | $month = str_pad($endOfRecurring->month, 2, '0', STR_PAD_LEFT); 274 | 275 | return TemplateCalendarEvent 276 | ::where(function ($q) use ($month) { 277 | $q->where('is_recurring', false) 278 | ->whereMonth('start_datetime', $month); 279 | }) 280 | ->orWhere(function ($q) use ($endOfRecurring) { 281 | $q->where('is_recurring', true) 282 | ->whereNull('end_of_recurring') 283 | ->where('start_datetime', '<=', $endOfRecurring); 284 | }) 285 | ->orWhere(function ($q) use ($endOfRecurring) { 286 | $q->where('is_recurring', true) 287 | ->where('end_of_recurring', '>=', $endOfRecurring) 288 | ->where('start_datetime', '<=', $endOfRecurring); 289 | }) 290 | ->orWhere(function ($q) use ($endOfRecurring, $month) { 291 | $q->where('is_recurring', true) 292 | ->whereYear('end_of_recurring', $endOfRecurring->year) 293 | ->whereMonth('end_of_recurring', $month) 294 | ->where('start_datetime', '<=', $endOfRecurring); 295 | }) 296 | ->with('events') 297 | ->get(); 298 | } 299 | } 300 | -------------------------------------------------------------------------------- /src/Models/TemplateCalendarEvent.php: -------------------------------------------------------------------------------- 1 | 'boolean', 44 | 'is_public' => 'boolean', 45 | 'start_datetime' => 'datetime', 46 | 'end_datetime' => 'datetime', 47 | 'end_of_recurring' => 'datetime', 48 | ]; 49 | 50 | /** 51 | * TemplateCalendarEvent parent 52 | * @return \Illuminate\Database\Eloquent\Relations\BelongsTo 53 | */ 54 | public function parent() 55 | { 56 | return $this->belongsTo(TemplateCalendarEvent::class, 'parent_id'); 57 | } 58 | 59 | /** 60 | * CalendarEvent 61 | * @return \Illuminate\Database\Eloquent\Relations\HasMany 62 | */ 63 | public function events() 64 | { 65 | return $this->hasMany(CalendarEvent::class, 'template_calendar_event_id'); 66 | } 67 | 68 | /** 69 | * @param $query 70 | * @return Builder 71 | */ 72 | public function scopeRecurring($query) 73 | { 74 | return $query->where('is_recurring', true); 75 | } 76 | 77 | /** 78 | * @return \Illuminate\Database\Eloquent\Relations\BelongsTo|null 79 | */ 80 | public function user() 81 | { 82 | // TODO If the config is null then you can not use ->user (LogicException), just ->user() 83 | $class = config('calendar-event.user.model'); 84 | return $class ? $this->belongsTo($class) : null; 85 | } 86 | 87 | /** 88 | * @return \Illuminate\Database\Eloquent\Relations\BelongsTo|null 89 | */ 90 | public function place() 91 | { 92 | // TODO If the config is null then you can not use ->place (LogicException), just ->place() 93 | $class = config('calendar-event.place.model'); 94 | return $class ? $this->belongsTo($class) : null; 95 | } 96 | 97 | /** 98 | * Create Calendar Event for TemplateCalendarEvent 99 | * @param \DateTimeInterface $startDateTime 100 | * @return \Illuminate\Database\Eloquent\Model 101 | */ 102 | public function createCalendarEvent(\DateTimeInterface $startDate) 103 | { 104 | $diffInSeconds = $this->start_datetime->diffInSeconds($this->end_datetime); 105 | $endDate = clone($startDate); 106 | $endDate = $endDate->addSeconds($diffInSeconds); 107 | $calendarEvent = $this->events()->make([ 108 | 'start_datetime' => $startDate, 109 | 'end_datetime' => Carbon::parse( $endDate->format('Y-m-d') . ' ' . $this->end_datetime->format('H:i:s') ), 110 | ]); 111 | 112 | $calendarEvent->template()->associate($this); 113 | $calendarEvent->save(); 114 | 115 | return $calendarEvent; 116 | } 117 | 118 | /** 119 | * Create or get calendar event 120 | * @param \DateTimeInterface $startDateTime 121 | * @return \Illuminate\Database\Eloquent\Model|null|static 122 | */ 123 | public function createOrGetCalendarEvent(\DateTimeInterface $startDateTime) 124 | { 125 | $calendarEvent = $this->events()->where('start_datetime', $startDateTime)->first(); 126 | if (!$calendarEvent) { 127 | $calendarEvent = $this->createCalendarEvent($startDateTime); 128 | } 129 | 130 | return $calendarEvent; 131 | } 132 | 133 | /** 134 | * Edit calendar event | Exist or not | Check data 135 | * @param \DateTimeInterface $startDateTime 136 | * @param array $attributes 137 | * @param UserInterface|null $user 138 | * @param PlaceInterface|null $place 139 | * @return null|CalendarEvent 140 | */ 141 | public function editCalendarEvent(\DateTimeInterface $startDateTime, array $attributes, UserInterface $user = null, PlaceInterface $place = null) 142 | { 143 | $calendarEvent = $this->createOrGetCalendarEvent($startDateTime); 144 | return $calendarEvent->editCalendarEvent($attributes, $user, $place); 145 | } 146 | 147 | /** 148 | * Edit calendar event | Exist or not | Do not check data 149 | * @param \DateTimeInterface $startDateTime 150 | * @param array $attributes 151 | * @param UserInterface|null $user 152 | * @param PlaceInterface|null $place 153 | * @return mixed 154 | */ 155 | public function updateCalendarEvent(\DateTimeInterface $startDateTime, array $attributes, UserInterface $user = null, PlaceInterface $place = null) 156 | { 157 | $calendarEvent = $this->createOrGetCalendarEvent($startDateTime); 158 | return $calendarEvent->updateCalendarEvent($attributes, $user, $place); 159 | } 160 | 161 | /** 162 | * Delete calendar event | Exist or not 163 | * @param \DateTimeInterface $startDateTime 164 | * @param bool|null $isRecurring 165 | * @return bool|null 166 | */ 167 | public function deleteCalendarEvent(\DateTimeInterface $startDateTime, bool $isRecurring = null) 168 | { 169 | if ($isRecurring === null) $isRecurring = $this->is_recurring; 170 | 171 | $calendarEvent = $this->events()->where('start_datetime', $startDateTime)->first(); 172 | if (!$calendarEvent) { 173 | $calendarEvent = $this->createCalendarEvent($startDateTime); 174 | } 175 | 176 | return $calendarEvent->deleteCalendarEvent($isRecurring); 177 | } 178 | 179 | /** 180 | * Generate next calendar event to template 181 | * @param \DateTimeInterface $now 182 | * @return \Illuminate\Database\Eloquent\Model|null 183 | */ 184 | public function generateNextCalendarEvent(\DateTimeInterface $now) 185 | { 186 | if (!$this->is_recurring 187 | || ($this->end_of_recurring !== null 188 | && $this->end_of_recurring <= $now) 189 | ) { 190 | return null; 191 | } 192 | 193 | $calendarEvent = $this->events()->withTrashed()->orderBy('start_datetime', 'desc')->first(); 194 | $startDateTime = $this->getNextCalendarEventStartDateTime($calendarEvent->start_datetime); 195 | if ($startDateTime === null) { 196 | return null; 197 | } 198 | 199 | return $this->createCalendarEvent($startDateTime); 200 | } 201 | 202 | /** 203 | * Get next calendar event start date 204 | * @param \DateTimeInterface $startDateTime 205 | * @return \DateTimeInterface|null 206 | */ 207 | public function getNextCalendarEventStartDateTime(\DateTimeInterface $startDateTime) 208 | { 209 | if (!$this->is_recurring) { 210 | return null; 211 | } 212 | 213 | // TODO Refactor: OCP, Strategy 214 | switch ($this->frequence_type_of_recurring) { 215 | case RecurringFrequenceType::DAY: 216 | $startDateTime->addDays($this->frequence_number_of_recurring); 217 | break; 218 | case RecurringFrequenceType::WEEK: 219 | $startDateTime->addWeeks($this->frequence_number_of_recurring); 220 | break; 221 | case RecurringFrequenceType::MONTH: 222 | $startDateTime->addMonths($this->frequence_number_of_recurring); 223 | break; 224 | case RecurringFrequenceType::YEAR: 225 | $startDateTime->addYears($this->frequence_number_of_recurring); 226 | break; 227 | case RecurringFrequenceType::NTHWEEKDAY: 228 | $nextMonth = $startDateTime->copy()->addMonths($this->frequence_number_of_recurring); 229 | $weekdays = getWeekdaysInMonth( 230 | $startDateTime->format('l'), 231 | $nextMonth 232 | ); 233 | $startDateTime = $weekdays[$startDateTime->weekOfMonth - 1]; 234 | } 235 | 236 | if (($this->end_of_recurring !== null 237 | && $this->end_of_recurring < $startDateTime) 238 | // || $this->events()->withTrashed()->where('start_datetime', $startDateTime)->whereNotNull('deleted_at')->first() 239 | ) { 240 | return null; 241 | } 242 | 243 | return $startDateTime; 244 | } 245 | } 246 | -------------------------------------------------------------------------------- /src/ServiceProvider.php: -------------------------------------------------------------------------------- 1 | loadMigrationsFrom(__DIR__ . '/database/migrations'); 23 | // $this->publishes([ 24 | // __DIR__ . '/database/migrations' => database_path('migrations'), 25 | // ], 'migrations'); 26 | 27 | $this->publishes([ 28 | __DIR__ . '/config' => config_path(), 29 | ], 'config'); 30 | 31 | $this->app->booted(function () { 32 | $schedule = $this->app->make(Schedule::class); 33 | $schedule->command('generate:calendar-event')->hourly(); 34 | }); 35 | } 36 | 37 | /** 38 | * Register any application services. 39 | * 40 | * @return void 41 | */ 42 | public function register() 43 | { 44 | $this->commands(GenerateCalendarEvent::class); 45 | 46 | // Package helpers 47 | if (file_exists($file = __DIR__ . '/Support/helpers.php')) require_once $file; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/Support/helpers.php: -------------------------------------------------------------------------------- 1 | getFillable(); 15 | foreach ($columns as $column) { 16 | if (in_array($column, $skippable)) continue; 17 | 18 | if (isset($array[$column])) { 19 | if ($array[$column] instanceof \Carbon\Carbon && !($model->{$column} instanceof \Carbon\Carbon)) { 20 | $model->{$column} = \Carbon\Carbon::parse($model->{$column}); 21 | } 22 | 23 | if ($model->{$column} != $array[$column]) { 24 | return false; 25 | } 26 | } elseif ($model->{$column} !== null) { 27 | return false; 28 | } 29 | 30 | } 31 | return true; 32 | } 33 | } 34 | 35 | if (!function_exists('getWeekdaysInMonth')) { 36 | /** 37 | * @param string $weekday 38 | * @param date $date 39 | * @return collection 40 | */ 41 | function getWeekdaysInMonth($weekday, $date) 42 | { 43 | $next = $date->copy()->addMonths(1); 44 | return collect(new \DatePeriod( 45 | 46 | Carbon::parse("first $weekday of $date"), 47 | CarbonInterval::week(), 48 | Carbon::parse("first $weekday of $next") 49 | )); 50 | } 51 | } -------------------------------------------------------------------------------- /src/Traits/CalendarEventPlaceTrait.php: -------------------------------------------------------------------------------- 1 | hasManyThrough( 22 | CalendarEvent::class, TemplateCalendarEvent::class, 23 | 'place_id', 'template_calendar_event_id', 'id' 24 | ); 25 | } 26 | } -------------------------------------------------------------------------------- /src/Traits/CalendarEventUserTrait.php: -------------------------------------------------------------------------------- 1 | hasManyThrough( 22 | CalendarEvent::class, TemplateCalendarEvent::class, 23 | 'user_id', 'template_calendar_event_id', 'id' 24 | ); 25 | } 26 | } -------------------------------------------------------------------------------- /src/config/calendar-event.php: -------------------------------------------------------------------------------- 1 | [ 5 | 'model' => null, 6 | ], 7 | 'place' => [ 8 | 'model' => null, 9 | ], 10 | ]; -------------------------------------------------------------------------------- /src/database/factories/CalendarEventFactory.php: -------------------------------------------------------------------------------- 1 | make(\Illuminate\Database\Eloquent\Factory::class); 8 | 9 | /** @var \Illuminate\Database\Eloquent\Factory $factory */ 10 | $factory->define(CalendarEvent::class, function (Faker $faker) { 11 | return [ 12 | 'start_datetime' => Carbon::now()->addWeek()->format('Y-m-d'), 13 | 'end_datetime' => Carbon::now()->addWeek()->format('Y-m-d'), 14 | ]; 15 | }); 16 | -------------------------------------------------------------------------------- /src/database/factories/TemplateCalendarEventFactory.php: -------------------------------------------------------------------------------- 1 | make(\Illuminate\Database\Eloquent\Factory::class); 8 | 9 | /** @var \Illuminate\Database\Eloquent\Factory $factory */ 10 | $factory->define(TemplateCalendarEvent::class, function (Faker $faker) { 11 | return [ 12 | 'title' => str_random(16), 13 | 'start_datetime' => Carbon::now()->addWeek(), 14 | 'end_datetime' => Carbon::now()->addWeek(), 15 | 'description' => str_random(32), 16 | 'is_recurring' => false, 17 | // 'frequence_number_of_recurring' => 1, 18 | // 'frequence_type_of_recurring' => 'week', 19 | 'is_public' => true, 20 | ]; 21 | }); 22 | -------------------------------------------------------------------------------- /src/database/migrations/2017_08_22_082938_create_template_calendar_events_table.php: -------------------------------------------------------------------------------- 1 | increments('id'); 21 | $table->integer('parent_id')->unsigned()->nullable(); 22 | $table->string('title')->nullable(); 23 | 24 | $table->dateTime('start_datetime'); 25 | $table->dateTime('end_datetime'); 26 | 27 | $table->text('description')->nullable(); 28 | $table->boolean('is_recurring')->default(false); 29 | $table->dateTime('end_of_recurring')->nullable(); 30 | $table->integer('frequence_number_of_recurring')->nullable(); 31 | $table->string('frequence_type_of_recurring', 32)->nullable(); 32 | $table->boolean('is_public')->default(false); 33 | $table->timestamps(); 34 | $table->softDeletes(); 35 | 36 | $table->foreign('parent_id') 37 | ->references('id')->on('template_calendar_events') 38 | ->onDelete('cascade'); 39 | 40 | $table->integer('user_id')->unsigned()->nullable(); 41 | $table->integer('place_id')->unsigned()->nullable(); 42 | }); 43 | } 44 | 45 | /** 46 | * Reverse the migrations. 47 | * 48 | * @return void 49 | */ 50 | public function down() 51 | { 52 | Schema::dropIfExists('template_calendar_events'); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/database/migrations/2017_08_22_093248_create_calendar_events_table.php: -------------------------------------------------------------------------------- 1 | increments('id'); 21 | $table->integer('template_calendar_event_id')->unsigned(); 22 | 23 | $table->dateTime('start_datetime'); 24 | $table->dateTime('end_datetime'); 25 | 26 | $table->timestamps(); 27 | $table->softDeletes(); 28 | 29 | $table->foreign('template_calendar_event_id') 30 | ->references('id')->on('template_calendar_events') 31 | ->onDelete('cascade'); 32 | }); 33 | } 34 | 35 | /** 36 | * Reverse the migrations. 37 | * 38 | * @return void 39 | */ 40 | public function down() 41 | { 42 | Schema::dropIfExists('calendar_events'); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /tests/TestCase.php: -------------------------------------------------------------------------------- 1 | setUpFactory(); 33 | $this->setUpDatabase(); 34 | 35 | $this->withoutMockingConsoleOutput(); 36 | } 37 | 38 | /** 39 | * Teardown 40 | */ 41 | public function tearDown(): void 42 | { 43 | $this->consoleOutput = ''; 44 | $this->artisan('migrate:reset'); 45 | 46 | parent::tearDown(); 47 | } 48 | 49 | /** 50 | * @param Application $app 51 | * @return array 52 | */ 53 | protected function getPackageProviders($app) 54 | { 55 | return [ 56 | LaravelCalendarEventServiceProvider::class, 57 | // \Orchestra\Database\ConsoleServiceProvider::class, 58 | ]; 59 | } 60 | 61 | /** 62 | * Resolve application Console Kernel implementation. 63 | * 64 | * @param Application $app 65 | */ 66 | public function resolveApplicationConsoleKernel($app) 67 | { 68 | $app->singleton(Kernel::class, \Orchestra\Testbench\Console\Kernel::class); 69 | } 70 | 71 | /** 72 | * @return mixed 73 | */ 74 | public function getConsoleOutput() 75 | { 76 | return $this->consoleOutput ?: $this->consoleOutput = $this->app[Kernel::class]->output(); 77 | } 78 | 79 | /** 80 | * Define environment setup 81 | * 82 | * @param Application $app 83 | */ 84 | protected function getEnvironmentSetUp($app) 85 | { 86 | $app['config']->set('calendar-event.user.model', null); 87 | $app['config']->set('calendar-event.place.model', null); 88 | 89 | $app['config']->set('database.default', 'testing'); 90 | $app['config']->set('database.connections.testing', [ 91 | 'driver' => 'sqlite', 92 | 'database' => ':memory:', 93 | ]); 94 | } 95 | 96 | /** 97 | * Configure the factory 98 | */ 99 | private function setUpFactory() 100 | { 101 | $this->withFactories(__DIR__ . '/../src/database/factories'); 102 | $this->withFactories(__DIR__ . '/fixtures/database/factories'); 103 | } 104 | 105 | /** 106 | * Configure the database 107 | * SQLite 108 | */ 109 | private function setUpDatabase() 110 | { 111 | $this->artisan('migrate', ['--database' => 'testing']); 112 | $this->artisan('migrate', [ 113 | '--database' => 'testing', 114 | // '--realpath' => realpath(__DIR__ . '/fixtures/database/migrations'), 115 | '--path' => '../../../../tests/fixtures/database/migrations', 116 | ]); 117 | 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /tests/Unit/Console/Command/GenerateCalendarEventTest.php: -------------------------------------------------------------------------------- 1 | templateCalendarEvent = new TemplateCalendarEvent(); 36 | $this->calendarEvent = new CalendarEvent(); 37 | } 38 | 39 | /** 40 | * @test 41 | */ 42 | public function handle_notRecurring_notGenerated() 43 | { 44 | $now = '2017-08-28'; 45 | $this->calendarEvent->createCalendarEvent([ 46 | 'start_datetime' => '2017-08-29 08:00:00', 47 | 'end_datetime' => '2017-08-29 10:00:00', 48 | 'description' => str_random(32), 49 | 'is_recurring' => false, 50 | 'is_public' => true, 51 | ]); 52 | 53 | $this->artisan('generate:calendar-event', ['--date' => $now]); 54 | $this->assertContains('Generated CalendarEvent from Console | Summary: 0', $this->getConsoleOutput()); 55 | $this->assertEquals(1, $this->calendarEvent->all()->count()); 56 | } 57 | 58 | /** 59 | * @test 60 | */ 61 | public function handle_recurring_endOfRecurring_notGenerated() 62 | { 63 | $now = '2017-08-09'; 64 | $calendarEvent = $this->calendarEvent->createCalendarEvent([ 65 | 'start_datetime' => '2017-08-01 10:00:00', 66 | 'end_datetime' => '2017-08-01 12:00:00', 67 | 'description' => str_random(32), 68 | 'is_recurring' => true, 69 | 'frequence_number_of_recurring' => 1, 70 | 'frequence_type_of_recurring' => RecurringFrequenceType::WEEK, 71 | 'is_public' => true, 72 | 'end_of_recurring' => '2017-08-14', 73 | ]); 74 | $calendarEvent->template->generateNextCalendarEvent(Carbon::parse($now)); // '2017-08-08' 75 | $this->artisan('generate:calendar-event', ['--date' => $now]); 76 | 77 | $this->assertContains('Generated CalendarEvent from Console | Summary: 0', $this->getConsoleOutput()); 78 | $this->assertEquals(2, $this->calendarEvent->all()->count()); 79 | } 80 | 81 | public function dataProvider_for_handle_recurring_generated() 82 | { 83 | return [ 84 | [ 85 | [ // input 86 | 'start_datetime' => '2017-08-01 10:00:00', 87 | 'end_datetime' => '2017-08-01 12:00:00', 88 | 'description' => str_random(32), 89 | 'is_recurring' => true, 90 | 'frequence_number_of_recurring' => 2, 91 | 'frequence_type_of_recurring' => RecurringFrequenceType::DAY, 92 | 'is_public' => true, 93 | ], 94 | Carbon::parse('2017-08-16'), // now 95 | Carbon::parse('2017-08-16 10:00:00'), // start datetime 96 | Carbon::parse('2017-08-16 12:00:00'), // end datetime 97 | ], 98 | [ 99 | [ 100 | 'start_datetime' => '2017-08-01 10:00:00', 101 | 'end_datetime' => '2017-08-01 12:00:00', 102 | 'description' => str_random(32), 103 | 'is_recurring' => true, 104 | 'frequence_number_of_recurring' => 2, 105 | 'frequence_type_of_recurring' => RecurringFrequenceType::DAY, 106 | 'is_public' => true, 107 | ], 108 | Carbon::parse('2017-08-17'), 109 | Carbon::parse('2017-08-18 10:00:00'), 110 | Carbon::parse('2017-08-18 12:00:00'), 111 | ], 112 | [ 113 | [ 114 | 'start_datetime' => '2017-08-02 10:00:00', 115 | 'end_datetime' => '2017-08-02 12:00:00', 116 | 'description' => str_random(32), 117 | 'is_recurring' => true, 118 | 'frequence_number_of_recurring' => 3, 119 | 'frequence_type_of_recurring' => RecurringFrequenceType::DAY, 120 | 'is_public' => true, 121 | ], 122 | Carbon::parse('2017-08-17'), 123 | Carbon::parse('2017-08-19 10:00:00'), 124 | Carbon::parse('2017-08-19 12:00:00'), 125 | ], 126 | [ 127 | [ 128 | 'start_datetime' => '2017-08-01 10:00:00', 129 | 'end_datetime' => '2017-08-01 12:00:00', 130 | 'description' => str_random(32), 131 | 'is_recurring' => true, 132 | 'frequence_number_of_recurring' => 1, 133 | 'frequence_type_of_recurring' => RecurringFrequenceType::WEEK, 134 | 'is_public' => true, 135 | ], 136 | Carbon::parse('2017-08-16'), 137 | Carbon::parse('2017-08-22 10:00:00'), 138 | Carbon::parse('2017-08-22 12:00:00'), 139 | ], 140 | [ 141 | [ 142 | 'start_datetime' => '2017-08-02 10:00:00', 143 | 'end_datetime' => '2017-08-04 12:00:00', 144 | 'description' => str_random(32), 145 | 'is_recurring' => true, 146 | 'frequence_number_of_recurring' => 2, 147 | 'frequence_type_of_recurring' => RecurringFrequenceType::WEEK, 148 | 'is_public' => true, 149 | ], 150 | Carbon::parse('2017-08-17'), 151 | Carbon::parse('2017-08-23 10:00:00'), 152 | Carbon::parse('2017-08-25 12:00:00'), 153 | ], 154 | [ 155 | [ 156 | 'start_datetime' => '2017-08-01 10:00:00', 157 | 'end_datetime' => '2017-08-02 12:20:00', 158 | 'description' => str_random(32), 159 | 'is_recurring' => true, 160 | 'frequence_number_of_recurring' => 1, 161 | 'frequence_type_of_recurring' => RecurringFrequenceType::MONTH, 162 | 'is_public' => true, 163 | ], 164 | Carbon::parse('2017-08-26'), 165 | Carbon::parse('2017-09-01 10:00:00'), 166 | Carbon::parse('2017-09-02 12:20:00'), 167 | ], 168 | [ 169 | [ 170 | 'start_datetime' => '2016-08-27 11:00:00', 171 | 'end_datetime' => '2016-08-28 01:00:00', 172 | 'description' => str_random(32), 173 | 'is_recurring' => true, 174 | 'frequence_number_of_recurring' => 1, 175 | 'frequence_type_of_recurring' => RecurringFrequenceType::YEAR, 176 | 'is_public' => true, 177 | ], 178 | Carbon::parse('2017-08-26'), // @TODO 179 | Carbon::parse('2017-08-27 11:00:00'), 180 | Carbon::parse('2017-08-28 01:00:00'), 181 | ], 182 | [ 183 | [ 184 | 'start_datetime' => '2019-07-17 19:30:00', 185 | 'end_datetime' => '2019-07-17 20:00:00', 186 | 'description' => str_random(32), 187 | 'is_recurring' => true, 188 | 'frequence_number_of_recurring' => 1, 189 | 'frequence_type_of_recurring' => RecurringFrequenceType::NTHWEEKDAY, 190 | 'is_public' => true, 191 | ], 192 | Carbon::parse('2019-07-15'), 193 | Carbon::parse('2019-07-17 19:30:00'), 194 | Carbon::parse('2019-07-17 20:00:00'), 195 | ], 196 | [ 197 | [ 198 | 'start_datetime' => '2019-08-21 19:30:00', 199 | 'end_datetime' => '2019-08-21 20:00:00', 200 | 'description' => str_random(32), 201 | 'is_recurring' => true, 202 | 'frequence_number_of_recurring' => 1, 203 | 'frequence_type_of_recurring' => RecurringFrequenceType::NTHWEEKDAY, 204 | 'is_public' => true, 205 | ], 206 | Carbon::parse('2019-08-19'), 207 | Carbon::parse('2019-08-21 19:30:00'), 208 | Carbon::parse('2019-08-21 20:00:00'), 209 | ], 210 | [ 211 | [ 212 | 'start_datetime' => '2019-09-18 19:30:00', 213 | 'end_datetime' => '2019-09-18 20:00:00', 214 | 'description' => str_random(32), 215 | 'is_recurring' => true, 216 | 'frequence_number_of_recurring' => 1, 217 | 'frequence_type_of_recurring' => RecurringFrequenceType::NTHWEEKDAY, 218 | 'is_public' => true, 219 | ], 220 | Carbon::parse('2019-09-16'), 221 | Carbon::parse('2019-09-18 19:30:00'), 222 | Carbon::parse('2019-09-18 20:00:00'), 223 | ], 224 | ]; 225 | } 226 | 227 | /** 228 | * @test 229 | * @dataProvider dataProvider_for_handle_recurring_generated 230 | * @param $input 231 | * @param $now 232 | * @param $startDateTime 233 | * @param $endDateTime 234 | */ 235 | public function handle_recurring_generated($input, $now, $startDateTime, $endDateTime) 236 | { 237 | $calendarEvent = $this->calendarEvent->createCalendarEvent($input); 238 | 239 | $this->artisan('generate:calendar-event', ['--date' => $now]); 240 | $this->artisan('generate:calendar-event', ['--date' => $now]); 241 | 242 | $calendarEventLast = $calendarEvent->template->events()->orderBy('start_datetime', 'desc')->first(); 243 | 244 | $this->assertContains('Generated CalendarEvent from Console | Summary:', $this->getConsoleOutput()); 245 | $this->assertEquals($startDateTime, $calendarEventLast->start_datetime); 246 | $this->assertEquals($endDateTime, $calendarEventLast->end_datetime); 247 | $this->assertDatabaseHas('calendar_events', ['start_datetime' => $startDateTime, 'end_datetime' => $endDateTime]); 248 | } 249 | 250 | /** 251 | * @test 252 | */ 253 | public function handle_recurring_generated_year_by_seconds() 254 | { 255 | $now = Carbon::parse('2017-08-26'); 256 | $startDateTime = Carbon::parse('2016-08-27 11:59:59'); 257 | $endDateTime = Carbon::parse('2016-08-28 00:00:01'); 258 | 259 | $calendarEvent = $this->calendarEvent->createCalendarEvent([ 260 | 'start_datetime' => $startDateTime, 261 | 'end_datetime' => $endDateTime, 262 | 'description' => str_random(32), 263 | 'is_recurring' => true, 264 | 'frequence_number_of_recurring' => 1, 265 | 'frequence_type_of_recurring' => RecurringFrequenceType::YEAR, 266 | 'is_public' => true, 267 | ]); 268 | 269 | $this->artisan('generate:calendar-event', ['--date' => $now]); 270 | $this->artisan('generate:calendar-event', ['--date' => $now]); 271 | 272 | $calendarEventLast = $calendarEvent->template->events()->orderBy('start_datetime', 'desc')->first(); 273 | 274 | $startDateTime->addYear(); 275 | $endDateTime->addYear(); 276 | 277 | $this->assertContains('Generated CalendarEvent from Console | Summary:', $this->getConsoleOutput()); 278 | $this->assertEquals($startDateTime, $calendarEventLast->start_datetime); 279 | $this->assertEquals($endDateTime, $calendarEventLast->end_datetime); 280 | $this->assertDatabaseHas('calendar_events', ['start_datetime' => $startDateTime, 'end_datetime' => $endDateTime]); 281 | } 282 | 283 | /** 284 | * @test 285 | */ 286 | public function handle_recurring_editedNotRecurring_notGenerated() 287 | { 288 | $now = Carbon::parse('2017-08-14'); 289 | $calendarEvent = $this->calendarEvent->createCalendarEvent([ 290 | 'title' => 'Lorem ipsum', 291 | 'start_datetime' => Carbon::parse('2017-08-01 10:00:00'), 292 | 'end_datetime' => Carbon::parse('2017-08-01 12:00:00'), 293 | 'description' => str_random(32), 294 | 'is_recurring' => true, 295 | 'frequence_number_of_recurring' => 1, 296 | 'frequence_type_of_recurring' => RecurringFrequenceType::WEEK, 297 | 'is_public' => true, 298 | ]); 299 | $calendarEventNextTmp = $calendarEvent->template->generateNextCalendarEvent(new \DateTime($now)); // '2017-08-08' 300 | $calendarEventNextTmp = $calendarEvent->template->generateNextCalendarEvent(Carbon::parse($now)); // '2017-08-15' 301 | $calendarEventNextTmpUpdated = $calendarEventNextTmp->editCalendarEvent([ 302 | 'start_datetime' => Carbon::parse('2017-08-09 10:00:00'), 303 | 'is_recurring' => false, 304 | ]); 305 | 306 | $this->artisan('generate:calendar-event', ['--date' => $now]); 307 | $this->artisan('generate:calendar-event', ['--date' => $now]); 308 | 309 | $calendarEventLast = $calendarEvent->template->events()->orderBy('start_datetime', 'desc')->first(); 310 | 311 | // The next is 2017-08-15 but is deleted 312 | $this->assertContains('Generated CalendarEvent from Console | Summary: 0', $this->getConsoleOutput()); 313 | $this->assertEquals('2017-08-08', $calendarEventLast->start_datetime->format('Y-m-d')); 314 | } 315 | 316 | /** 317 | * @test 318 | */ 319 | public function handle_recurring_editedNotRecurring_generated() 320 | { 321 | $now = Carbon::parse('2017-08-16'); 322 | $calendarEvent = $this->calendarEvent->createCalendarEvent([ 323 | 'title' => 'Lorem ipsum', 324 | 'start_datetime' => Carbon::parse('2017-08-01 10:00:00'), 325 | 'end_datetime' => Carbon::parse('2017-08-01 12:00:00'), 326 | 'description' => str_random(32), 327 | 'is_recurring' => true, 328 | 'frequence_number_of_recurring' => 1, 329 | 'frequence_type_of_recurring' => RecurringFrequenceType::WEEK, 330 | 'is_public' => true, 331 | ]); 332 | $calendarEventNextTmp = $calendarEvent->template->generateNextCalendarEvent(new \DateTime($now)); // '2017-08-08' 333 | $calendarEventNextTmp = $calendarEvent->template->generateNextCalendarEvent(Carbon::parse($now)); // '2017-08-15' 334 | $calendarEventNextTmpUpdated = $calendarEventNextTmp->editCalendarEvent([ 335 | 'start_datetime' => Carbon::parse('2017-08-09 10:00:00'), 336 | 'is_recurring' => false, 337 | ]); 338 | 339 | $this->artisan('generate:calendar-event', ['--date' => $now]); 340 | $this->artisan('generate:calendar-event', ['--date' => $now]); 341 | 342 | $calendarEventLast = $calendarEvent->template->events()->orderBy('start_datetime', 'desc')->first(); 343 | 344 | $this->assertContains('Generated CalendarEvent from Console | Summary:', $this->getConsoleOutput()); 345 | $this->assertEquals('2017-08-22', $calendarEventLast->start_datetime->format('Y-m-d')); 346 | 347 | $this->assertDatabaseHas('calendar_events', ['start_datetime' => Carbon::parse('2017-08-22 10:00:00')]); 348 | } 349 | } -------------------------------------------------------------------------------- /tests/Unit/Models/CalendarEventTest.php: -------------------------------------------------------------------------------- 1 | calendarEvent = new CalendarEvent(); 32 | } 33 | 34 | /** 35 | * @test 36 | */ 37 | public function createInstanceFromClass() 38 | { 39 | $this->assertInstanceOf(CalendarEvent::class, $this->calendarEvent); 40 | } 41 | 42 | /** 43 | * @test 44 | */ 45 | public function getFillable() 46 | { 47 | $expectedFillables = [ 48 | 'start_datetime', 49 | 'end_datetime', 50 | ]; 51 | $this->assertArraySubset($expectedFillables, $this->calendarEvent->getFillable()); 52 | } 53 | 54 | /** 55 | * @test 56 | */ 57 | public function template() 58 | { 59 | $templateCalendarEvent = factory(TemplateCalendarEvent::class)->create(); 60 | 61 | $calendarEvent = factory(CalendarEvent::class)->make(); 62 | $calendarEvent->template()->associate($templateCalendarEvent); 63 | $calendarEvent->save(); 64 | 65 | $this->assertInstanceOf(TemplateCalendarEvent::class, $calendarEvent->template); 66 | } 67 | 68 | /** 69 | * Data provider for create calendar event 70 | * @return array 71 | */ 72 | public function dataProvider_for_createCalendarEvent() 73 | { 74 | return [ 75 | 'not_recurring' => [ 76 | [ 77 | 'title' => str_random(16), 78 | 'start_datetime' => Carbon::parse('2017-08-25 10:00:00'), 79 | 'end_datetime' => Carbon::parse('2017-08-25 12:00:00'), 80 | 'description' => str_random(32), 81 | 'is_recurring' => false, 82 | 'is_public' => true, 83 | ], 84 | ], 85 | 'recurring' => [ 86 | [ 87 | 'title' => str_random(16), 88 | 'start_datetime' => Carbon::parse('2017-08-25 10:00:00'), 89 | 'end_datetime' => Carbon::parse('2017-08-25 12:00:00'), 90 | 'description' => str_random(32), 91 | 'is_recurring' => true, 92 | 'frequence_number_of_recurring' => 1, 93 | 'frequence_type_of_recurring' => RecurringFrequenceType::WEEK, 94 | 'is_public' => true, 95 | ], 96 | ], 97 | ]; 98 | } 99 | 100 | /** 101 | * Create event, not recurring 102 | * @test 103 | * @dataProvider dataProvider_for_createCalendarEvent 104 | * @param array $input 105 | */ 106 | public function createCalendarEvent(array $input) 107 | { 108 | $calendarEvent = $this->calendarEvent->createCalendarEvent($input); 109 | 110 | $this->assertInstanceOf(CalendarEvent::class, $calendarEvent); 111 | $this->assertEquals($input['start_datetime']->format('Y-m-d'), $calendarEvent->start_datetime->format('Y-m-d')); 112 | $this->assertInstanceOf(TemplateCalendarEvent::class, $calendarEvent->template); 113 | 114 | $this->assertDatabaseHas('template_calendar_events', $input); 115 | } 116 | 117 | /** 118 | * @test 119 | * @dataProvider dataProvider_for_createCalendarEvent 120 | * @param array $input 121 | */ 122 | public function createCalendarEvent_user_place(array $input) 123 | { 124 | $this->app['config']->set('calendar-event.user.model', User::class); 125 | $this->app['config']->set('calendar-event.place.model', Place::class); 126 | 127 | $user = factory(config('calendar-event.user.model'))->create(); 128 | $place = factory(config('calendar-event.place.model'))->create(); 129 | $calendarEvent = $this->calendarEvent->createCalendarEvent($input, $user, $place); 130 | 131 | $this->assertInstanceOf(CalendarEvent::class, $calendarEvent); 132 | $this->assertEquals($input['start_datetime']->format('Y-m-d'), $calendarEvent->start_datetime->format('Y-m-d')); 133 | $this->assertEquals($user, $calendarEvent->template->user); 134 | $this->assertEquals($place, $calendarEvent->template->place); 135 | $this->assertInstanceOf(TemplateCalendarEvent::class, $calendarEvent->template); 136 | 137 | $this->assertInstanceOf(CalendarEvent::class, $user->calendarEvents()->first()); 138 | $this->assertInstanceOf(CalendarEvent::class, $place->calendarEvents()->first()); 139 | 140 | $this->assertDatabaseHas('template_calendar_events', $input); 141 | $this->assertDatabaseHas('users', $user->toArray()); 142 | $this->assertDatabaseHas('places', $place->toArray()); 143 | } 144 | 145 | /** 146 | * Edit event but not modified 147 | * @test 148 | */ 149 | public function editCalendarEvent_notModified() 150 | { 151 | $inputCreate = [ 152 | 'title' => str_random(16), 153 | 'start_datetime' => Carbon::parse('2017-08-01 11:00:00'), 154 | 'end_datetime' => Carbon::parse('2017-08-01 12:00:00'), 155 | 'description' => str_random(32), 156 | 'is_recurring' => false, 157 | 'is_public' => true, 158 | 159 | ]; 160 | $calendarEvent = $this->calendarEvent->createCalendarEvent($inputCreate); 161 | $calendarEventUpdated = $calendarEvent->editCalendarEvent($inputCreate); 162 | 163 | $this->assertNull($calendarEventUpdated); 164 | } 165 | 166 | /** 167 | * Edit event and modified, calendar event data 168 | * @test 169 | */ 170 | public function editCalendarEvent_recurring_modifiedCalendarEventData_recurring() 171 | { 172 | $inputCreate = [ 173 | 'title' => str_random(16), 174 | 'start_datetime' => Carbon::parse('2017-08-01 11:00'), 175 | 'end_datetime' => Carbon::parse('2017-08-01 12:00'), 176 | 'description' => str_random(32), 177 | 'is_recurring' => true, 178 | 'frequence_number_of_recurring' => 1, 179 | 'frequence_type_of_recurring' => RecurringFrequenceType::WEEK, 180 | 'is_public' => true, 181 | 'end_of_recurring' => Carbon::parse('2017-08-22'), 182 | ]; 183 | $inputUpdate = [ 184 | 'start_datetime' => Carbon::parse('2017-08-03'), 185 | ]; 186 | 187 | $calendarEvent = $this->calendarEvent->createCalendarEvent($inputCreate); 188 | $calendarEventNext = $calendarEvent->template->generateNextCalendarEvent(Carbon::parse('2017-08-06')); 189 | $calendarEventUpdated = $calendarEventNext->editCalendarEvent($inputUpdate); 190 | 191 | $this->assertNull($calendarEvent->deleted_at); 192 | $this->assertNotNull($calendarEventNext->deleted_at); 193 | $this->assertEquals($calendarEventNext->start_datetime->format('Y-m-d'), $calendarEventNext->template->end_of_recurring->format('Y-m-d')); 194 | 195 | $this->assertInstanceOf(CalendarEvent::class, $calendarEventUpdated); 196 | $this->assertEquals($inputUpdate['start_datetime']->format('Y-m-d'), $calendarEventUpdated->start_datetime->format('Y-m-d')); 197 | $this->assertEquals($inputCreate['end_of_recurring']->format('Y-m-d'), $calendarEventUpdated->template->end_of_recurring->format('Y-m-d')); 198 | 199 | $this->assertInstanceOf(TemplateCalendarEvent::class, $calendarEventUpdated->template); 200 | $this->assertEquals($calendarEvent->id, $calendarEventUpdated->template->parent_id); 201 | 202 | $this->assertDatabaseHas('template_calendar_events', $inputUpdate); 203 | $this->assertDatabaseHas('calendar_events', $inputUpdate); 204 | } 205 | 206 | /** 207 | * Edit event and modified, calendar event data 208 | * @test 209 | */ 210 | public function editCalendarEvent_recurring_modifiedCalendarEventData_recurring_nextCalendarEventIsDeleted() 211 | { 212 | $inputCreate = [ 213 | 'title' => str_random(16), 214 | 'start_datetime' => Carbon::parse('2017-08-01 11:00:00'), 215 | 'end_datetime' => Carbon::parse('2017-08-01 12:00:00'), 216 | 'description' => str_random(32), 217 | 'is_recurring' => true, 218 | 'frequence_number_of_recurring' => 1, 219 | 'frequence_type_of_recurring' => RecurringFrequenceType::WEEK, 220 | 'is_public' => true, 221 | 'end_of_recurring' => Carbon::parse('2017-08-22'), 222 | ]; 223 | $inputUpdate = [ 224 | 'start_datetime' => Carbon::parse('2017-08-03'), 225 | ]; 226 | 227 | $calendarEvent = $this->calendarEvent->createCalendarEvent($inputCreate); 228 | $calendarEventNext = $calendarEvent->template->generateNextCalendarEvent(Carbon::parse('2017-08-06')); 229 | $calendarEventUpdated = $calendarEvent->editCalendarEvent($inputUpdate); 230 | 231 | $this->assertNotNull($calendarEvent->deleted_at); 232 | $this->assertNotNull($calendarEventNext->fresh()->deleted_at); 233 | $this->assertEquals($calendarEvent->start_datetime->format('Y-m-d'), $calendarEventNext->template->end_of_recurring->format('Y-m-d')); 234 | 235 | $this->assertInstanceOf(CalendarEvent::class, $calendarEventUpdated); 236 | $this->assertEquals($inputUpdate['start_datetime']->format('Y-m-d'), $calendarEventUpdated->start_datetime->format('Y-m-d')); 237 | $this->assertEquals($inputCreate['end_of_recurring']->format('Y-m-d'), $calendarEventUpdated->template->end_of_recurring->format('Y-m-d')); 238 | 239 | $this->assertInstanceOf(TemplateCalendarEvent::class, $calendarEventUpdated->template); 240 | $this->assertEquals($calendarEvent->id, $calendarEventUpdated->template->parent_id); 241 | 242 | $this->assertDatabaseHas('template_calendar_events', $inputUpdate); 243 | $this->assertDatabaseHas('calendar_events', $inputUpdate); 244 | } 245 | 246 | /** 247 | * Edit event and modified, calendar event data 248 | * @test 249 | */ 250 | public function editCalendarEvent_recurring_modifiedCalendarEventData_notRecurring() 251 | { 252 | $inputCreate = [ 253 | 'title' => str_random(16), 254 | 'start_datetime' => Carbon::parse('2017-08-25 16:00:00'), 255 | 'end_datetime' => Carbon::parse('2017-08-25 17:00:00'), 256 | 'description' => str_random(32), 257 | 'is_recurring' => true, 258 | 'frequence_number_of_recurring' => 1, 259 | 'frequence_type_of_recurring' => RecurringFrequenceType::WEEK, 260 | 'is_public' => true, 261 | 'end_of_recurring' => Carbon::parse('2017-09-08'), 262 | ]; 263 | $inputUpdate = [ 264 | 'start_datetime' => Carbon::parse('2017-08-27'), 265 | 'is_recurring' => false, 266 | ]; 267 | 268 | $calendarEvent = $this->calendarEvent->createCalendarEvent($inputCreate); 269 | $calendarEventNext = $calendarEvent->template->generateNextCalendarEvent(Carbon::parse('2017-08-27')); 270 | $calendarEventUpdated = $calendarEventNext->editCalendarEvent($inputUpdate); 271 | 272 | $this->assertNull($calendarEvent->deleted_at); 273 | $this->assertNotNull($calendarEventNext->deleted_at); 274 | $this->assertEquals($inputCreate['end_of_recurring']->format('Y-m-d'), $calendarEventNext->template->end_of_recurring->format('Y-m-d')); 275 | 276 | $this->assertInstanceOf(CalendarEvent::class, $calendarEventUpdated); 277 | $this->assertEquals($inputUpdate['start_datetime']->format('Y-m-d'), $calendarEventUpdated->start_datetime->format('Y-m-d')); 278 | $this->assertNull($calendarEventUpdated->template->end_of_recurring); 279 | 280 | $this->assertInstanceOf(TemplateCalendarEvent::class, $calendarEventUpdated->template); 281 | $this->assertEquals($calendarEvent->id, $calendarEventUpdated->template->parent_id); 282 | 283 | $this->assertDatabaseHas('template_calendar_events', $inputUpdate); 284 | $this->assertDatabaseHas('calendar_events', ['start_datetime' => $inputUpdate['start_datetime']]); 285 | } 286 | 287 | /** 288 | * Edit event and modified, calendar event data 289 | * @test 290 | */ 291 | public function editCalendarEvent_notRecurring_modifiedToRecurring() 292 | { 293 | $inputCreate = [ 294 | 'title' => str_random(16), 295 | 'start_datetime' => Carbon::parse('2017-08-25 08:00:00'), 296 | 'end_datetime' => Carbon::parse('2017-08-25 14:00:00'), 297 | 'description' => str_random(32), 298 | 'is_recurring' => false, 299 | 'is_public' => true, 300 | ]; 301 | $inputUpdate = [ 302 | 'is_recurring' => true, 303 | ]; 304 | 305 | $calendarEvent = $this->calendarEvent->createCalendarEvent($inputCreate); 306 | $calendarEventUpdated = $calendarEvent->editCalendarEvent($inputUpdate); 307 | 308 | $this->assertNotNull($calendarEvent->deleted_at); 309 | $this->assertNull($calendarEvent->template->end_of_recurring); 310 | 311 | $this->assertInstanceOf(CalendarEvent::class, $calendarEventUpdated); 312 | 313 | $this->assertInstanceOf(TemplateCalendarEvent::class, $calendarEventUpdated->template); 314 | $this->assertEquals($inputUpdate['is_recurring'], $calendarEventUpdated->template->is_recurring); 315 | $this->assertEquals($calendarEvent->id, $calendarEventUpdated->template->parent_id); 316 | 317 | $this->assertDatabaseHas('template_calendar_events', array_merge($inputCreate, $inputUpdate)); 318 | } 319 | 320 | /** 321 | * @test 322 | */ 323 | public function updateCalendarEvent() 324 | { 325 | $inputCreate = [ 326 | 'title' => str_random(16), 327 | 'start_datetime' => Carbon::parse('2017-08-25 08:00:00'), 328 | 'end_datetime' => Carbon::parse('2017-08-25 14:00:00'), 329 | 'description' => str_random(32), 330 | 'is_recurring' => false, 331 | 'is_public' => true, 332 | ]; 333 | 334 | $calendarEvent = $this->calendarEvent->createCalendarEvent($inputCreate); 335 | $calendarEventUpdated = $calendarEvent->updateCalendarEvent($inputCreate); 336 | 337 | $this->assertDatabaseHas('template_calendar_events', $inputCreate + ['id' => $calendarEventUpdated->template->id]); 338 | $this->assertDatabaseHas('calendar_events', ['id' => $calendarEventUpdated->id]); 339 | $this->assertNotEquals($calendarEvent->id, $calendarEventUpdated->id); 340 | $this->assertEquals($calendarEvent->template->id, $calendarEventUpdated->template->parent_id); 341 | } 342 | 343 | /** 344 | * NOT data provider 345 | * @return array 346 | */ 347 | public function data_for_eventOfMonth() 348 | { 349 | return [ 350 | 'dates' => [ 351 | '2017-08-12', '2017-08-26', 352 | '2017-08-02', 353 | '2017-08-03', '2017-08-10', '2017-08-17', '2017-08-24', '2017-08-31', 354 | '2017-08-04', '2017-08-18', 355 | '2017-08-06', 356 | '2017-08-07', 357 | ], 358 | 'inputs' => [ 359 | [ 360 | 'title' => str_random(16), 361 | 'start_datetime' => Carbon::parse('2016-10-01 11:30:00'), 362 | 'end_datetime' => Carbon::parse('2016-10-01 12:30:00'), 363 | 'description' => str_random(32), 364 | 'is_recurring' => true, 365 | 'frequence_number_of_recurring' => 1, 366 | 'frequence_type_of_recurring' => RecurringFrequenceType::YEAR, 367 | 'is_public' => true, 368 | ], 369 | [ 370 | 'title' => str_random(16), 371 | 'start_datetime' => Carbon::parse('2017-07-13 14:45:00'), 372 | 'end_datetime' => Carbon::parse('2017-07-13 15:45:00'), 373 | 'description' => str_random(32), 374 | 'is_recurring' => true, 375 | 'frequence_number_of_recurring' => 2, 376 | 'frequence_type_of_recurring' => RecurringFrequenceType::MONTH, 377 | 'end_of_recurring' => Carbon::parse('2017-12-15'), 378 | 'is_public' => true, 379 | ], 380 | [ 381 | 'title' => str_random(16), 382 | 'start_datetime' => Carbon::parse('2017-07-14 11:45:00'), 383 | 'end_datetime' => Carbon::parse('2017-07-14 12:45:00'), 384 | 'description' => str_random(32), 385 | 'is_recurring' => false, 386 | 'is_public' => true, 387 | ], 388 | [ 389 | // 2017-08-12, 2017-08-26 390 | 'title' => str_random(16), 391 | 'start_datetime' => Carbon::parse('2017-07-15 14:45:00'), 392 | 'end_datetime' => Carbon::parse('2017-07-15 15:45:00'), 393 | 'description' => str_random(32), 394 | 'is_recurring' => true, 395 | 'frequence_number_of_recurring' => 2, 396 | 'frequence_type_of_recurring' => RecurringFrequenceType::WEEK, 397 | 'is_public' => true, 398 | ], 399 | [ 400 | // 2017-08-07 401 | 'title' => str_random(16), 402 | 'start_datetime' => Carbon::parse('2017-07-17 14:45:00'), 403 | 'end_datetime' => Carbon::parse('2017-07-17 15:45:00'), 404 | 'description' => str_random(32), 405 | 'is_recurring' => true, 406 | 'frequence_number_of_recurring' => 1, 407 | 'frequence_type_of_recurring' => RecurringFrequenceType::WEEK, 408 | 'end_of_recurring' => Carbon::parse('2017-08-08'), 409 | 'is_public' => true, 410 | ], 411 | [ 412 | // 2017-08-02 413 | 'title' => str_random(16), 414 | 'start_datetime' => Carbon::parse('2017-08-02 9:00:00'), 415 | 'end_datetime' => Carbon::parse('2017-08-02 10:00:00'), 416 | 'description' => str_random(32), 417 | 'is_recurring' => false, 418 | 'is_public' => true, 419 | ], 420 | [ 421 | // 2017-08-03, 2017-08-10, 2017-08-17, 2017-08-24, 2017-08-31 422 | 'title' => str_random(16), 423 | 'start_datetime' => Carbon::parse('2017-08-03 10:00:00'), 424 | 'end_datetime' => Carbon::parse('2017-08-03 14:00:00'), 425 | 'description' => str_random(32), 426 | 'is_recurring' => true, 427 | 'frequence_number_of_recurring' => 1, 428 | 'frequence_type_of_recurring' => RecurringFrequenceType::WEEK, 429 | 'is_public' => true, 430 | ], 431 | [ 432 | // 2017-08-04, 2017-08-18 433 | 'title' => str_random(16), 434 | 'start_datetime' => Carbon::parse('2017-08-04 10:00:00'), 435 | 'end_datetime' => Carbon::parse('2017-08-04 14:00:00'), 436 | 'description' => str_random(32), 437 | 'is_recurring' => true, 438 | 'frequence_number_of_recurring' => 2, 439 | 'frequence_type_of_recurring' => RecurringFrequenceType::WEEK, 440 | 'is_public' => true, 441 | 'end_of_recurring' => Carbon::parse('2017-09-25'), 442 | ], 443 | [ 444 | // 2017-08-06 445 | 'title' => str_random(16), 446 | 'start_datetime' => Carbon::parse('2017-07-06 10:00:00'), 447 | 'end_datetime' => Carbon::parse('2017-07-06 14:00:00'), 448 | 'description' => str_random(32), 449 | 'is_recurring' => true, 450 | 'frequence_number_of_recurring' => 1, 451 | 'frequence_type_of_recurring' => RecurringFrequenceType::MONTH, 452 | 'is_public' => true, 453 | ], 454 | [ 455 | 'title' => str_random(16), 456 | 'start_datetime' => Carbon::parse('2017-09-01 10:00:00'), 457 | 'end_datetime' => Carbon::parse('2017-09-01 14:00:00'), 458 | 'description' => str_random(32), 459 | 'is_recurring' => true, 460 | 'frequence_number_of_recurring' => 1, 461 | 'frequence_type_of_recurring' => RecurringFrequenceType::WEEK, 462 | 'is_public' => true, 463 | 'end_of_recurring' => Carbon::parse('2017-09-22'), 464 | ], 465 | ], 466 | ]; 467 | } 468 | 469 | /** 470 | * @test 471 | */ 472 | public function test_eventsOfMonth() 473 | { 474 | $data = $this->data_for_eventOfMonth(); 475 | extract($data); // $inputs, $dates 476 | 477 | foreach ($inputs as $input) { 478 | $this->calendarEvent->createCalendarEvent($input); 479 | } 480 | $calendarEvents = CalendarEvent::showPotentialCalendarEventsOfMonth(Carbon::parse('2017-08')); 481 | 482 | $this->assertInstanceOf(CalendarEvent::class, $calendarEvents[0]); 483 | $this->assertEquals(count($dates), $calendarEvents->count()); 484 | foreach ($calendarEvents as $calendarEvent) { 485 | $isExist = in_array($calendarEvent->start_datetime->format('Y-m-d'), $dates); 486 | $this->assertTrue($isExist); 487 | } 488 | } 489 | 490 | public function dataProvider_for_eventsOfMonth_after_merge_date_time() 491 | { 492 | for ($i = 1; $i <= 31; $i++) { 493 | $array[] = [$i]; 494 | } 495 | 496 | return $array; 497 | } 498 | 499 | /** 500 | * @test 501 | * @dataProvider dataProvider_for_eventsOfMonth_after_merge_date_time 502 | * @param int $day 503 | */ 504 | public function getEventsOfMonth_after_merge_date_time(int $day) 505 | { 506 | $startDateTime = Carbon::parse('2019-07-' . $day . ' 18:00:00'); 507 | $this->calendarEvent->createCalendarEvent([ 508 | 'title' => str_random(16), 509 | 'start_datetime' => $startDateTime, 510 | 'end_datetime' => clone($startDateTime)->addHours(2), 511 | 'description' => str_random(32), 512 | 'is_recurring' => true, 513 | 'frequence_number_of_recurring' => 1, 514 | 'frequence_type_of_recurring' => RecurringFrequenceType::DAY, 515 | 'is_public' => true, 516 | 'end_of_recurring' => null, 517 | ]); 518 | 519 | $calendarEvents = CalendarEvent::showPotentialCalendarEventsOfMonth(Carbon::parse('2019-07')); 520 | $this->assertEquals(32 - $day, $calendarEvents->count()); 521 | } 522 | 523 | /** 524 | * @test 525 | * @dataProvider dataProvider_for_eventsOfMonth_after_merge_date_time 526 | * @param int $day 527 | */ 528 | public function getEventsOfMonth_after_merge_date_time_nextMonth(int $day) 529 | { 530 | $startDateTime = Carbon::parse('2019-07-' . $day . ' 18:00:00'); 531 | $this->calendarEvent->createCalendarEvent([ 532 | 'title' => str_random(16), 533 | 'start_datetime' => $startDateTime, 534 | 'end_datetime' => (clone($startDateTime))->addHours(2), 535 | 'description' => str_random(32), 536 | 'is_recurring' => true, 537 | 'frequence_number_of_recurring' => 1, 538 | 'frequence_type_of_recurring' => RecurringFrequenceType::DAY, 539 | 'is_public' => true, 540 | 'end_of_recurring' => null, 541 | ]); 542 | 543 | $calendarEvents = CalendarEvent::showPotentialCalendarEventsOfMonth(Carbon::parse('2019-08')); 544 | $this->assertEquals(31, $calendarEvents->count()); 545 | } 546 | 547 | /** 548 | * @test 549 | */ 550 | public function getEventsOfMonth_after_merge_date_time_Type_week() 551 | { 552 | $startDateTime = Carbon::parse('2019-07-03 18:00:00'); 553 | $this->calendarEvent->createCalendarEvent([ 554 | 'title' => str_random(16), 555 | 'start_datetime' => $startDateTime, 556 | 'end_datetime' => (clone($startDateTime))->addHours(2), 557 | 'description' => str_random(32), 558 | 'is_recurring' => true, 559 | 'frequence_number_of_recurring' => 1, 560 | 'frequence_type_of_recurring' => RecurringFrequenceType::WEEK, 561 | 'is_public' => true, 562 | 'end_of_recurring' => null, 563 | ]); 564 | 565 | $calendarEvents = CalendarEvent::showPotentialCalendarEventsOfMonth(Carbon::parse('2019-07')); 566 | $this->assertEquals(5, $calendarEvents->count()); 567 | } 568 | 569 | /** 570 | * @test 571 | */ 572 | public function getEventsOfMonth_after_merge_date_time_Type_month() 573 | { 574 | $startDateTime = Carbon::parse('2019-07-18 20:45:00'); 575 | $this->calendarEvent->createCalendarEvent([ 576 | 'title' => str_random(16), 577 | 'start_datetime' => $startDateTime, 578 | 'end_datetime' => (clone($startDateTime))->addHours(2), 579 | 'description' => str_random(32), 580 | 'is_recurring' => true, 581 | 'frequence_number_of_recurring' => 1, 582 | 'frequence_type_of_recurring' => RecurringFrequenceType::MONTH, 583 | 'is_public' => true, 584 | 'end_of_recurring' => null, 585 | ]); 586 | 587 | $calendarEvents = CalendarEvent::showPotentialCalendarEventsOfMonth(Carbon::parse('2019-07')); 588 | $this->assertEquals(1, $calendarEvents->count()); 589 | $this->assertEquals('2019-07-18 20:45:00', $calendarEvents->first()->start_datetime->format('Y-m-d H:i:s')); 590 | } 591 | 592 | /** 593 | * @test 594 | */ 595 | public function getEventsOfMonth_after_merge_date_time_Type_year() 596 | { 597 | $startDateTime = Carbon::parse('2019-07-18 20:00:00'); 598 | $this->calendarEvent->createCalendarEvent([ 599 | 'title' => str_random(16), 600 | 'start_datetime' => $startDateTime, 601 | 'end_datetime' => (clone($startDateTime))->addHours(2), 602 | 'description' => str_random(32), 603 | 'is_recurring' => true, 604 | 'frequence_number_of_recurring' => 1, 605 | 'frequence_type_of_recurring' => RecurringFrequenceType::YEAR, 606 | 'is_public' => true, 607 | 'end_of_recurring' => null, 608 | ]); 609 | 610 | $calendarEvents = CalendarEvent::showPotentialCalendarEventsOfMonth(Carbon::parse('2019-07')); 611 | $this->assertEquals(1, $calendarEvents->count()); 612 | $this->assertEquals('2019-07-18 20:00:00', $calendarEvents->first()->start_datetime->format('Y-m-d H:i:s')); 613 | } 614 | 615 | /** 616 | * @test 617 | */ 618 | public function getEventsOfMonth_for_nthweekRecurring() 619 | { 620 | $this->calendarEvent->createCalendarEvent([ 621 | 'start_datetime' => '2019-06-19 19:30:00', 622 | 'end_datetime' => '2019-06-19 20:00:00', 623 | 'description' => str_random(32), 624 | 'is_recurring' => true, 625 | 'frequence_number_of_recurring' => 1, 626 | 'frequence_type_of_recurring' => RecurringFrequenceType::NTHWEEKDAY, 627 | 'is_public' => true, 628 | 'end_of_recurring' => null, 629 | ]); 630 | 631 | 632 | $calendarEvents = CalendarEvent::showPotentialCalendarEventsOfMonth(Carbon::parse('2019-07')); 633 | $this->assertEquals(1, $calendarEvents->count()); 634 | $this->assertEquals('2019-07-17', $calendarEvents->first()->start_datetime->format('Y-m-d')); 635 | 636 | $calendarEvents = CalendarEvent::showPotentialCalendarEventsOfMonth(Carbon::parse('2019-08')); 637 | $this->assertEquals(1, $calendarEvents->count()); 638 | $this->assertEquals('2019-08-21', $calendarEvents->first()->start_datetime->format('Y-m-d')); 639 | 640 | $calendarEvents = CalendarEvent::showPotentialCalendarEventsOfMonth(Carbon::parse('2019-09')); 641 | $this->assertEquals(1, $calendarEvents->count()); 642 | $this->assertEquals('2019-09-18', $calendarEvents->first()->start_datetime->format('Y-m-d')); 643 | } 644 | 645 | /** 646 | * @test 647 | */ 648 | public function eventsOfMonth_empty() 649 | { 650 | $calendarEvents = CalendarEvent::showPotentialCalendarEventsOfMonth(Carbon::parse('2017-08')); 651 | $this->assertEquals(0, $calendarEvents->count()); 652 | } 653 | 654 | /** 655 | * @test 656 | */ 657 | public function deleteCalendarEvent_notRecurring_recurring() 658 | { 659 | $inputCreate = [ 660 | 'title' => str_random(16), 661 | 'start_datetime' => Carbon::parse('2017-08-25 16:00:00'), 662 | 'end_datetime' => Carbon::parse('2017-08-25 17:00:00'), 663 | 'description' => str_random(32), 664 | 'is_recurring' => false, 665 | 'is_public' => true, 666 | ]; 667 | $calendarEvent = $this->calendarEvent->createCalendarEvent($inputCreate); 668 | 669 | $calendarEvent->deleteCalendarEvent(true); 670 | 671 | $this->assertNotNull($calendarEvent->deleted_at); 672 | $this->assertNotNull($calendarEvent->template->deleted_at); 673 | $this->assertNull($calendarEvent->template->end_of_recurring); 674 | } 675 | 676 | /** 677 | * @test 678 | */ 679 | public function deleteCalendarEvent_recurring_recurring_generatedEvent() 680 | { 681 | $inputCreate = [ 682 | 'title' => str_random(16), 683 | 'start_datetime' => Carbon::parse('2017-08-25 16:00:00'), 684 | 'end_datetime' => Carbon::parse('2017-08-25 17:00:00'), 685 | 'description' => str_random(32), 686 | 'is_recurring' => true, 687 | 'frequence_number_of_recurring' => 1, 688 | 'frequence_type_of_recurring' => RecurringFrequenceType::WEEK, 689 | 'is_public' => true, 690 | ]; 691 | $calendarEvent = $this->calendarEvent->createCalendarEvent($inputCreate); 692 | $calendarEventNext = $calendarEvent->template->generateNextCalendarEvent(Carbon::parse('2017-08-27')); 693 | 694 | $calendarEvent->deleteCalendarEvent(true); 695 | 696 | $this->assertNotNull($calendarEvent->deleted_at); 697 | $this->assertNotNull($calendarEventNext->fresh()->deleted_at); 698 | $this->assertNotNull($calendarEvent->template->deleted_at); 699 | $this->assertEquals($calendarEvent->start_datetime->format('Y-m-d'), $calendarEvent->template->end_of_recurring->format('Y-m-d')); 700 | } 701 | 702 | /** 703 | * @test 704 | */ 705 | public function deleteCalendarEvent_recurring_notRecurring() 706 | { 707 | $inputCreate = [ 708 | 'title' => str_random(16), 709 | 'start_datetime' => Carbon::parse('2017-08-25 16:00:00'), 710 | 'end_datetime' => Carbon::parse('2017-08-25 17:00:00'), 711 | 'description' => str_random(32), 712 | 'is_recurring' => true, 713 | 'frequence_number_of_recurring' => 1, 714 | 'frequence_type_of_recurring' => RecurringFrequenceType::WEEK, 715 | 'is_public' => true, 716 | ]; 717 | $calendarEvent = $this->calendarEvent->createCalendarEvent($inputCreate); 718 | $calendarEvent->deleteCalendarEvent(false); 719 | 720 | $this->assertNotNull($calendarEvent->deleted_at); 721 | $this->assertNull($calendarEvent->template->end_of_recurring); 722 | } 723 | 724 | /** 725 | * @test 726 | */ 727 | public function deleteCalendarEvent_recurring_recurring_deleted() 728 | { 729 | $inputCreate = [ 730 | 'title' => str_random(16), 731 | 'start_datetime' => Carbon::parse('2017-08-25 16:00:00'), 732 | 'end_datetime' => Carbon::parse('2017-08-25 17:00:00'), 733 | 'description' => str_random(32), 734 | 'is_recurring' => true, 735 | 'frequence_number_of_recurring' => 1, 736 | 'frequence_type_of_recurring' => RecurringFrequenceType::WEEK, 737 | 'is_public' => true, 738 | ]; 739 | $calendarEvent = $this->calendarEvent->createCalendarEvent($inputCreate); 740 | $calendarEvent->deleteCalendarEvent(true); 741 | 742 | $this->assertNotNull($calendarEvent->deleted_at); 743 | $this->assertNotNull($calendarEvent->template->deleted_at); 744 | $this->assertEquals($calendarEvent->start_datetime->format('Y-m-d'), $calendarEvent->template->end_of_recurring->format('Y-m-d')); 745 | } 746 | 747 | /** 748 | * @test 749 | */ 750 | public function deleteCalendarEvent_recurring_recurring_deleted_withoutInput() 751 | { 752 | $inputCreate = [ 753 | 'title' => str_random(16), 754 | 'start_datetime' => Carbon::parse('2017-08-25 16:00:00'), 755 | 'end_datetime' => Carbon::parse('2017-08-25 17:00:00'), 756 | 'description' => str_random(32), 757 | 'is_recurring' => true, 758 | 'frequence_number_of_recurring' => 1, 759 | 'frequence_type_of_recurring' => RecurringFrequenceType::WEEK, 760 | 'is_public' => true, 761 | ]; 762 | $calendarEvent = $this->calendarEvent->createCalendarEvent($inputCreate); 763 | $calendarEvent->deleteCalendarEvent(); 764 | 765 | $this->assertNotNull($calendarEvent->deleted_at); 766 | $this->assertNotNull($calendarEvent->template->deleted_at); 767 | $this->assertEquals($calendarEvent->start_datetime->format('Y-m-d'), $calendarEvent->template->end_of_recurring->format('Y-m-d')); 768 | } 769 | 770 | /** 771 | * @test 772 | */ 773 | public function deleteCalendarEvent_recurring_recurring_notDeleted() 774 | { 775 | $inputCreate = [ 776 | 'title' => str_random(16), 777 | 'start_datetime' => Carbon::parse('2017-08-25 16:00:00'), 778 | 'end_datetime' => Carbon::parse('2017-08-25 17:00:00'), 779 | 'description' => str_random(32), 780 | 'is_recurring' => true, 781 | 'frequence_number_of_recurring' => 1, 782 | 'frequence_type_of_recurring' => RecurringFrequenceType::WEEK, 783 | 'is_public' => true, 784 | ]; 785 | $calendarEvent = $this->calendarEvent->createCalendarEvent($inputCreate); 786 | $calendarEventNext = $calendarEvent->template->generateNextCalendarEvent(Carbon::now()); 787 | $calendarEventNext->deleteCalendarEvent(true); 788 | 789 | $this->assertNull($calendarEvent->deleted_at); 790 | $this->assertNotNull($calendarEventNext->deleted_at); 791 | $this->assertNull($calendarEvent->template->deleted_at); 792 | $this->assertEquals($calendarEventNext->start_datetime->format('Y-m-d'), $calendarEvent->template->end_of_recurring->format('Y-m-d')); 793 | } 794 | } 795 | -------------------------------------------------------------------------------- /tests/Unit/Models/TemplateCalendarEventTest.php: -------------------------------------------------------------------------------- 1 | templateCalendarEvent = new TemplateCalendarEvent(); 38 | $this->calendarEvent = new CalendarEvent(); 39 | } 40 | 41 | /** 42 | * Check instance 43 | * @test 44 | */ 45 | public function createInstanceFromClass() 46 | { 47 | $this->assertInstanceOf(TemplateCalendarEvent::class, $this->templateCalendarEvent); 48 | } 49 | 50 | /** 51 | * Check fillables 52 | * @test 53 | */ 54 | public function getFillable() 55 | { 56 | $expectedFillables = [ 57 | 'title', 58 | 'start_datetime', 59 | 'end_datetime', 60 | 'description', 61 | 'is_recurring', 62 | 'end_of_recurring', 63 | 'frequence_number_of_recurring', 64 | 'frequence_type_of_recurring', 65 | 'is_public', 66 | ]; 67 | 68 | $this->assertArraySubset($expectedFillables, $this->templateCalendarEvent->getFillable()); 69 | } 70 | 71 | /** 72 | * Check relation with calendar_events 73 | * @test 74 | */ 75 | public function events() 76 | { 77 | $templateCalendarEvent = factory(TemplateCalendarEvent::class)->create(); 78 | $calendarEvent = factory(CalendarEvent::class)->make(); 79 | $calendarEvent->template()->associate($templateCalendarEvent); 80 | $calendarEvent->save(); 81 | 82 | $this->assertInstanceOf(CalendarEvent::class, $templateCalendarEvent->events()->first()); 83 | } 84 | 85 | /** 86 | * Check parent (template_calendar_events) 87 | * @test 88 | */ 89 | public function parent() 90 | { 91 | $templateCalendarEvent = factory(TemplateCalendarEvent::class)->create(); 92 | $templateCalendarEventChild = factory(TemplateCalendarEvent::class)->create(); 93 | $templateCalendarEventChild->parent()->associate($templateCalendarEvent); 94 | 95 | $this->assertInstanceOf(TemplateCalendarEvent::class, $templateCalendarEventChild->parent); 96 | } 97 | 98 | /** 99 | * Check user relation with null 100 | * @test 101 | */ 102 | public function user_null() 103 | { 104 | $this->app['config']->set('calendar-event.user.model', null); 105 | $templateCalendarEvent = factory(TemplateCalendarEvent::class)->create(); 106 | $user = $templateCalendarEvent->user(); 107 | 108 | $this->assertNull($user); 109 | } 110 | 111 | /** 112 | * Check user relation 113 | * @test 114 | */ 115 | public function user() 116 | { 117 | $this->app['config']->set('calendar-event.user.model', User::class); 118 | $user = factory(config('calendar-event.user.model'))->create(); 119 | $templateCalendarEvent = factory(TemplateCalendarEvent::class)->create(); 120 | $templateCalendarEvent->user()->associate($user); 121 | 122 | $this->assertInstanceOf(User::class, $user); 123 | $this->assertInstanceOf(User::class, $templateCalendarEvent->user); 124 | } 125 | 126 | /** 127 | * Check place relation with null 128 | * @test 129 | */ 130 | public function place_null() 131 | { 132 | $this->app['config']->set('calendar-event.place.model', null); 133 | $templateCalendarEvent = factory(TemplateCalendarEvent::class)->create(); 134 | $place = $templateCalendarEvent->place(); 135 | 136 | $this->assertNull($place); 137 | } 138 | 139 | /** 140 | * Check place relation 141 | * @test 142 | */ 143 | public function place() 144 | { 145 | $this->app['config']->set('calendar-event.place.model', Place::class); 146 | $place = factory(config('calendar-event.place.model'))->create(); 147 | $templateCalendarEvent = factory(TemplateCalendarEvent::class)->create(); 148 | $templateCalendarEvent->place()->associate($place); 149 | 150 | $this->assertInstanceOf(Place::class, $place); 151 | $this->assertInstanceOf(Place::class, $templateCalendarEvent->place); 152 | } 153 | 154 | /** 155 | * @test 156 | */ 157 | public function scopeRecurring() 158 | { 159 | factory(TemplateCalendarEvent::class)->create([ 160 | 'title' => str_random(16), 161 | 'start_datetime' => Carbon::parse('2017-08-29 10:00:00'), 162 | 'end_datetime' => Carbon::parse('2017-08-29 11:00:00'), 163 | 'description' => str_random(32), 164 | 'is_recurring' => false, 165 | 'is_public' => true, 166 | ]); 167 | factory(TemplateCalendarEvent::class)->create([ 168 | 'title' => str_random(16), 169 | 'start_datetime' => Carbon::parse('2017-08-29 10:00:00'), 170 | 'end_datetime' => Carbon::parse('2017-08-29 11:00:00'), 171 | 'description' => str_random(32), 172 | 'is_recurring' => true, 173 | 'frequence_number_of_recurring' => 1, 174 | 'frequence_type_of_recurring' => RecurringFrequenceType::WEEK, 175 | 'is_public' => true, 176 | ]); 177 | 178 | $templateCalendarEvents = $this->templateCalendarEvent->recurring()->get(); 179 | 180 | $this->assertEquals(1, $templateCalendarEvents->count()); 181 | $this->assertEquals(true, $templateCalendarEvents->first()->is_recurring); 182 | } 183 | 184 | /** 185 | * @test 186 | */ 187 | public function scopePublic() 188 | { 189 | $input = [ 190 | 'title' => str_random(16), 191 | 'start_datetime' => Carbon::parse('2017-08-29 10:00:00'), 192 | 'end_datetime' => Carbon::parse('2017-08-29 11:00:00'), 193 | 'description' => str_random(32), 194 | 'is_recurring' => false, 195 | 'is_public' => true, 196 | ]; 197 | 198 | factory(TemplateCalendarEvent::class)->create(array_merge($input, ['is_public' => false])); 199 | factory(TemplateCalendarEvent::class)->create($input); 200 | factory(TemplateCalendarEvent::class)->create($input); 201 | 202 | $this->assertEquals(2, $this->templateCalendarEvent->public()->count()); 203 | } 204 | 205 | public function dataProvider_for_getNextCalendarEventStartDateTime() 206 | { 207 | return [ 208 | [ 209 | [ 210 | 'title' => str_random(16), 211 | 'start_datetime' => Carbon::parse('2017-08-30 10:00:00'), 212 | 'end_datetime' => Carbon::parse('2017-08-30 11:00:00'), 213 | 'description' => 'Foo bar', 214 | 'is_recurring' => false, 215 | ], 216 | null, 217 | ], 218 | [ 219 | [ 220 | 'title' => str_random(16), 221 | 'start_datetime' => Carbon::parse('2017-08-30 10:00:00'), 222 | 'end_datetime' => Carbon::parse('2017-08-30 11:00:00'), 223 | 'description' => 'Foo bar', 224 | 'is_recurring' => true, 225 | 'frequence_number_of_recurring' => 1, 226 | 'frequence_type_of_recurring' => RecurringFrequenceType::DAY, 227 | 'is_public' => true, 228 | ], 229 | Carbon::parse('2017-08-30')->addDay(), 230 | ], 231 | [ 232 | [ 233 | 'title' => str_random(16), 234 | 'start_datetime' => Carbon::parse('2017-08-30 10:00:00'), 235 | 'end_datetime' => Carbon::parse('2017-08-30 11:00:00'), 236 | 'description' => 'Foo bar', 237 | 'is_recurring' => true, 238 | 'frequence_number_of_recurring' => 1, 239 | 'frequence_type_of_recurring' => RecurringFrequenceType::WEEK, 240 | 'is_public' => true, 241 | 'end_of_recurring' => Carbon::parse('2017-08-30'), 242 | ], 243 | null, 244 | ], 245 | ]; 246 | } 247 | 248 | /** 249 | * @test 250 | * @dataProvider dataProvider_for_getNextCalendarEventStartDateTime 251 | * @param $input 252 | * @param $result 253 | */ 254 | public function getNextCalendarEventStartDateTime($input, $result) 255 | { 256 | $calendarEvent = $this->calendarEvent->createCalendarEvent($input); 257 | $startDateTime = $calendarEvent->template->getNextCalendarEventStartDateTime(Carbon::parse($input['start_datetime']->format('Y-m-d'))); 258 | $this->assertEquals($result, $startDateTime); 259 | } 260 | 261 | /** 262 | * Data provider for generate next calendar event 263 | * @return array 264 | */ 265 | public function dataProvider_for_generateNextCalendarEvent() 266 | { 267 | return [ 268 | [Carbon::parse('2017-08-10'), 4, RecurringFrequenceType::DAY, Carbon::parse('2017-08-14')], 269 | [Carbon::parse('2017-08-10'), 2, RecurringFrequenceType::WEEK, Carbon::parse('2017-08-24')], 270 | [Carbon::parse('2017-08-10'), 3, RecurringFrequenceType::MONTH, Carbon::parse('2017-11-10')], 271 | [Carbon::parse('2017-08-10'), 1, RecurringFrequenceType::YEAR, Carbon::parse('2018-08-10')], 272 | ]; 273 | } 274 | 275 | /** 276 | * @test 277 | * @dataProvider dataProvider_for_generateNextCalendarEvent 278 | * @param $startDateTime 279 | * @param $frequence_number_of_recurring 280 | * @param $frequence_type_of_recurring 281 | * @param $calendarEventNext_startDateTime 282 | */ 283 | public function generateNextCalendarEvent($startDateTime, $frequence_number_of_recurring, $frequence_type_of_recurring, $calendarEventNext_startDateTime) 284 | { 285 | $templateCalendarEvent = factory(TemplateCalendarEvent::class)->create([ 286 | 'title' => str_random(16), 287 | 'start_datetime' => $startDateTime, 288 | 'end_datetime' => $startDateTime, 289 | 'description' => str_random(32), 290 | 'is_recurring' => true, 291 | 'frequence_number_of_recurring' => $frequence_number_of_recurring, 292 | 'frequence_type_of_recurring' => $frequence_type_of_recurring, 293 | 'is_public' => true, 294 | ]); 295 | $calendarEvent = factory(CalendarEvent::class)->make(['start_datetime' => $startDateTime]); 296 | $calendarEvent->template()->associate($templateCalendarEvent); 297 | $calendarEvent->save(); 298 | 299 | $calendarEventNext = $templateCalendarEvent->generateNextCalendarEvent($startDateTime); 300 | 301 | $this->assertInstanceOf(CalendarEvent::class, $calendarEventNext); 302 | $this->assertInstanceOf(TemplateCalendarEvent::class, $calendarEventNext->template); 303 | $this->assertEquals($calendarEvent->template->id, $calendarEventNext->template->id); 304 | $this->assertEquals($calendarEventNext_startDateTime->format('Y-m-d'), $calendarEventNext->start_datetime->format('Y-m-d')); 305 | 306 | $this->assertDatabaseHas('calendar_events', ['start_datetime' => $calendarEventNext_startDateTime]); 307 | } 308 | 309 | /** 310 | * @test 311 | */ 312 | public function createCalendarEvent() 313 | { 314 | $templateCalendarEvent = factory(TemplateCalendarEvent::class)->create(); 315 | $startDateTime = Carbon::parse('2017-08-29'); 316 | $calendarEvent = $templateCalendarEvent->createCalendarEvent($startDateTime); 317 | 318 | $this->assertInstanceOf(CalendarEvent::class, $calendarEvent); 319 | $this->assertInstanceOf(CalendarEvent::class, $templateCalendarEvent->events->first()); 320 | $this->assertEquals($templateCalendarEvent->id, $calendarEvent->template_calendar_event_id); 321 | $this->assertEquals($startDateTime->format('Y-m-d'), $calendarEvent->start_datetime->format('Y-m-d')); 322 | 323 | $this->assertDatabaseHas('calendar_events', ['start_datetime' => $startDateTime]); 324 | } 325 | 326 | /** 327 | * @test 328 | */ 329 | public function updateCalendarEvent() 330 | { 331 | $inputCreate = [ 332 | 'title' => str_random(16), 333 | 'start_datetime' => Carbon::parse('2017-08-30 10:00:00'), 334 | 'end_datetime' => Carbon::parse('2017-08-30 11:00:00'), 335 | 'description' => str_random(16), 336 | 'is_recurring' => true, 337 | 'frequence_number_of_recurring' => 1, 338 | 'frequence_type_of_recurring' => RecurringFrequenceType::WEEK, 339 | 'is_public' => true, 340 | ]; 341 | $templateCalendarEvent = factory(TemplateCalendarEvent::class)->create($inputCreate); 342 | $calendarEvent = $templateCalendarEvent->createCalendarEvent(Carbon::parse($inputCreate['start_datetime'])); 343 | $startDateTimeNext = $calendarEvent->start_datetime->addWeek(); 344 | $calendarEventUpdated = $templateCalendarEvent->updateCalendarEvent($startDateTimeNext, $inputCreate); 345 | 346 | $this->assertNotEquals($templateCalendarEvent->id, $calendarEventUpdated->template->id); 347 | $this->assertDatabaseHas('template_calendar_events', $inputCreate + ['id' => $calendarEventUpdated->template->id]); 348 | } 349 | 350 | /** 351 | * @test 352 | */ 353 | public function editCalendarEvent_notExistCalendarEvent() 354 | { 355 | $inputCreate = [ 356 | 'title' => str_random(16), 357 | 'start_datetime' => Carbon::parse('2017-08-30 10:00:00'), 358 | 'end_datetime' => Carbon::parse('2017-08-30 11:00:00'), 359 | 'description' => str_random(16), 360 | 'is_recurring' => true, 361 | 'frequence_number_of_recurring' => 1, 362 | 'frequence_type_of_recurring' => RecurringFrequenceType::WEEK, 363 | 'is_public' => true, 364 | ]; 365 | $inputUpdate = [ 366 | 'description' => str_random(16), 367 | ]; 368 | 369 | $templateCalendarEvent = factory(TemplateCalendarEvent::class)->create($inputCreate); 370 | $calendarEvent = $templateCalendarEvent->createCalendarEvent(Carbon::parse($inputCreate['start_datetime'])); 371 | $startDateTimeNext = $calendarEvent->start_datetime->addWeek(); 372 | $calendarEventUpdated = $templateCalendarEvent->editCalendarEvent($startDateTimeNext, $inputUpdate); 373 | 374 | $this->assertInstanceOf(CalendarEvent::class, $calendarEvent); 375 | $this->assertEquals($templateCalendarEvent->id, $calendarEventUpdated->template->parent_id); 376 | $this->assertEquals($templateCalendarEvent->fresh()->end_of_recurring->format('Y-m-d'), $startDateTimeNext->format('Y-m-d')); 377 | 378 | $this->assertDatabaseHas('template_calendar_events', $inputUpdate); 379 | } 380 | 381 | /** 382 | * @test 383 | */ 384 | public function editCalendarEvent_existCalendarEvent() 385 | { 386 | $inputCreate = [ 387 | 'title' => str_random(16), 388 | 'start_datetime' => Carbon::parse('2017-08-30 10:00:00'), 389 | 'end_datetime' => Carbon::parse('2017-08-30 11:00:00'), 390 | 'description' => 'Foo bar', 391 | 'is_recurring' => true, 392 | 'frequence_number_of_recurring' => 1, 393 | 'frequence_type_of_recurring' => RecurringFrequenceType::WEEK, 394 | 'is_public' => true, 395 | ]; 396 | $inputUpdate = [ 397 | 'description' => str_random(16), 398 | ]; 399 | 400 | $templateCalendarEvent = factory(TemplateCalendarEvent::class)->create($inputCreate); 401 | $calendarEvent = $templateCalendarEvent->createCalendarEvent(Carbon::parse($inputCreate['start_datetime'])); 402 | $calendarEventUpdated = $templateCalendarEvent->editCalendarEvent($calendarEvent->start_datetime, $inputUpdate); 403 | 404 | $this->assertNotNull($calendarEvent->fresh()->deleted_at); 405 | $this->assertInstanceOf(CalendarEvent::class, $calendarEvent); 406 | $this->assertEquals($templateCalendarEvent->id, $calendarEventUpdated->template->parent_id); 407 | $this->assertEquals($templateCalendarEvent->fresh()->end_of_recurring->format('Y-m-d'), $calendarEvent->start_datetime->format('Y-m-d')); 408 | 409 | $this->assertDatabaseHas('template_calendar_events', $inputUpdate); 410 | } 411 | 412 | /** 413 | * Data provider for generateNextCalendarEvent_notGenerated 414 | * @return array 415 | */ 416 | public function dataProvider_for_generateNextCalendarEvent_notGenerated() 417 | { 418 | return [ 419 | [ 420 | [ 421 | 'title' => str_random(16), 422 | 'start_datetime' => Carbon::parse('2017-08-29 10:00:00'), 423 | 'end_datetime' => Carbon::parse('2017-08-29 11:00:00'), 424 | 'description' => str_random(32), 425 | 'is_recurring' => true, 426 | 'frequence_number_of_recurring' => 1, 427 | 'frequence_type_of_recurring' => RecurringFrequenceType::WEEK, 428 | 'is_public' => true, 429 | 'end_of_recurring' => '2017-09-04', 430 | ], 431 | ], 432 | [ 433 | [ 434 | 'title' => str_random(16), 435 | 'start_datetime' => Carbon::parse('2017-08-29 10:00:00'), 436 | 'end_datetime' => Carbon::parse('2017-08-29 11:00:00'), 437 | 'description' => str_random(32), 438 | 'is_recurring' => false, 439 | 'is_public' => true, 440 | ], 441 | ], 442 | ]; 443 | } 444 | 445 | /** 446 | * @test 447 | * @dataProvider dataProvider_for_generateNextCalendarEvent_notGenerated 448 | */ 449 | public function generateNextCalendarEvent_notGenerated(array $input) 450 | { 451 | $templateCalendarEvent = factory(TemplateCalendarEvent::class)->create($input); 452 | $calendarEvent = factory(CalendarEvent::class)->make(['start_datetime' => $input['start_datetime']]); 453 | $calendarEvent->template()->associate($templateCalendarEvent); 454 | $calendarEvent->save(); 455 | 456 | $calendarEventNext = $templateCalendarEvent->generateNextCalendarEvent(Carbon::parse($input['start_datetime'])); 457 | 458 | $this->assertNull($calendarEventNext); 459 | } 460 | 461 | /** 462 | * @test 463 | */ 464 | public function deleteCalendarEvent_notExist() 465 | { 466 | $inputCreate = [ 467 | 'title' => str_random(16), 468 | 'start_datetime' => Carbon::parse('2017-08-25 16:00:00'), 469 | 'end_datetime' => Carbon::parse('2017-08-25 17:00:00'), 470 | 'description' => str_random(32), 471 | 'is_recurring' => true, 472 | 'frequence_number_of_recurring' => 1, 473 | 'frequence_type_of_recurring' => RecurringFrequenceType::WEEK, 474 | 'is_public' => true, 475 | ]; 476 | $calendarEvent = $this->calendarEvent->createCalendarEvent($inputCreate); 477 | $templateCalendarEvent = $calendarEvent->template; 478 | 479 | $startDateTime = Carbon::parse('2017-09-08'); 480 | $isDeleted = $templateCalendarEvent->deleteCalendarEvent($startDateTime); 481 | 482 | $this->assertTrue($isDeleted); 483 | $this->assertNull($calendarEvent->deleted_at); 484 | $this->assertNull($templateCalendarEvent->deleted_at); 485 | $this->assertEquals($startDateTime->format('Y-m-d'), $templateCalendarEvent->end_of_recurring->format('Y-m-d')); 486 | } 487 | 488 | /** 489 | * @test 490 | */ 491 | public function deleteCalendarEvent_exist() 492 | { 493 | $startDateTime = Carbon::parse('2017-08-25'); 494 | $inputCreate = [ 495 | 'title' => str_random(16), 496 | 'start_datetime' => $startDateTime, 497 | 'end_datetime' => $startDateTime, 498 | 'description' => str_random(32), 499 | 'is_recurring' => true, 500 | 'frequence_number_of_recurring' => 1, 501 | 'frequence_type_of_recurring' => RecurringFrequenceType::WEEK, 502 | 'is_public' => true, 503 | ]; 504 | 505 | $calendarEvent = $this->calendarEvent->createCalendarEvent($inputCreate); 506 | $templateCalendarEvent = $calendarEvent->template; 507 | $isDeleted = $templateCalendarEvent->deleteCalendarEvent($startDateTime); 508 | 509 | $this->assertTrue($isDeleted); 510 | $this->assertNotNull($calendarEvent->fresh()->deleted_at); 511 | $this->assertNotNull($templateCalendarEvent->fresh()->deleted_at); 512 | $this->assertEquals($startDateTime->format('Y-m-d'), $templateCalendarEvent->fresh()->end_of_recurring->format('Y-m-d')); 513 | } 514 | } 515 | -------------------------------------------------------------------------------- /tests/Unit/Traits/CalendarEventPlaceTraitTest.php: -------------------------------------------------------------------------------- 1 | calendarEvents(); 22 | $this->assertInstanceOf(\Illuminate\Database\Eloquent\Relations\HasManyThrough::class, $events); 23 | } 24 | } -------------------------------------------------------------------------------- /tests/Unit/Traits/CalendarEventUserTraitTest.php: -------------------------------------------------------------------------------- 1 | calendarEvents(); 22 | $this->assertInstanceOf(\Illuminate\Database\Eloquent\Relations\HasManyThrough::class, $events); 23 | } 24 | } -------------------------------------------------------------------------------- /tests/Unit/config/ConfigTest.php: -------------------------------------------------------------------------------- 1 | [ 17 | "model" => null 18 | ], 19 | "place" => [ 20 | "model" => null 21 | ] 22 | ]; 23 | $path = __DIR__ . '/../../../src/config/calendar-event.php'; 24 | 25 | $this->assertFileExists($path); 26 | $this->assertEquals(include $path, $config); 27 | } 28 | } -------------------------------------------------------------------------------- /tests/fixtures/Models/Place.php: -------------------------------------------------------------------------------- 1 | make(\Illuminate\Database\Eloquent\Factory::class); 7 | 8 | /** @var \Illuminate\Database\Eloquent\Factory $factory */ 9 | $factory->define(Place::class, function (Faker $faker) { 10 | return [ 11 | 'name' => $faker->name(), 12 | 'address' => $faker->address, 13 | ]; 14 | }); -------------------------------------------------------------------------------- /tests/fixtures/database/factories/UserFactory.php: -------------------------------------------------------------------------------- 1 | make(\Illuminate\Database\Eloquent\Factory::class); 7 | 8 | /** @var \Illuminate\Database\Eloquent\Factory $factory */ 9 | $factory->define(User::class, function (Faker $faker) { 10 | return [ 11 | 'name' => $faker->name(), 12 | 'email' => $faker->email 13 | ]; 14 | }); -------------------------------------------------------------------------------- /tests/fixtures/database/migrations/2017_09_08_064317_create_places_table.php: -------------------------------------------------------------------------------- 1 | increments('id'); 21 | $table->string('name'); 22 | $table->text('address'); 23 | $table->timestamps(); 24 | }); 25 | } 26 | 27 | /** 28 | * Reverse the migrations. 29 | * 30 | * @return void 31 | */ 32 | public function down() 33 | { 34 | Schema::dropIfExists('places'); 35 | } 36 | } -------------------------------------------------------------------------------- /tests/fixtures/database/migrations/2017_09_08_064448_create_users_table.php: -------------------------------------------------------------------------------- 1 | increments('id'); 21 | $table->string('name'); 22 | $table->string('email')->unique(); 23 | $table->timestamps(); 24 | }); 25 | } 26 | 27 | /** 28 | * Reverse the migrations. 29 | * 30 | * @return void 31 | */ 32 | public function down() 33 | { 34 | Schema::dropIfExists('users'); 35 | } 36 | } --------------------------------------------------------------------------------