File: D:/HostingSpaces/SBogers10/ehbo.today/app/KommaApp/Courses/Kms/CourseService.php
<?php
/**
* Created by PhpStorm.
* Course: julesgraus
* Date: 31/01/2018
* Time: 13:13
*/
namespace App\KommaApp\Courses\Kms;
use App\KommaApp\Competences\Kms\CompetenceService;
use App\KommaApp\Competences\Models\Competence;
use App\KommaApp\Documents\Kms\DocumentableInterface;
use App\KommaApp\Kms\Core\AbstractTranslatableModel;
use App\KommaApp\Kms\Core\Attributes\Attribute;
use App\KommaApp\Kms\Core\HasRoutesInterface;
use App\KommaApp\Kms\Core\Sections\AbstractSectionTabItem;
use App\KommaApp\Kms\Core\Sections\SectionService;
use App\KommaApp\Courses\Models\Course;
use App\KommaApp\Kms\Core\Sections\SectionTabItem;
use App\KommaApp\Users\Kms\UserService;
use App\KommaApp\Users\Models\User;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Query\Builder;
class CourseService extends SectionService
{
protected $sortable = false;
protected $orderBy = 'date';
/** @var CompetenceService $competenceService */
protected $competenceService;
/** @var UserService $userService */
private $userService;
function __construct()
{
$this->forModelName = Course::class;
$this->competenceService = \App::make(CompetenceService::class);
$this->userService = \App::make(UserService::class);
parent::__construct();
}
public function saveModel(Model $model = null, Collection $sectionTabItems): Model
{
$userPresenceData = [];
/** @var Course $model */
$sectionTabItems->each(function($sectionTabItem, $key) use($model, &$userPresenceData) {
/** @var SectionTabItem $sectionTabItem */
$attribute = $sectionTabItem->getAttribute();
$value = $attribute->getValue();
$reference = $attribute->getsValueFromReference();
switch ($attribute->getsValueFrom())
{
case Attribute::ValueFromItself:
if($reference == 'subscribed_users' || $reference == 'present_users') {
if($reference == 'subscribed_users') $status = Course::UserStatusSubscribed;
elseif($reference == 'present_users') $status = Course::UserStatusPresent;
if($value == '') break;
$idsToProcess = collect(explode(',', $value));
$syncData = $idsToProcess->mapWithKeys(function($userId) use ($status) {
return [$userId => ['user_status' => $status]];
})->toArray();
$userPresenceData = $userPresenceData + $syncData; //Union operation (combine arrays, left one is leading of both have same key)
}
break;
}
});
$model->save(); //Save the page
$model = parent::saveModel($model, $sectionTabItems); //First make sure we have a model and save the attributes in them from the SectionTabItem attributes
$statusArray = $model->users()->sync($userPresenceData);
//Ids (Users) that are attached should get the competences of the course
$competences = $model->competences()->get();
foreach($statusArray['attached'] as $attachedId) {
$user = User::find($attachedId);
$competences->each(function(Competence $competence) use ($user) {
$this->competenceService->userAcquiredCompetence($user, $competence);
});
}
foreach($statusArray['detached'] as $attachedId) {
$user = User::find($attachedId);
$competences->each(function(Competence $competence) use ($user) {
$this->competenceService->revertAcquiredCompetence($user, $competence);
});
}
//Return the page
return $model;
}
/**
* This method will remove an TranslatableModelInterface instance
*
* @param $model
* @throws \Exception
*/
public function destroyModel(Model $model)
{
//delete the images
$this->imageService->deleteModelImages($model);
if(is_a($model, DocumentableInterface::class)) $this->documentService->deleteDocumentsForModel($model);
if(is_a($model,AbstractTranslatableModel::class)) {
foreach ($model->translations()->get() as $translation) {
//Delete the route of the translation
if(is_a($model, HasRoutesInterface::class)){
$translation->routes()->delete();
}
//Delete the translation
$translation->delete();
}
}
$model->users()->detach();
$model->competences()->detach();
$model->delete();
}
public function fillAttributesWithData(Collection $sectionTabItems, Model $model)
{
//Validate that each Collection item is an instance of AbstractSectionTabItem
$sectionTabItems->every(function($item, $key) {
if(!is_a($item, AbstractSectionTabItem::class)) throw new \InvalidArgumentException("The attributes passed must be a Collection of AbstractSectionTabItem instances but was not");
});
$filledAttributes = new Collection();
if(is_a($model, AbstractTranslatableModel::class)) $model->load('translations');
/** @var Course $model */
$sectionTabItems->each(
function ($sectionTabItem, $key) use ($model, $filledAttributes) {
/** @var $sectionTabItem SectionTabItem */
if(!is_a($sectionTabItem->getAttribute(), Attribute::class)) throw new \InvalidArgumentException("One of the attributes in a AbstractSectionTabItem instance is not but must be an child instance of Attribute.");
$value = null;
$valueReference = $sectionTabItem->getAttribute()->getsValueFromReference();
switch($sectionTabItem->getAttribute()->getsValueFrom())
{
case Attribute::ValueFromItself:
switch ($valueReference)
{
case 'subscribed_users':
$ids = $model->users()->wherePivot('user_status', '=', Course::UserStatusSubscribed)->get()->map(function(User $user) {
return $user->id;
})->toArray();
$value = implode(',', $ids);
break;
case 'present_users':
$ids = $model->users()->wherePivot('user_status', '=', Course::UserStatusPresent)->get()->map(function(User $user) {
return $user->id;
})->toArray();
$value = implode(',', $ids);
break;
}
break;
}
//Finally when we have a value we will set it on the attribute and then process the next one.
if($value !== null) {
$sectionTabItem->getAttribute()->setValue((string)$value);
}
$filledAttributes->push($sectionTabItem);
}
);
$filledAttributes = parent::fillAttributesWithData($sectionTabItems, $model);
return $filledAttributes;
}
/**
* @param User $user
* @return \Illuminate\Support\Collection
*/
public function recommendedCourses(User $user)
{
//Get the competences for a user that are expired and missing. Collect them into a new collection
$expiredCompetences = $this->getExpiredCompetences($user);
$missingCompetences = $this->getMissingCompetences($user);
$almostExpired = $this->getAlmostExpiredCompetences($user);
$neededCompetences = collect($expiredCompetences)->merge($missingCompetences)->merge($almostExpired);
//Find upcoming courses
$courses = Course::has('competences')->with('competences')->whereDate('date', '>=', Carbon::now()->startOfDay())->get();
//Use the expired competences to find upcoming courses that cover those competences.
$recommendedCourses = collect();
$neededCompetences->each(function(Competence $neededCompetence) use (&$recommendedCourses, $courses) {
//dd($courses);
$courses->each(function (Course $course) use (&$recommendedCourses, $neededCompetence) {
$course->competences->each(function(Competence $currentCourseCompetence) use(&$recommendedCourses, $course, $neededCompetence) {
if($neededCompetence->id == $currentCourseCompetence->id) $recommendedCourses->push($course);
});
});
});
//Filter out doubles
$recommendedCourses = $recommendedCourses->unique();
$recommendedCourses = $recommendedCourses->sortBy('date');
//Filter out courses that the user already signed up on or is present in
// $recommendedCourses = $recommendedCourses->filter(function(Course $course) use ($user) {
// return $user->courses()->get()->contains('id','=',$course->id);
// });
return $recommendedCourses;
}
/**
* @param User $user
* @param bool $includeUserCompetenceStatuses
* @return Collection
*/
public function subscribedCourses(User $user)
{
$subscribedCourses = $user->courses()->withPivot(['user_status'])->wherePivot('user_status', '=', Course::UserStatusSubscribed)->whereDate('date', '>=', Carbon::now()->startOfDay()->toDateString())->orderBy('date')->get();
return $subscribedCourses;
}
/**
* @param User $user
* @param bool $includeUserCompetenceStatuses
* @return Collection
*/
public function completedCourses(User $user)
{
$completedCourses = $user->courses()->whereDate('date', '>', Carbon::now()->subYear(2)->startOfDay())->withPivot(['user_status'])->wherePivot('user_status', '=', Course::UserStatusPresent)->orderBy('date', 'desc')->get();
return $completedCourses;
}
/**
* @param Course|Collection $course
* @param User $user
* @return Course|\Illuminate\Support\Collection
*/
public function includeUserCompetenceStatusesForCourses($course, User $user) {
//Convert course to collection if needed
if(!is_a($course, Course::class)) {
$courses = collect($course);
} else {
$courses = $course;
}
//Validate that each collection member is a course
$courses->each(function($course) { if(!is_a($course, Course::class)) throw new \RuntimeException('One of the given "courses" was no instance of "'.Course::class.'"'); });
$courses->each(function(Course $course) use ($user) {
$course->load(['translations', 'competences', 'competences.translations']);
$this->includeUserCompetenceStatusesForCompetences($course->competences, $user);
$course->competences->sortBy('name');
});
if(!is_a($course, Course::class)) {
return $courses;
} else {
return $courses->first();
}
}
/**
* @param Course|Collection $course
* @param User $user
* @return Course|\Illuminate\Support\Collection
*/
public function includeUserCompetenceStatusesForCompetences($competence, User $user) {
//Convert course to collection if needed
if(!is_a($competence, Competence::class)) {
$competences = collect($competence);
} else {
$competences = $competence;
}
$missing = $this->getMissingCompetences($user);
$expired = $this->getExpiredCompetences($user);
$almost_expired = $this->getAlmostExpiredCompetences($user);
$valid = $this->getValidCompetences($user);
$competences->each(function(Competence $courseCompetence) use ($missing, $expired, $almost_expired, $valid) {
$courseCompetence->authUserCompetenceStatus = Competence::StatusUnknown;
if($missing->contains($courseCompetence->id)) $courseCompetence->authUserCompetenceStatus = Competence::StatusMissing;
if($expired->contains($courseCompetence->id)) $courseCompetence->authUserCompetenceStatus = Competence::StatusExpired;
if($almost_expired->contains($courseCompetence->id)) $courseCompetence->authUserCompetenceStatus = Competence::StatusAlmostExpired;
if($valid->contains($courseCompetence->id)) $courseCompetence->authUserCompetenceStatus = Competence::StatusValid;
});
if(!is_a($competence, Competence::class)) {
return $competences;
} else {
return $competences->first();
}
}
/**
* Get the competences that are not expired yet and thus are valid
*
* @param User $user
* @return Collection
*/
public function getValidCompetences(User $user):Collection
{
$userCompetences = $user->competences()->withPivot(['expires_at', 'acquired_at'])->with('translations')->get();
$validCompetences = $userCompetences->filter(function(Competence $competence) {
$currentExpireDate = Carbon::parse($competence->pivot->expires_at);
return Carbon::now()->diffInMonths($currentExpireDate, false) >= 2;
});
return $validCompetences;
}
/**
* Get the competences that are almost expiring (in one month)
*
* @param User $user
* @return Collection
*/
public function getAlmostExpiredCompetences(User $user):Collection
{
$userCompetences = $user->competences()->withPivot(['expires_at', 'acquired_at'])->with('translations')->get();
$validCompetences = $userCompetences->filter(function(Competence $competence) {
$currentExpireDate = Carbon::parse($competence->pivot->expires_at);
$diffInMonths = Carbon::now()->diffInMonths($currentExpireDate, false);
return $currentExpireDate->isFuture() && $diffInMonths < 2;
});
return $validCompetences;
}
/**
* Get the expired competences
*
* @param User $user
* @return Collection
*/
public function getExpiredCompetences(User $user):Collection
{
//Get the competences from the user that are expired
$userCompetences = $user->competences()->withPivot(['expires_at', 'acquired_at'])->with('translations')->get();
$expiredCompetences = $userCompetences->filter(function(Competence $competence) {
return Carbon::parse($competence->pivot->expires_at)->isPast();
});
return $expiredCompetences;
}
/**
* Get the missing competences
*
* @param User $user
* @return Collection
*/
public function getMissingCompetences(User $user):Collection
{
//Get the competences that the user does not have
$allCompetences = Competence::with('users')->get();
$missingCompetences = $allCompetences->filter(function(Competence $competence) use ($user) {
return !$competence->users->contains($user);
});
return $missingCompetences;
}
}