Skip to content

Commit

Permalink
Merge pull request #84 from HiEventsDev/develop
Browse files Browse the repository at this point in the history
Add ability to duplicate events
  • Loading branch information
daveearley authored Jul 6, 2024
2 parents 1f0bf47 + e38747f commit 2aa2bf8
Show file tree
Hide file tree
Showing 56 changed files with 1,781 additions and 541 deletions.
2 changes: 1 addition & 1 deletion backend/app/DataTransferObjects/BaseDTO.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@

namespace HiEvents\DataTransferObjects;

use HiEvents\DataTransferObjects\Attributes\CollectionOf;
use Illuminate\Support\Collection;
use ReflectionClass;
use ReflectionProperty;
use RuntimeException;
use Throwable;
use HiEvents\DataTransferObjects\Attributes\CollectionOf;

abstract class BaseDTO
{
Expand Down
14 changes: 14 additions & 0 deletions backend/app/DomainObjects/EventDomainObject.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ class EventDomainObject extends Generated\EventDomainObjectAbstract implements I

private ?Collection $images = null;

private ?Collection $promoCodes = null;

private ?EventSettingDomainObject $settings = null;

private ?OrganizerDomainObject $organizer = null;
Expand Down Expand Up @@ -190,4 +192,16 @@ public function getLifecycleStatus(): string

return EventLifecycleStatus::ENDED->name;
}

public function getPromoCodes(): ?Collection
{
return $this->promoCodes;
}

public function setPromoCodes(?Collection $promoCodes): self
{
$this->promoCodes = $promoCodes;

return $this;
}
}
33 changes: 31 additions & 2 deletions backend/app/Http/Actions/Events/DuplicateEventAction.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,41 @@

namespace HiEvents\Http\Actions\Events;

use HiEvents\DomainObjects\EventDomainObject;
use HiEvents\Http\Actions\BaseAction;
use HiEvents\Http\Request\Event\DuplicateEventRequest;
use HiEvents\Resources\Event\EventResource;
use HiEvents\Services\Domain\Event\DTO\DuplicateEventDataDTO;
use HiEvents\Services\Handlers\Event\DuplicateEventHandler;
use Illuminate\Http\JsonResponse;
use Throwable;

class DuplicateEventAction extends BaseAction
{
public function __invoke(): void
public function __construct(private readonly DuplicateEventHandler $handler)
{
// todo
}

/**
* @throws Throwable
*/
public function __invoke(int $eventId, DuplicateEventRequest $request): JsonResponse
{
$this->isActionAuthorized($eventId, EventDomainObject::class);

$event = $this->handler->handle(new DuplicateEventDataDTO(
eventId: $eventId,
accountId: $this->getAuthenticatedAccountId(),
title: $request->validated('title'),
startDate: $request->validated('start_date'),
duplicateTickets: $request->validated('duplicate_tickets'),
duplicateQuestions: $request->validated('duplicate_questions'),
duplicateSettings: $request->validated('duplicate_settings'),
duplicatePromoCodes: $request->validated('duplicate_promo_codes'),
description: $request->validated('description'),
endDate: $request->validated('end_date'),
));

return $this->resourceResponse(EventResource::class, $event);
}
}
30 changes: 30 additions & 0 deletions backend/app/Http/Request/Event/DuplicateEventRequest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

namespace HiEvents\Http\Request\Event;

use HiEvents\Http\Request\BaseRequest;
use HiEvents\Validators\EventRules;

