JFIF $ d d 7 * Z qn )ZP 0 !1 A "#a$2Qr D8 a Ri [f \m I ykI w0 c u F c Rı?lO7 к _f˓ [C $殷WF< _W ԣ s Kc ëI zyQy /_LK ℂ ;C",p F A : /] =H ~ , l s / 9 ć: [=/#f ; ) x { ٛE Q )~ = 𘙲r*2~ a _V = ' ku m F D } KYYC )( { * g& f `툪 r y ` = ^ c J. I ]( * `wq 1dđ #̩ ͑ 0 ; H ] u 搂 @:~ ו K L N s h } O I R* 8 :2 ! lD JV o ( 3 = M ( z Ȱ+i *N A r6K n S l ) ! J J Ӂ* % ݉?|D } d5: eP0 R; {$X' x F @ .ÊB {,WJ uQ ɲRI ;9 Q E琯 6 2fT .DU J ;*c P A\IL Nj! J ۱+ O\ ͔ ] ޒ S J ȧ c%A N ol ՎprU L Zԛe r E2= X D Xg VQeӓ k y P7 U* o m Q I s ,K `) 6\G3t?pgjr m ۛجw luGt f h 9uyP 0D;Uڽ"OXl i f $ ) &|ML 0 Z rm1 [ H XP l P R0 'G=i 2N +0 e 2 ]] 9 VT P O 7 h(F * 癈 ' = Q VZD F,d ߬ ~ T X G[ `le 69C R(!S2! P < 0 x < !1AQ "Raq 02Br #S CTb ? Ζ "]mH5WR 7 k.ۛ ! } Q~ + yԏ z |@ T20 S~ K e k *zF f^2X *( @ 8 r ? C I uI|֓>^Ex LgNU Y+{.R Ѫ τV YT D I 6 2 '8Y2 7'\T P.6 d &˦ @V q i |8 -O Ε ] ʔ U= T L 8= ;6c | !qfF3 a ů& ~$ l} 'NW U s$U k^S V : U#6w + +s &r+n ڐ { @ 29gL u " T Ù M=6 (^ "7 r}=6Yݾ lC u hquymp Ǧ G jhs ǜNl ɻ} o 7 #S6 a w 4 ! OS r D 5 7 % | ?x> L | / nD 6?/8 w # [ ) L 7 +6〼T A T g ! %5 Mm Z /c -{1_Je "| ^$ 'O & ޱմ < H vNqϡQ$ < 6 &Nݛ ֨tǻN (9 n ru~'όN sMG& ,uH Kw *Ī 5@ u6 BԒ i YN * f 0 _ GhKW u lT <nj- 0{ M L~ | /a v\ E = ?1 \ɟuq Ђ Y O a K ޡP q $ H8o Q˼EV ~ k 0 h' L Pp M -M" m^ !6l nմ 쮝C U T . ! :vMǨb h ډ Vc r @~p # !1QAa q ? AL0L x LU 4 d ; x " 16ad0 7.\ߋ 0 = P l v/ " c A Ay A|2pO8"d MV #1] = \ " ˡ ] k v I9 d ۓ & q ) ǐq E ꞅ v "P )q X &Q З non]я u ג./ ; gZ$ M QР 2M ]h ni 0 P $ێ 3CɏGjώy : m c # S_ l@ '_ \m|C 2`K _ 1 cIvGc $? PCQ S+D ? K aײ k Fr b 8 ύ NEدg ĝ U 3 6 #Ik+ v { v i ( ވ ? a e) X xy/*@Q Ǽ Ծ ) $ IWO l c G; !i k{ gOŀ L ('x T rb $ w) R $ & N1E td U3Uȉ1p M"N* (DNy d96.(jQ) X 5 c QɎMy W ? Q *!R > 6=7)Xj 5`J ] e 8%t ! + ' ! 1Q 5 !1 AQaq ё #2 "0BRb ? G t^ ## .l lQ T $ v , ,m 㵜5u bV =sY+ @d { N ! dn O < .- B; _w J t 6;Q Jd. Q c%p{ 1 ,sND dF H I0 Гo X ш e黅X ۢ F :)[ F G X ƹ / w _c MeD ,ʡc c . W D tA $ j@ : ) - #u c1< @ۗ9F ) KJ- hpP] _ x [ q B lb p ʖ w q " LFGd ƶ * s +ډ_Zc " ? % t[I P 6 J ]# =ɺ V v v CGs Gh1 > )6 |e y ? L ӣ m,4 G W Ui ` ] uJVo VDG< SB 6ϏQ @ TiU l y O U 0 kfV ~ ~}S Z @ *WU Ui ## ; s /[ = ! 7}" W N ] ' (L ! ~ y5 g9T̅J kbM' +s :S +B) v @ M j e C f j E 0 Y\Qn z G 1 д~ W o{T 9?` Rm yhsy3 !HAD ] m c1 ~ 2 L S u7x T ;j$ `} 4-> L# vzŏ I L S ֭ T { r jGK C ; b pU= - ` Bs K. S F w4 M q] Z dH S 0 ) tLg
<?php
namespace AmeliaBooking\Infrastructure\Repository\User;
use AmeliaBooking\Domain\Collection\Collection;
use AmeliaBooking\Domain\Common\Exceptions\InvalidArgumentException;
use AmeliaBooking\Domain\Entity\User\AbstractUser;
use AmeliaBooking\Domain\Entity\User\Provider;
use AmeliaBooking\Domain\Factory\User\ProviderFactory;
use AmeliaBooking\Domain\Repository\User\ProviderRepositoryInterface;
use AmeliaBooking\Domain\Services\DateTime\DateTimeService;
use AmeliaBooking\Domain\ValueObjects\String\DayOffType;
use AmeliaBooking\Domain\ValueObjects\String\Status;
use AmeliaBooking\Infrastructure\Common\Exceptions\QueryExecutionException;
use AmeliaBooking\Infrastructure\Connection;
use AmeliaBooking\Infrastructure\Licence\Licence;
use AmeliaBooking\Infrastructure\WP\InstallActions\DB\Bookable\ExtrasTable;
use AmeliaBooking\Infrastructure\WP\InstallActions\DB\Booking\AppointmentsTable;
use AmeliaBooking\Infrastructure\Licence\LicenceConstants;
use AmeliaBooking\Infrastructure\WP\InstallActions\DB\User\WPUsersTable;
/**
* Class ProviderRepository
*
* @package AmeliaBooking\Infrastructure\Repository
*/
class ProviderRepository extends UserRepository implements ProviderRepositoryInterface
{
public const FACTORY = ProviderFactory::class;
/** @var string */
protected $providerWeekDayTable;
/** @var string */
protected $providerPeriodTable;
/** @var string */
protected $providerPeriodServiceTable;
/** @var string */
protected $providerPeriodLocationTable;
/** @var string */
protected $providerTimeOutTable;
/** @var string */
protected $providerSpecialDayTable;
/** @var string */
protected $providerSpecialDayPeriodTable;
/** @var string */
protected $providerSpecialDayPeriodServiceTable;
/** @var string */
protected $providerSpecialDayPeriodLocationTable;
/** @var string */
protected $providerDayOffTable;
/** @var string */
protected $providerServicesTable;
/** @var string */
protected $providerLocationTable;
/** @var string */
protected $serviceTable;
/** @var string */
protected $locationTable;
/** @var string */
protected $providerViewsTable;
/** @var string */
protected $providersGoogleCalendarTable;
/** @var string */
protected $providersOutlookCalendarTable;
/**
* @param Connection $connection
* @param string $table
* @param string $providerWeekDayTable
* @param string $providerPeriodTable
* @param string $providerPeriodServiceTable
* @param string $providerPeriodLocationTable
* @param string $providerTimeOutTable
* @param string $providerSpecialDayTable
* @param string $providerSpecialDayPeriodTable
* @param string $providerSpecialDayPeriodServiceTable
* @param string $providerSpecialDayPeriodLocationTable
* @param string $providerDayOffTable
* @param string $providerServicesTable
* @param string $providerLocationTable
* @param string $serviceTable
* @param string $locationTable
* @param string $providerViewsTable
* @param string $providersGoogleCalendarTable
* @param string $providersOutlookCalendarTable
*/
public function __construct(
Connection $connection,
$table,
$providerWeekDayTable,
$providerPeriodTable,
$providerPeriodServiceTable,
$providerPeriodLocationTable,
$providerTimeOutTable,
$providerSpecialDayTable,
$providerSpecialDayPeriodTable,
$providerSpecialDayPeriodServiceTable,
$providerSpecialDayPeriodLocationTable,
$providerDayOffTable,
$providerServicesTable,
$providerLocationTable,
$serviceTable,
$locationTable,
$providerViewsTable,
$providersGoogleCalendarTable,
$providersOutlookCalendarTable
) {
parent::__construct($connection, $table);
$this->providerWeekDayTable = $providerWeekDayTable;
$this->providerPeriodTable = $providerPeriodTable;
$this->providerPeriodServiceTable = $providerPeriodServiceTable;
$this->providerPeriodLocationTable = $providerPeriodLocationTable;
$this->providerTimeOutTable = $providerTimeOutTable;
$this->providerSpecialDayTable = $providerSpecialDayTable;
$this->providerSpecialDayPeriodTable = $providerSpecialDayPeriodTable;
$this->providerSpecialDayPeriodServiceTable = $providerSpecialDayPeriodServiceTable;
$this->providerSpecialDayPeriodLocationTable = $providerSpecialDayPeriodLocationTable;
$this->providerDayOffTable = $providerDayOffTable;
$this->providerServicesTable = $providerServicesTable;
$this->providerLocationTable = $providerLocationTable;
$this->serviceTable = $serviceTable;
$this->locationTable = $locationTable;
$this->providerViewsTable = $providerViewsTable;
$this->providersGoogleCalendarTable = $providersGoogleCalendarTable;
$this->providersOutlookCalendarTable = $providersOutlookCalendarTable;
}
/**
* @param int $id
*
* @return Provider|null
* @throws QueryExecutionException
*/
public function getById($id)
{
try {
$statement = $this->connection->prepare(
"SELECT
u.id AS user_id,
u.status AS user_status,
u.externalId AS external_id,
u.firstName AS user_firstName,
u.lastName AS user_lastName,
u.email AS user_email,
u.note AS note,
u.phone AS phone,
u.countryPhoneIso AS user_countryPhoneIso,
u.pictureFullPath AS picture_full_path,
u.pictureThumbPath AS picture_thumb_path,
u.zoomUserId AS user_zoom_user_id,
u.appleCalendarId as user_apple_calendar_id,
u.googleCalendarId as user_google_calendar_id,
u.outlookCalendarId as user_outlook_calendar_id,
u.employeeAppleCalendar as user_employee_apple_calendar,
u.stripeConnect AS user_stripeConnect,
u.translations AS user_translations,
u.timeZone AS user_timeZone,
u.badgeId AS badge_id,
gd.id AS google_calendar_id,
gd.token AS google_calendar_token,
gd.calendarId AS google_calendar_calendar_id,
gd.insertPendingAppointments AS google_calendar_insert_pending_appointments,
gd.includeBufferTime AS google_calendar_include_buffer_time,
gd.title AS google_calendar_title,
gd.description AS google_calendar_description,
gd.accountId AS google_calendar_account_id,
gd.syncId AS google_calendar_sync_id,
od.id AS outlook_calendar_id,
od.token AS outlook_calendar_token,
od.calendarId AS outlook_calendar_calendar_id,
od.insertPendingAppointments AS outlook_calendar_insert_pending_appointments,
od.includeBufferTime AS outlook_calendar_include_buffer_time,
od.title AS outlook_calendar_title,
od.description AS outlook_calendar_description,
od.accountId AS outlook_calendar_account_id,
od.syncId AS outlook_calendar_sync_id
FROM {$this->table} u
LEFT JOIN {$this->providersGoogleCalendarTable} gd ON gd.userId = u.id
LEFT JOIN {$this->providersOutlookCalendarTable} od ON od.userId = u.id
WHERE u.type = :type AND u.id = :userId
ORDER BY u.id"
);
$type = AbstractUser::USER_ROLE_PROVIDER;
$statement->bindParam(':type', $type);
$statement->bindParam(':userId', $id);
$statement->execute();
$providerRows = [];
$serviceRows = [];
$providerServiceRows = [];
if ($statement->rowCount() === 0) {
return null;
}
while ($row = $statement->fetch()) {
$this->parseUserRow($row, $providerRows, $serviceRows, $providerServiceRows);
}
} catch (\Exception $e) {
throw new QueryExecutionException('Unable to find by id in ' . __CLASS__ . '. ' . $e->getMessage(), $e->getCode(), $e);
}
return call_user_func([static::FACTORY, 'createCollection'], $providerRows, $serviceRows, $providerServiceRows)->getItem($id);
}
/**
*
* @return Collection
* @throws QueryExecutionException
*/
public function getAll()
{
try {
$statement = $this->connection->prepare(
"SELECT
u.id AS user_id,
u.status AS user_status,
u.externalId AS external_id,
u.firstName AS user_firstName,
u.lastName AS user_lastName,
u.email AS user_email,
u.note AS note,
u.phone AS phone,
u.countryPhoneIso AS user_countryPhoneIso,
u.pictureFullPath AS picture_full_path,
u.pictureThumbPath AS picture_thumb_path,
u.translations AS user_translations,
u.badgeId AS user_badge_id,
u.googleCalendarId as user_google_calendar_id,
u.outlookCalendarId as user_outlook_calendar_id,
lt.locationId AS user_locationId
FROM {$this->table} u
LEFT JOIN {$this->providerLocationTable} lt ON lt.userId = u.id
WHERE u.type = :type
ORDER BY CONCAT(u.firstName, ' ', u.lastName)"
);
$type = AbstractUser::USER_ROLE_PROVIDER;
$statement->bindParam(':type', $type);
$statement->execute();
$providerRows = [];
$serviceRows = [];
$providerServiceRows = [];
while ($row = $statement->fetch()) {
$this->parseUserRow($row, $providerRows, $serviceRows, $providerServiceRows);
}
} catch (\Exception $e) {
throw new QueryExecutionException('Unable to find by id in ' . __CLASS__ . '. ' . $e->getMessage(), $e->getCode(), $e);
}
return call_user_func([static::FACTORY, 'createCollection'], $providerRows, $serviceRows, $providerServiceRows);
}
/**
* @param array $criteria
* @param int $itemsPerPage
*
* @return Collection
* @throws QueryExecutionException
* @throws InvalidArgumentException
*/
public function getFiltered($criteria, $itemsPerPage)
{
// Apply Lite license restriction
$criteria = $this->applyLiteLicenseRestriction($criteria);
try {
$wpUserTable = WPUsersTable::getTableName();
$params[':type'] = AbstractUser::USER_ROLE_PROVIDER;
$order = '';
if (!empty($criteria['sort'])) {
$orderColumn = 'CONCAT(u.firstName, \' \', u.lastName)';
$orderDirection = $criteria['sort'][0] === '-' ? 'DESC' : 'ASC';
$order = "ORDER BY {$orderColumn} {$orderDirection}";
}
$where = [];
if (!empty($criteria['search'])) {
$terms = preg_split('/\s+/', trim($criteria['search']));
$termIndex = 0;
$where1 = [];
foreach ($terms as $term) {
$param = ":search{$termIndex}";
$params[$param] = "%{$term}%";
$where1[] = "(
u.firstName LIKE {$param}
OR u.lastName LIKE {$param}
OR u.email LIKE {$param}
OR u.phone LIKE {$param}
OR u.note LIKE {$param}
OR wpUser.display_name LIKE {$param}
OR u.id LIKE {$param}
)";
$termIndex++;
}
$where1 = implode(' AND ', $where1);
$where[] = "u.id IN(
SELECT DISTINCT(user.id)
FROM {$this->table} user
LEFT JOIN {$wpUserTable} wpUser ON user.externalId = wpUser.ID
WHERE ({$where1})
)";
}
if (!empty($criteria['services'])) {
$queryServices = [];
foreach ((array)$criteria['services'] as $index => $value) {
$param = ':service' . $index;
$queryServices[] = $param;
$params[$param] = $value;
}
$where[] = "u.id IN (
SELECT pst.userId FROM {$this->providerServicesTable} pst
WHERE pst.userId = u.id AND pst.serviceId IN (" . implode(', ', $queryServices) . ')
)';
}
if (!empty($criteria['providerStatus'])) {
$params[':providerStatus'] = $criteria['providerStatus'];
$where[] = 'u.status = :providerStatus';
}
if (!empty($criteria['providers'])) {
$queryProviders = [];
$index = 0;
foreach ((array)$criteria['providers'] as $value) {
$param = ':provider' . $index++;
$queryProviders[] = $param;
$params[$param] = (int)$value;
}
$where[] = 'u.id IN (' . implode(', ', $queryProviders) . ')';
}
if (!empty($criteria['location'])) {
$queryLocations = [];
foreach ((array)$criteria['location'] as $index => $value) {
$param = ':location' . $index;
$queryLocations[] = $param;
$params[$param] = $value;
}
$where[] = "u.id IN (
SELECT plt.userId FROM {$this->providerLocationTable} plt
WHERE plt.userId = u.id AND plt.locationId IN ( " . implode(', ', $queryLocations) . "))";
}
$where[] = "u.status NOT LIKE 'disabled'";
$where = ' AND ' . implode(' AND ', $where);
$limit = $this->getLimit(
!empty($criteria['page']) ? (int)$criteria['page'] : 0,
(int)$itemsPerPage
);
$statement = $this->connection->prepare(
"SELECT u.*
FROM {$this->table} u
WHERE u.type = :type $where
{$order}
{$limit}"
);
$statement->execute($params);
$rows = $statement->fetchAll();
} catch (\Exception $e) {
throw new QueryExecutionException('Unable to get data from ' . __CLASS__ . '. ' . $e->getMessage(), $e->getCode(), $e);
}
$items = new Collection();
foreach ($rows as $row) {
$items->addItem(call_user_func([static::FACTORY, 'create'], $row), $row['id']);
}
return $items;
}
/**
*
* @param array $criteria
* @param bool $filterForLicence
*
* @return Collection
* @throws QueryExecutionException
* @throws InvalidArgumentException
*/
public function getWithSchedule($criteria, $filterForLicence = true)
{
// Apply Lite license restriction
$criteria = $filterForLicence ? $this->applyLiteLicenseRestriction($criteria) : $criteria;
$providerRows = [];
$serviceRows = [];
$providerServiceRows = [];
$where = ['u.type = :type'];
$userParams = [];
$params[':type'] = AbstractUser::USER_ROLE_PROVIDER;
$queryProviders = [];
if (!empty($criteria['dates'][0])) {
$criteria['dates'][0] = explode(' ', $criteria['dates'][0])[0];
}
if (!empty($criteria['dates'][1])) {
$criteria['dates'][1] = explode(' ', $criteria['dates'][1])[0];
}
if (!empty($criteria['providerStatus'])) {
$params[':providerStatus'] = $criteria['providerStatus'];
$where[] = 'u.status = :providerStatus';
}
if (isset($criteria['show'])) {
$params[':show'] = $criteria['show'];
$where[] = 'u.show = :show';
}
if (!empty($criteria['providers'])) {
$index = 0;
foreach ((array)$criteria['providers'] as $value) {
$param = ':provider' . $index++;
$queryProviders[] = $param;
$userParams[$param] = (int)$value;
}
}
if (!empty($criteria['locations'])) {
$queryLocations = [];
foreach ((array)$criteria['locations'] as $index => $value) {
$param = ':location' . $index;
$queryLocations[] = $param;
$params[$param] = $value;
}
$where[] = "u.id IN (
SELECT plt.userId FROM {$this->providerLocationTable} plt
WHERE plt.userId = u.id AND plt.locationId IN ( " . implode(', ', $queryLocations) . "))";
}
if (!empty($criteria['services'])) {
$queryServices = [];
foreach ((array)$criteria['services'] as $index => $value) {
$param = ':service' . $index;
$queryServices[] = $param;
$params[$param] = $value;
}
$where[] = "u.id IN (
SELECT pst.userId FROM {$this->providerServicesTable} pst
WHERE pst.userId = u.id AND pst.serviceId IN (" . implode(', ', $queryServices) . ')
)';
}
if ($queryProviders) {
$where[] = 'u.id IN (' . implode(', ', $queryProviders) . ')';
}
$dotJoinQuery = '';
if (isset($criteria['dates'][0], $criteria['dates'][1])) {
$dotJoinQuery = "AND (dot.repeat = 1 OR (
dot.startDate BETWEEN :from1 AND :to1 OR
dot.endDate BETWEEN :from2 AND :to2 OR
(dot.startDate <= :from3 AND dot.endDate >= :to3)
))";
$userParams[':from1'] = $userParams[':from2'] = $userParams[':from3'] = $criteria['dates'][0];
$userParams[':to1'] = $userParams[':to2'] = $userParams[':to3'] = $criteria['dates'][1];
} elseif (isset($criteria['dates'][0])) {
$dotJoinQuery = "AND (dot.repeat = 1 OR (dot.startDate >= :from1 OR dot.endDate >= :from2))";
$userParams[':from1'] = $userParams[':from2'] = $criteria['dates'][0];
}
$where = 'WHERE ' . implode(' AND ', $where);
try {
$statement = $this->connection->prepare(
"SELECT
u.id AS user_id,
u.status AS user_status,
u.externalId AS external_id,
u.firstName AS user_firstName,
u.lastName AS user_lastName,
u.email AS user_email,
u.zoomUserId AS user_zoom_user_id,
u.appleCalendarId AS user_apple_calendar_id,
u.googleCalendarId as user_google_calendar_id,
u.outlookCalendarId as user_outlook_calendar_id,
u.employeeAppleCalendar AS user_employee_apple_calendar,
u.stripeConnect AS user_stripeConnect,
u.countryPhoneIso AS user_countryPhoneIso,
u.note AS note,
u.description AS description,
u.phone AS phone,
u.pictureFullPath AS picture_full_path,
u.pictureThumbPath AS picture_thumb_path,
u.translations AS user_translations,
u.timeZone AS user_timeZone,
u.badgeId AS badge_id,
u.show AS user_show,
plt.locationId AS user_locationId,
pst.serviceId AS service_id,
pst.price AS service_price,
pst.customPricing AS service_customPricing,
pst.minCapacity AS service_minCapacity,
pst.maxCapacity AS service_maxCapacity,
dot.id AS dayOff_id,
dot.name AS dayOff_name,
dot.startDate AS dayOff_startDate,
dot.endDate AS dayOff_endDate,
dot.repeat AS dayOff_repeat,
dot.type AS dayOff_type
FROM {$this->table} u
LEFT JOIN {$this->providerServicesTable} pst ON pst.userId = u.id
LEFT JOIN {$this->providerLocationTable} plt ON plt.userId = u.id
LEFT JOIN {$this->providerDayOffTable} dot ON (dot.userId = u.id OR dot.userId IS null) {$dotJoinQuery}
{$where}
ORDER BY CONCAT(u.firstName, ' ', u.lastName), u.id"
);
$statement->execute(array_merge($params, $userParams));
while ($row = $statement->fetch()) {
$this->parseUserRow($row, $providerRows, $serviceRows, $providerServiceRows);
}
} catch (\Exception $e) {
throw new QueryExecutionException('Unable to find by id in ' . __CLASS__ . '. ' . $e->getMessage(), $e->getCode(), $e);
}
/** @var Collection $providers */
$providers = call_user_func(
[static::FACTORY, 'createCollection'],
$providerRows,
$serviceRows,
$providerServiceRows
);
if (!$providers->length()) {
return new Collection();
}
$where = 'WHERE wdt.userId IN (' . implode(', ', $providers->keys()) . ')';
try {
$statement = $this->connection->prepare(
"SELECT
wdt.id AS weekDay_id,
wdt.userId AS user_id,
wdt.dayIndex AS weekDay_dayIndex,
wdt.startTime AS weekDay_startTime,
wdt.endTime As weekDay_endTime,
tot.id AS timeOut_id,
tot.startTime AS timeOut_startTime,
tot.endTime AS timeOut_endTime,
pt.id AS period_id,
pt.startTime AS period_startTime,
pt.endTime AS period_endTime,
pt.locationId AS period_locationId,
pst.id AS periodService_id,
pst.serviceId AS periodService_serviceId,
plt.id AS periodLocation_id,
plt.locationId AS periodLocation_locationId
FROM {$this->providerWeekDayTable} wdt
LEFT JOIN {$this->providerTimeOutTable} tot ON tot.weekDayId = wdt.id
LEFT JOIN {$this->providerPeriodTable} pt ON pt.weekDayId = wdt.id
LEFT JOIN {$this->providerPeriodServiceTable} pst ON pst.periodId = pt.id
LEFT JOIN {$this->providerPeriodLocationTable} plt ON plt.periodId = pt.id
{$where}"
);
$statement->execute();
while ($row = $statement->fetch()) {
$this->parseUserRow($row, $providerRows, $serviceRows, $providerServiceRows);
}
} catch (\Exception $e) {
throw new QueryExecutionException('Unable to find by id in ' . __CLASS__ . '. ' . $e->getMessage(), $e->getCode(), $e);
}
/** @var Collection $providersWithWeekDays */
$providersWithWeekDays = call_user_func(
[static::FACTORY, 'createCollection'],
$providerRows,
$serviceRows,
$providerServiceRows
);
/** @var Provider $provider */
foreach ($providersWithWeekDays->getItems() as $provider) {
$providers->getItem(
$provider->getId()->getValue()
)->setWeekDayList($provider->getWeekDayList());
}
$where = ['sdt.userId IN (' . implode(', ', $providers->keys()) . ')'];
$sdtParams = [];
if (isset($criteria['dates'][0], $criteria['dates'][1])) {
$where[] = "(
sdt.startDate BETWEEN :from1 AND :to1 OR
sdt.endDate BETWEEN :from2 AND :to2 OR
(sdt.startDate <= :from3 AND sdt.endDate >= :to3)
)";
$sdtParams[':from1'] = $sdtParams[':from2'] = $sdtParams[':from3'] = $criteria['dates'][0];
$sdtParams[':to1'] = $sdtParams[':to2'] = $sdtParams[':to3'] = $criteria['dates'][1];
} elseif (isset($criteria['dates'][0])) {
$where[] = "(
sdt.startDate >= :from1 OR
sdt.endDate >= :from2
)";
$sdtParams[':from1'] = $sdtParams[':from2'] = $criteria['dates'][0];
}
$where = 'WHERE ' . implode(' AND ', $where);
try {
$statement = $this->connection->prepare(
"SELECT
sdt.id AS specialDay_id,
sdt.userId AS user_id,
sdt.startDate AS specialDay_startDate,
sdt.endDate As specialDay_endDate,
sdpt.id AS specialDayPeriod_id,
sdpt.startTime AS specialDayPeriod_startTime,
sdpt.endTime AS specialDayPeriod_endTime,
sdpt.locationId AS specialDayPeriod_locationId,
sdpst.id AS specialDayPeriodService_id,
sdpst.serviceId AS specialDayPeriodService_serviceId,
sdplt.id AS specialDayPeriodLocation_id,
sdplt.locationId AS specialDayPeriodLocation_locationId
FROM {$this->providerSpecialDayTable} sdt
LEFT JOIN {$this->providerSpecialDayPeriodTable} sdpt ON sdpt.specialDayId = sdt.id
LEFT JOIN {$this->providerSpecialDayPeriodServiceTable} sdpst ON sdpst.periodId = sdpt.id
LEFT JOIN {$this->providerSpecialDayPeriodLocationTable} sdplt ON sdplt.periodId = sdpt.id
{$where}
ORDER BY sdt.startDate ASC"
);
$statement->execute($sdtParams);
while ($row = $statement->fetch()) {
$this->parseUserRow($row, $providerRows, $serviceRows, $providerServiceRows);
}
} catch (\Exception $e) {
throw new QueryExecutionException('Unable to find by id in ' . __CLASS__ . '. ' . $e->getMessage(), $e->getCode(), $e);
}
/** @var Collection $providersWithSpecialDays */
$providersWithSpecialDays = call_user_func(
[static::FACTORY, 'createCollection'],
$providerRows,
$serviceRows,
$providerServiceRows
);
/** @var Provider $provider */
foreach ($providersWithSpecialDays->getItems() as $provider) {
$providers->getItem(
$provider->getId()->getValue()
)->setSpecialDayList($provider->getSpecialDayList());
}
if (!empty($criteria['fetchCalendars'])) {
$where = 'WHERE u.id IN (' . implode(', ', $providers->keys()) . ')';
try {
$statement = $this->connection->prepare(
"SELECT
u.id AS user_id,
gd.id AS google_calendar_id,
gd.token AS google_calendar_token,
gd.calendarId AS google_calendar_calendar_id,
gd.insertPendingAppointments AS google_calendar_insert_pending_appointments,
gd.includeBufferTime AS google_calendar_include_buffer_time,
gd.title AS google_calendar_title,
gd.description AS google_calendar_description,
gd.accountId AS google_calendar_account_id,
gd.syncId AS google_calendar_sync_id,
od.id AS outlook_calendar_id,
od.token AS outlook_calendar_token,
od.calendarId AS outlook_calendar_calendar_id,
od.insertPendingAppointments AS outlook_calendar_insert_pending_appointments,
od.includeBufferTime AS outlook_calendar_include_buffer_time,
od.title AS outlook_calendar_title,
od.description AS outlook_calendar_description,
od.accountId AS outlook_calendar_account_id,
od.syncId AS outlook_calendar_sync_id
FROM {$this->table} u
LEFT JOIN {$this->providersGoogleCalendarTable} gd ON gd.userId = u.id
LEFT JOIN {$this->providersOutlookCalendarTable} od ON od.userId = u.id
{$where}"
);
$statement->execute();
while ($row = $statement->fetch()) {
$this->parseUserRow($row, $providerRows, $serviceRows, $providerServiceRows);
}
} catch (\Exception $e) {
throw new QueryExecutionException('Unable to find by id in ' . __CLASS__ . '. ' . $e->getMessage(), $e->getCode(), $e);
}
/** @var Collection $providersWithCalendars */
$providersWithCalendars = call_user_func(
[static::FACTORY, 'createCollection'],
$providerRows,
$serviceRows,
$providerServiceRows
);
/** @var Provider $provider */
foreach ($providersWithCalendars->getItems() as $provider) {
/** @var Provider $targetProvider */
$targetProvider = $providers->getItem(
$provider->getId()->getValue()
);
if ($provider->getGoogleCalendar()) {
$targetProvider->setGoogleCalendar($provider->getGoogleCalendar());
}
if ($provider->getOutlookCalendar()) {
$targetProvider->setOutlookCalendar($provider->getOutlookCalendar());
}
}
}
return $providers;
}
/**
* @param array $criteria
*
* @return mixed
* @throws QueryExecutionException
*/
public function getCount($criteria)
{
// Apply Lite license restriction
$criteria = $this->applyLiteLicenseRestriction($criteria);
$params = [
':type' => AbstractUser::USER_ROLE_PROVIDER,
':visibleStatus' => Status::VISIBLE,
':hiddenStatus' => Status::HIDDEN,
];
try {
$wpUserTable = WPUsersTable::getTableName();
$where = [];
if (!empty($criteria['search'])) {
$terms = preg_split('/\s+/', trim($criteria['search']));
$termIndex = 0;
$where1 = [];
foreach ($terms as $term) {
$param = ":search{$termIndex}";
$params[$param] = "%{$term}%";
$where1[] = "(
u.firstName LIKE {$param}
OR u.lastName LIKE {$param}
OR u.email LIKE {$param}
OR u.phone LIKE {$param}
OR u.note LIKE {$param}
OR wpUser.display_name LIKE {$param}
OR u.id LIKE {$param}
)";
$termIndex++;
}
$where1 = implode(' AND ', $where1);
$where[] = "u.id IN(
SELECT DISTINCT(user.id)
FROM {$this->table} user
LEFT JOIN {$wpUserTable} wpUser ON user.externalId = wpUser.ID
WHERE ({$where1})
)";
}
if (!empty($criteria['services'])) {
$queryServices = [];
foreach ((array)$criteria['services'] as $index => $value) {
$param = ':service' . $index;
$queryServices[] = $param;
$params[$param] = $value;
}
$where[] = "u.id IN (
SELECT pst.userId FROM {$this->providerServicesTable} pst
WHERE pst.userId = u.id AND pst.serviceId IN (" . implode(', ', $queryServices) . ')
)';
}
if (!empty($criteria['location'])) {
$queryLocations = [];
foreach ((array)$criteria['location'] as $index => $value) {
$param = ':location' . $index;
$queryLocations[] = $param;
$params[$param] = $value;
}
$where[] = "u.id IN (
SELECT plt.userId FROM {$this->providerLocationTable} plt
WHERE plt.userId = u.id AND plt.locationId IN ( " . implode(', ', $queryLocations) . "))";
}
if (!empty($criteria['providers'])) {
$queryProviders = [];
foreach ((array)$criteria['providers'] as $index => $value) {
$param = ':provider' . $index;
$queryProviders[] = $param;
$params[$param] = $value;
}
$where[] = 'u.id IN (' . implode(', ', $queryProviders) . ')';
}
$where = $where ? ' AND ' . implode(' AND ', $where) : '';
$statement = $this->connection->prepare(
"SELECT COUNT(*) AS count
FROM {$this->table} u
WHERE u.type = :type AND u.status IN (:visibleStatus, :hiddenStatus) $where"
);
$statement->execute($params);
$row = $statement->fetch()['count'];
} catch (\Exception $e) {
throw new QueryExecutionException('Unable to get data from ' . __CLASS__ . '. ' . $e->getMessage(), $e->getCode(), $e);
}
return $row;
}
/**
* @param $criteria
*
* @return Collection
* @throws InvalidArgumentException
* @throws QueryExecutionException
*/
public function getWithServicesAndExtras($criteria)
{
$extrasTable = ExtrasTable::getTableName();
$params = [
':type' => AbstractUser::USER_ROLE_PROVIDER,
':userStatus' => Status::VISIBLE,
':serviceStatus' => Status::VISIBLE
];
$where = [];
foreach ((array)$criteria as $index => $value) {
$params[':service' . $index] = $value['serviceId'];
$params[':provider' . $index] = $value['providerId'];
$where[] = "(s.id = :service$index AND u.id = :provider$index)";
}
$where = $where ? ' AND ' . implode(' OR ', $where) : '';
try {
$statement = $this->connection->prepare(
"SELECT
u.id AS user_id,
u.firstName AS user_firstName,
u.lastName AS user_lastName,
u.email AS user_email,
u.timeZone AS user_timeZone,
u.translations AS user_translations,
st.serviceId AS service_id,
st.price AS service_price,
st.customPricing AS service_customPricing,
st.minCapacity AS service_minCapacity,
st.maxCapacity AS service_maxCapacity,
s.name AS service_name,
s.description AS service_description,
s.color AS service_color,
s.status AS service_status,
s.categoryId AS service_categoryId,
s.duration AS service_duration,
s.bringingAnyone AS service_bringingAnyone,
s.pictureFullPath AS service_picture_full,
s.pictureThumbPath AS service_picture_thumb,
s.aggregatedPrice AS service_aggregatedPrice,
s.recurringPayment AS service_recurringPayment,
s.translations AS service_translations,
s.timeBefore AS service_timeBefore,
s.timeAfter AS service_timeAfter,
s.deposit AS service_deposit,
s.depositPayment AS service_depositPayment,
s.depositPerPerson AS service_depositPerPerson,
s.fullPayment AS service_fullPayment,
e.id AS extra_id,
e.name AS extra_name,
e.price AS extra_price,
e.maxQuantity AS extra_maxQuantity,
e.duration AS extra_duration,
e.description AS extra_description,
e.position AS extra_position,
e.aggregatedPrice AS extra_aggregatedPrice
FROM {$this->table} u
INNER JOIN {$this->providerServicesTable} st ON st.userId = u.id
INNER JOIN {$this->serviceTable} s ON s.id = st.serviceId
LEFT JOIN {$extrasTable} e ON e.serviceId = s.id
WHERE u.status = :userStatus AND s.status = :serviceStatus AND u.type = :type $where"
);
$statement->execute($params);
$providerRows = [];
$serviceRows = [];
$providerServiceRows = [];
while ($row = $statement->fetch()) {
$this->parseUserRow($row, $providerRows, $serviceRows, $providerServiceRows);
}
} catch (\Exception $e) {
throw new QueryExecutionException('Unable to find by id in ' . __CLASS__ . '. ' . $e->getMessage(), $e->getCode(), $e);
}
return call_user_func([static::FACTORY, 'createCollection'], $providerRows, $serviceRows, $providerServiceRows);
}
/**
* Returns array of available (currently working) Providers where keys are Provider ID's and array values are
* Working Hours Data
*
* @param $dayIndex
*
* @return array
* @throws QueryExecutionException
*/
public function getAvailable($dayIndex, $providerTimeZone)
{
$currentDateTime = DateTimeService::getNowDateTime();
$currentDateTimeInTimeZone = DateTimeService::getCustomDateTimeObjectInTimeZone($currentDateTime, $providerTimeZone);
$currentDateTimeSQL = "STR_TO_DATE('" . $currentDateTimeInTimeZone->format('Y-m-d H:i:s') . "', '%Y-%m-%d %H:%i:%s')";
$params = [
':dayIndex' => $dayIndex === 0 ? 7 : $dayIndex,
':type' => AbstractUser::USER_ROLE_PROVIDER,
':providerTimeZone' => $providerTimeZone,
':WPtimeZone' => DateTimeService::getTimeZone()->getName()
];
try {
$statement = $this->connection->prepare(
"SELECT
u.id AS user_id,
u.firstName AS user_firstName,
u.lastName AS user_lastName,
u.translations AS user_translations,
u.timeZone AS user_timeZone,
wdt.id AS weekDay_id,
wdt.dayIndex AS weekDay_dayIndex,
wdt.startTime AS weekDay_startTime,
wdt.endTime AS weekDay_endTime,
pt.id AS period_id,
pt.startTime AS period_startTime,
pt.endTime AS period_endTime
FROM {$this->table} u
LEFT JOIN {$this->providerWeekDayTable} wdt ON wdt.userId = u.id
LEFT JOIN {$this->providerPeriodTable} pt ON pt.weekDayId = wdt.id
WHERE u.type = :type AND
wdt.dayIndex = :dayIndex AND
(COALESCE(u.timeZone, :WPtimeZone) = :providerTimeZone) AND
((
{$currentDateTimeSQL} >= wdt.startTime AND
{$currentDateTimeSQL} <= wdt.endTime AND
pt.startTime IS NULL AND
pt.endTime IS NULL
) OR (
{$currentDateTimeSQL} >= pt.startTime AND
{$currentDateTimeSQL} <= pt.endTime AND
pt.startTime IS NOT NULL AND
pt.endTime IS NOT NULL
))"
);
$statement->execute($params);
$rows = $statement->fetchAll();
} catch (\Exception $e) {
throw new QueryExecutionException('Unable to find by id in ' . __CLASS__ . '. ' . $e->getMessage(), $e->getCode(), $e);
}
$result = [];
foreach ($rows as $row) {
if (!array_key_exists($row['user_id'], $result)) {
$result[$row['user_id']] = $row;
}
$result[$row['user_id']]['periods'][$row['period_id']] = [
'startTime' => $row['period_startTime'],
'endTime' => $row['period_endTime']
];
}
return $result;
}
/**
* Returns array of available (currently working) Providers where keys are Provider ID's and array values are
* Working Hours Data on special day
*
* @return array
* @throws QueryExecutionException
*/
public function getOnSpecialDay()
{
$dateTimeNowString = DateTimeService::getNowDateTime();
$currentDateTime = "STR_TO_DATE('" . $dateTimeNowString . "', '%Y-%m-%d %H:%i:%s')";
$currentDateString = DateTimeService::getNowDate();
$params = [
':type' => AbstractUser::USER_ROLE_PROVIDER
];
try {
$statement = $this->connection->prepare(
"SELECT
u.id AS user_id,
u.firstName AS user_firstName,
u.lastName AS user_lastName,
sdpt.startTime AS sdp_startTime,
sdpt.endTime AS sdp_endTime,
IF (
{$currentDateTime} >= STR_TO_DATE(CONCAT(sdt.startDate, ' 00:00:00'), '%Y-%m-%d %H:%i:%s') AND
{$currentDateTime} <= DATE_ADD(STR_TO_DATE(CONCAT(sdt.endDate, ' 00:00:00'), '%Y-%m-%d %H:%i:%s'), INTERVAL 1 DAY) AND
{$currentDateTime} >= STR_TO_DATE(CONCAT('{$currentDateString}', ' ', sdpt.startTime), '%Y-%m-%d %H:%i:%s') AND
{$currentDateTime} <= STR_TO_DATE(CONCAT('{$currentDateString}', ' ', sdpt.endTime), '%Y-%m-%d %H:%i:%s'),
1,
0
) AS available
FROM {$this->table} u
INNER JOIN {$this->providerSpecialDayTable} sdt ON sdt.userId = u.id
INNER JOIN {$this->providerSpecialDayPeriodTable} sdpt ON sdpt.specialDayId = sdt.id
WHERE u.type = :type AND
STR_TO_DATE('{$currentDateString}', '%Y-%m-%d') BETWEEN sdt.startDate AND sdt.endDate
"
);
$statement->execute($params);
$rows = $statement->fetchAll();
} catch (\Exception $e) {
throw new QueryExecutionException('Unable to find by id in ' . __CLASS__ . '. ' . $e->getMessage(), $e->getCode(), $e);
}
$result = [];
$dateTimeNow = DateTimeService::getNowDateTimeObject();
foreach ($rows as $row) {
$dateTimeEnd = DateTimeService::getCustomDateTimeObject($currentDateString . " " . $row['sdp_endTime']);
if (!array_key_exists($row['user_id'], $result) && $dateTimeNow <= $dateTimeEnd) {
$result[$row['user_id']] = $row;
}
}
return $result;
}
/**
* @param $dayIndex
*
* @return array
* @throws QueryExecutionException
*/
public function getOnBreak($dayIndex)
{
$currentDateTime = "STR_TO_DATE('" . DateTimeService::getNowDateTime() . "', '%Y-%m-%d %H:%i:%s')";
$params = [
':dayIndex' => $dayIndex === 0 ? 7 : $dayIndex,
':type' => AbstractUser::USER_ROLE_PROVIDER
];
try {
$statement = $this->connection->prepare(
"SELECT
u.id AS user_id,
u.firstName AS user_firstName,
u.lastName AS user_lastName,
wdt.id AS weekDay_id,
wdt.dayIndex AS weekDay_dayIndex,
wdt.startTime AS weekDay_startTime,
wdt.endTime As weekDay_endTime,
tot.id AS timeOut_id,
tot.startTime AS timeOut_startTime,
tot.endTime AS timeOut_endTime
FROM {$this->table} u
LEFT JOIN {$this->providerWeekDayTable} wdt ON wdt.userId = u.id
LEFT JOIN {$this->providerTimeOutTable} tot ON tot.weekDayId = wdt.id
WHERE u.type = :type AND
wdt.dayIndex = :dayIndex AND
{$currentDateTime} >= wdt.startTime AND
{$currentDateTime} <= wdt.endTime AND
{$currentDateTime} >= tot.startTime AND
{$currentDateTime} <= tot.endTime"
);
$statement->execute($params);
$rows = $statement->fetchAll();
} catch (\Exception $e) {
throw new QueryExecutionException('Unable to find by id in ' . __CLASS__ . '. ' . $e->getMessage(), $e->getCode(), $e);
}
$result = [];
foreach ($rows as $row) {
$result[$row['user_id']] = $row;
}
return $result;
}
/**
* @return array
* @throws QueryExecutionException
*/
public function getOnVacation()
{
$currentDateTime = explode(' ', DateTimeService::getNowDateTime())[0];
$params = [
':type' => AbstractUser::USER_ROLE_PROVIDER,
':currentDate' => $currentDateTime,
':dayOffType' => DayOffType::DAY_OFF
];
try {
$statement = $this->connection->prepare(
"SELECT
u.id,
u.firstName,
u.lastName,
dot.startDate,
dot.endDate,
dot.name
FROM {$this->table} u
LEFT JOIN {$this->providerDayOffTable} dot ON dot.userId = u.id AND dot.type = :dayOffType
WHERE u.type = :type AND
:currentDate BETWEEN dot.startDate AND dot.endDate"
);
$statement->execute($params);
$rows = $statement->fetchAll();
} catch (\Exception $e) {
throw new QueryExecutionException('Unable to find by id in ' . __CLASS__ . '. ' . $e->getMessage(), $e->getCode(), $e);
}
$result = [];
foreach ($rows as $row) {
$result[$row['id']] = $row;
}
return $result;
}
/**
* Return an array of providers with the number of appointments for the given date period.
* Keys of the array are Provider IDs.
*
* @param $criteria
*
* @return array
* @throws InvalidArgumentException
* @throws QueryExecutionException
*/
public function getAllNumberOfAppointments($criteria)
{
$appointmentTable = AppointmentsTable::getTableName();
$params = [];
$where = [];
if ($criteria['dates']) {
$where[] = "(a.bookingStart BETWEEN :bookingFrom AND :bookingTo)";
$params[':bookingFrom'] = DateTimeService::getCustomDateTimeInUtc($criteria['dates'][0]);
$params[':bookingTo'] = DateTimeService::getCustomDateTimeInUtc($criteria['dates'][1]);
}
if (isset($criteria['status'])) {
$where[] = 'u.status = :status';
$params[':status'] = $criteria['status'];
}
$where = $where ? 'WHERE ' . implode(' AND ', $where) : '';
try {
$statement = $this->connection->prepare(
"SELECT
u.id,
CONCAT(u.firstName, ' ', u.lastName) AS name,
COUNT(a.providerId) AS appointments
FROM {$this->table} u
INNER JOIN {$appointmentTable} a ON u.id = a.providerId
$where
GROUP BY providerId"
);
$statement->execute($params);
$rows = $statement->fetchAll();
} catch (\Exception $e) {
throw new QueryExecutionException('Unable to get data from ' . __CLASS__ . '. ' . $e->getMessage(), $e->getCode(), $e);
}
$result = [];
foreach ($rows as $row) {
$result[$row['id']] = $row;
}
return $result;
}
/**
* Return an array of providers with the number of views for the given date period.
* Keys of the array are Providers IDs.
*
* @param $criteria
*
* @return array
* @throws QueryExecutionException
*/
public function getAllNumberOfViews($criteria)
{
$params = [];
$where = [];
if ($criteria['dates']) {
$where[] = "(DATE_FORMAT(pv.date, '%Y-%m-%d') BETWEEN :bookingFrom AND :bookingTo)";
$params[':bookingFrom'] = DateTimeService::getCustomDateTimeInUtc($criteria['dates'][0]);
$params[':bookingTo'] = DateTimeService::getCustomDateTimeInUtc($criteria['dates'][1]);
}
if (isset($criteria['status'])) {
$where[] = 'u.status = :status';
$params[':status'] = $criteria['status'];
}
$where = $where ? 'WHERE ' . implode(' AND ', $where) : '';
try {
$statement = $this->connection->prepare(
"SELECT
u.id,
CONCAT(u.firstName, ' ', u.lastName) as name,
SUM(pv.views) AS views
FROM {$this->table} u
INNER JOIN {$this->providerViewsTable} pv ON pv.userId = u.id
$where
GROUP BY u.id"
);
$statement->execute($params);
$rows = $statement->fetchAll();
} catch (\Exception $e) {
throw new QueryExecutionException('Unable to get data from ' . __CLASS__ . '. ' . $e->getMessage(), $e->getCode(), $e);
}
$result = [];
foreach ($rows as $row) {
$result[$row['id']] = $row;
}
return $result;
}
/**
* @param $providerId
*
* @return bool
* @throws QueryExecutionException
*/
public function addViewStats($providerId)
{
$date = DateTimeService::getNowDate();
$params = [
':userId' => $providerId,
':date' => $date,
':views' => 1
];
try {
// Check if there is already data for this provider for this date
$statement = $this->connection->prepare(
"SELECT COUNT(*) AS count
FROM {$this->providerViewsTable} AS pv
WHERE pv.userId = :userId
AND pv.date = :date"
);
$statement->bindParam(':userId', $providerId);
$statement->bindParam(':date', $date);
$statement->execute();
$count = $statement->fetch()['count'];
if (!$count) {
$statement = $this->connection->prepare(
"INSERT INTO {$this->providerViewsTable}
(`userId`, `date`, `views`)
VALUES
(:userId, :date, :views)"
);
} else {
$statement = $this->connection->prepare(
"UPDATE {$this->providerViewsTable} pv SET pv.views = pv.views + :views
WHERE pv.userId = :userId
AND pv.date = :date"
);
}
$statement->execute($params);
} catch (\Exception $e) {
throw new QueryExecutionException('Unable to add data in ' . __CLASS__ . '. ' . $e->getMessage(), $e->getCode(), $e);
}
return true;
}
/**
* @param array $serviceIds
*
* @return array
* @throws QueryExecutionException
*/
public function getProvidersServices($serviceIds = [])
{
// Apply Lite license restriction
$criteria = $this->applyLiteLicenseRestriction([]);
try {
$type = AbstractUser::USER_ROLE_PROVIDER;
$params = [
':type' => $type
];
$where = 'WHERE u.type = :type';
// Apply provider restriction for Lite license
if (!empty($criteria['providers'])) {
$queryProviders = [];
foreach ((array)$criteria['providers'] as $index => $value) {
$param = ':provider' . $index;
$queryProviders[] = $param;
$params[$param] = $value;
}
$where .= ' AND u.id IN (' . implode(', ', $queryProviders) . ')';
}
if (!empty($serviceIds)) {
$query = [];
foreach ((array)$serviceIds as $index => $value) {
$param = ':serviceId' . $index;
$query[] = $param;
$params[$param] = $value;
}
$where .= ' AND st.serviceId IN (' . implode(', ', $query) . ')';
}
$statement = $this->connection->prepare(
"SELECT
u.id AS user_id,
st.serviceId AS service_id,
st.price AS service_price,
st.customPricing AS service_customPricing,
st.minCapacity AS service_minCapacity,
st.maxCapacity AS service_maxCapacity
FROM {$this->table} u
INNER JOIN {$this->providerServicesTable} st ON st.userId = u.id
{$where}
ORDER BY CONCAT(u.firstName, ' ', u.lastName)"
);
$statement->execute($params);
$rows = $statement->fetchAll();
} catch (\Exception $e) {
throw new QueryExecutionException('Unable to find by id in ' . __CLASS__ . '. ' . $e->getMessage(), $e->getCode(), $e);
}
$result = [];
foreach ($rows as $row) {
$userId = (int)$row['user_id'];
$serviceId = (int)$row['service_id'];
if (!array_key_exists($userId, $result) || !array_key_exists($serviceId, $result[$userId])) {
$result[$userId][$serviceId] = [
'price' => $row['service_price'],
'customPricing' => $row['service_customPricing'],
'minCapacity' => (int)$row['service_minCapacity'],
'maxCapacity' => (int)$row['service_maxCapacity'],
];
}
}
return $result;
}
/** @noinspection MoreThanThreeArgumentsInspection */
/**
* @param array $row
* @param array $providerRows
* @param array $serviceRows
* @param array $providerServiceRows
*
* @return void
*/
private function parseUserRow($row, &$providerRows, &$serviceRows, &$providerServiceRows)
{
$userId = (int)$row['user_id'];
$serviceId = isset($row['service_id']) ? (int)$row['service_id'] : null;
$extraId = isset($row['extra_id']) ? $row['extra_id'] : null;
$couponId = isset($row['coupon_id']) ? $row['coupon_id'] : null;
$googleCalendarId = isset($row['google_calendar_id']) ? $row['google_calendar_id'] : null;
$outlookCalendarId = isset($row['outlook_calendar_id']) ? $row['outlook_calendar_id'] : null;
$weekDayId = isset($row['weekDay_id']) ? $row['weekDay_id'] : null;
$timeOutId = isset($row['timeOut_id']) ? $row['timeOut_id'] : null;
$periodId = isset($row['period_id']) ? $row['period_id'] : null;
$periodServiceId = isset($row['periodService_id']) ? $row['periodService_id'] : null;
$periodLocationId = isset($row['periodLocation_id']) ? $row['periodLocation_id'] : null;
$specialDayId = isset($row['specialDay_id']) ? $row['specialDay_id'] : null;
$specialDayPeriodId = isset($row['specialDayPeriod_id']) ? $row['specialDayPeriod_id'] : null;
$specialDayPeriodServiceId = isset($row['specialDayPeriodService_id'])
? $row['specialDayPeriodService_id'] : null;
$specialDayPeriodLocationId = isset($row['specialDayPeriodLocation_id'])
? $row['specialDayPeriodLocation_id'] : null;
$dayOffId = isset($row['dayOff_id']) ? $row['dayOff_id'] : null;
if (!array_key_exists($userId, $providerRows)) {
$providerRows[$userId] = [
'id' => $userId,
'type' => 'provider',
'status' => isset($row['user_status']) ? $row['user_status'] : null,
'externalId' => isset($row['external_id']) ? $row['external_id'] : null,
'firstName' => $row['user_firstName'],
'lastName' => $row['user_lastName'],
'email' => $row['user_email'],
'note' => isset($row['note']) ? $row['note'] : null,
'description' => isset($row['description']) ? $row['description'] : null,
'phone' => isset($row['phone']) ? $row['phone'] : null,
'zoomUserId' => isset($row['user_zoom_user_id']) ? $row['user_zoom_user_id'] : null,
'stripeConnect' => isset($row['user_stripeConnect']) ? $row['user_stripeConnect'] : null,
'countryPhoneIso' => isset($row['user_countryPhoneIso']) ? $row['user_countryPhoneIso'] : null,
'locationId' => isset($row['user_locationId']) ? $row['user_locationId'] : null,
'pictureFullPath' => isset($row['picture_full_path']) ? $row['picture_full_path'] : null,
'pictureThumbPath' => isset($row['picture_thumb_path']) ? $row['picture_thumb_path'] : null,
'translations' => $row['user_translations'],
'googleCalendar' => [],
'outlookCalendar' => [],
'weekDayList' => [],
'dayOffList' => [],
'blockTimeList' => [],
'specialDayList' => [],
'serviceList' => [],
'timeZone' => isset($row['user_timeZone']) ? $row['user_timeZone'] : null,
'badgeId' => isset($row['badge_id']) ? $row['badge_id'] : null,
'appleCalendarId' => isset($row['user_apple_calendar_id']) ? $row['user_apple_calendar_id'] : null,
'googleCalendarId' => isset($row['user_google_calendar_id']) ? $row['user_google_calendar_id'] : null,
'outlookCalendarId' => isset($row['user_outlook_calendar_id']) ? $row['user_outlook_calendar_id'] : null,
'employeeAppleCalendar' => isset($row['user_employee_apple_calendar']) ? $row['user_employee_apple_calendar'] : null,
'show' => isset($row['user_show']) ? $row['user_show'] : 0,
];
}
$rowGoogleCalendarId = isset($row['google_calendar_calendar_id']) ? $row['google_calendar_calendar_id'] : null;
if (
$googleCalendarId &&
array_key_exists($userId, $providerRows) &&
(
empty($providerRows[$userId]['googleCalendar']) ||
(
empty($providerRows[$userId]['googleCalendar']['calendarId']) &&
!empty($rowGoogleCalendarId)
)
)
) {
$providerRows[$userId]['googleCalendar']['id'] = $row['google_calendar_id'];
$providerRows[$userId]['googleCalendar']['token'] = $row['google_calendar_token'];
$providerRows[$userId]['googleCalendar']['calendarId'] = $rowGoogleCalendarId;
$providerRows[$userId]['googleCalendar']['insertPendingAppointments'] = isset($row['google_calendar_insert_pending_appointments']) ?
$row['google_calendar_insert_pending_appointments'] :
null;
$providerRows[$userId]['googleCalendar']['includeBufferTime'] = isset($row['google_calendar_include_buffer_time']) ?
$row['google_calendar_include_buffer_time'] :
null;
$providerRows[$userId]['googleCalendar']['title'] = isset($row['google_calendar_title']) && $row['google_calendar_title'] ?
json_decode($row['google_calendar_title'], true) :
['appointment' => '%service_name%', 'event' => '%event_name%'];
$providerRows[$userId]['googleCalendar']['description'] = isset($row['google_calendar_description']) && $row['google_calendar_description'] ?
json_decode($row['google_calendar_description'], true) :
['appointment' => '', 'event' => ''];
$providerRows[$userId]['googleCalendar']['userId'] = $userId;
$providerRows[$userId]['googleCalendar']['calendarId'] = isset($row['google_calendar_calendar_id'])
? $row['google_calendar_calendar_id']
: null;
$providerRows[$userId]['googleCalendar']['accountId'] = isset($row['google_calendar_account_id'])
? $row['google_calendar_account_id']
: null;
$providerRows[$userId]['googleCalendar']['syncId'] = isset($row['google_calendar_sync_id'])
? $row['google_calendar_sync_id']
: null;
}
$rowOutlookCalendarId = isset($row['outlook_calendar_calendar_id']) ? $row['outlook_calendar_calendar_id'] : null;
if (
$outlookCalendarId &&
array_key_exists($userId, $providerRows) &&
(
empty($providerRows[$userId]['outlookCalendar']) ||
(
empty($providerRows[$userId]['outlookCalendar']['calendarId']) &&
!empty($rowOutlookCalendarId)
)
)
) {
$providerRows[$userId]['outlookCalendar']['id'] = $row['outlook_calendar_id'];
$providerRows[$userId]['outlookCalendar']['token'] = $row['outlook_calendar_token'];
$providerRows[$userId]['outlookCalendar']['calendarId'] = $rowOutlookCalendarId;
$providerRows[$userId]['outlookCalendar']['insertPendingAppointments'] = isset($row['outlook_calendar_insert_pending_appointments']) ?
$row['outlook_calendar_insert_pending_appointments'] :
null;
$providerRows[$userId]['outlookCalendar']['includeBufferTime'] = isset($row['outlook_calendar_include_buffer_time']) ?
$row['outlook_calendar_include_buffer_time'] :
null;
$providerRows[$userId]['outlookCalendar']['title'] = isset($row['outlook_calendar_title']) && $row['outlook_calendar_title'] ?
json_decode($row['outlook_calendar_title'], true) :
['appointment' => '%service_name%', 'event' => '%event_name%'];
$providerRows[$userId]['outlookCalendar']['description'] = isset($row['outlook_calendar_description']) && $row['outlook_calendar_description'] ?
json_decode($row['outlook_calendar_description'], true) :
['appointment' => '', 'event' => ''];
$providerRows[$userId]['outlookCalendar']['accountId'] = isset($row['outlook_calendar_account_id'])
? $row['outlook_calendar_account_id']
: null;
$providerRows[$userId]['outlookCalendar']['syncId'] = isset($row['outlook_calendar_sync_id'])
? $row['outlook_calendar_sync_id']
: null;
}
if (
$weekDayId &&
array_key_exists($userId, $providerRows) &&
!array_key_exists($weekDayId, $providerRows[$userId]['weekDayList'])
) {
$providerRows[$userId]['weekDayList'][$weekDayId] = [
'id' => $weekDayId,
'dayIndex' => $row['weekDay_dayIndex'],
'startTime' => $row['weekDay_startTime'],
'endTime' => $row['weekDay_endTime'],
'timeOutList' => [],
'periodList' => [],
];
}
if (
$periodId &&
$weekDayId &&
array_key_exists($userId, $providerRows) &&
array_key_exists($weekDayId, $providerRows[$userId]['weekDayList']) &&
!array_key_exists($periodId, $providerRows[$userId]['weekDayList'][$weekDayId]['periodList'])
) {
$providerRows[$userId]['weekDayList'][$weekDayId]['periodList'][$periodId] = [
'id' => $periodId,
'startTime' => $row['period_startTime'],
'endTime' => $row['period_endTime'],
'locationId' => $row['period_locationId'],
'periodServiceList' => [],
'periodLocationList' => [],
];
}
if (
$periodServiceId &&
$periodId &&
$weekDayId &&
array_key_exists($userId, $providerRows) &&
array_key_exists($weekDayId, $providerRows[$userId]['weekDayList']) &&
array_key_exists($periodId, $providerRows[$userId]['weekDayList'][$weekDayId]['periodList']) &&
!array_key_exists($periodServiceId, $providerRows[$userId]['weekDayList'][$weekDayId]['periodList'][$periodId]['periodServiceList'])
) {
$providerRows[$userId]['weekDayList'][$weekDayId]['periodList'][$periodId]['periodServiceList'][$periodServiceId] = [
'id' => $periodServiceId,
'serviceId' => $row['periodService_serviceId'],
];
}
if (
$periodLocationId &&
$periodId &&
$weekDayId &&
array_key_exists($userId, $providerRows) &&
array_key_exists($weekDayId, $providerRows[$userId]['weekDayList']) &&
array_key_exists($periodId, $providerRows[$userId]['weekDayList'][$weekDayId]['periodList']) &&
!array_key_exists($periodLocationId, $providerRows[$userId]['weekDayList'][$weekDayId]['periodList'][$periodId]['periodLocationList'])
) {
$providerRows[$userId]['weekDayList'][$weekDayId]['periodList'][$periodId]['periodLocationList'][$periodLocationId] = [
'id' => $periodLocationId,
'locationId' => $row['periodLocation_locationId'],
];
}
if (
$timeOutId &&
$weekDayId &&
array_key_exists($userId, $providerRows) &&
array_key_exists($weekDayId, $providerRows[$userId]['weekDayList']) &&
!array_key_exists($timeOutId, $providerRows[$userId]['weekDayList'][$weekDayId]['timeOutList'])
) {
$providerRows[$userId]['weekDayList'][$weekDayId]['timeOutList'][$timeOutId] = [
'id' => $timeOutId,
'startTime' => $row['timeOut_startTime'],
'endTime' => $row['timeOut_endTime'],
];
}
if (
$specialDayId &&
array_key_exists($userId, $providerRows) &&
!array_key_exists($specialDayId, $providerRows[$userId]['specialDayList'])
) {
$providerRows[$userId]['specialDayList'][$specialDayId] = [
'id' => $specialDayId,
'startDate' => $row['specialDay_startDate'],
'endDate' => $row['specialDay_endDate'],
'periodList' => [],
];
}
if (
$specialDayPeriodId &&
$specialDayId &&
array_key_exists($userId, $providerRows) &&
array_key_exists($specialDayId, $providerRows[$userId]['specialDayList']) &&
!array_key_exists($specialDayPeriodId, $providerRows[$userId]['specialDayList'][$specialDayId]['periodList'])
) {
$providerRows[$userId]['specialDayList'][$specialDayId]['periodList'][$specialDayPeriodId] = [
'id' => $specialDayPeriodId,
'startTime' => $row['specialDayPeriod_startTime'],
'endTime' => $row['specialDayPeriod_endTime'],
'locationId' => $row['specialDayPeriod_locationId'],
'periodServiceList' => [],
'periodLocationList' => [],
];
}
if (
$specialDayPeriodServiceId &&
$specialDayPeriodId &&
$specialDayId &&
array_key_exists($userId, $providerRows) &&
array_key_exists($specialDayId, $providerRows[$userId]['specialDayList']) &&
array_key_exists($specialDayPeriodId, $providerRows[$userId]['specialDayList'][$specialDayId]['periodList']) &&
!array_key_exists(
$specialDayPeriodServiceId,
$providerRows[$userId]['specialDayList'][$specialDayId]['periodList'][$specialDayPeriodId]['periodServiceList']
)
) {
$providerRows[$userId]['specialDayList'][$specialDayId]['periodList'][$specialDayPeriodId]['periodServiceList'][$specialDayPeriodServiceId] = [
'id' => $specialDayPeriodServiceId,
'serviceId' => $row['specialDayPeriodService_serviceId'],
];
}
if (
$specialDayPeriodLocationId &&
$specialDayPeriodId &&
$specialDayId &&
array_key_exists($userId, $providerRows) &&
array_key_exists($specialDayId, $providerRows[$userId]['specialDayList']) &&
array_key_exists($specialDayPeriodId, $providerRows[$userId]['specialDayList'][$specialDayId]['periodList']) &&
!array_key_exists(
$specialDayPeriodLocationId,
$providerRows[$userId]['specialDayList'][$specialDayId]['periodList'][$specialDayPeriodId]['periodLocationList']
)
) {
$providerRows[$userId]['specialDayList'][$specialDayId]['periodList'][$specialDayPeriodId]['periodLocationList'][$specialDayPeriodLocationId] = [
'id' => $specialDayPeriodLocationId,
'locationId' => $row['specialDayPeriodLocation_locationId'],
];
}
if (
$dayOffId &&
array_key_exists($userId, $providerRows) &&
!array_key_exists($dayOffId, $providerRows[$userId]['dayOffList'])
) {
if ($row['dayOff_type'] === DayOffType::DAY_OFF) {
$providerRows[$userId]['dayOffList'][$dayOffId] = [
'id' => $dayOffId,
'name' => $row['dayOff_name'],
'startDate' => $row['dayOff_startDate'],
'endDate' => $row['dayOff_endDate'],
'repeat' => $row['dayOff_repeat'],
];
}
if ($row['dayOff_type'] === DayOffType::BLOCK_TIME) {
$providerRows[$userId]['blockTimeList'][$dayOffId] = [
'id' => $dayOffId,
'name' => $row['dayOff_name'],
'startDate' => $row['dayOff_startDate'],
'endDate' => $row['dayOff_endDate'],
'repeat' => $row['dayOff_repeat'],
];
}
}
if (
$serviceId &&
!array_key_exists($serviceId, $serviceRows)
) {
$serviceRows[$serviceId] = [
'id' => $serviceId,
'customPricing' => isset($row['service_customPricing']) ? $row['service_customPricing'] : null,
'price' => $row['service_price'],
'minCapacity' => $row['service_minCapacity'],
'maxCapacity' => $row['service_maxCapacity'],
'name' => isset($row['service_name']) ? $row['service_name'] : null,
'description' => isset($row['service_description']) ? $row['service_description'] : null,
'color' => isset($row['service_color']) ? $row['service_color'] : null,
'status' => isset($row['service_status']) ? $row['service_status'] : null,
'categoryId' => isset($row['service_categoryId']) ? (int)$row['service_categoryId'] : null,
'duration' => isset($row['service_duration']) ? $row['service_duration'] : null,
'bringingAnyone' => isset($row['service_bringingAnyone']) ? $row['service_bringingAnyone'] : null,
'show' => isset($row['service_show']) ? $row['service_show'] : null,
'aggregatedPrice' => isset($row['service_aggregatedPrice']) ? $row['service_aggregatedPrice'] : null,
'pictureFullPath' => isset($row['service_picture_full']) ? $row['service_picture_full'] : null,
'pictureThumbPath' => isset($row['service_picture_thumb']) ? $row['service_picture_thumb'] : null,
'timeBefore' => isset($row['service_timeBefore']) ? $row['service_timeBefore'] : null,
'timeAfter' => isset($row['service_timeAfter']) ? $row['service_timeAfter'] : null,
'extras' => [],
'coupons' => [],
'settings' => isset($row['service_settings']) ? $row['service_settings'] : null,
'recurringCycle' => isset($row['service_recurringCycle']) ? $row['service_recurringCycle'] : null,
'recurringSub' => isset($row['service_recurringSub']) ? $row['service_recurringSub'] : null,
'recurringPayment' => isset($row['service_recurringPayment']) ? $row['service_recurringPayment'] : null,
'translations' => isset($row['service_translations']) ? $row['service_translations'] : null,
'deposit' => isset($row['service_deposit']) ? $row['service_deposit'] : 0,
'depositPayment' => isset($row['service_depositPayment']) ? $row['service_depositPayment'] : 'disabled',
'depositPerPerson' => isset($row['service_depositPerPerson']) ? $row['service_depositPerPerson'] : 1,
'fullPayment' => isset($row['service_fullPayment']) ? $row['service_fullPayment'] : false,
];
}
if (
$extraId &&
$serviceId &&
array_key_exists($serviceId, $serviceRows) &&
!array_key_exists($extraId, $serviceRows[$serviceId]['extras'])
) {
$serviceRows[$serviceId]['extras'][$extraId] = [
'id' => $extraId,
'name' => $row['extra_name'],
'price' => $row['extra_price'],
'maxQuantity' => $row['extra_maxQuantity'],
'position' => $row['extra_position'],
'aggregatedPrice' => $row['extra_aggregatedPrice'],
'description' => $row['extra_description']
];
}
if (
$couponId &&
$serviceId &&
array_key_exists($serviceId, $serviceRows) &&
!array_key_exists($couponId, $serviceRows[$serviceId]['coupons'])
) {
$serviceRows[$serviceId]['coupons'][$couponId] = [
'id' => $couponId,
'code' => $row['coupon_code'],
'discount' => $row['coupon_discount'],
'deduction' => $row['coupon_deduction'],
'limit' => $row['coupon_limit'],
'customerLimit' => $row['coupon_customerLimit'],
'status' => $row['coupon_status']
];
}
if ($serviceId && (!array_key_exists($userId, $providerServiceRows) || !array_key_exists($serviceId, $providerServiceRows[$userId]))) {
$providerServiceRows[$userId][$serviceId] = [
'price' => $row['service_price'],
'customPricing' => $row['service_customPricing'],
'minCapacity' => (int)$row['service_minCapacity'],
'maxCapacity' => (int)$row['service_maxCapacity']
];
}
}
/**
* @param int $userId
*
* @return mixed
* @throws QueryExecutionException
*/
public function deleteViewStats($userId)
{
$params = [
':userId' => $userId,
];
try {
$statement = $this->connection->prepare(
"DELETE FROM {$this->providerViewsTable} WHERE userId = :userId"
);
$statement->execute($params);
return true;
} catch (\Exception $e) {
throw new QueryExecutionException('Unable to delete data from ' . __CLASS__ . '. ' . $e->getMessage(), $e->getCode(), $e);
}
}
/**
* Get max service capacity for a specific provider
*
* @param int $providerId
* @param int $serviceId
*
* @return int|null
* @throws QueryExecutionException
*/
public function getMaxCapacityByServiceId($providerId, $serviceId)
{
$params = [
':userId' => $providerId,
':serviceId' => $serviceId
];
try {
$statement = $this->connection->prepare(
"SELECT maxCapacity
FROM {$this->providerServicesTable}
WHERE userId = :userId AND serviceId = :serviceId"
);
$statement->execute($params);
$row = $statement->fetch();
return $row ? (int)$row['maxCapacity'] : 0;
} catch (\Exception $e) {
throw new QueryExecutionException('Unable to get max capacity from ' . __CLASS__ . '. ' . $e->getMessage(), $e->getCode(), $e);
}
}
/**
* Batch update to clear googleCalendarId for all providers with a single SQL query
*
* @return void
* @throws QueryExecutionException
*/
public function clearGoogleCalendarIds()
{
try {
$statement = $this->connection->prepare(
"UPDATE {$this->table}
SET googleCalendarId = NULL
WHERE googleCalendarId IS NOT NULL
AND type = 'provider'"
);
$statement->execute();
} catch (\Exception $e) {
throw new QueryExecutionException('Unable to batch clear googleCalendarId in ' . __CLASS__ . '. ' . $e->getMessage(), $e->getCode(), $e);
}
}
/**
* Batch update to clear outlookCalendarId for all providers with a single SQL query
*
* @return void
* @throws QueryExecutionException
*/
public function clearOutlookCalendarIds()
{
try {
$statement = $this->connection->prepare(
"UPDATE {$this->table}
SET outlookCalendarId = NULL
WHERE outlookCalendarId IS NOT NULL
AND type = 'provider'"
);
$statement->execute();
} catch (\Exception $e) {
throw new QueryExecutionException('Unable to batch clear outlookCalendarId in ' . __CLASS__ . '. ' . $e->getMessage(), $e->getCode(), $e);
}
}
/**
* Apply license restriction for Lite license - forces only the first provider (smallest ID)
* to be returned regardless of filters
*
* @param array $criteria
* @return array Modified criteria array
* @throws QueryExecutionException
*/
private function applyLiteLicenseRestriction($criteria)
{
if (!Licence::hasLicenseAccess(LicenceConstants::STARTER)) {
try {
$params = [
':type' => AbstractUser::USER_ROLE_PROVIDER
];
$statement = $this->connection->prepare(
"SELECT u.id
FROM {$this->table} u
WHERE u.type = :type
AND u.status NOT LIKE 'disabled'
ORDER BY u.id ASC
LIMIT 1"
);
$statement->execute($params);
$result = $statement->fetch();
if ($result) {
// Force only this specific provider to be returned
$criteria['providers'] = [(int)$result['id']];
}
} catch (\Exception $e) {
throw new QueryExecutionException('Unable to apply Lite license restriction in ' . __CLASS__ . '. ' . $e->getMessage(), $e->getCode(), $e);
}
}
return $criteria;
}
/**
* Update the calendarId for a specific Google Calendar account row.
*
* @param int $accountId
* @param string|null $calendarId
*
* @return void
* @throws QueryExecutionException
*/
public function updateGoogleCalendarCalendarIdByAccountId($accountId, $calendarId)
{
try {
$statement = $this->connection->prepare(
"UPDATE {$this->providersGoogleCalendarTable}
SET calendarId = :calendarId
WHERE id = :accountId"
);
$statement->bindParam(':calendarId', $calendarId);
$statement->bindParam(':accountId', $accountId);
$statement->execute();
} catch (\Exception $e) {
throw new QueryExecutionException(
'Unable to update Google Calendar calendarId in ' . __CLASS__ . ': ' . $e->getMessage(),
$e->getCode(),
$e
);
}
}
/**
* Get all Google Calendar accounts for a provider
*
* @param int $providerId
*
* @return array
* @throws QueryExecutionException
*/
/**
* Employees whose calendar selection lives on the user row, which is how the global Settings page
* connection stores it - those employees have no providers_to_*_calendar row of their own.
*
* @param string $calendarId
* @param string $column googleCalendarId|outlookCalendarId
*
* @return int[]
* @throws QueryExecutionException
*/
public function getProviderIdsByCalendarId($calendarId, $column): array
{
if (!in_array($column, ['googleCalendarId', 'outlookCalendarId'], true)) {
throw new QueryExecutionException('Unsupported calendar column ' . $column . ' in ' . __CLASS__);
}
try {
// Restricted to providers: the calendar columns live on the shared users table, so without
// this a row of any other role holding the same value would be handed back as an employee.
$statement = $this->connection->prepare(
"SELECT id FROM {$this->table} WHERE `{$column}` = :calendarId AND type = :type"
);
$statement->execute(
[
':calendarId' => $calendarId,
':type' => AbstractUser::USER_ROLE_PROVIDER,
]
);
$rows = $statement->fetchAll();
} catch (\Exception $e) {
throw new QueryExecutionException('Unable to get providers by calendar in ' . __CLASS__ . '. ' . $e->getMessage(), $e->getCode(), $e);
}
return array_map(
function ($row) {
return (int)$row['id'];
},
$rows ?: []
);
}
/**
* @param int $providerId
* @param string $calendarId
* @param string $column googleCalendarId|outlookCalendarId
*
* @return bool
* @throws QueryExecutionException
*/
public function isProviderUsingCalendarId($providerId, $calendarId, $column): bool
{
if (!in_array($column, ['googleCalendarId', 'outlookCalendarId'], true)) {
throw new QueryExecutionException('Unsupported calendar column ' . $column . ' in ' . __CLASS__);
}
try {
$statement = $this->connection->prepare(
"SELECT id FROM {$this->table}
WHERE id = :id AND `{$column}` = :calendarId AND type = :type
LIMIT 1"
);
$statement->execute(
[
':id' => $providerId,
':calendarId' => $calendarId,
':type' => AbstractUser::USER_ROLE_PROVIDER,
]
);
return (bool)$statement->fetch();
} catch (\Exception $e) {
throw new QueryExecutionException('Unable to check provider calendar in ' . __CLASS__ . '. ' . $e->getMessage(), $e->getCode(), $e);
}
}
public function getGoogleCalendarAccounts($providerId): array
{
$accounts = [];
try {
$statement = $this->connection->prepare(
"SELECT id, token, calendarId, blockedCalendars, accountId, syncId
FROM {$this->providersGoogleCalendarTable}
WHERE userId = :userId"
);
$statement->bindParam(':userId', $providerId);
$statement->execute();
while ($row = $statement->fetch()) {
$blockedCalendars = [];
if (isset($row['blockedCalendars']) && $row['blockedCalendars']) {
$blockedCalendars = json_decode($row['blockedCalendars'], true) ?: [];
}
$accounts[] = [
'id' => $row['id'],
'token' => $row['token'],
'calendarId' => $row['calendarId'],
'blockedCalendars' => $blockedCalendars,
'accountId' => isset($row['accountId']) ? $row['accountId'] : null,
'syncId' => isset($row['syncId']) ? $row['syncId'] : null,
];
}
} catch (\Exception $e) {
throw new QueryExecutionException('Unable to get Google Calendar accounts in ' . __CLASS__, $e->getCode(), $e);
}
return $accounts;
}
/**
* Get employee level Google Calendar "Event title and description" templates
*
* The account that has a calendar selected has priority, the same way the provider entity is
* hydrated in self::parseUserRow()
*
* @param int $providerId
*
* @return array
* @throws QueryExecutionException
*/
public function getGoogleCalendarSettings($providerId)
{
try {
$statement = $this->connection->prepare(
"SELECT calendarId, title, description
FROM {$this->providersGoogleCalendarTable}
WHERE userId = :userId"
);
$statement->bindParam(':userId', $providerId);
$statement->execute();
$settingsRow = null;
while ($row = $statement->fetch()) {
if ($settingsRow === null || (empty($settingsRow['calendarId']) && !empty($row['calendarId']))) {
$settingsRow = $row;
}
}
} catch (\Exception $e) {
throw new QueryExecutionException(
'Unable to get Google Calendar settings in ' . __CLASS__ . ': ' . $e->getMessage(),
$e->getCode(),
$e
);
}
if ($settingsRow === null) {
return [];
}
return [
'title' => !empty($settingsRow['title']) ? json_decode($settingsRow['title'], true) : null,
'description' => !empty($settingsRow['description']) ? json_decode($settingsRow['description'], true) : null,
];
}
/**
* Update blocked calendars for a Google Calendar account
*
* @param int $accountId
* @param array $blockedCalendars
*
* @return void
* @throws QueryExecutionException
*/
public function updateGoogleCalendarBlockedCalendars($accountId, $blockedCalendars)
{
try {
$blockedCalendarsJson = json_encode($blockedCalendars);
if ($blockedCalendarsJson === false) {
throw new QueryExecutionException(
'Unable to encode blocked calendars to JSON in ' . __CLASS__ . '. Error: ' . json_last_error_msg()
);
}
$statement = $this->connection->prepare(
"UPDATE {$this->providersGoogleCalendarTable}
SET blockedCalendars = :blockedCalendars
WHERE id = :accountId"
);
$statement->bindParam(':blockedCalendars', $blockedCalendarsJson);
$statement->bindParam(':accountId', $accountId);
$statement->execute();
} catch (\Exception $e) {
throw new QueryExecutionException(
'Unable to update blocked calendars in ' . __CLASS__ . ': ' . $e->getMessage(),
$e->getCode(),
$e
);
}
}
/**
* Update Google Calendar account token (used when refreshing expired tokens)
*
* @param int $accountId
* @param string $token
*
* @return bool
* @throws QueryExecutionException
*/
public function updateGoogleCalendarAccountToken($accountId, $token): bool
{
try {
$statement = $this->connection->prepare(
"UPDATE {$this->providersGoogleCalendarTable}
SET token = :token
WHERE id = :accountId"
);
$statement->bindParam(':token', $token);
$statement->bindParam(':accountId', $accountId);
$statement->execute();
return true;
} catch (\Exception $e) {
throw new QueryExecutionException(
'Unable to update Google Calendar account token in ' . __CLASS__ . ': ' . $e->getMessage(),
$e->getCode(),
$e
);
}
}
/**
* Update Google Calendar account settings (insertPendingAppointments, includeBufferTime, title, description)
*
* @param int $providerId
* @param array $settings
*
* @return bool
* @throws QueryExecutionException
*/
public function updateGoogleCalendarAccountSettings($providerId, $settings)
{
try {
$updateFields = [];
$params = [':userId' => $providerId];
if (isset($settings['insertPendingAppointments'])) {
$updateFields[] = 'insertPendingAppointments = :insertPendingAppointments';
$params[':insertPendingAppointments'] = (bool)$settings['insertPendingAppointments'];
}
if (isset($settings['includeBufferTime'])) {
$updateFields[] = 'includeBufferTime = :includeBufferTime';
$params[':includeBufferTime'] = (bool)$settings['includeBufferTime'];
}
if (isset($settings['title'])) {
$updateFields[] = 'title = :title';
$params[':title'] = json_encode($settings['title']);
}
if (isset($settings['description'])) {
$updateFields[] = 'description = :description';
$params[':description'] = json_encode($settings['description']);
}
if (empty($updateFields)) {
return true;
}
$statement = $this->connection->prepare(
"UPDATE {$this->providersGoogleCalendarTable}
SET " . implode(', ', $updateFields) . "
WHERE userId = :userId"
);
foreach ($params as $key => $value) {
$statement->bindValue($key, $value);
}
return $statement->execute();
} catch (\Exception $e) {
throw new QueryExecutionException(
'Unable to update Google Calendar account settings in ' . __CLASS__ . ': ' . $e->getMessage(),
$e->getCode(),
$e
);
}
}
/**
* Get all Google Calendar accounts for a provider
*
* @param int $providerId
*
* @return array
* @throws QueryExecutionException
*/
public function getOutlookCalendarAccounts($providerId): array
{
$accounts = [];
try {
$statement = $this->connection->prepare(
"SELECT id, token, calendarId, blockedCalendars, insertPendingAppointments, includeBufferTime, title, description,
accountId, syncId
FROM {$this->providersOutlookCalendarTable}
WHERE userId = :userId"
);
$statement->bindParam(':userId', $providerId);
$statement->execute();
while ($row = $statement->fetch()) {
$blockedCalendars = [];
if (isset($row['blockedCalendars']) && $row['blockedCalendars']) {
$blockedCalendars = json_decode($row['blockedCalendars'], true) ?: [];
}
$accounts[] = [
'id' => $row['id'],
'token' => $row['token'],
'calendarId' => $row['calendarId'],
'blockedCalendars' => $blockedCalendars,
'insertPendingAppointments' => isset($row['insertPendingAppointments']) ? $row['insertPendingAppointments'] : null,
'includeBufferTime' => isset($row['includeBufferTime']) ? $row['includeBufferTime'] : null,
'title' => isset($row['title']) && $row['title'] ? json_decode($row['title'], true) : null,
'description' => isset($row['description']) && $row['description'] ? json_decode($row['description'], true) : null,
'accountId' => isset($row['accountId']) ? $row['accountId'] : null,
'syncId' => isset($row['syncId']) ? $row['syncId'] : null,
];
}
} catch (\Exception $e) {
throw new QueryExecutionException('Unable to get Outlook Calendar accounts in ' . __CLASS__, $e->getCode(), $e);
}
return $accounts;
}
/**
* Update blocked calendars for a Outlook Calendar account
*
* @param int $accountId
* @param array $blockedCalendars
*
* @return void
* @throws QueryExecutionException
*/
public function updateOutlookCalendarBlockedCalendars($accountId, $blockedCalendars)
{
try {
$blockedCalendarsJson = json_encode($blockedCalendars);
if ($blockedCalendarsJson === false) {
throw new QueryExecutionException(
'Unable to encode blocked calendars to JSON in ' . __CLASS__ . '. Error: ' . json_last_error_msg()
);
}
$statement = $this->connection->prepare(
"UPDATE {$this->providersOutlookCalendarTable}
SET blockedCalendars = :blockedCalendars
WHERE id = :accountId"
);
$statement->bindParam(':blockedCalendars', $blockedCalendarsJson);
$statement->bindParam(':accountId', $accountId);
$statement->execute();
} catch (\Exception $e) {
throw new QueryExecutionException(
'Unable to update blocked calendars in ' . __CLASS__ . ': ' . $e->getMessage(),
$e->getCode(),
$e
);
}
}
/**
* Update the calendarId for a specific Outlook Calendar account row
*
* @param int $accountId
* @param string|null $calendarId
*
* @return void
* @throws QueryExecutionException
*/
public function updateOutlookCalendarCalendarIdByAccountId($accountId, $calendarId)
{
try {
$statement = $this->connection->prepare(
"UPDATE {$this->providersOutlookCalendarTable}
SET calendarId = :calendarId
WHERE id = :accountId"
);
$statement->bindParam(':calendarId', $calendarId);
$statement->bindParam(':accountId', $accountId);
$statement->execute();
} catch (\Exception $e) {
throw new QueryExecutionException(
'Unable to update Outlook Calendar calendarId in ' . __CLASS__ . ': ' . $e->getMessage(),
$e->getCode(),
$e
);
}
}
/**
* Update Outlook Calendar account token (used when refreshing expired tokens)
*
* @param int $accountId
* @param string $token
*
* @return void
* @throws QueryExecutionException
*/
public function updateOutlookCalendarAccountToken($accountId, $token): void
{
try {
$statement = $this->connection->prepare(
"UPDATE {$this->providersOutlookCalendarTable}
SET token = :token
WHERE id = :accountId"
);
$statement->bindParam(':token', $token);
$statement->bindParam(':accountId', $accountId);
$statement->execute();
} catch (\Exception $e) {
throw new QueryExecutionException(
'Unable to update Outlook Calendar account token in ' . __CLASS__ . ': ' . $e->getMessage(),
$e->getCode(),
$e
);
}
}
/**
* Update Outlook Calendar account settings (insertPendingAppointments, includeBufferTime, title, description)
*
* @param int $providerId
* @param array $settings
*
* @return void
* @throws QueryExecutionException
*/
public function updateOutlookCalendarAccountSettings($providerId, $settings)
{
try {
$updateFields = [];
$params = [':userId' => $providerId];
if (isset($settings['insertPendingAppointments'])) {
$updateFields[] = 'insertPendingAppointments = :insertPendingAppointments';
$params[':insertPendingAppointments'] = (bool)$settings['insertPendingAppointments'];
}
if (isset($settings['includeBufferTime'])) {
$updateFields[] = 'includeBufferTime = :includeBufferTime';
$params[':includeBufferTime'] = (bool)$settings['includeBufferTime'];
}
if (isset($settings['title'])) {
$updateFields[] = 'title = :title';
$params[':title'] = json_encode($settings['title']);
}
if (isset($settings['description'])) {
$updateFields[] = 'description = :description';
$params[':description'] = json_encode($settings['description']);
}
if (empty($updateFields)) {
return;
}
$statement = $this->connection->prepare(
"UPDATE {$this->providersOutlookCalendarTable}
SET " . implode(', ', $updateFields) . "
WHERE userId = :userId"
);
foreach ($params as $key => $value) {
$statement->bindValue($key, $value);
}
$statement->execute();
} catch (\Exception $e) {
throw new QueryExecutionException(
'Unable to update Outlook Calendar account settings in ' . __CLASS__ . ': ' . $e->getMessage(),
$e->getCode(),
$e
);
}
}
}