diff --git a/application/Espo/Repositories/EmailAddress.php b/application/Espo/Repositories/EmailAddress.php index 83667e71d5..eb471e20d6 100644 --- a/application/Espo/Repositories/EmailAddress.php +++ b/application/Espo/Repositories/EmailAddress.php @@ -35,18 +35,27 @@ use Espo\ORM\Entity; use Espo\Entities\EmailAddress as EmailAddressEntity; use Espo\Core\Di; +use stdClass; + /** + * Not to be used directly. Use utilities from `Espo\Tools\EmailAddress` instead. + * @internal * @extends Database */ class EmailAddress extends Database implements Di\ApplicationStateAware, - Di\AclManagerAware + Di\AclManagerAware, + Di\ConfigAware { use Di\ApplicationStateSetter; use Di\AclManagerSetter; + use Di\ConfigSetter; protected $hooksDisabled = true; + private const LOOKUP_SMALL_MAX_SIZE = 20; + private const LOOKUP_MAX_SIZE = 50; + /** * @param string[] $addressList * @return string[] @@ -63,45 +72,44 @@ class EmailAddress extends Database implements */ public function getIds(array $addressList = []): array { + if (empty($addressList)) { + return []; + } + $ids = []; - if (!empty($addressList)) { - $lowerAddressList = []; + $lowerAddressList = []; - foreach ($addressList as $address) { - $lowerAddressList[] = trim(strtolower($address)); + foreach ($addressList as $address) { + $lowerAddressList[] = trim(strtolower($address)); + } + + $eaCollection = $this + ->where(['lower' => $lowerAddressList]) + ->find(); + + $exist = []; + + foreach ($eaCollection as $ea) { + $ids[] = $ea->getId(); + $exist[] = $ea->get('lower'); + } + + foreach ($addressList as $address) { + $address = trim($address); + + if (empty($address) || !filter_var($address, FILTER_VALIDATE_EMAIL)) { + continue; } - $eaCollection = $this - ->where([ - ['lower' => $lowerAddressList] - ]) - ->find(); + if (!in_array(strtolower($address), $exist)) { + $ea = $this->getNew(); - $ids = []; - $exist = []; + $ea->set('name', $address); + + $this->save($ea); - foreach ($eaCollection as $ea) { $ids[] = $ea->getId(); - $exist[] = $ea->get('lower'); - } - - foreach ($addressList as $address) { - $address = trim($address); - - if (empty($address) || !filter_var($address, FILTER_VALIDATE_EMAIL)) { - continue; - } - - if (!in_array(strtolower($address), $exist)) { - $ea = $this->getNew(); - - $ea->set('name', $address); - - $this->save($ea); - - $ids[] = $ea->getId(); - } } } @@ -109,7 +117,7 @@ class EmailAddress extends Database implements } /** - * @return \stdClass[] + * @return stdClass[] */ public function getEmailAddressData(Entity $entity): array { @@ -193,6 +201,7 @@ class EmailAddress extends Database implements ->sth() ->select(['entityType', 'entityId']) ->where($where) + ->limit(0, self::LOOKUP_MAX_SIZE) ->find(); foreach ($itemList as $item) { @@ -257,7 +266,7 @@ class EmailAddress extends Database implements ->sth() ->select(['entityType', 'entityId']) ->where($where) - ->limit(0, 20) + ->limit(0, self::LOOKUP_SMALL_MAX_SIZE) ->order([ ['primary', 'DESC'], ['LIST:entityType:User,Contact,Lead,Account'], @@ -310,16 +319,9 @@ class EmailAddress extends Database implements /** * @param string[] $order */ - public function getEntityByAddress( - string $address, - ?string $entityType = null, - array $order = [ - 'User', - 'Contact', - 'Lead', - 'Account', - ] - ): ?Entity { + public function getEntityByAddress(string $address, ?string $entityType = null, ?array $order = null): ?Entity + { + $order ??= $this->config->get('emailAddressEntityLookupDefaultOrder') ?? []; $selectBuilder = $this->entityManager ->getRDBRepository(EmailAddressEntity::RELATION_ENTITY_EMAIL_ADDRESS) @@ -337,7 +339,8 @@ class EmailAddress extends Database implements ->order([ ['LIST:entityType:' . implode(',', $order)], ['primary', 'DESC'], - ]); + ]) + ->limit(0, self::LOOKUP_MAX_SIZE); if ($entityType) { $selectBuilder->where('entityType=', $entityType); diff --git a/application/Espo/Repositories/PhoneNumber.php b/application/Espo/Repositories/PhoneNumber.php index 56508b3d28..387449e630 100644 --- a/application/Espo/Repositories/PhoneNumber.php +++ b/application/Espo/Repositories/PhoneNumber.php @@ -35,63 +35,69 @@ use Espo\Entities\PhoneNumber as PhoneNumberEntity; use Espo\Core\Repositories\Database; use Espo\Core\Di; +use stdClass; + /** + * Not to be used directly. Use utilities from `Espo\Tools\PhoneNumber` instead. + * @internal * @extends Database */ class PhoneNumber extends Database implements Di\ApplicationStateAware, - Di\AclManagerAware + Di\AclManagerAware, + Di\ConfigAware { use Di\ApplicationStateSetter; use Di\AclManagerSetter; + use Di\ConfigSetter; protected $hooksDisabled = true; - const ERASED_PREFIX = 'ERASED:'; + private const ERASED_PREFIX = 'ERASED:'; + + private const LOOKUP_SMALL_MAX_SIZE = 20; + private const LOOKUP_MAX_SIZE = 50; /** - * @param string [] $numberList + * @param string[] $numberList * @return string[] */ public function getIds($numberList = []): array { + if (empty($numberList)) { + return []; + } + $ids = []; - if (!empty($numberList)) { - $phoneNumbers = $this - ->where([ - [ - 'name' => $numberList, - 'hash' => null, - ] - ]) - ->find(); + $phoneNumbers = $this + ->where([ + 'name' => $numberList, + 'hash' => null, + ]) + ->find(); - $ids = []; - $exist = []; + $exist = []; - foreach ($phoneNumbers as $phoneNumber) { - $ids[] = $phoneNumber->getId(); - $exist[] = $phoneNumber->get('name'); + foreach ($phoneNumbers as $phoneNumber) { + $ids[] = $phoneNumber->getId(); + $exist[] = $phoneNumber->get('name'); + } + + foreach ($numberList as $number) { + $number = trim($number); + + if (empty($number)) { + continue; } - foreach ($numberList as $number) { - $number = trim($number); + if (!in_array($number, $exist)) { + $phoneNumber = $this->getNew(); + $phoneNumber->set('name', $number); + $this->save($phoneNumber); - if (empty($number)) { - continue; - } - - if (!in_array($number, $exist)) { - $phoneNumber = $this->getNew(); - - $phoneNumber->set('name', $number); - - $this->save($phoneNumber); - - $ids[] = $phoneNumber->getId(); - } + $ids[] = $phoneNumber->getId(); } } @@ -99,7 +105,7 @@ class PhoneNumber extends Database implements } /** - * @return array + * @return array */ public function getPhoneNumberData(Entity $entity): array { @@ -172,6 +178,7 @@ class PhoneNumber extends Database implements ->sth() ->select(['entityType', 'entityId']) ->where($where) + ->limit(0, self::LOOKUP_MAX_SIZE) ->find(); foreach ($itemList as $item) { @@ -198,29 +205,36 @@ class PhoneNumber extends Database implements return $entityList; } - public function getEntityByPhoneNumberId(string $phoneNumberId, ?string $entityType = null): ?Entity - { - $where = [ - 'phoneNumberId' => $phoneNumberId, - ]; + /** + * @param string[] $order + */ + public function getEntityByPhoneNumberId( + string $phoneNumberId, + ?string $entityType = null, + ?array $order = null + ): ?Entity { + + $order ??= $this->config->get('phoneNumberEntityLookupDefaultOrder') ?? []; + + $where = ['phoneNumberId' => $phoneNumberId]; if ($entityType) { $where[] = ['entityType' => $entityType]; } - $itemList = $this->entityManager + $collection = $this->entityManager ->getRDBRepository(PhoneNumberEntity::RELATION_ENTITY_PHONE_NUMBER) ->sth() ->select(['entityType', 'entityId']) ->where($where) - ->limit(0, 20) + ->limit(0, self::LOOKUP_SMALL_MAX_SIZE) ->order([ + ['LIST:entityType:' . implode(',', $order)], ['primary', 'DESC'], - ['LIST:entityType:User,Contact,Lead,Account'], ]) ->find(); - foreach ($itemList as $item) { + foreach ($collection as $item) { $itemEntityType = $item->get('entityType'); $itemEntityId = $item->get('entityId'); @@ -255,7 +269,7 @@ class PhoneNumber extends Database implements if ($entity->has('name')) { $number = $entity->get('name'); - if (is_string($number) && strpos($number, self::ERASED_PREFIX) !== 0) { + if (is_string($number) && !str_starts_with($number, self::ERASED_PREFIX)) { $numeric = preg_replace('/[^0-9]/', '', $number); } else { diff --git a/application/Espo/Resources/defaults/config.php b/application/Espo/Resources/defaults/config.php index 8dbbc1de80..532fb653e6 100644 --- a/application/Espo/Resources/defaults/config.php +++ b/application/Espo/Resources/defaults/config.php @@ -145,6 +145,8 @@ return [ 'personalEmailMaxPortionSize' => 50, 'inboundEmailMaxPortionSize' => 50, 'emailAddressLookupEntityTypeList' => ['User', 'Contact', 'Lead', 'Account'], + 'emailAddressEntityLookupDefaultOrder' => ['User', 'Contact', 'Lead', 'Account'], + 'phoneNumberEntityLookupDefaultOrder' => ['User', 'Contact', 'Lead', 'Account'], 'authTokenLifetime' => 0, 'authTokenMaxIdleTime' => 48, 'userNameRegularExpression' => '[^a-z0-9\-@_\.\s]', diff --git a/application/Espo/Resources/defaults/systemConfig.php b/application/Espo/Resources/defaults/systemConfig.php index 33edbfc39a..d1b0a6eb47 100644 --- a/application/Espo/Resources/defaults/systemConfig.php +++ b/application/Espo/Resources/defaults/systemConfig.php @@ -188,6 +188,8 @@ return [ 'outboundSmsFromNumber', 'currencyNoJoinMode', 'authAnotherUserDisabled', + 'emailAddressEntityLookupDefaultOrder', + 'phoneNumberEntityLookupDefaultOrder', 'latestVersion', ], 'superAdminItems' => [ diff --git a/application/Espo/Tools/EmailAddress/EntityLookup.php b/application/Espo/Tools/EmailAddress/EntityLookup.php new file mode 100644 index 0000000000..4f796284ef --- /dev/null +++ b/application/Espo/Tools/EmailAddress/EntityLookup.php @@ -0,0 +1,90 @@ +getRDBRepository(EmailAddress::ENTITY_TYPE); + + if (!$repository instanceof EmailAddressRepository) { + throw new RuntimeException(); + } + + $this->internalRepository = $repository; + } + + /** + * Find entities by an email address. + * + * @param string $address An email address. + * @return Entity[] + */ + public function find(string $address): array + { + $emailAddress = $this->repository->getByAddress($address); + + if (!$emailAddress) { + return []; + } + + return $this->internalRepository->getEntityListByAddressId($emailAddress->getId()); + } + + /** + * Find a first entity by an email address. + * + * @param string $address An email address. + * @param string[] $order An order entity type list. + */ + public function findOne(string $address, ?array $order = null): ?Entity + { + if ($order) { + $this->internalRepository->getEntityByAddress($address, null, $order); + } + + return $this->internalRepository->getEntityByAddress($address); + } +} diff --git a/application/Espo/Tools/EmailAddress/Repository.php b/application/Espo/Tools/EmailAddress/Repository.php new file mode 100644 index 0000000000..7d135c37c1 --- /dev/null +++ b/application/Espo/Tools/EmailAddress/Repository.php @@ -0,0 +1,62 @@ +getRDBRepository(EmailAddress::ENTITY_TYPE); + + if (!$repository instanceof EmailAddressRepository) { + throw new RuntimeException(); + } + + $this->repository = $repository; + } + + /** + * Get an email address entity by a string address. + */ + public function getByAddress(string $address): ?EmailAddress + { + return $this->repository->getByAddress($address); + } +} diff --git a/application/Espo/Tools/PhoneNumber/EntityLookup.php b/application/Espo/Tools/PhoneNumber/EntityLookup.php new file mode 100644 index 0000000000..a78fa3c15a --- /dev/null +++ b/application/Espo/Tools/PhoneNumber/EntityLookup.php @@ -0,0 +1,96 @@ +getRDBRepository(PhoneNumber::ENTITY_TYPE); + + if (!$repository instanceof PhoneNumberRepository) { + throw new RuntimeException(); + } + + $this->internalRepository = $repository; + } + + /** + * Find entities by a phone number. + * + * @param string $number A phone number. + * @return Entity[] + */ + public function find(string $number): array + { + $phoneNumber = $this->repository->getByNumber($number); + + if (!$phoneNumber) { + return []; + } + + return $this->internalRepository->getEntityListByPhoneNumberId($phoneNumber->getId()); + } + + /** + * Find a first entity by a phone number. + * + * @param string $number A phone number. + * @param string[] $order An order entity type list. + */ + public function findOne(string $number, ?array $order = null): ?Entity + { + $phoneNumber = $this->repository->getByNumber($number); + + if (!$phoneNumber) { + return null; + } + + if ($order) { + $this->internalRepository->getEntityByPhoneNumberId($phoneNumber->getId(), null, $order); + } + + return $this->internalRepository->getEntityByPhoneNumberId($phoneNumber->getId()); + } +} diff --git a/application/Espo/Tools/PhoneNumber/Repository.php b/application/Espo/Tools/PhoneNumber/Repository.php new file mode 100644 index 0000000000..92729f903f --- /dev/null +++ b/application/Espo/Tools/PhoneNumber/Repository.php @@ -0,0 +1,63 @@ +getRDBRepository(PhoneNumber::ENTITY_TYPE); + + if (!$repository instanceof PhoneNumberRepository) { + throw new RuntimeException(); + } + + $this->repository = $repository; + } + + /** + * Get a phone number entity by a string number. + */ + public function getByNumber(string $number): ?PhoneNumber + { + return $this->repository->getByNumber($number); + } +}