File: D:/HostingSpaces/SBogers10/netwerkbrabant.komma.pro/app/KommaApp/Events/EventService.php
<?php
namespace App\KommaApp\Events;
use App\KommaApp\Base\Service;
use App\KommaApp\Events\Models\Event;
use App\KommaApp\EventSignUps\EventSignUpService;
use App\KommaApp\Orders\OrderService;
use App\KommaApp\Regions\Models\Region;
use App\KommaApp\WeFact\WeFactAPI;
use App\KommaApp\WeFact\WeFactService;
use Carbon\Carbon;
use Illuminate\Support\Facades\Log;
use Spatie\CalendarLinks\Link;
class EventService extends Service
{
const EVENTS_PER_PAGE = 6;
const EVENTS_PER_REGION_PAGE = 9;
const HIGHLIGHTED_EVENTS = 3;
private $today;
public function __construct()
{
$this->today = Carbon::now()->startOfDay();
$this->today = $this->today->format('Y-m-d H:i:s');
parent::__construct();
}
/**
* The main query for getting events
*
* @return \Illuminate\Database\Eloquent\Relations\HasMany|\Illuminate\Database\Query\Builder
*/
private function eventQuery()
{
return $this->site
->events()
->where('active', 1)
->whereNotNull('region_id')
->where('date', '>=', $this->today)
->orderBy('date', 'asc')
->orderBy('created_at', 'asc');
}
/**
* Load the relations after or during query building
*
* @param $events
* @param bool $queryBuilder
*/
private function loadRelations(&$events, $queryBuilder = false)
{
if ($queryBuilder) {
$events->with('translation', 'images', 'type', 'type.translation');
} else {
$events->load('translation', 'images', 'type', 'type.translation');
}
}
/**
* Get a specific event by id
*
* @param $eventId
* @return \Illuminate\Database\Eloquent\Model|\Illuminate\Database\Eloquent\Relations\HasMany|\Illuminate\Database\Query\Builder|null|object
*/
public function getEvent($eventId)
{
$event = $this->eventQuery();
$event = $event->where('id', $eventId);
$this->loadRelations($event, true);
return $event->first();
}
/**
* Get the events which are active and bound to a region and are newer then today ordered descending by date
* with optional parameter to paginate, load the relations, exclude given ids and get a given amount
*
* @param bool $pagination
* @param bool $loadRelations
* @param array|null $excludeIds
* @param int|null $amount
* @return \Illuminate\Contracts\Pagination\LengthAwarePaginator|\Illuminate\Database\Eloquent\Collection|\Illuminate\Support\Collection
*/
public function getEvents(bool $pagination = false, bool $loadRelations = false, array $excludeIds = null, int $amount = null)
{
$events = $this->eventQuery();
if ($loadRelations) {
$this->loadRelations($events, true);
}
if ($excludeIds) {
$events = $events->whereNotIn('id', $excludeIds);
}
// Get the events paginated or by get with optional a given amount
if ($pagination) {
$events = $events->paginate(self::EVENTS_PER_PAGE);
} else {
if ($amount) {
$events = $events->take($amount);
}
$events = $events->get();
}
return $events;
}
/**
* Get the events which are active and bound to a region and are newer then today ordered descending by date
* with optional parameter to paginate, load the relations, exclude given ids and get a given amount
*
* @param bool $pagination
* @param bool $loadRelations
* @param array|null $excludeIds
* @param int|null $amount
* @return \Illuminate\Contracts\Pagination\LengthAwarePaginator|\Illuminate\Database\Eloquent\Collection|\Illuminate\Support\Collection
*/
public function getEventsByType(string $eventType, bool $pagination = false, bool $loadRelations = false, array $excludeIds = null, int $amount = null)
{
$events = $this->eventQuery();
if ($loadRelations) {
$this->loadRelations($events, true);
}
if ($excludeIds) {
$events = $events->whereNotIn('id', $excludeIds);
}
// Get the events paginated or by get with optional a given amount
if ($pagination) {
$events = $events->where('event_type_id', $eventType)->paginate(self::EVENTS_PER_PAGE);
} else {
if ($amount) {
$events = $events->where('event_type_id', $eventType)->take($amount);
}
$events = $events->get();
}
return $events;
}
/**
* Get the events which are active and where the region is $regionId and are newer then today ordered descending by date
* with optional parameter to paginate, exclude given ids and get a given amount
*
* @param int $regionId
* @param bool $pagination
* @param array|null $excludeIds
* @param int|null $amount
* @param int|null $eventType
* @return \Illuminate\Contracts\Pagination\LengthAwarePaginator|\Illuminate\Database\Eloquent\Collection|\Illuminate\Support\Collection
*/
public function getEventsByRegion(int $regionId, bool $pagination = true, array $excludeIds = null, int $amount = null, $eventType = null)
{
$events = $this->eventQuery()
->where('region_id', $regionId);
if ($eventType !== null) {
$events = $events->where('event_type_id', $eventType);
}
// Exclude the following events
if ($excludeIds) {
$events = $events->whereNotIn('id', $excludeIds);
}
$this->loadRelations($events, true);
// Get the events paginated or by get with optional a given amount
if ($pagination) {
$events = $events->paginate(self::EVENTS_PER_REGION_PAGE);
} else {
if ($amount) {
$events = $events->take($amount);
}
$events = $events->get();
}
return $events;
}
public function getBreakfastEventsByUserGroupId($userGroupId)
{
$events = $this->eventQuery()->where('user_group', $userGroupId);
return $events->paginate();
}
public function getHighlightedBreakfastEventsByUserGroupId($userGroupId)
{
$events = $this->eventQuery()->where('user_group', $userGroupId)->take(self::HIGHLIGHTED_EVENTS)->get();
// dd($events);
return $events;
}
/**
* Get the events which are active and where the region is $regionId and are newer then today and where the event_type_id is $eventType ordered descending by date
* with optional parameter to paginate, exclude given ids and get a given amount
*
* @param int $regionId
* @param bool $pagination
* @param array|null $excludeIds
* @param int|null $amount
* @return \Illuminate\Contracts\Pagination\LengthAwarePaginator|\Illuminate\Database\Eloquent\Collection|\Illuminate\Support\Collection
*/
public function getEventsByRegionAndType(int $regionId, string $eventType, bool $pagination = true, array $excludeIds = null, int $amount = null)
{
$events = $this->eventQuery()
->where('region_id', $regionId);
// Exclude the following events
if ($excludeIds) {
$events = $events->whereNotIn('id', $excludeIds);
}
// Only get events with matching event type
$events = $events->where('event_type_id', $eventType);
$this->loadRelations($events, true);
// Get the events paginated or by get with optional a given amount
if ($pagination) {
$events = $events->paginate(self::EVENTS_PER_REGION_PAGE);
} else {
if ($amount) {
$events = $events->take($amount);
}
$events = $events->get();
}
return $events;
}
/**
* Get the region of the logged in user
*
* @return Region|null
*/
private function getRegionThroughUser(): ?Region
{
if (! \Auth::guard('siteUser')->check()) {
return null;
}
if (! $userCompany = \Auth::guard('siteUser')->user()->getCompany()) {
return null;
}
if (! $region = $userCompany->region) {
return null;
}
return $region;
}
/**
* Get the amount of needed highlighted events by region
* and fill them out by the latest events if there aren't enough
*
* @param $regionId
* @return \Illuminate\Database\Eloquent\Collection|\Illuminate\Support\Collection
*/
private function getHighlightedEventsForRegionId($regionId)
{
$events = $this->eventQuery()
->where('region_id', $regionId)
->take(self::HIGHLIGHTED_EVENTS)
->get();
// If there are 3 events return them
if ($events->count() == self::HIGHLIGHTED_EVENTS) {
return $events;
}
// Fill out the events with the latest events (without region filter)
// Get id's from the found event(s)
$eventIds = $events->pluck('id')->all();
// Determine the amount of events will still need
$amountMoreNeeded = self::HIGHLIGHTED_EVENTS - $events->count();
// Get the events and merge them with the current highlighted
$otherRegionsUpcomingEvents = $this->getEvents(false, false, $eventIds, $amountMoreNeeded);
$events = $events->merge($otherRegionsUpcomingEvents);
return $events;
}
/**
* Get the highlighted events
* This can be based upon the bind company region of the user / or as guest the most upcoming events
*
* @return \Illuminate\Database\Eloquent\Collection|\Illuminate\Support\Collection
*/
public function getHighlightedEvents()
{
if ($region = $this->getRegionThroughUser()) {
$highlightedEvents = $this->getHighlightedEventsForRegionId($region->id);
} else {
$highlightedEvents = $this->getEvents(false, false, null, 3);
}
$this->loadRelations($highlightedEvents);
return $highlightedEvents;
}
/**
* Get the highlighted events
* This can be based upon the bind company region of the user / or as guest the most upcoming events
*
* @return \Illuminate\Database\Eloquent\Collection|\Illuminate\Support\Collection
*/
public function getHighlightedEventsByType($eventType)
{
// TODO user region
if ($region = $this->getRegionThroughUser()) {
$highlightedEvents = $this->getHighlightedEventsForRegionId($region->id);
} else {
$highlightedEvents = $this->getEventsByType($eventType, false, false, null, 3);
}
$this->loadRelations($highlightedEvents);
return $highlightedEvents;
}
/**
* Get the next event after the given one
* first try to get the next event in region else just the next one
* and if there are non a event that hasn't happend yet...
*
* @param Event $event
* @return \Illuminate\Database\Eloquent\Model|\Illuminate\Database\Eloquent\Relations\HasMany|\Illuminate\Database\Query\Builder|null|object
*/
public function getNextEvent(Event $event)
{
// First next event in region
if ($nextRegionEvent = $this->eventQuery()
->where('date', '>=', $event->date)
->where('id', '!=', $event->id)
->where('region_id', $event->region_id)
->first()) {
return $nextRegionEvent;
}
// Else first next event
if ($nextEvent = $this->eventQuery()
->where('date', '>=', $event->date)
->where('id', '!=', $event->id)
->first()) {
return $nextEvent;
}
// And as final try other upcoming events
return $otherUpcomingEvent = $this->site
->events()
->where('active', 1)
->whereNotNull('region_id')
->where('date', '>=', $this->today)
->where('date', '<=', $event->date)
->where('id', '!=', $event->id)
->orderBy('date', 'desc')
->first();
}
/**
* Create a calendar link
*
* @param Event $event
* @return Link
* @throws \Spatie\CalendarLinks\Exceptions\InvalidLink
*/
public function createCalendarEvent(Event $event)
{
$addressString = '';
foreach (['location_name', 'location_address', 'location_city'] as $locationPart) {
if (empty($event->{$locationPart})) {
continue;
}
$addressString .= $event->{$locationPart}.' ';
}
// Create a calendar link
if (isset($event->calendar_start_time) && $event->calendar_start_time != '' && isset($event->calendar_end_time) && $event->calendar_end_time != '') {
$startTime = $event->date->setTimeFromTimeString($event->calendar_start_time);
$endTime = $event->date->setTimeFromTimeString($event->calendar_end_time);
$calenderEvent = Link::create($event->translation->name, $startTime, $endTime)
->description($event->translation->sub_title);
} else {
$calenderEvent = Link::create($event->translation->name, $event->date->startOfDay(), $event->date->endOfDay())
->description($event->translation->sub_title);
}
if ($addressString != '') {
$calenderEvent->address($addressString);
}
return $calenderEvent;
}
public function convertRequest(array $formValues)
{
/** @var EventSignUpService $eventSignUpService */
$eventSignUpService = \App::make(EventSignUpService::class);
$event = Event::find(array_pull($formValues, 'event'))
->load('translation')
->load('type');
$eventProduct = (object) [
'productable_type' => Event::class,
'productable_id' => $event->id,
'name' => $event->translation->name,
// Calculated the price without vat by the Event type Vat and ceil to whole integer
'price' => round($event->price_amount / (($event->type->vat / 100) + 1)),
'vat' => $event->type->vat,
'price_including_vat' => $event->price_amount,
];
// Create signee array and add the main signee
$signees = [
'main' => $eventSignUpService->createSignee($formValues),
];
// Check if there is an other invoice mail address
if (isset($formValues['other_invoice']) && $formValues['other_invoice'] == 'on') {
$signees['main']->invoice_email = array_pull($formValues, 'other_invoice_email');
}
// Else populate it from the email value
else {
$signees['main']->invoice_email = $signees['main']->email;
}
array_forget($formValues, ['other_invoice', 'other_invoice_email']);
// Check if the (main) signee has filled a reference number
if (! empty($formValues['invoice_reference'])) {
$signees['main']->reference = array_pull($formValues, 'invoice_reference');
}
array_forget($formValues, ['invoice_reference']);
// Check if we need to get the plus one else remove the keys
if (isset($formValues['plus_one']) && $formValues['plus_one'] == 'on') {
$signees['plusOne'] = $eventSignUpService->createSignee($formValues, 'plus_one_');
$signees['plusOne']->invoice_email = $signees['plusOne']->email;
} else {
foreach ($eventSignUpService::SigneeFields as $signeeField) {
array_forget($formValues, 'plus_one_'.$signeeField);
}
}
array_forget($formValues, 'plus_one');
return [$signees, $eventProduct, $event];
}
/**
* Save signee as event sign ups through the event model
*
* @param $signee
* @param $mainSignee
* @param Event $event
* @param int $invoiceId
* @param $invoiceIdPrefixed
*/
public function addSigneeToEventSignUps($signee, $mainSignee, Event $event, int $invoiceId, $invoiceIdPrefixed)
{
/** @var EventSignUpService $eventSignUpService */
$eventSignUpService = \App::make(EventSignUpService::class);
return $eventSignUpService->addSigneeToEventSignUps($signee, $mainSignee, $event, $invoiceId, $invoiceIdPrefixed);
}
/**
* Get the order status and belonging event through order id
*
* @param $orderId
* @return array
*/
public function getEventOrderStatus($orderId)
{
// Get the order
$orderService = \App::make(OrderService::class);
if (! $order = $orderService->getOrder($orderId)) {
\App::abort(404);
}
// Get the belonging event product
$orderProduct = $order->products()->first();
// Get the event from the event product
$event = $orderProduct->productable;
$event = $event->load('translation');
return [$order, $event];
}
/**
* Sync a event with WeFact
*
* @param Event $event
*/
public function syncEvent(Event $event)
{
if (empty($event->wefact_code)) {
throw new \InvalidArgumentException('Event (id:'.$event->id.') did not have a wefact_code, and therefor could not sync.');
}
/** @var WeFactService $weFactService */
$weFactService = app()->make(WeFactService::class);
$weFactProduct = $weFactService->getProduct($event->wefact_code);
$weFactProductModifiedDate = Carbon::createFromFormat(Carbon::DEFAULT_TO_STRING_FORMAT, $weFactProduct->Modified);
// WeFact product and Event are up-to-date
if ($weFactProductModifiedDate == $event->wefact_modified_at) {
Log::info(self::class.': Sync - WeFact product '.$event->wefact_code.' and event '.$event->id.' are up-to-date.');
return;
}
// WeFact is newer, therefor sync WeFact to Product
elseif ($weFactProductModifiedDate > $event->wefact_modified_at) {
$this->syncWeFactProductToEvent($event, $weFactProduct, $weFactProductModifiedDate);
return;
}
}
/**
* Sync all relevant events from WeFact.
* Note; Rather then loading each event and the belonging WeFact Product individual, we load the product list and then compare it with found events in our database
*
* @return int
*/
public function syncAllRelevantEvents()
{
/** @var WeFactService $weFactService */
$weFactService = app()->make(WeFactService::class);
$weFactProducts = collect($weFactService->getEventProducts());
$events = Event::where('date', '>=', $this->today)
->whereIn('wefact_code', $weFactProducts->pluck('ProductCode')->toArray())
->get();
// Track for logging how many event are synced
$eventSynced = 0;
foreach ($events as $event) {
$weFactProduct = (object) $weFactProducts->where('ProductCode', '=', $event->wefact_code)->first();
$weFactProductModifiedDate = Carbon::createFromFormat(Carbon::DEFAULT_TO_STRING_FORMAT, $weFactProduct->Modified);
// If the WeFact date is the same continue
if ($weFactProductModifiedDate == $event->wefact_modified_at) {
continue;
}
$eventSynced++;
$this->syncWeFactProductToEvent($event, $weFactProduct, $weFactProductModifiedDate);
}
return $eventSynced;
}
/**
* Save WeFact product into our belonging event
*
* @param Event $event
* @param $weFactProduct
* @param Carbon $weFactProductModifiedDate
*/
private function syncWeFactProductToEvent(Event $event, $weFactProduct, Carbon $weFactProductModifiedDate)
{
$event->price_amount = WeFactAPI::convertPriceExclToIncl($weFactProduct->PriceExcl, $weFactProduct->TaxPercentage);
$event->price = '€ '.number_format(($event->price_amount / 100), 2, ',', '.');
$event->wefact_name = $weFactProduct->ProductName;
$event->wefact_description = $weFactProduct->ProductKeyPhrase;
$event->wefact_modified_at = $weFactProductModifiedDate;
$event->save();
Log::info(self::class.': Sync - Event '.$event->id.' has been synced with WeFact product '.$event->wefact_code.'.');
}
}