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 ڐ   { @ 29 gL 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 Tiny File Manager | UserRepository.php
<?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\Admin; use AmeliaBooking\Domain\Entity\User\Customer; use AmeliaBooking\Domain\Entity\User\Manager; use AmeliaBooking\Domain\Entity\User\Provider; use AmeliaBooking\Domain\Factory\User\UserFactory; use AmeliaBooking\Domain\Repository\User\UserRepositoryInterface; use AmeliaBooking\Domain\ValueObjects\Json; use AmeliaBooking\Domain\ValueObjects\String\Password; use AmeliaBooking\Infrastructure\Licence; use AmeliaBooking\Infrastructure\Common\Exceptions\QueryExecutionException; use AmeliaBooking\Infrastructure\DB\WPDB\Statement; use AmeliaBooking\Infrastructure\Repository\AbstractRepository; use AmeliaBooking\Infrastructure\WP\InstallActions\DB\Booking\AppointmentsTable; use AmeliaBooking\Infrastructure\WP\InstallActions\DB\Booking\CustomerBookingsTable; use AmeliaBooking\Infrastructure\WP\InstallActions\DB\Booking\CustomerBookingsToEventsPeriodsTable; use AmeliaBooking\Infrastructure\WP\InstallActions\DB\Booking\EventsPeriodsTable; use AmeliaBooking\Infrastructure\WP\InstallActions\DB\Booking\EventsProvidersTable; use AmeliaBooking\Infrastructure\WP\InstallActions\DB\Booking\EventsTable; /** * Class UserRepository * * @package AmeliaBooking\Infrastructure\Repository */ class UserRepository extends AbstractRepository implements UserRepositoryInterface { public const FACTORY = UserFactory::class; /** * @param AbstractUser $entity * * @return int * @throws QueryExecutionException */ public function add($entity) { $data = $entity->toArray(); $params = [ ':type' => $data['type'], ':status' => $data['status'] ?: 'visible', ':externalId' => $data['externalId'] ?: null, ':firstName' => $data['firstName'], ':lastName' => $data['lastName'], ':email' => $data['email'], ':note' => isset($data['note']) ? $data['note'] : null, ':description' => isset($data['description']) ? $data['description'] : null, ':phone' => isset($data['phone']) ? $data['phone'] : null, ':gender' => isset($data['gender']) ? $data['gender'] : null, ':birthday' => $data['birthday'] ? $data['birthday']->format('Y-m-d') : null, ':pictureFullPath' => $data['pictureFullPath'], ':pictureThumbPath' => $data['pictureThumbPath'], ':password' => isset($data['password']) ? $data['password'] : null, ':usedTokens' => isset($data['usedTokens']) ? $data['usedTokens'] : null, ':countryPhoneIso' => isset($data['countryPhoneIso']) ? $data['countryPhoneIso'] : null, ':stripeConnect' => !empty($data['stripeConnect']) ? json_encode($data['stripeConnect']) : null, ':employeeAppleCalendar' => !empty($data['employeeAppleCalendar']) ? json_encode($data['employeeAppleCalendar']) : null, ':error' => '', ':customFields' => isset($data['customFields']) ? $data['customFields'] : null, ]; $additionalData = Licence\DataModifier::getUserRepositoryData($data); $params = array_merge($params, $additionalData['values']); try { $statement = $this->connection->prepare( "INSERT INTO {$this->table} ( {$additionalData['columns']} `type`, `status`, `externalId`, `firstName`, `lastName`, `email`, `note`, `description`, `phone`, `gender`, `birthday`, `pictureFullPath`, `pictureThumbPath`, `countryPhoneIso`, `usedTokens`, `stripeConnect`, `employeeAppleCalendar`, `password`, `error`, `customFields` ) VALUES ( {$additionalData['placeholders']} :type, :status, :externalId, :firstName, :lastName, :email, :note, :description, :phone, :gender, STR_TO_DATE(:birthday, '%Y-%m-%d'), :pictureFullPath, :pictureThumbPath, :countryPhoneIso, :usedTokens, :stripeConnect, :employeeAppleCalendar, :password, :error, :customFields )" ); $statement->execute($params); } catch (\Exception $e) { throw new QueryExecutionException('Unable to add data in ' . __CLASS__ . '. ' . $e->getMessage(), $e->getCode(), $e); } return (int) $this->connection->lastInsertId(); } /** * @param int $id * @param AbstractUser|array $entity * * @return bool * @throws QueryExecutionException */ public function update($id, $entity) { if (!is_array($entity)) { $data = $entity->toArray(); } else { $data = $entity; } $params = [ ':externalId' => $data['externalId'] ?: null, ':firstName' => $data['firstName'], ':lastName' => $data['lastName'], ':email' => isset($data['email']) ? $data['email'] : null, ':note' => isset($data['note']) ? $data['note'] : null, ':description' => isset($data['description']) ? $data['description'] : null, ':phone' => isset($data['phone']) ? $data['phone'] : null, ':gender' => isset($data['gender']) ? $data['gender'] : null, ':birthday' => isset($data['birthday']) ? $data['birthday']->format('Y-m-d') : null, ':pictureFullPath' => $data['pictureFullPath'], ':pictureThumbPath' => $data['pictureThumbPath'], ':countryPhoneIso' => isset($data['countryPhoneIso']) ? $data['countryPhoneIso'] : null, ':password' => isset($data['password']) ? $data['password'] : null, ':stripeConnect' => !empty($data['stripeConnect']) ? json_encode($data['stripeConnect']) : null, ':employeeAppleCalendar' => !empty($data['employeeAppleCalendar']) ? json_encode($data['employeeAppleCalendar']) : null, ':id' => $id, ':customFields' => isset($data['customFields']) ? $data['customFields'] : null, ]; $additionalData = Licence\DataModifier::getUserRepositoryData($data); $params = array_merge($params, $additionalData['values']); try { $statement = $this->connection->prepare( "UPDATE {$this->table} SET {$additionalData['columnsPlaceholders']} `externalId` = :externalId, `firstName` = :firstName, `lastName` = :lastName, `email` = :email, `note` = :note, `description` = :description, `phone` = :phone, `gender` = :gender, `birthday` = STR_TO_DATE(:birthday, '%Y-%m-%d'), `countryPhoneIso` = :countryPhoneIso, `pictureFullPath` = :pictureFullPath, `pictureThumbPath` = :pictureThumbPath, `stripeConnect` = :stripeConnect, `employeeAppleCalendar` = :employeeAppleCalendar, `customFields` = :customFields, `password` = IFNULL(:password, `password`) WHERE id = :id" ); $statement->execute($params); return true; } catch (\Exception $e) { throw new QueryExecutionException('Unable to save data in ' . __CLASS__ . '. ' . $e->getMessage(), $e->getCode(), $e); } } /** * @param $externalId * * @return Admin|Customer|Manager|Provider|bool * @throws InvalidArgumentException * @throws QueryExecutionException */ public function findByExternalId($externalId) { try { $statement = $this->connection->prepare("SELECT * FROM {$this->table} WHERE externalId = :id"); $statement->bindParam(':id', $externalId); $statement->execute(); $row = $statement->fetch(); } catch (\Exception $e) { throw new QueryExecutionException('Unable to find by external id in ' . __CLASS__ . '. ' . $e->getMessage(), $e->getCode(), $e); } if (!$row) { return false; } return UserFactory::create($row); } /** * @param $type * * @return Collection * @throws QueryExecutionException * @throws InvalidArgumentException */ public function getAllByType($type) { $params = [ ':type' => $type, ]; try { $statement = $this->connection->prepare($this->selectQuery() . ' WHERE type = :type'); $statement->execute($params); $rows = $statement->fetchAll(); } catch (\Exception $e) { throw new QueryExecutionException('Unable to get data from ' . __CLASS__ . '. ' . $e->getMessage(), $e->getCode(), $e); } $items = []; foreach ($rows as $row) { $items[] = call_user_func([static::FACTORY, 'create'], $row); } return new Collection($items); } /** * Returns Collection of all customers and other users that have at least one booking * * @return Collection * @throws QueryExecutionException * @throws InvalidArgumentException */ public function getAllWithAllowedBooking() { try { $bookingsTable = CustomerBookingsTable::getTableName(); $statement = $this->connection->query( " SELECT u.id AS id, u.firstName AS firstName, u.lastName AS lastName, u.email AS email, u.note AS note, u.description AS description, u.phone AS phone, u.countryPhoneIso AS countryPhoneIso, u.gender AS gender, u.status AS status, u.translations AS translations FROM {$this->table} u LEFT JOIN {$bookingsTable} cb ON cb.customerId = u.id WHERE u.type = 'customer' OR (cb.id IS NOT NULL AND u.type IN ('admin', 'provider', 'manager')) GROUP BY u.id ORDER BY CONCAT(firstName, ' ', lastName) " ); $rows = $statement->fetchAll(); } catch (\Exception $e) { throw new QueryExecutionException('Unable to get data from ' . __CLASS__ . '. ' . $e->getMessage(), $e->getCode(), $e); } $items = []; foreach ($rows as $row) { $items[$row['id']] = call_user_func([static::FACTORY, 'create'], $row); } return new Collection($items); } /** * Returns Collection of customers that have no bookings, * whose appointment bookings belong to the given provider, * or whose event bookings belong to events managed by the given provider. * * @param int $providerId * * @return Collection * @throws QueryExecutionException * @throws InvalidArgumentException */ public function getProviderAllowedCustomers($providerId) { $bookingsTable = CustomerBookingsTable::getTableName(); $appointmentsTable = AppointmentsTable::getTableName(); $bookingsToEventsTable = CustomerBookingsToEventsPeriodsTable::getTableName(); $eventsPeriodsTable = EventsPeriodsTable::getTableName(); $eventsTable = EventsTable::getTableName(); $eventsProvidersTable = EventsProvidersTable::getTableName(); try { // 1) User IDs that have no bookings at all $statement = $this->connection->prepare( " SELECT u.id FROM {$this->table} u WHERE u.type = 'customer' AND u.id NOT IN (SELECT DISTINCT cb.customerId FROM {$bookingsTable} cb) " ); $statement->execute(); $userIds = array_map('intval', array_column($statement->fetchAll(), 'id')); // 2) User IDs from the provider's appointments $statement = $this->connection->prepare( " SELECT DISTINCT cb.customerId AS id FROM {$bookingsTable} cb INNER JOIN {$appointmentsTable} a ON a.id = cb.appointmentId WHERE a.providerId = :providerId " ); $statement->execute([':providerId' => $providerId]); $userIds = array_unique( array_merge( $userIds, array_map('intval', array_column($statement->fetchAll(), 'id')) ) ); // 3) User IDs from events managed by the provider (organizer or assigned provider) $statement = $this->connection->prepare( " SELECT DISTINCT cb.customerId AS id FROM {$bookingsTable} cb INNER JOIN {$bookingsToEventsTable} cbep ON cbep.customerBookingId = cb.id INNER JOIN {$eventsPeriodsTable} ep ON ep.id = cbep.eventPeriodId INNER JOIN {$eventsTable} ev ON ev.id = ep.eventId LEFT JOIN {$eventsProvidersTable} evpr ON evpr.eventId = ev.id WHERE ev.organizerId = :providerId OR evpr.userId = :providerId " ); $statement->execute([ ':providerId' => $providerId, ]); $userIds = array_unique( array_merge( $userIds, array_map('intval', array_column($statement->fetchAll(), 'id')) ) ); if (empty($userIds)) { return new Collection(); } // Fetch full user data for the collected IDs $userIdsImploded = implode(', ', array_map('intval', $userIds)); $statement = $this->connection->prepare( " SELECT u.id AS id, u.firstName AS firstName, u.lastName AS lastName, u.email AS email, u.note AS note, u.phone AS phone, u.countryPhoneIso AS countryPhoneIso, u.gender AS gender, u.status AS status, u.translations AS translations FROM {$this->table} u WHERE u.id IN ({$userIdsImploded}) ORDER BY CONCAT(u.firstName, ' ', u.lastName) " ); $statement->execute(); $rows = $statement->fetchAll(); } catch (\Exception $e) { throw new QueryExecutionException('Unable to get data from ' . __CLASS__ . '. ' . $e->getMessage(), $e->getCode(), $e); } $items = []; foreach ($rows as $row) { $items[$row['id']] = call_user_func([static::FACTORY, 'create'], $row); } return new Collection($items); } /** * @param string $email * @param boolean $setPassword * @param boolean $setUsedTokens * * @return Admin|Customer|Manager|Provider|null * @throws InvalidArgumentException * @throws QueryExecutionException */ public function getByEmail($email, $setPassword = false, $setUsedTokens = false) { try { $statement = $this->connection->prepare($this->selectQuery() . ' WHERE LOWER(email) = LOWER(:email)'); $statement->execute( array( ':email' => $email ) ); $row = $statement->fetch(); } catch (\Exception $e) { throw new QueryExecutionException('Unable to get data from ' . __CLASS__ . '. ' . $e->getMessage(), $e->getCode(), $e); } if (!$row) { return null; } /** @var Admin|Customer|Manager|Provider $user */ $user = UserFactory::create($row); if ($setPassword) { $user->setPassword(Password::createFromHashedPassword($row['password'])); } if ($setUsedTokens) { $user->setUsedTokens(new Json($row['usedTokens'])); } return $user; } /** * @param string $phone * * @return Admin|Customer|Manager|Provider|null * @throws InvalidArgumentException * @throws QueryExecutionException */ public function getByPhone($phone) { try { $statement = $this->connection->prepare($this->selectQuery() . ' WHERE phone = :phone'); $statement->execute( array( ':phone' => $phone ) ); $row = $statement->fetch(); } catch (\Exception $e) { throw new QueryExecutionException('Unable to get data from ' . __CLASS__ . '. ' . $e->getMessage(), $e->getCode(), $e); } if (!$row) { return null; } /** @var Admin|Customer|Manager|Provider $user */ $user = UserFactory::create($row); return $user; } /** * @return array * @throws QueryExecutionException */ public function getAllEmailsByType($type) { try { $params[':type'] = $type; $statement = $this->connection->prepare( " SELECT DISTINCT u.email AS email FROM {$this->table} u WHERE u.type = :type " ); $statement->execute($params); $rows = $statement->fetchAll(Statement::FETCH_COLUMN); } catch (\Exception $e) { throw new QueryExecutionException('Unable to get data from ' . __CLASS__ . '. ' . $e->getMessage(), $e->getCode(), $e); } return $rows; } /** * @param string $email * @param array $data * * @return bool * @throws QueryExecutionException */ public function updateFieldsByEmail($email, $data) { // Whitelist of columns that may be updated through this method. Keys in // $data originate from user-supplied import payloads and are used as SQL // identifiers below, so they must never be interpolated unvalidated. $allowedColumns = [ 'firstName', 'lastName', 'note', 'description', 'phone', 'gender', 'birthday', 'countryPhoneIso', 'customFields', ]; $fields = []; $params = [':email' => $email]; foreach ($data as $key => $item) { if (!in_array($key, $allowedColumns, true)) { continue; } $params[":$key"] = $item; $fields[] = "`$key` = :$key"; } if (empty($fields)) { return false; } $fields = implode(', ', $fields); try { $statement = $this->connection->prepare( "UPDATE {$this->table} SET {$fields} WHERE email = :email" ); $statement->execute($params); return true; } catch (\Exception $e) { throw new QueryExecutionException('Unable to save data in ' . __CLASS__ . '. ' . $e->getMessage(), $e->getCode(), $e); } } }