class DuplicateEventRequest extends BaseRequest
{
use EventRules;

public function rules(): array
{
$eventValidations = $this->minimalRules();

$duplicateValidations = [
'duplicate_tickets' => ['boolean', 'required'],
'duplicate_questions' => ['boolean', 'required'],
'duplicate_settings' => ['boolean', 'required'],
'duplicate_promo_codes' => ['boolean', 'required'],
];

return array_merge($eventValidations, $duplicateValidations);
}

public function messages(): array
{
return $this->eventMessages();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

class UpdateEventSettingsRequest extends BaseRequest
{
// @todo these should all be required for the update request. They should only be nullable for the PATCH request
public function rules(): array
{
return [
Expand Down
14 changes: 5 additions & 9 deletions backend/app/Http/Request/Ticket/UpsertTicketRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@

namespace HiEvents\Http\Request\Ticket;

use Illuminate\Validation\Rule;
use HiEvents\DomainObjects\Enums\TicketType;
use HiEvents\Http\Request\BaseRequest;
use HiEvents\Validators\Rules\RulesHelper;
use Illuminate\Validation\Rule;

class UpsertTicketRequest extends BaseRequest
{
Expand All @@ -20,16 +20,12 @@ public function rules(): array
'sale_start_date' => 'date|nullable',
'sale_end_date' => 'date|nullable|after:sale_start_date',
'max_per_order' => 'integer|nullable',
'price' => array_merge(RulesHelper::MONEY, [
'required_without:prices',
Rule::requiredIf($this->input('type') !== TicketType::TIERED->name),
]),
'prices' => 'required_without:price|array|required_if:type,' . TicketType::TIERED->name,
'prices.*.price' => RulesHelper::MONEY,
'prices.*.label' => ['nullable', ...RulesHelper::STRING],
'prices' => ['required', 'array'],
'prices.*.price' => [...RulesHelper::MONEY, 'required'],
'prices.*.label' => ['nullable', ...RulesHelper::STRING, 'required_if:type,' . TicketType::TIERED->name],
'prices.*.sale_start_date' => ['date', 'nullable', 'after:sale_start_date'],
'prices.*.sale_end_date' => 'date|nullable|after:prices.*.sale_start_date',
'prices.*.initial_quantity_available' => 'integer|nullable',
'prices.*.initial_quantity_available' => ['integer', 'nullable', 'min:0'],
'prices.*.is_hidden' => ['boolean'],
'description' => 'string|nullable',
'min_per_order' => 'integer|nullable',
Expand Down
5 changes: 5 additions & 0 deletions backend/app/Models/Event.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ public function event_settings(): HasOne
return $this->hasOne(EventSetting::class);
}

public function promo_codes(): HasMany
{
return $this->hasMany(PromoCode::class);
}

public static function boot()
{
parent::boot();
Expand Down
144 changes: 144 additions & 0 deletions backend/app/Services/Domain/Event/CreateEventService.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
<?php

namespace HiEvents\Services\Domain\Event;

use HiEvents\DomainObjects\Enums\HomepageBackgroundType;
use HiEvents\DomainObjects\EventDomainObject;
use HiEvents\DomainObjects\EventSettingDomainObject;
use HiEvents\DomainObjects\OrganizerDomainObject;
use HiEvents\Exceptions\OrganizerNotFoundException;
use HiEvents\Helper\DateHelper;
use HiEvents\Helper\IdHelper;
use HiEvents\Repository\Interfaces\EventRepositoryInterface;
use HiEvents\Repository\Interfaces\EventSettingsRepositoryInterface;
use HiEvents\Repository\Interfaces\EventStatisticRepositoryInterface;
use HiEvents\Repository\Interfaces\OrganizerRepositoryInterface;
use Illuminate\Database\DatabaseManager;
use Throwable;

class CreateEventService
{
public function __construct(
private readonly EventRepositoryInterface $eventRepository,
private readonly EventSettingsRepositoryInterface $eventSettingsRepository,
private readonly OrganizerRepositoryInterface $organizerRepository,
private readonly DatabaseManager $databaseManager,
private readonly EventStatisticRepositoryInterface $eventStatisticsRepository,
)
{
}

/**
* @throws Throwable
*/
public function createEvent(
EventDomainObject $eventData,
EventSettingDomainObject $eventSettings = null
): EventDomainObject
{
$this->databaseManager->beginTransaction();

$organizer = $this->getOrganizer(
organizerId: $eventData->getOrganizerId(),
accountId: $eventData->getAccountId()
);

$event = $this->handleEventCreate($eventData);

$this->createEventSettings(
eventSettings: $eventSettings,
event: $event,
organizer: $organizer
);

$this->createEventStatistics($event);

$this->databaseManager->commit();

return $event;
}

/**
* @throws OrganizerNotFoundException
*/
private function getOrganizer(int $organizerId, int $accountId): OrganizerDomainObject
{
$organizer = $this->organizerRepository->findFirstWhere([
'id' => $organizerId,
'account_id' => $accountId,
]);

if ($organizer === null) {
throw new OrganizerNotFoundException(
__('Organizer :id not found', ['id' => $organizerId])
);
}

return $organizer;
}

private function handleEventCreate(EventDomainObject $eventData): EventDomainObject
{
return $this->eventRepository->create([
'title' => $eventData->getTitle(),
'organizer_id' => $eventData->getOrganizerId(),
'start_date' => DateHelper::convertToUTC($eventData->getStartDate(), $eventData->getTimezone()),
'end_date' => $eventData->getEndDate()
? DateHelper::convertToUTC($eventData->getEndDate(), $eventData->getTimezone())
: null,
'description' => $eventData->getDescription(),
'timezone' => $eventData->getTimezone(),
'currency' => $eventData->getCurrency(),
'location_details' => $eventData->getLocationDetails(),
'account_id' => $eventData->getAccountId(),
'user_id' => $eventData->getUserId(),
'status' => $eventData->getStatus(),
'short_id' => IdHelper::randomPrefixedId(IdHelper::EVENT_PREFIX),
'attributes' => $eventData->getAttributes(),
]);
}

private function createEventStatistics(EventDomainObject $event): void
{
$this->eventStatisticsRepository->create([
'event_id' => $event->getId(),
'tickets_sold' => 0,
'sales_total_gross' => 0,
'sales_total_before_additions' => 0,
'total_tax' => 0,
'total_fee' => 0,
'orders_created' => 0,
]);
}

private function createEventSettings(
?EventSettingDomainObject $eventSettings,
EventDomainObject $event,
OrganizerDomainObject $organizer
): void
{
if ($eventSettings !== null) {
$eventSettings->setEventId($event->getId());
$eventSettingsArray = $eventSettings->toArray();

unset($eventSettingsArray['id']);

$this->eventSettingsRepository->create($eventSettingsArray);

return;
}

$this->eventSettingsRepository->create([
'event_id' => $event->getId(),
'homepage_background_color' => '#ffffff',
'homepage_primary_text_color' => '#000000',
'homepage_primary_color' => '#7b5db8',
'homepage_secondary_text_color' => '#ffffff',
'homepage_secondary_color' => '#7b5eb9',
'homepage_background_type' => HomepageBackgroundType::COLOR->name,
'homepage_body_background_color' => '#7a5eb9',
'continue_button_text' => __('Continue'),
'support_email' => $organizer->getEmail(),
]);
}
}
23 changes: 23 additions & 0 deletions backend/app/Services/Domain/Event/DTO/DuplicateEventDataDTO.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

namespace HiEvents\Services\Domain\Event\DTO;

use HiEvents\DataTransferObjects\BaseDTO;

class DuplicateEventDataDTO extends BaseDTO
{
public function __construct(
public int $eventId,
public int $accountId,
public string $title,
public string $startDate,
public bool $duplicateTickets = true,
public bool $duplicateQuestions = true,
public bool $duplicateSettings = true,
public bool $duplicatePromoCodes = true,
public ?string $description = null,
public ?string $endDate = null,
)
{
}
}
Loading

0 comments on commit 2aa2bf8

Please sign in to comment.