Initial commit
This commit is contained in:
1040
application/Espo/Core/Utils/Database/Orm/Converter.php
Normal file
1040
application/Espo/Core/Utils/Database/Orm/Converter.php
Normal file
File diff suppressed because it is too large
Load Diff
178
application/Espo/Core/Utils/Database/Orm/Defs/AttributeDefs.php
Normal file
178
application/Espo/Core/Utils/Database/Orm/Defs/AttributeDefs.php
Normal file
@@ -0,0 +1,178 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2025 EspoCRM, Inc.
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\Utils\Database\Orm\Defs;
|
||||
|
||||
use Espo\Core\Utils\Util;
|
||||
use Espo\ORM\Defs\Params\AttributeParam;
|
||||
use Espo\ORM\Type\AttributeType;
|
||||
|
||||
/**
|
||||
* Immutable.
|
||||
*/
|
||||
class AttributeDefs
|
||||
{
|
||||
/** @var array<string, mixed> */
|
||||
private array $params = [];
|
||||
|
||||
private function __construct(private string $name) {}
|
||||
|
||||
public static function create(string $name): self
|
||||
{
|
||||
return new self($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an attribute name.
|
||||
*/
|
||||
public function getName(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a type.
|
||||
*
|
||||
* @return AttributeType::*
|
||||
*/
|
||||
public function getType(): ?string
|
||||
{
|
||||
/** @var ?AttributeType::* $value */
|
||||
$value = $this->getParam(AttributeParam::TYPE);
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clone with a type.
|
||||
*
|
||||
* @param AttributeType::* $type
|
||||
*/
|
||||
public function withType(string $type): self
|
||||
{
|
||||
return $this->withParam(AttributeParam::TYPE, $type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clone with a DB type.
|
||||
*/
|
||||
public function withDbType(string $dbType): self
|
||||
{
|
||||
return $this->withParam(AttributeParam::DB_TYPE, $dbType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clone with not-storable.
|
||||
*/
|
||||
public function withNotStorable(bool $value = true): self
|
||||
{
|
||||
return $this->withParam(AttributeParam::NOT_STORABLE, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clone with a length.
|
||||
*/
|
||||
public function withLength(int $length): self
|
||||
{
|
||||
return $this->withParam(AttributeParam::LEN, $length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clone with a default value.
|
||||
*/
|
||||
public function withDefault(mixed $value): self
|
||||
{
|
||||
return $this->withParam(AttributeParam::DEFAULT, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether a parameter is set.
|
||||
*/
|
||||
public function hasParam(string $name): bool
|
||||
{
|
||||
return array_key_exists($name, $this->params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a parameter value.
|
||||
*/
|
||||
public function getParam(string $name): mixed
|
||||
{
|
||||
return $this->params[$name] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clone with a parameter.
|
||||
*/
|
||||
public function withParam(string $name, mixed $value): self
|
||||
{
|
||||
$obj = clone $this;
|
||||
$obj->params[$name] = $value;
|
||||
|
||||
return $obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clone without a parameter.
|
||||
*/
|
||||
public function withoutParam(string $name): self
|
||||
{
|
||||
$obj = clone $this;
|
||||
unset($obj->params[$name]);
|
||||
|
||||
return $obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clone with parameters merged.
|
||||
*
|
||||
* @param array<string, mixed> $params
|
||||
*/
|
||||
public function withParamsMerged(array $params): self
|
||||
{
|
||||
$obj = clone $this;
|
||||
|
||||
/** @var array<string, mixed> $params */
|
||||
$params = Util::merge($this->params, $params);
|
||||
|
||||
$obj->params = $params;
|
||||
|
||||
return $obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* To an associative array.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function toAssoc(): array
|
||||
{
|
||||
return $this->params;
|
||||
}
|
||||
}
|
||||
155
application/Espo/Core/Utils/Database/Orm/Defs/EntityDefs.php
Normal file
155
application/Espo/Core/Utils/Database/Orm/Defs/EntityDefs.php
Normal file
@@ -0,0 +1,155 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2025 EspoCRM, Inc.
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\Utils\Database\Orm\Defs;
|
||||
|
||||
use Espo\ORM\Defs\Params\EntityParam;
|
||||
|
||||
/**
|
||||
* Immutable.
|
||||
*/
|
||||
class EntityDefs
|
||||
{
|
||||
/** @var array<string, AttributeDefs> */
|
||||
private array $attributes = [];
|
||||
/** @var array<string, RelationDefs> */
|
||||
private array $relations = [];
|
||||
/** @var array<string, IndexDefs> */
|
||||
private array $indexes = [];
|
||||
|
||||
private function __construct() {}
|
||||
|
||||
public static function create(): self
|
||||
{
|
||||
return new self();
|
||||
}
|
||||
|
||||
public function withAttribute(AttributeDefs $attributeDefs): self
|
||||
{
|
||||
$obj = clone $this;
|
||||
$obj->attributes[$attributeDefs->getName()] = $attributeDefs;
|
||||
|
||||
return $obj;
|
||||
}
|
||||
|
||||
public function withRelation(RelationDefs $relationDefs): self
|
||||
{
|
||||
$obj = clone $this;
|
||||
$obj->relations[$relationDefs->getName()] = $relationDefs;
|
||||
|
||||
return $obj;
|
||||
}
|
||||
|
||||
public function withIndex(IndexDefs $index): self
|
||||
{
|
||||
$obj = clone $this;
|
||||
$obj->indexes[$index->getName()] = $index;
|
||||
|
||||
return $obj;
|
||||
}
|
||||
|
||||
public function withoutAttribute(string $name): self
|
||||
{
|
||||
$obj = clone $this;
|
||||
unset($obj->attributes[$name]);
|
||||
|
||||
return $obj;
|
||||
}
|
||||
|
||||
public function withoutRelation(string $name): self
|
||||
{
|
||||
$obj = clone $this;
|
||||
unset($obj->relations[$name]);
|
||||
|
||||
return $obj;
|
||||
}
|
||||
|
||||
public function withoutIndex(string $name): self
|
||||
{
|
||||
$obj = clone $this;
|
||||
unset($obj->indexes[$name]);
|
||||
|
||||
return $obj;
|
||||
}
|
||||
|
||||
public function getAttribute(string $name): ?AttributeDefs
|
||||
{
|
||||
return $this->attributes[$name] ?? null;
|
||||
}
|
||||
|
||||
public function getRelation(string $name): ?RelationDefs
|
||||
{
|
||||
return $this->relations[$name] ?? null;
|
||||
}
|
||||
|
||||
public function getIndex(string $name): ?IndexDefs
|
||||
{
|
||||
return $this->indexes[$name] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, array<string, mixed>>
|
||||
*/
|
||||
public function toAssoc(): array
|
||||
{
|
||||
$data = [];
|
||||
|
||||
if (count($this->attributes)) {
|
||||
$attributesData = [];
|
||||
|
||||
foreach ($this->attributes as $name => $attributeDefs) {
|
||||
$attributesData[$name] = $attributeDefs->toAssoc();
|
||||
}
|
||||
|
||||
$data[EntityParam::ATTRIBUTES] = $attributesData;
|
||||
}
|
||||
|
||||
if (count($this->relations)) {
|
||||
$relationsData = [];
|
||||
|
||||
foreach ($this->relations as $name => $relationDefs) {
|
||||
$relationsData[$name] = $relationDefs->toAssoc();
|
||||
}
|
||||
|
||||
$data[EntityParam::RELATIONS] = $relationsData;
|
||||
}
|
||||
|
||||
if (count($this->indexes)) {
|
||||
$indexesData = [];
|
||||
|
||||
foreach ($this->indexes as $name => $indexDefs) {
|
||||
$indexesData[$name] = $indexDefs->toAssoc();
|
||||
}
|
||||
|
||||
$data[EntityParam::INDEXES] = $indexesData;
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
175
application/Espo/Core/Utils/Database/Orm/Defs/IndexDefs.php
Normal file
175
application/Espo/Core/Utils/Database/Orm/Defs/IndexDefs.php
Normal file
@@ -0,0 +1,175 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2025 EspoCRM, Inc.
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\Utils\Database\Orm\Defs;
|
||||
|
||||
use Espo\Core\Utils\Util;
|
||||
use Espo\ORM\Defs\Params\IndexParam;
|
||||
|
||||
/**
|
||||
* Immutable.
|
||||
*/
|
||||
class IndexDefs
|
||||
{
|
||||
/** @var array<string, mixed> */
|
||||
private array $params = [];
|
||||
|
||||
private function __construct(private string $name) {}
|
||||
|
||||
public static function create(string $name): self
|
||||
{
|
||||
return new self($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a relation name.
|
||||
*/
|
||||
public function getName(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether a parameter is set.
|
||||
*/
|
||||
public function hasParam(string $name): bool
|
||||
{
|
||||
return array_key_exists($name, $this->params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a parameter value.
|
||||
*/
|
||||
public function getParam(string $name): mixed
|
||||
{
|
||||
return $this->params[$name] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clone with a parameter.
|
||||
*/
|
||||
public function withParam(string $name, mixed $value): self
|
||||
{
|
||||
$obj = clone $this;
|
||||
$obj->params[$name] = $value;
|
||||
|
||||
return $obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clone without a parameter.
|
||||
*/
|
||||
public function withoutParam(string $name): self
|
||||
{
|
||||
$obj = clone $this;
|
||||
unset($obj->params[$name]);
|
||||
|
||||
return $obj;
|
||||
}
|
||||
|
||||
public function withUnique(): self
|
||||
{
|
||||
$obj = clone $this;
|
||||
$obj->params[IndexParam::TYPE] = 'unique';
|
||||
|
||||
return $obj;
|
||||
}
|
||||
|
||||
public function withoutUnique(): self
|
||||
{
|
||||
$obj = clone $this;
|
||||
unset($obj->params[IndexParam::TYPE]);
|
||||
|
||||
return $obj;
|
||||
}
|
||||
|
||||
public function withFlag(string $flag): self
|
||||
{
|
||||
$obj = clone $this;
|
||||
|
||||
$flags = $obj->params[IndexParam::FLAGS] ?? [];
|
||||
|
||||
if (!in_array($flag, $flags)) {
|
||||
$flags[] = $flag;
|
||||
}
|
||||
|
||||
$obj->params[IndexParam::FLAGS] = $flags;
|
||||
|
||||
return $obj;
|
||||
}
|
||||
|
||||
public function withoutFlag(string $flag): self
|
||||
{
|
||||
$obj = clone $this;
|
||||
|
||||
$flags = $obj->params[IndexParam::FLAGS] ?? [];
|
||||
|
||||
$index = array_search($flag, $flags, true);
|
||||
|
||||
if ($index !== -1) {
|
||||
unset($flags[$index]);
|
||||
$flags = array_values($flags);
|
||||
}
|
||||
|
||||
$obj->params[IndexParam::FLAGS] = $flags;
|
||||
|
||||
if ($flags === []) {
|
||||
unset($obj->params[IndexParam::FLAGS]);
|
||||
}
|
||||
|
||||
return $obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clone with parameters merged.
|
||||
*
|
||||
* @param array<string, mixed> $params
|
||||
*/
|
||||
public function withParamsMerged(array $params): self
|
||||
{
|
||||
$obj = clone $this;
|
||||
|
||||
/** @var array<string, mixed> $params */
|
||||
$params = Util::merge($this->params, $params);
|
||||
|
||||
$obj->params = $params;
|
||||
|
||||
return $obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* To an associative array.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function toAssoc(): array
|
||||
{
|
||||
return $this->params;
|
||||
}
|
||||
}
|
||||
256
application/Espo/Core/Utils/Database/Orm/Defs/RelationDefs.php
Normal file
256
application/Espo/Core/Utils/Database/Orm/Defs/RelationDefs.php
Normal file
@@ -0,0 +1,256 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2025 EspoCRM, Inc.
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\Utils\Database\Orm\Defs;
|
||||
|
||||
use Espo\Core\Utils\Util;
|
||||
use Espo\ORM\Defs\Params\RelationParam;
|
||||
use Espo\ORM\Type\RelationType;
|
||||
|
||||
class RelationDefs
|
||||
{
|
||||
/** @var array<string, mixed> */
|
||||
private array $params = [];
|
||||
|
||||
private function __construct(private string $name) {}
|
||||
|
||||
public static function create(string $name): self
|
||||
{
|
||||
return new self($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a relation name.
|
||||
*/
|
||||
public function getName(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a type.
|
||||
*
|
||||
* @return RelationType::*
|
||||
*/
|
||||
public function getType(): ?string
|
||||
{
|
||||
/** @var ?RelationType::* */
|
||||
return $this->getParam(RelationParam::TYPE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clone with a type.
|
||||
*
|
||||
* @param RelationType::* $type
|
||||
*/
|
||||
public function withType(string $type): self
|
||||
{
|
||||
return $this->withParam(RelationParam::TYPE, $type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clone with a foreign entity type.
|
||||
*/
|
||||
public function withForeignEntityType(string $entityType): self
|
||||
{
|
||||
return $this->withParam(RelationParam::ENTITY, $entityType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a foreign entity type.
|
||||
*/
|
||||
public function getForeignEntityType(): ?string
|
||||
{
|
||||
return $this->getParam(RelationParam::ENTITY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clone with a foreign relation name.
|
||||
*/
|
||||
public function withForeignRelationName(?string $name): self
|
||||
{
|
||||
return $this->withParam(RelationParam::FOREIGN, $name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a foreign relation name.
|
||||
*/
|
||||
public function getForeignRelationName(): ?string
|
||||
{
|
||||
return $this->getParam(RelationParam::FOREIGN);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clone with a relationship name.
|
||||
*/
|
||||
public function withRelationshipName(string $name): self
|
||||
{
|
||||
return $this->withParam(RelationParam::RELATION_NAME, $name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a foreign relation name.
|
||||
*/
|
||||
public function getRelationshipName(): ?string
|
||||
{
|
||||
return $this->getParam(RelationParam::RELATION_NAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clone with a key.
|
||||
*/
|
||||
public function withKey(string $key): self
|
||||
{
|
||||
return $this->withParam(RelationParam::KEY, $key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a key.
|
||||
*/
|
||||
public function getKey(): ?string
|
||||
{
|
||||
return $this->getParam(RelationParam::KEY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clone with a key.
|
||||
*/
|
||||
public function withForeignKey(string $foreignKey): self
|
||||
{
|
||||
return $this->withParam(RelationParam::FOREIGN_KEY, $foreignKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a key.
|
||||
*/
|
||||
public function getForeignKey(): ?string
|
||||
{
|
||||
return $this->getParam(RelationParam::FOREIGN_KEY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clone with middle keys.
|
||||
*/
|
||||
public function withMidKeys(string $midKey, string $foreignMidKey): self
|
||||
{
|
||||
return $this->withParam(RelationParam::MID_KEYS, [$midKey, $foreignMidKey]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether a parameter is set.
|
||||
*/
|
||||
public function hasParam(string $name): bool
|
||||
{
|
||||
return array_key_exists($name, $this->params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a parameter value.
|
||||
*/
|
||||
public function getParam(string $name): mixed
|
||||
{
|
||||
return $this->params[$name] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clone with a parameter.
|
||||
*/
|
||||
public function withParam(string $name, mixed $value): self
|
||||
{
|
||||
$obj = clone $this;
|
||||
$obj->params[$name] = $value;
|
||||
|
||||
return $obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clone without a parameter.
|
||||
*/
|
||||
public function withoutParam(string $name): self
|
||||
{
|
||||
$obj = clone $this;
|
||||
unset($obj->params[$name]);
|
||||
|
||||
return $obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clone with conditions. Conditions are used for relationships that share a same middle table.
|
||||
*
|
||||
* @param array<string, scalar|(array<int, mixed>)|null> $conditions
|
||||
*/
|
||||
public function withConditions(array $conditions): self
|
||||
{
|
||||
$obj = clone $this;
|
||||
|
||||
return $obj->withParam(RelationParam::CONDITIONS, $conditions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clone with an additional middle table column.
|
||||
*/
|
||||
public function withAdditionalColumn(AttributeDefs $attributeDefs): self
|
||||
{
|
||||
$obj = clone $this;
|
||||
|
||||
/** @var array<string, array<string, mixed>> $list */
|
||||
$list = $obj->getParam(RelationParam::ADDITIONAL_COLUMNS) ?? [];
|
||||
|
||||
$list[$attributeDefs->getName()] = $attributeDefs->toAssoc();
|
||||
|
||||
return $obj->withParam(RelationParam::ADDITIONAL_COLUMNS, $list);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clone with parameters merged.
|
||||
*
|
||||
* @param array<string, mixed> $params
|
||||
*/
|
||||
public function withParamsMerged(array $params): self
|
||||
{
|
||||
$obj = clone $this;
|
||||
|
||||
/** @var array<string, mixed> $params */
|
||||
$params = Util::merge($this->params, $params);
|
||||
|
||||
$obj->params = $params;
|
||||
|
||||
return $obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* To an associative array.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function toAssoc(): array
|
||||
{
|
||||
return $this->params;
|
||||
}
|
||||
}
|
||||
41
application/Espo/Core/Utils/Database/Orm/FieldConverter.php
Normal file
41
application/Espo/Core/Utils/Database/Orm/FieldConverter.php
Normal file
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2025 EspoCRM, Inc.
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\Utils\Database\Orm;
|
||||
|
||||
use Espo\ORM\Defs\FieldDefs;
|
||||
use Espo\Core\Utils\Database\Orm\Defs\EntityDefs;
|
||||
|
||||
/**
|
||||
* Converts field definitions to ORM definitions.
|
||||
*/
|
||||
interface FieldConverter
|
||||
{
|
||||
public function convert(FieldDefs $fieldDefs, string $entityType): EntityDefs;
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2025 EspoCRM, Inc.
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\Utils\Database\Orm\FieldConverters;
|
||||
|
||||
use Espo\Core\Name\Field;
|
||||
use Espo\Core\ORM\Defs\AttributeParam;
|
||||
use Espo\Core\Utils\Database\Orm\Defs\AttributeDefs;
|
||||
use Espo\Core\Utils\Database\Orm\Defs\EntityDefs;
|
||||
use Espo\Core\Utils\Database\Orm\FieldConverter;
|
||||
use Espo\ORM\Defs\FieldDefs;
|
||||
use Espo\ORM\Query\Part\Order;
|
||||
use Espo\ORM\Type\AttributeType;
|
||||
|
||||
class AttachmentMultiple implements FieldConverter
|
||||
{
|
||||
public function convert(FieldDefs $fieldDefs, string $entityType): EntityDefs
|
||||
{
|
||||
$name = $fieldDefs->getName();
|
||||
|
||||
return EntityDefs::create()
|
||||
->withAttribute(
|
||||
AttributeDefs::create($name . 'Ids')
|
||||
->withType(AttributeType::JSON_ARRAY)
|
||||
->withNotStorable()
|
||||
->withParamsMerged([
|
||||
'orderBy' => [
|
||||
[Field::CREATED_AT, Order::ASC],
|
||||
[Field::NAME, Order::ASC],
|
||||
],
|
||||
AttributeParam::IS_LINK_MULTIPLE_ID_LIST => true,
|
||||
'relation' => $name,
|
||||
])
|
||||
)
|
||||
->withAttribute(
|
||||
AttributeDefs::create($name . 'Names')
|
||||
->withType(AttributeType::JSON_OBJECT)
|
||||
->withNotStorable()
|
||||
->withParamsMerged([
|
||||
AttributeParam::IS_LINK_MULTIPLE_NAME_MAP => true,
|
||||
])
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,350 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2025 EspoCRM, Inc.
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\Utils\Database\Orm\FieldConverters;
|
||||
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Espo\Core\Currency\ConfigDataProvider;
|
||||
use Espo\Core\ORM\Type\FieldType;
|
||||
use Espo\Core\Utils\Config;
|
||||
use Espo\Core\Utils\Database\Orm\Defs\AttributeDefs;
|
||||
use Espo\Core\Utils\Database\Orm\Defs\EntityDefs;
|
||||
use Espo\Core\Utils\Database\Orm\FieldConverter;
|
||||
use Espo\ORM\Defs\FieldDefs;
|
||||
use Espo\ORM\Defs\Params\AttributeParam;
|
||||
use Espo\ORM\Defs\Params\FieldParam;
|
||||
use Espo\ORM\Query\Part\Expression as Expr;
|
||||
use Espo\ORM\Type\AttributeType;
|
||||
|
||||
class Currency implements FieldConverter
|
||||
{
|
||||
private const DEFAULT_PRECISION = 13;
|
||||
private const DEFAULT_SCALE = 4;
|
||||
|
||||
public function __construct(
|
||||
private Config $config,
|
||||
private ConfigDataProvider $configDataProvider
|
||||
) {}
|
||||
|
||||
public function convert(FieldDefs $fieldDefs, string $entityType): EntityDefs
|
||||
{
|
||||
$name = $fieldDefs->getName();
|
||||
|
||||
$amountDefs = AttributeDefs::create($name)
|
||||
->withType(AttributeType::FLOAT)
|
||||
->withParamsMerged([
|
||||
'attributeRole' => 'value',
|
||||
'fieldType' => FieldType::CURRENCY,
|
||||
]);
|
||||
|
||||
$currencyDefs = AttributeDefs::create($name . 'Currency')
|
||||
->withType(AttributeType::VARCHAR)
|
||||
->withParamsMerged([
|
||||
'attributeRole' => 'currency',
|
||||
'fieldType' => FieldType::CURRENCY,
|
||||
]);
|
||||
|
||||
$convertedDefs = null;
|
||||
|
||||
if ($fieldDefs->getParam(FieldParam::DECIMAL)) {
|
||||
$dbType = $fieldDefs->getParam(FieldParam::DB_TYPE) ?? Types::DECIMAL;
|
||||
$precision = $fieldDefs->getParam(FieldParam::PRECISION) ?? self::DEFAULT_PRECISION;
|
||||
$scale = $fieldDefs->getParam(FieldParam::SCALE) ?? self::DEFAULT_SCALE;
|
||||
|
||||
$amountDefs = $amountDefs
|
||||
->withType(AttributeType::VARCHAR)
|
||||
->withDbType($dbType)
|
||||
->withParam(AttributeParam::PRECISION, $precision)
|
||||
->withParam(AttributeParam::SCALE, $scale);
|
||||
|
||||
$defaultValue = $fieldDefs->getParam(AttributeParam::DEFAULT);
|
||||
|
||||
if (is_int($defaultValue) || is_float($defaultValue)) {
|
||||
$defaultValue = number_format($defaultValue, $scale, '.', '');
|
||||
|
||||
$amountDefs = $amountDefs->withParam(AttributeParam::DEFAULT, $defaultValue);
|
||||
}
|
||||
}
|
||||
|
||||
if ($fieldDefs->isNotStorable()) {
|
||||
$amountDefs = $amountDefs->withNotStorable();
|
||||
$currencyDefs = $currencyDefs->withNotStorable();
|
||||
}
|
||||
|
||||
if (!$fieldDefs->isNotStorable()) {
|
||||
[$amountDefs, $convertedDefs] = $this->config->get('currencyNoJoinMode') ?
|
||||
$this->applyNoJoinMode($fieldDefs, $amountDefs) :
|
||||
$this->applyJoinMode($fieldDefs, $amountDefs, $entityType);
|
||||
}
|
||||
|
||||
$entityDefs = EntityDefs::create()
|
||||
->withAttribute($amountDefs)
|
||||
->withAttribute($currencyDefs);
|
||||
|
||||
if ($convertedDefs) {
|
||||
$entityDefs = $entityDefs->withAttribute($convertedDefs);
|
||||
}
|
||||
|
||||
return $entityDefs;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{AttributeDefs, AttributeDefs}
|
||||
*/
|
||||
private function applyNoJoinMode(FieldDefs $fieldDefs, AttributeDefs $amountDefs): array
|
||||
{
|
||||
$name = $fieldDefs->getName();
|
||||
|
||||
$currencyAttribute = $name . 'Currency';
|
||||
|
||||
$defaultCurrency = $this->configDataProvider->getDefaultCurrency();
|
||||
$baseCurrency = $this->configDataProvider->getBaseCurrency();
|
||||
$rates = $this->configDataProvider->getCurrencyRates()->toAssoc();
|
||||
|
||||
if ($defaultCurrency !== $baseCurrency) {
|
||||
$rates = $this->exchangeRates($baseCurrency, $defaultCurrency, $rates);
|
||||
}
|
||||
|
||||
$expr = Expr::multiply(
|
||||
Expr::column($name),
|
||||
Expr::if(
|
||||
Expr::equal(Expr::column($currencyAttribute), $defaultCurrency),
|
||||
1.0,
|
||||
$this->buildExpression($currencyAttribute, $rates)
|
||||
)
|
||||
)->getValue();
|
||||
|
||||
$exprForeign = Expr::multiply(
|
||||
Expr::column("ALIAS.{$name}"),
|
||||
Expr::if(
|
||||
Expr::equal(Expr::column("ALIAS.{$name}Currency"), $defaultCurrency),
|
||||
1.0,
|
||||
$this->buildExpression("ALIAS.{$name}Currency", $rates)
|
||||
)
|
||||
)->getValue();
|
||||
|
||||
$exprForeign = str_replace('ALIAS', '{alias}', $exprForeign);
|
||||
|
||||
$convertedDefs = AttributeDefs::create($name . 'Converted')
|
||||
->withType(AttributeType::FLOAT)
|
||||
->withParamsMerged([
|
||||
'select' => [
|
||||
'select' => $expr,
|
||||
],
|
||||
'selectForeign' => [
|
||||
'select' => $exprForeign,
|
||||
],
|
||||
'where' => [
|
||||
"=" => [
|
||||
'whereClause' => [
|
||||
$expr . '=' => '{value}',
|
||||
],
|
||||
],
|
||||
">" => [
|
||||
'whereClause' => [
|
||||
$expr . '>' => '{value}',
|
||||
],
|
||||
],
|
||||
"<" => [
|
||||
'whereClause' => [
|
||||
$expr . '<' => '{value}',
|
||||
],
|
||||
],
|
||||
">=" => [
|
||||
'whereClause' => [
|
||||
$expr . '>=' => '{value}',
|
||||
],
|
||||
],
|
||||
"<=" => [
|
||||
'whereClause' => [
|
||||
$expr . '<=' => '{value}',
|
||||
],
|
||||
],
|
||||
"<>" => [
|
||||
'whereClause' => [
|
||||
$expr . '!=' => '{value}',
|
||||
],
|
||||
],
|
||||
"IS NULL" => [
|
||||
'whereClause' => [
|
||||
$expr . '=' => null,
|
||||
],
|
||||
],
|
||||
"IS NOT NULL" => [
|
||||
'whereClause' => [
|
||||
$expr . '!=' => null,
|
||||
],
|
||||
],
|
||||
],
|
||||
AttributeParam::NOT_STORABLE => true,
|
||||
'order' => [
|
||||
'order' => [
|
||||
[$expr, '{direction}'],
|
||||
],
|
||||
],
|
||||
'attributeRole' => 'valueConverted',
|
||||
'fieldType' => FieldType::CURRENCY,
|
||||
]);
|
||||
|
||||
return [$amountDefs, $convertedDefs];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, float> $currencyRates
|
||||
* @return array<string, float>
|
||||
*/
|
||||
private function exchangeRates(string $baseCurrency, string $defaultCurrency, array $currencyRates): array
|
||||
{
|
||||
$precision = 5;
|
||||
$defaultCurrencyRate = round(1 / $currencyRates[$defaultCurrency], $precision);
|
||||
|
||||
$exchangedRates = [];
|
||||
$exchangedRates[$baseCurrency] = $defaultCurrencyRate;
|
||||
|
||||
unset($currencyRates[$baseCurrency], $currencyRates[$defaultCurrency]);
|
||||
|
||||
foreach ($currencyRates as $currencyName => $rate) {
|
||||
$exchangedRates[$currencyName] = round($rate * $defaultCurrencyRate, $precision);
|
||||
}
|
||||
|
||||
return $exchangedRates;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, float> $rates
|
||||
*/
|
||||
private function buildExpression(string $currencyAttribute, array $rates): Expr|float
|
||||
{
|
||||
if ($rates === []) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
$currency = array_key_first($rates);
|
||||
$value = $rates[$currency];
|
||||
unset($rates[$currency]);
|
||||
|
||||
return Expr::if(
|
||||
Expr::equal(Expr::column($currencyAttribute), $currency),
|
||||
$value,
|
||||
$this->buildExpression($currencyAttribute, $rates)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{AttributeDefs, AttributeDefs}
|
||||
*/
|
||||
private function applyJoinMode(FieldDefs $fieldDefs, AttributeDefs $amountDefs, string $entityType): array
|
||||
{
|
||||
$name = $fieldDefs->getName();
|
||||
|
||||
$alias = $name . 'CurrencyRate';
|
||||
$leftJoins = [
|
||||
[
|
||||
'Currency',
|
||||
$alias,
|
||||
[$alias . '.id:' => $name . 'Currency'],
|
||||
]
|
||||
];
|
||||
$foreignCurrencyAlias = "{$alias}{$entityType}{alias}Foreign";
|
||||
$mulExpression = "MUL:({$name}, {$alias}.rate)";
|
||||
|
||||
$amountDefs = $amountDefs->withParamsMerged([
|
||||
'order' => [
|
||||
'order' => [
|
||||
[$mulExpression, '{direction}'],
|
||||
],
|
||||
'leftJoins' => $leftJoins,
|
||||
'additionalSelect' => ["{$alias}.rate"],
|
||||
]
|
||||
]);
|
||||
|
||||
$convertedDefs = AttributeDefs::create($name . 'Converted')
|
||||
->withType(AttributeType::FLOAT)
|
||||
->withParamsMerged([
|
||||
'select' => [
|
||||
'select' => $mulExpression,
|
||||
'leftJoins' => $leftJoins,
|
||||
],
|
||||
'selectForeign' => [
|
||||
'select' => "MUL:({alias}.{$name}, {$foreignCurrencyAlias}.rate)",
|
||||
'leftJoins' => [
|
||||
[
|
||||
'Currency',
|
||||
$foreignCurrencyAlias,
|
||||
[$foreignCurrencyAlias . '.id:' => "{alias}.{$name}Currency"]
|
||||
]
|
||||
],
|
||||
],
|
||||
'where' => [
|
||||
"=" => [
|
||||
'whereClause' => [$mulExpression . '=' => '{value}'],
|
||||
'leftJoins' => $leftJoins,
|
||||
],
|
||||
">" => [
|
||||
'whereClause' => [$mulExpression . '>' => '{value}'],
|
||||
'leftJoins' => $leftJoins,
|
||||
],
|
||||
"<" => [
|
||||
'whereClause' => [$mulExpression . '<' => '{value}'],
|
||||
'leftJoins' => $leftJoins,
|
||||
],
|
||||
">=" => [
|
||||
'whereClause' => [$mulExpression . '>=' => '{value}'],
|
||||
'leftJoins' => $leftJoins,
|
||||
],
|
||||
"<=" => [
|
||||
'whereClause' => [$mulExpression . '<=' => '{value}'],
|
||||
'leftJoins' => $leftJoins,
|
||||
],
|
||||
"<>" => [
|
||||
'whereClause' => [$mulExpression . '!=' => '{value}'],
|
||||
'leftJoins' => $leftJoins,
|
||||
],
|
||||
"IS NULL" => [
|
||||
'whereClause' => [$name . '=' => null],
|
||||
],
|
||||
"IS NOT NULL" => [
|
||||
'whereClause' => [$name . '!=' => null],
|
||||
],
|
||||
],
|
||||
AttributeParam::NOT_STORABLE => true,
|
||||
'order' => [
|
||||
'order' => [
|
||||
[$mulExpression, '{direction}'],
|
||||
],
|
||||
'leftJoins' => $leftJoins,
|
||||
'additionalSelect' => ["{$alias}.rate"],
|
||||
],
|
||||
'attributeRole' => 'valueConverted',
|
||||
'fieldType' => FieldType::CURRENCY,
|
||||
]);
|
||||
|
||||
return [$amountDefs, $convertedDefs];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,423 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2025 EspoCRM, Inc.
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\Utils\Database\Orm\FieldConverters;
|
||||
|
||||
use Espo\Core\ORM\Defs\AttributeParam;
|
||||
use Espo\Core\ORM\Type\FieldType;
|
||||
use Espo\Core\Utils\Database\Orm\Defs\AttributeDefs;
|
||||
use Espo\Core\Utils\Database\Orm\Defs\EntityDefs;
|
||||
use Espo\Core\Utils\Database\Orm\Defs\RelationDefs;
|
||||
use Espo\Core\Utils\Database\Orm\FieldConverter;
|
||||
use Espo\Entities\EmailAddress;
|
||||
use Espo\ORM\Defs\FieldDefs;
|
||||
use Espo\ORM\Name\Attribute;
|
||||
use Espo\ORM\Type\AttributeType;
|
||||
use Espo\ORM\Type\RelationType;
|
||||
|
||||
class Email implements FieldConverter
|
||||
{
|
||||
private const COLUMN_ENTITY_TYPE_LENGTH = 100;
|
||||
|
||||
public function convert(FieldDefs $fieldDefs, string $entityType): EntityDefs
|
||||
{
|
||||
$name = $fieldDefs->getName();
|
||||
|
||||
$foreignJoinAlias = "$name$entityType{alias}Foreign";
|
||||
$foreignJoinMiddleAlias = "$name$entityType{alias}ForeignMiddle";
|
||||
|
||||
$emailAddressDefs = AttributeDefs
|
||||
::create($name)
|
||||
->withType(AttributeType::VARCHAR)
|
||||
->withParamsMerged(
|
||||
$this->getEmailAddressParams($entityType, $foreignJoinAlias, $foreignJoinMiddleAlias)
|
||||
);
|
||||
|
||||
$dataDefs = AttributeDefs
|
||||
::create($name . 'Data')
|
||||
->withType(AttributeType::JSON_ARRAY)
|
||||
->withNotStorable()
|
||||
->withParamsMerged([
|
||||
AttributeParam::NOT_EXPORTABLE => true,
|
||||
'isEmailAddressData' => true,
|
||||
'field' => $name,
|
||||
]);
|
||||
|
||||
$isOptedOutDefs = AttributeDefs
|
||||
::create($name . 'IsOptedOut')
|
||||
->withType(AttributeType::BOOL)
|
||||
->withNotStorable()
|
||||
->withParamsMerged(
|
||||
$this->getIsOptedOutParams($foreignJoinAlias, $foreignJoinMiddleAlias)
|
||||
);
|
||||
|
||||
$isInvalidDefs = AttributeDefs
|
||||
::create($name . 'IsInvalid')
|
||||
->withType(AttributeType::BOOL)
|
||||
->withNotStorable()
|
||||
->withParamsMerged(
|
||||
$this->getIsInvalidParams($foreignJoinAlias, $foreignJoinMiddleAlias)
|
||||
);
|
||||
|
||||
$relationDefs = RelationDefs
|
||||
::create('emailAddresses')
|
||||
->withType(RelationType::MANY_MANY)
|
||||
->withForeignEntityType(EmailAddress::ENTITY_TYPE)
|
||||
->withRelationshipName('entityEmailAddress')
|
||||
->withMidKeys('entityId', 'emailAddressId')
|
||||
->withConditions(['entityType' => $entityType])
|
||||
->withAdditionalColumn(
|
||||
AttributeDefs
|
||||
::create('entityType')
|
||||
->withType(AttributeType::VARCHAR)
|
||||
->withLength(self::COLUMN_ENTITY_TYPE_LENGTH)
|
||||
)
|
||||
->withAdditionalColumn(
|
||||
AttributeDefs
|
||||
::create('primary')
|
||||
->withType(AttributeType::BOOL)
|
||||
->withDefault(false)
|
||||
);
|
||||
|
||||
return EntityDefs::create()
|
||||
->withAttribute($emailAddressDefs)
|
||||
->withAttribute($dataDefs)
|
||||
->withAttribute($isOptedOutDefs)
|
||||
->withAttribute($isInvalidDefs)
|
||||
->withRelation($relationDefs);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
private function getEmailAddressParams(
|
||||
string $entityType,
|
||||
string $foreignJoinAlias,
|
||||
string $foreignJoinMiddleAlias,
|
||||
): array {
|
||||
|
||||
return [
|
||||
'select' => [
|
||||
"select" => "emailAddresses.name",
|
||||
'leftJoins' => [['emailAddresses', 'emailAddresses', ['primary' => true]]],
|
||||
],
|
||||
'selectForeign' => [
|
||||
"select" => "$foreignJoinAlias.name",
|
||||
'leftJoins' => [
|
||||
[
|
||||
'EntityEmailAddress',
|
||||
$foreignJoinMiddleAlias,
|
||||
[
|
||||
"$foreignJoinMiddleAlias.entityId:" => "{alias}.id",
|
||||
"$foreignJoinMiddleAlias.primary" => true,
|
||||
"$foreignJoinMiddleAlias.deleted" => false,
|
||||
]
|
||||
],
|
||||
[
|
||||
EmailAddress::ENTITY_TYPE,
|
||||
$foreignJoinAlias,
|
||||
[
|
||||
"$foreignJoinAlias.id:" => "$foreignJoinMiddleAlias.emailAddressId",
|
||||
"$foreignJoinAlias.deleted" => false,
|
||||
]
|
||||
]
|
||||
],
|
||||
],
|
||||
'fieldType' => FieldType::EMAIL,
|
||||
'where' => [
|
||||
'LIKE' => [
|
||||
'whereClause' => [
|
||||
'id=s' => [
|
||||
'from' => 'EntityEmailAddress',
|
||||
'select' => ['entityId'],
|
||||
'joins' => [
|
||||
[
|
||||
'emailAddress',
|
||||
'emailAddress',
|
||||
[
|
||||
'emailAddress.id:' => 'emailAddressId',
|
||||
'emailAddress.deleted' => false,
|
||||
],
|
||||
]
|
||||
],
|
||||
'whereClause' => [
|
||||
Attribute::DELETED => false,
|
||||
'entityType' => $entityType,
|
||||
"LIKE:(emailAddress.lower, LOWER:({value})):" => null,
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
'NOT LIKE' => [
|
||||
'whereClause' => [
|
||||
'id!=s' => [
|
||||
'from' => 'EntityEmailAddress',
|
||||
'select' => ['entityId'],
|
||||
'joins' => [
|
||||
[
|
||||
'emailAddress',
|
||||
'emailAddress',
|
||||
[
|
||||
'emailAddress.id:' => 'emailAddressId',
|
||||
'emailAddress.deleted' => false,
|
||||
],
|
||||
]
|
||||
],
|
||||
'whereClause' => [
|
||||
Attribute::DELETED => false,
|
||||
'entityType' => $entityType,
|
||||
"LIKE:(emailAddress.lower, LOWER:({value})):" => null,
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
'=' => [
|
||||
'leftJoins' => [['emailAddresses', 'emailAddressesMultiple']],
|
||||
'whereClause' => [
|
||||
"EQUAL:(emailAddressesMultiple.lower, LOWER:({value})):" => null,
|
||||
]
|
||||
],
|
||||
'<>' => [
|
||||
'whereClause' => [
|
||||
'id!=s' => [
|
||||
'from' => 'EntityEmailAddress',
|
||||
'select' => ['entityId'],
|
||||
'joins' => [
|
||||
[
|
||||
'emailAddress',
|
||||
'emailAddress',
|
||||
[
|
||||
'emailAddress.id:' => 'emailAddressId',
|
||||
'emailAddress.deleted' => false,
|
||||
],
|
||||
]
|
||||
],
|
||||
'whereClause' => [
|
||||
Attribute::DELETED => false,
|
||||
'entityType' => $entityType,
|
||||
"EQUAL:(emailAddress.lower, LOWER:({value})):" => null,
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
'IN' => [
|
||||
'whereClause' => [
|
||||
'id=s' => [
|
||||
'from' => 'EntityEmailAddress',
|
||||
'select' => ['entityId'],
|
||||
'joins' => [
|
||||
[
|
||||
'emailAddress',
|
||||
'emailAddress',
|
||||
[
|
||||
'emailAddress.id:' => 'emailAddressId',
|
||||
'emailAddress.deleted' => false,
|
||||
],
|
||||
]
|
||||
],
|
||||
'whereClause' => [
|
||||
Attribute::DELETED => false,
|
||||
'entityType' => $entityType,
|
||||
"emailAddress.lower" => '{value}',
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
'NOT IN' => [
|
||||
'whereClause' => [
|
||||
'id!=s' => [
|
||||
'from' => 'EntityEmailAddress',
|
||||
'select' => ['entityId'],
|
||||
'joins' => [
|
||||
[
|
||||
'emailAddress',
|
||||
'emailAddress',
|
||||
[
|
||||
'emailAddress.id:' => 'emailAddressId',
|
||||
'emailAddress.deleted' => false,
|
||||
],
|
||||
]
|
||||
],
|
||||
'whereClause' => [
|
||||
Attribute::DELETED => false,
|
||||
'entityType' => $entityType,
|
||||
"emailAddress.lower" => '{value}',
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
'IS NULL' => [
|
||||
'leftJoins' => [['emailAddresses', 'emailAddressesMultiple']],
|
||||
'whereClause' => [
|
||||
'emailAddressesMultiple.lower=' => null,
|
||||
]
|
||||
],
|
||||
'IS NOT NULL' => [
|
||||
'whereClause' => [
|
||||
'id=s' => [
|
||||
'from' => 'EntityEmailAddress',
|
||||
'select' => ['entityId'],
|
||||
'whereClause' => [
|
||||
Attribute::DELETED => false,
|
||||
'entityType' => $entityType,
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
'order' => [
|
||||
'order' => [
|
||||
['emailAddresses.lower', '{direction}'],
|
||||
],
|
||||
'leftJoins' => [['emailAddresses', 'emailAddresses', ['primary' => true]]],
|
||||
'additionalSelect' => ['emailAddresses.lower'],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
private function getIsOptedOutParams(string $foreignJoinAlias, string $foreignJoinMiddleAlias): array
|
||||
{
|
||||
return [
|
||||
'select' => [
|
||||
'select' => "emailAddresses.optOut",
|
||||
'leftJoins' => [['emailAddresses', 'emailAddresses', ['primary' => true]]],
|
||||
],
|
||||
'selectForeign' => [
|
||||
'select' => "$foreignJoinAlias.optOut",
|
||||
'leftJoins' => [
|
||||
[
|
||||
'EntityEmailAddress',
|
||||
$foreignJoinMiddleAlias,
|
||||
[
|
||||
"$foreignJoinMiddleAlias.entityId:" => "{alias}.id",
|
||||
"$foreignJoinMiddleAlias.primary" => true,
|
||||
"$foreignJoinMiddleAlias.deleted" => false,
|
||||
]
|
||||
],
|
||||
[
|
||||
EmailAddress::ENTITY_TYPE,
|
||||
$foreignJoinAlias,
|
||||
[
|
||||
"$foreignJoinAlias.id:" => "$foreignJoinMiddleAlias.emailAddressId",
|
||||
"$foreignJoinAlias.deleted" => false,
|
||||
]
|
||||
]
|
||||
],
|
||||
],
|
||||
'where' => [
|
||||
'= TRUE' => [
|
||||
'whereClause' => [
|
||||
['emailAddresses.optOut=' => true],
|
||||
['emailAddresses.optOut!=' => null],
|
||||
],
|
||||
'leftJoins' => [['emailAddresses', 'emailAddresses', ['primary' => true]]],
|
||||
],
|
||||
'= FALSE' => [
|
||||
'whereClause' => [
|
||||
'OR' => [
|
||||
['emailAddresses.optOut=' => false],
|
||||
['emailAddresses.optOut=' => null],
|
||||
]
|
||||
],
|
||||
'leftJoins' => [['emailAddresses', 'emailAddresses', ['primary' => true]]],
|
||||
]
|
||||
],
|
||||
'order' => [
|
||||
'order' => [
|
||||
['emailAddresses.optOut', '{direction}'],
|
||||
],
|
||||
'leftJoins' => [['emailAddresses', 'emailAddresses', ['primary' => true]]],
|
||||
'additionalSelect' => ['emailAddresses.optOut'],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
private function getIsInvalidParams(string $foreignJoinAlias, string $foreignJoinMiddleAlias): array
|
||||
{
|
||||
return [
|
||||
'select' => [
|
||||
'select' => "emailAddresses.invalid",
|
||||
'leftJoins' => [['emailAddresses', 'emailAddresses', ['primary' => true]]],
|
||||
],
|
||||
'selectForeign' => [
|
||||
'select' => "$foreignJoinAlias.invalid",
|
||||
'leftJoins' => [
|
||||
[
|
||||
'EntityEmailAddress',
|
||||
$foreignJoinMiddleAlias,
|
||||
[
|
||||
"$foreignJoinMiddleAlias.entityId:" => "{alias}.id",
|
||||
"$foreignJoinMiddleAlias.primary" => true,
|
||||
"$foreignJoinMiddleAlias.deleted" => false,
|
||||
]
|
||||
],
|
||||
[
|
||||
EmailAddress::ENTITY_TYPE,
|
||||
$foreignJoinAlias,
|
||||
[
|
||||
"$foreignJoinAlias.id:" => "$foreignJoinMiddleAlias.emailAddressId",
|
||||
"$foreignJoinAlias.deleted" => false,
|
||||
]
|
||||
]
|
||||
],
|
||||
],
|
||||
'where' => [
|
||||
'= TRUE' => [
|
||||
'whereClause' => [
|
||||
['emailAddresses.invalid=' => true],
|
||||
['emailAddresses.invalid!=' => null],
|
||||
],
|
||||
'leftJoins' => [['emailAddresses', 'emailAddresses', ['primary' => true]]],
|
||||
],
|
||||
'= FALSE' => [
|
||||
'whereClause' => [
|
||||
'OR' => [
|
||||
['emailAddresses.invalid=' => false],
|
||||
['emailAddresses.invalid=' => null],
|
||||
]
|
||||
],
|
||||
'leftJoins' => [['emailAddresses', 'emailAddresses', ['primary' => true]]],
|
||||
]
|
||||
],
|
||||
'order' => [
|
||||
'order' => [
|
||||
['emailAddresses.invalid', '{direction}'],
|
||||
],
|
||||
'leftJoins' => [['emailAddresses', 'emailAddresses', ['primary' => true]]],
|
||||
'additionalSelect' => ['emailAddresses.invalid'],
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2025 EspoCRM, Inc.
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\Utils\Database\Orm\FieldConverters;
|
||||
|
||||
use Espo\Core\Name\Field;
|
||||
use Espo\Core\Utils\Database\Orm\Defs\AttributeDefs;
|
||||
use Espo\Core\Utils\Database\Orm\Defs\EntityDefs;
|
||||
use Espo\Core\Utils\Database\Orm\Defs\RelationDefs;
|
||||
use Espo\Core\Utils\Database\Orm\FieldConverter;
|
||||
use Espo\Entities\Attachment;
|
||||
use Espo\ORM\Defs\FieldDefs;
|
||||
use Espo\ORM\Defs\Params\AttributeParam;
|
||||
use Espo\ORM\Defs\Params\RelationParam;
|
||||
use Espo\ORM\Name\Attribute;
|
||||
use Espo\ORM\Type\AttributeType;
|
||||
use Espo\ORM\Type\RelationType;
|
||||
|
||||
class File implements FieldConverter
|
||||
{
|
||||
public function convert(FieldDefs $fieldDefs, string $entityType): EntityDefs
|
||||
{
|
||||
$name = $fieldDefs->getName();
|
||||
|
||||
$idName = $name . 'Id';
|
||||
$nameName = $name . 'Name';
|
||||
|
||||
$idDefs = AttributeDefs::create($idName)
|
||||
->withType(AttributeType::FOREIGN_ID)
|
||||
->withParam('index', false);
|
||||
|
||||
$nameDefs = AttributeDefs::create($nameName)
|
||||
->withType(AttributeType::FOREIGN);
|
||||
|
||||
if ($fieldDefs->isNotStorable()) {
|
||||
$idDefs = $idDefs->withNotStorable();
|
||||
|
||||
$nameDefs = $nameDefs->withType(AttributeType::VARCHAR);
|
||||
}
|
||||
|
||||
/** @var array<string, mixed> $defaults */
|
||||
$defaults = $fieldDefs->getParam('defaultAttributes') ?? [];
|
||||
|
||||
if (array_key_exists($idName, $defaults)) {
|
||||
$idDefs = $idDefs->withDefault($defaults[$idName]);
|
||||
}
|
||||
|
||||
$relationDefs = null;
|
||||
|
||||
if (!$fieldDefs->isNotStorable()) {
|
||||
$nameDefs = $nameDefs->withParamsMerged([
|
||||
AttributeParam::RELATION => $name,
|
||||
AttributeParam::FOREIGN => Field::NAME,
|
||||
]);
|
||||
|
||||
$relationDefs = RelationDefs::create($name)
|
||||
->withType(RelationType::BELONGS_TO)
|
||||
->withForeignEntityType(Attachment::ENTITY_TYPE)
|
||||
->withKey($idName)
|
||||
->withForeignKey(Attribute::ID)
|
||||
->withParam(RelationParam::FOREIGN, null);
|
||||
}
|
||||
|
||||
$entityDefs = EntityDefs::create()
|
||||
->withAttribute($idDefs)
|
||||
->withAttribute($nameDefs);
|
||||
|
||||
if ($relationDefs) {
|
||||
$entityDefs = $entityDefs->withRelation($relationDefs);
|
||||
}
|
||||
|
||||
return $entityDefs;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2025 EspoCRM, Inc.
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\Utils\Database\Orm\FieldConverters;
|
||||
|
||||
use Espo\Core\ORM\Type\FieldType;
|
||||
use Espo\Core\Utils\Database\Orm\Defs\AttributeDefs;
|
||||
use Espo\Core\Utils\Database\Orm\Defs\EntityDefs;
|
||||
use Espo\Core\Utils\Database\Orm\FieldConverter;
|
||||
use Espo\ORM\Defs\FieldDefs;
|
||||
use Espo\ORM\Type\AttributeType;
|
||||
|
||||
class Link implements FieldConverter
|
||||
{
|
||||
public function convert(FieldDefs $fieldDefs, string $entityType): EntityDefs
|
||||
{
|
||||
$name = $fieldDefs->getName();
|
||||
|
||||
$idName = $name . 'Id';
|
||||
$nameName = $name . 'Name';
|
||||
|
||||
$idDefs = AttributeDefs::create($idName)
|
||||
->withType(AttributeType::FOREIGN_ID)
|
||||
->withParamsMerged([
|
||||
'index' => $name,
|
||||
'attributeRole' => 'id',
|
||||
'fieldType' => FieldType::LINK,
|
||||
]);
|
||||
|
||||
$nameDefs = AttributeDefs::create($nameName)
|
||||
->withType(AttributeType::VARCHAR)
|
||||
->withNotStorable()
|
||||
->withParamsMerged([
|
||||
'attributeRole' => 'name',
|
||||
'fieldType' => FieldType::LINK,
|
||||
]);
|
||||
|
||||
if ($fieldDefs->isNotStorable()) {
|
||||
$idDefs = $idDefs->withNotStorable();
|
||||
}
|
||||
|
||||
/** @var array<string, mixed> $defaults */
|
||||
$defaults = $fieldDefs->getParam('defaultAttributes') ?? [];
|
||||
|
||||
if (array_key_exists($idName, $defaults)) {
|
||||
$idDefs = $idDefs->withDefault($defaults[$idName]);
|
||||
}
|
||||
|
||||
return EntityDefs::create()
|
||||
->withAttribute($idDefs)
|
||||
->withAttribute($nameDefs);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2025 EspoCRM, Inc.
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\Utils\Database\Orm\FieldConverters;
|
||||
|
||||
use Espo\Core\ORM\Defs\AttributeParam;
|
||||
use Espo\Core\ORM\Type\FieldType;
|
||||
use Espo\Core\Utils\Database\Orm\Defs\AttributeDefs;
|
||||
use Espo\Core\Utils\Database\Orm\Defs\EntityDefs;
|
||||
use Espo\Core\Utils\Database\Orm\FieldConverter;
|
||||
use Espo\ORM\Defs\FieldDefs;
|
||||
use Espo\ORM\Type\AttributeType;
|
||||
|
||||
class LinkMultiple implements FieldConverter
|
||||
{
|
||||
public function convert(FieldDefs $fieldDefs, string $entityType): EntityDefs
|
||||
{
|
||||
$name = $fieldDefs->getName();
|
||||
|
||||
$idsName = $name . 'Ids';
|
||||
$namesName = $name . 'Names';
|
||||
$columnsName = $name . 'Columns';
|
||||
|
||||
$idsDefs = AttributeDefs::create($idsName)
|
||||
->withType(AttributeType::JSON_ARRAY)
|
||||
->withNotStorable()
|
||||
->withParamsMerged([
|
||||
AttributeParam::IS_LINK_MULTIPLE_ID_LIST => true,
|
||||
'relation' => $name,
|
||||
'isUnordered' => true,
|
||||
'attributeRole' => 'idList',
|
||||
'fieldType' => FieldType::LINK_MULTIPLE,
|
||||
]);
|
||||
|
||||
/** @var array<string, mixed> $defaults */
|
||||
$defaults = $fieldDefs->getParam('defaultAttributes') ?? [];
|
||||
|
||||
if (array_key_exists($idsName, $defaults)) {
|
||||
$idsDefs = $idsDefs->withDefault($defaults[$idsName]);
|
||||
}
|
||||
|
||||
$namesDefs = AttributeDefs::create($namesName)
|
||||
->withType(AttributeType::JSON_OBJECT)
|
||||
->withNotStorable()
|
||||
->withParamsMerged([
|
||||
AttributeParam::IS_LINK_MULTIPLE_NAME_MAP => true,
|
||||
'attributeRole' => 'nameMap',
|
||||
'fieldType' => FieldType::LINK_MULTIPLE,
|
||||
]);
|
||||
|
||||
$orderBy = $fieldDefs->getParam('orderBy');
|
||||
$orderDirection = $fieldDefs->getParam('orderDirection');
|
||||
|
||||
if ($orderBy) {
|
||||
$idsDefs = $idsDefs->withParam('orderBy', $orderBy);
|
||||
|
||||
if ($orderDirection !== null) {
|
||||
$idsDefs = $idsDefs->withParam('orderDirection', $orderDirection);
|
||||
}
|
||||
}
|
||||
|
||||
$columns = $fieldDefs->getParam('columns');
|
||||
|
||||
$columnsDefs = $columns ?
|
||||
AttributeDefs::create($columnsName)
|
||||
->withType(AttributeType::JSON_OBJECT)
|
||||
->withNotStorable()
|
||||
->withParamsMerged([
|
||||
'columns' => $columns,
|
||||
'attributeRole' => 'columnsMap',
|
||||
])
|
||||
: null;
|
||||
|
||||
$entityDefs = EntityDefs::create()
|
||||
->withAttribute($idsDefs)
|
||||
->withAttribute($namesDefs);
|
||||
|
||||
if ($columnsDefs) {
|
||||
$entityDefs = $entityDefs->withAttribute($columnsDefs);
|
||||
}
|
||||
|
||||
return $entityDefs;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2025 EspoCRM, Inc.
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\Utils\Database\Orm\FieldConverters;
|
||||
|
||||
use Espo\Core\ORM\Type\FieldType;
|
||||
use Espo\Core\Utils\Database\Orm\Defs\AttributeDefs;
|
||||
use Espo\Core\Utils\Database\Orm\Defs\EntityDefs;
|
||||
use Espo\Core\Utils\Database\Orm\FieldConverter;
|
||||
use Espo\ORM\Defs\FieldDefs;
|
||||
use Espo\ORM\Type\AttributeType;
|
||||
|
||||
class LinkOne implements FieldConverter
|
||||
{
|
||||
public function convert(FieldDefs $fieldDefs, string $entityType): EntityDefs
|
||||
{
|
||||
$name = $fieldDefs->getName();
|
||||
|
||||
return EntityDefs::create()
|
||||
->withAttribute(
|
||||
AttributeDefs::create($name . 'Id')
|
||||
->withType(AttributeType::VARCHAR)
|
||||
->withNotStorable()
|
||||
->withParamsMerged([
|
||||
'attributeRole' => 'id',
|
||||
'fieldType' => FieldType::LINK_ONE,
|
||||
])
|
||||
)
|
||||
->withAttribute(
|
||||
AttributeDefs::create($name . 'Name')
|
||||
->withType(AttributeType::VARCHAR)
|
||||
->withNotStorable()
|
||||
->withParamsMerged([
|
||||
'attributeRole' => 'name',
|
||||
'fieldType' => FieldType::LINK_ONE,
|
||||
])
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2025 EspoCRM, Inc.
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\Utils\Database\Orm\FieldConverters;
|
||||
|
||||
use Espo\Core\ORM\Type\FieldType;
|
||||
use Espo\Core\Utils\Database\Orm\Defs\AttributeDefs;
|
||||
use Espo\Core\Utils\Database\Orm\Defs\EntityDefs;
|
||||
use Espo\Core\Utils\Database\Orm\FieldConverter;
|
||||
use Espo\ORM\Defs\FieldDefs;
|
||||
use Espo\ORM\Defs\Params\AttributeParam;
|
||||
use Espo\ORM\Type\AttributeType;
|
||||
|
||||
class LinkParent implements FieldConverter
|
||||
{
|
||||
private const TYPE_LENGTH = 100;
|
||||
|
||||
public function convert(FieldDefs $fieldDefs, string $entityType): EntityDefs
|
||||
{
|
||||
$name = $fieldDefs->getName();
|
||||
|
||||
$idName = $name . 'Id';
|
||||
$typeName = $name . 'Type';
|
||||
$nameName = $name . 'Name';
|
||||
|
||||
$idDefs = AttributeDefs::create($idName)
|
||||
->withType(AttributeType::FOREIGN_ID)
|
||||
->withParamsMerged([
|
||||
'index' => $name,
|
||||
'attributeRole' => 'id',
|
||||
'fieldType' => FieldType::LINK_PARENT,
|
||||
]);
|
||||
|
||||
$typeDefs = AttributeDefs::create($typeName)
|
||||
->withType(AttributeType::FOREIGN_TYPE)
|
||||
->withParam(AttributeParam::NOT_NULL, false)
|
||||
->withParam('index', $name)
|
||||
->withLength(self::TYPE_LENGTH)
|
||||
->withParamsMerged([
|
||||
'attributeRole' => 'type',
|
||||
'fieldType' => FieldType::LINK_PARENT,
|
||||
]);
|
||||
|
||||
$nameDefs = AttributeDefs::create($nameName)
|
||||
->withType(AttributeType::VARCHAR)
|
||||
->withNotStorable()
|
||||
->withParamsMerged([
|
||||
AttributeParam::RELATION => $name,
|
||||
'isParentName' => true,
|
||||
'attributeRole' => 'name',
|
||||
'fieldType' => FieldType::LINK_PARENT,
|
||||
]);
|
||||
|
||||
if ($fieldDefs->isNotStorable()) {
|
||||
$idDefs = $idDefs->withNotStorable();
|
||||
$typeDefs = $typeDefs->withNotStorable();
|
||||
}
|
||||
|
||||
/** @var array<string, mixed> $defaults */
|
||||
$defaults = $fieldDefs->getParam('defaultAttributes') ?? [];
|
||||
|
||||
if (array_key_exists($idName, $defaults)) {
|
||||
$idDefs = $idDefs->withDefault($defaults[$idName]);
|
||||
}
|
||||
|
||||
if (array_key_exists($typeName, $defaults)) {
|
||||
$typeDefs = $idDefs->withDefault($defaults[$typeName]);
|
||||
}
|
||||
|
||||
return EntityDefs::create()
|
||||
->withAttribute($idDefs)
|
||||
->withAttribute($typeDefs)
|
||||
->withAttribute($nameDefs);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,187 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2025 EspoCRM, Inc.
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\Utils\Database\Orm\FieldConverters;
|
||||
|
||||
use Espo\Core\Utils\Config;
|
||||
use Espo\Core\Utils\Database\Orm\Defs\AttributeDefs;
|
||||
use Espo\Core\Utils\Database\Orm\Defs\EntityDefs;
|
||||
use Espo\Core\Utils\Database\Orm\FieldConverter;
|
||||
use Espo\ORM\Defs\FieldDefs;
|
||||
use Espo\ORM\Defs\Params\AttributeParam;
|
||||
use Espo\ORM\Defs\Params\FieldParam;
|
||||
use Espo\ORM\Type\AttributeType;
|
||||
|
||||
/**
|
||||
* @noinspection PhpUnused
|
||||
*/
|
||||
class PersonName implements FieldConverter
|
||||
{
|
||||
private const FORMAT_LAST_FIRST = 'lastFirst';
|
||||
private const FORMAT_LAST_FIRST_MIDDLE = 'lastFirstMiddle';
|
||||
private const FORMAT_FIRST_MIDDLE_LAST = 'firstMiddleLast';
|
||||
|
||||
public function __construct(private Config $config) {}
|
||||
|
||||
public function convert(FieldDefs $fieldDefs, string $entityType): EntityDefs
|
||||
{
|
||||
$format = $this->config->get('personNameFormat');
|
||||
|
||||
$name = $fieldDefs->getName();
|
||||
$firstName = 'first' . ucfirst($name);
|
||||
$lastName = 'last' . ucfirst($name);
|
||||
$middleName = 'middle' . ucfirst($name);
|
||||
|
||||
$subList = match ($format) {
|
||||
self::FORMAT_LAST_FIRST => [$lastName, ' ', $firstName],
|
||||
self::FORMAT_LAST_FIRST_MIDDLE => [$lastName, ' ', $firstName, ' ', $middleName],
|
||||
self::FORMAT_FIRST_MIDDLE_LAST => [$firstName, ' ', $middleName, ' ', $lastName],
|
||||
default => [$firstName, ' ', $lastName],
|
||||
};
|
||||
|
||||
if (
|
||||
$format === self::FORMAT_LAST_FIRST_MIDDLE ||
|
||||
$format === self::FORMAT_LAST_FIRST
|
||||
) {
|
||||
$orderBy1Field = $lastName;
|
||||
$orderBy2Field = $firstName;
|
||||
} else {
|
||||
$orderBy1Field = $firstName;
|
||||
$orderBy2Field = $lastName;
|
||||
}
|
||||
|
||||
$fullList = [];
|
||||
$whereItems = [];
|
||||
|
||||
foreach ($subList as $subFieldName) {
|
||||
$fieldNameTrimmed = trim($subFieldName);
|
||||
|
||||
if (empty($fieldNameTrimmed)) {
|
||||
$fullList[] = "'" . $subFieldName . "'";
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$fullList[] = $fieldNameTrimmed;
|
||||
$whereItems[] = $fieldNameTrimmed;
|
||||
}
|
||||
|
||||
$whereItems[] = "CONCAT:($firstName, ' ', $lastName)";
|
||||
$whereItems[] = "CONCAT:($lastName, ' ', $firstName)";
|
||||
|
||||
if ($format === self::FORMAT_FIRST_MIDDLE_LAST) {
|
||||
$whereItems[] = "CONCAT:($firstName, ' ', $middleName, ' ', $lastName)";
|
||||
} else if ($format === self::FORMAT_LAST_FIRST_MIDDLE) {
|
||||
$whereItems[] = "CONCAT:($lastName, ' ', $firstName, ' ', $middleName)";
|
||||
}
|
||||
|
||||
$selectExpression = $this->getSelect($fullList);
|
||||
$selectForeignExpression = $this->getSelect($fullList, '{alias}');
|
||||
|
||||
if (
|
||||
$format === self::FORMAT_FIRST_MIDDLE_LAST ||
|
||||
$format === self::FORMAT_LAST_FIRST_MIDDLE
|
||||
) {
|
||||
$selectExpression = "REPLACE:($selectExpression, ' ', ' ')";
|
||||
$selectForeignExpression = "REPLACE:($selectForeignExpression, ' ', ' ')";
|
||||
}
|
||||
|
||||
$attributeDefs = AttributeDefs::create($name)
|
||||
->withType(AttributeType::VARCHAR)
|
||||
->withNotStorable()
|
||||
->withParamsMerged([
|
||||
'select' => [
|
||||
'select' => $selectExpression,
|
||||
],
|
||||
'selectForeign' => [
|
||||
'select' => $selectForeignExpression,
|
||||
],
|
||||
'where' => [
|
||||
'LIKE' => [
|
||||
'whereClause' => [
|
||||
'OR' => array_fill_keys(
|
||||
array_map(fn ($item) => $item . '*', $whereItems),
|
||||
'{value}'
|
||||
),
|
||||
],
|
||||
],
|
||||
'NOT LIKE' => [
|
||||
'whereClause' => [
|
||||
'AND' => array_fill_keys(
|
||||
array_map(fn ($item) => $item . '!*', $whereItems),
|
||||
'{value}'
|
||||
),
|
||||
],
|
||||
],
|
||||
'=' => [
|
||||
'whereClause' => [
|
||||
'OR' => array_fill_keys($whereItems, '{value}'),
|
||||
],
|
||||
],
|
||||
],
|
||||
'order' => [
|
||||
'order' => [
|
||||
[$orderBy1Field, '{direction}'],
|
||||
[$orderBy2Field, '{direction}'],
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
$dependeeAttributeList = $fieldDefs->getParam(FieldParam::DEPENDEE_ATTRIBUTE_LIST);
|
||||
|
||||
if ($dependeeAttributeList) {
|
||||
$attributeDefs = $attributeDefs->withParam(AttributeParam::DEPENDEE_ATTRIBUTE_LIST, $dependeeAttributeList);
|
||||
}
|
||||
|
||||
return EntityDefs::create()
|
||||
->withAttribute($attributeDefs);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $fullList
|
||||
*/
|
||||
private function getSelect(array $fullList, ?string $alias = null): string
|
||||
{
|
||||
foreach ($fullList as &$item) {
|
||||
$rowItem = trim($item, " '");
|
||||
|
||||
if (empty($rowItem)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($alias) {
|
||||
$item = $alias . '.' . $item;
|
||||
}
|
||||
|
||||
$item = "IFNULL:($item, '')";
|
||||
}
|
||||
|
||||
return "NULLIF:(TRIM:(CONCAT:(" . implode(", ", $fullList) . ")), '')";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,603 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2025 EspoCRM, Inc.
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\Utils\Database\Orm\FieldConverters;
|
||||
|
||||
use Espo\Core\ORM\Defs\AttributeParam;
|
||||
use Espo\Core\ORM\Type\FieldType;
|
||||
use Espo\Core\Utils\Database\Orm\Defs\AttributeDefs;
|
||||
use Espo\Core\Utils\Database\Orm\Defs\EntityDefs;
|
||||
use Espo\Core\Utils\Database\Orm\Defs\RelationDefs;
|
||||
use Espo\Core\Utils\Database\Orm\FieldConverter;
|
||||
use Espo\Entities\PhoneNumber;
|
||||
use Espo\ORM\Defs\FieldDefs;
|
||||
use Espo\ORM\Name\Attribute;
|
||||
use Espo\ORM\Type\AttributeType;
|
||||
use Espo\ORM\Type\RelationType;
|
||||
|
||||
/**
|
||||
* @noinspection PhpUnused
|
||||
*/
|
||||
class Phone implements FieldConverter
|
||||
{
|
||||
private const COLUMN_ENTITY_TYPE_LENGTH = 100;
|
||||
|
||||
public function convert(FieldDefs $fieldDefs, string $entityType): EntityDefs
|
||||
{
|
||||
$name = $fieldDefs->getName();
|
||||
|
||||
$foreignJoinAlias = "$name$entityType{alias}Foreign";
|
||||
$foreignJoinMiddleAlias = "$name$entityType{alias}ForeignMiddle";
|
||||
|
||||
$emailAddressDefs = AttributeDefs
|
||||
::create($name)
|
||||
->withType(AttributeType::VARCHAR)
|
||||
->withParamsMerged(
|
||||
$this->getPhoneNumberParams($entityType, $foreignJoinAlias, $foreignJoinMiddleAlias)
|
||||
);
|
||||
|
||||
$dataDefs = AttributeDefs
|
||||
::create($name . 'Data')
|
||||
->withType(AttributeType::JSON_ARRAY)
|
||||
->withNotStorable()
|
||||
->withParamsMerged([
|
||||
AttributeParam::NOT_EXPORTABLE => true,
|
||||
'isPhoneNumberData' => true,
|
||||
'field' => $name,
|
||||
]);
|
||||
|
||||
$isOptedOutDefs = AttributeDefs
|
||||
::create($name . 'IsOptedOut')
|
||||
->withType(AttributeType::BOOL)
|
||||
->withNotStorable()
|
||||
->withParamsMerged(
|
||||
$this->getIsOptedOutParams($foreignJoinAlias, $foreignJoinMiddleAlias)
|
||||
);
|
||||
|
||||
$isInvalidDefs = AttributeDefs
|
||||
::create($name . 'IsInvalid')
|
||||
->withType(AttributeType::BOOL)
|
||||
->withNotStorable()
|
||||
->withParamsMerged(
|
||||
$this->getIsInvalidParams($foreignJoinAlias, $foreignJoinMiddleAlias)
|
||||
);
|
||||
|
||||
$numericAttribute = AttributeDefs
|
||||
::create($name . 'Numeric')
|
||||
->withType(AttributeType::VARCHAR)
|
||||
->withNotStorable()
|
||||
->withParamsMerged(
|
||||
$this->getNumericParams($entityType)
|
||||
);
|
||||
|
||||
$relationDefs = RelationDefs
|
||||
::create('phoneNumbers')
|
||||
->withType(RelationType::MANY_MANY)
|
||||
->withForeignEntityType(PhoneNumber::ENTITY_TYPE)
|
||||
->withRelationshipName('entityPhoneNumber')
|
||||
->withMidKeys('entityId', 'phoneNumberId')
|
||||
->withConditions(['entityType' => $entityType])
|
||||
->withAdditionalColumn(
|
||||
AttributeDefs
|
||||
::create('entityType')
|
||||
->withType(AttributeType::VARCHAR)
|
||||
->withLength(self::COLUMN_ENTITY_TYPE_LENGTH)
|
||||
)
|
||||
->withAdditionalColumn(
|
||||
AttributeDefs
|
||||
::create('primary')
|
||||
->withType(AttributeType::BOOL)
|
||||
->withDefault(false)
|
||||
);
|
||||
|
||||
return EntityDefs::create()
|
||||
->withAttribute($emailAddressDefs)
|
||||
->withAttribute($dataDefs)
|
||||
->withAttribute($isOptedOutDefs)
|
||||
->withAttribute($isInvalidDefs)
|
||||
->withAttribute($numericAttribute)
|
||||
->withRelation($relationDefs);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
private function getPhoneNumberParams(
|
||||
string $entityType,
|
||||
string $foreignJoinAlias,
|
||||
string $foreignJoinMiddleAlias,
|
||||
): array {
|
||||
|
||||
return [
|
||||
'select' => [
|
||||
"select" => "phoneNumbers.name",
|
||||
'leftJoins' => [['phoneNumbers', 'phoneNumbers', ['primary' => true]]],
|
||||
],
|
||||
'selectForeign' => [
|
||||
"select" => "$foreignJoinAlias.name",
|
||||
'leftJoins' => [
|
||||
[
|
||||
'EntityPhoneNumber',
|
||||
$foreignJoinMiddleAlias,
|
||||
[
|
||||
"$foreignJoinMiddleAlias.entityId:" => "{alias}.id",
|
||||
"$foreignJoinMiddleAlias.primary" => true,
|
||||
"$foreignJoinMiddleAlias.deleted" => false,
|
||||
]
|
||||
],
|
||||
[
|
||||
PhoneNumber::ENTITY_TYPE,
|
||||
$foreignJoinAlias,
|
||||
[
|
||||
"$foreignJoinAlias.id:" => "$foreignJoinMiddleAlias.phoneNumberId",
|
||||
"$foreignJoinAlias.deleted" => false,
|
||||
]
|
||||
]
|
||||
],
|
||||
],
|
||||
'fieldType' => FieldType::PHONE,
|
||||
'where' => [
|
||||
'LIKE' => [
|
||||
'whereClause' => [
|
||||
'id=s' => [
|
||||
'from' => 'EntityPhoneNumber',
|
||||
'select' => ['entityId'],
|
||||
'joins' => [
|
||||
[
|
||||
'phoneNumber',
|
||||
'phoneNumber',
|
||||
[
|
||||
'phoneNumber.id:' => 'phoneNumberId',
|
||||
'phoneNumber.deleted' => false,
|
||||
],
|
||||
],
|
||||
],
|
||||
'whereClause' => [
|
||||
Attribute::DELETED => false,
|
||||
'entityType' => $entityType,
|
||||
'phoneNumber.name*' => '{value}',
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
'NOT LIKE' => [
|
||||
'whereClause' => [
|
||||
'id!=s' => [
|
||||
'from' => 'EntityPhoneNumber',
|
||||
'select' => ['entityId'],
|
||||
'joins' => [
|
||||
[
|
||||
'phoneNumber',
|
||||
'phoneNumber',
|
||||
[
|
||||
'phoneNumber.id:' => 'phoneNumberId',
|
||||
'phoneNumber.deleted' => false,
|
||||
],
|
||||
],
|
||||
],
|
||||
'whereClause' => [
|
||||
Attribute::DELETED => false,
|
||||
'entityType' => $entityType,
|
||||
'phoneNumber.name*' => '{value}',
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
'=' => [
|
||||
'leftJoins' => [['phoneNumbers', 'phoneNumbersMultiple']],
|
||||
'whereClause' => [
|
||||
'phoneNumbersMultiple.name=' => '{value}',
|
||||
]
|
||||
],
|
||||
'<>' => [
|
||||
'whereClause' => [
|
||||
'id!=s' => [
|
||||
'from' => 'EntityPhoneNumber',
|
||||
'select' => ['entityId'],
|
||||
'joins' => [
|
||||
[
|
||||
'phoneNumber',
|
||||
'phoneNumber',
|
||||
[
|
||||
'phoneNumber.id:' => 'phoneNumberId',
|
||||
'phoneNumber.deleted' => false,
|
||||
],
|
||||
],
|
||||
],
|
||||
'whereClause' => [
|
||||
Attribute::DELETED => false,
|
||||
'entityType' => $entityType,
|
||||
'phoneNumber.name' => '{value}',
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
'IN' => [
|
||||
'whereClause' => [
|
||||
'id=s' => [
|
||||
'from' => 'EntityPhoneNumber',
|
||||
'select' => ['entityId'],
|
||||
'joins' => [
|
||||
[
|
||||
'phoneNumber',
|
||||
'phoneNumber',
|
||||
[
|
||||
'phoneNumber.id:' => 'phoneNumberId',
|
||||
'phoneNumber.deleted' => false,
|
||||
],
|
||||
],
|
||||
],
|
||||
'whereClause' => [
|
||||
Attribute::DELETED => false,
|
||||
'entityType' => $entityType,
|
||||
'phoneNumber.name' => '{value}',
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
'NOT IN' => [
|
||||
'whereClause' => [
|
||||
'id=s' => [
|
||||
'from' => 'EntityPhoneNumber',
|
||||
'select' => ['entityId'],
|
||||
'joins' => [
|
||||
[
|
||||
'phoneNumber',
|
||||
'phoneNumber',
|
||||
[
|
||||
'phoneNumber.id:' => 'phoneNumberId',
|
||||
'phoneNumber.deleted' => false,
|
||||
],
|
||||
],
|
||||
],
|
||||
'whereClause' => [
|
||||
Attribute::DELETED => false,
|
||||
'entityType' => $entityType,
|
||||
'phoneNumber.name!=' => '{value}',
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
'IS NULL' => [
|
||||
'leftJoins' => [['phoneNumbers', 'phoneNumbersMultiple']],
|
||||
'whereClause' => [
|
||||
'phoneNumbersMultiple.name=' => null,
|
||||
]
|
||||
],
|
||||
'IS NOT NULL' => [
|
||||
'whereClause' => [
|
||||
'id=s' => [
|
||||
'from' => 'EntityPhoneNumber',
|
||||
'select' => ['entityId'],
|
||||
'whereClause' => [
|
||||
Attribute::DELETED => false,
|
||||
'entityType' => $entityType,
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
'order' => [
|
||||
'order' => [
|
||||
['phoneNumbers.name', '{direction}'],
|
||||
],
|
||||
'leftJoins' => [['phoneNumbers', 'phoneNumbers', ['primary' => true]]],
|
||||
'additionalSelect' => ['phoneNumbers.name'],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
private function getIsOptedOutParams(string $foreignJoinAlias, string $foreignJoinMiddleAlias): array
|
||||
{
|
||||
return [
|
||||
'select' => [
|
||||
'select' => 'phoneNumbers.optOut',
|
||||
'leftJoins' => [['phoneNumbers', 'phoneNumbers', ['primary' => true]]],
|
||||
],
|
||||
'selectForeign' => [
|
||||
'select' => "$foreignJoinAlias.optOut",
|
||||
'leftJoins' => [
|
||||
[
|
||||
'EntityPhoneNumber',
|
||||
$foreignJoinMiddleAlias,
|
||||
[
|
||||
"$foreignJoinMiddleAlias.entityId:" => "{alias}.id",
|
||||
"$foreignJoinMiddleAlias.primary" => true,
|
||||
"$foreignJoinMiddleAlias.deleted" => false,
|
||||
]
|
||||
],
|
||||
[
|
||||
PhoneNumber::ENTITY_TYPE,
|
||||
$foreignJoinAlias,
|
||||
[
|
||||
"$foreignJoinAlias.id:" => "$foreignJoinMiddleAlias.phoneNumberId",
|
||||
"$foreignJoinAlias.deleted" => false,
|
||||
]
|
||||
]
|
||||
],
|
||||
],
|
||||
'where' => [
|
||||
'= TRUE' => [
|
||||
'whereClause' => [
|
||||
['phoneNumbers.optOut=' => true],
|
||||
['phoneNumbers.optOut!=' => null],
|
||||
],
|
||||
'leftJoins' => [['phoneNumbers', 'phoneNumbers', ['primary' => true]]],
|
||||
],
|
||||
'= FALSE' => [
|
||||
'whereClause' => [
|
||||
'OR' => [
|
||||
['phoneNumbers.optOut=' => false],
|
||||
['phoneNumbers.optOut=' => null],
|
||||
]
|
||||
],
|
||||
'leftJoins' => [['phoneNumbers', 'phoneNumbers', ['primary' => true]]],
|
||||
]
|
||||
],
|
||||
'order' => [
|
||||
'order' => [
|
||||
['phoneNumbers.optOut', '{direction}'],
|
||||
],
|
||||
'leftJoins' => [['phoneNumbers', 'phoneNumbers', ['primary' => true]]],
|
||||
'additionalSelect' => ['phoneNumbers.optOut'],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
private function getIsInvalidParams(string $foreignJoinAlias, string $foreignJoinMiddleAlias): array
|
||||
{
|
||||
return [
|
||||
'select' => [
|
||||
'select' => 'phoneNumbers.invalid',
|
||||
'leftJoins' => [['phoneNumbers', 'phoneNumbers', ['primary' => true]]],
|
||||
],
|
||||
'selectForeign' => [
|
||||
'select' => "$foreignJoinAlias.invalid",
|
||||
'leftJoins' => [
|
||||
[
|
||||
'EntityPhoneNumber',
|
||||
$foreignJoinMiddleAlias,
|
||||
[
|
||||
"$foreignJoinMiddleAlias.entityId:" => "{alias}.id",
|
||||
"$foreignJoinMiddleAlias.primary" => true,
|
||||
"$foreignJoinMiddleAlias.deleted" => false,
|
||||
]
|
||||
],
|
||||
[
|
||||
PhoneNumber::ENTITY_TYPE,
|
||||
$foreignJoinAlias,
|
||||
[
|
||||
"$foreignJoinAlias.id:" => "$foreignJoinMiddleAlias.phoneNumberId",
|
||||
"$foreignJoinAlias.deleted" => false,
|
||||
]
|
||||
]
|
||||
],
|
||||
],
|
||||
'where' => [
|
||||
'= TRUE' => [
|
||||
'whereClause' => [
|
||||
['phoneNumbers.invalid=' => true],
|
||||
['phoneNumbers.invalid!=' => null],
|
||||
],
|
||||
'leftJoins' => [['phoneNumbers', 'phoneNumbers', ['primary' => true]]],
|
||||
],
|
||||
'= FALSE' => [
|
||||
'whereClause' => [
|
||||
'OR' => [
|
||||
['phoneNumbers.invalid=' => false],
|
||||
['phoneNumbers.invalid=' => null],
|
||||
]
|
||||
],
|
||||
'leftJoins' => [['phoneNumbers', 'phoneNumbers', ['primary' => true]]],
|
||||
]
|
||||
],
|
||||
'order' => [
|
||||
'order' => [
|
||||
['phoneNumbers.invalid', '{direction}'],
|
||||
],
|
||||
'leftJoins' => [['phoneNumbers', 'phoneNumbers', ['primary' => true]]],
|
||||
'additionalSelect' => ['phoneNumbers.invalid'],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
private function getNumericParams(string $entityType): array
|
||||
{
|
||||
return [
|
||||
AttributeParam::NOT_EXPORTABLE => true,
|
||||
'where' => [
|
||||
'LIKE' => [
|
||||
'whereClause' => [
|
||||
'id=s' => [
|
||||
'from' => 'EntityPhoneNumber',
|
||||
'select' => ['entityId'],
|
||||
'joins' => [
|
||||
[
|
||||
'phoneNumber',
|
||||
'phoneNumber',
|
||||
[
|
||||
'phoneNumber.id:' => 'phoneNumberId',
|
||||
'phoneNumber.deleted' => false,
|
||||
],
|
||||
],
|
||||
],
|
||||
'whereClause' => [
|
||||
Attribute::DELETED => false,
|
||||
'entityType' => $entityType,
|
||||
'phoneNumber.numeric*' => '{value}',
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
'NOT LIKE' => [
|
||||
'whereClause' => [
|
||||
'id!=s' => [
|
||||
'from' => 'EntityPhoneNumber',
|
||||
'select' => ['entityId'],
|
||||
'joins' => [
|
||||
[
|
||||
'phoneNumber',
|
||||
'phoneNumber',
|
||||
[
|
||||
'phoneNumber.id:' => 'phoneNumberId',
|
||||
'phoneNumber.deleted' => false,
|
||||
],
|
||||
]
|
||||
],
|
||||
'whereClause' => [
|
||||
Attribute::DELETED => false,
|
||||
'entityType' => $entityType,
|
||||
'phoneNumber.numeric*' => '{value}',
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
'=' => [
|
||||
'whereClause' => [
|
||||
'id=s' => [
|
||||
'from' => 'EntityPhoneNumber',
|
||||
'select' => ['entityId'],
|
||||
'joins' => [
|
||||
[
|
||||
'phoneNumber',
|
||||
'phoneNumber',
|
||||
[
|
||||
'phoneNumber.id:' => 'phoneNumberId',
|
||||
'phoneNumber.deleted' => false,
|
||||
],
|
||||
],
|
||||
],
|
||||
'whereClause' => [
|
||||
Attribute::DELETED => false,
|
||||
'entityType' => $entityType,
|
||||
'phoneNumber.numeric' => '{value}',
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
'<>' => [
|
||||
'whereClause' => [
|
||||
'id!=s' => [
|
||||
'from' => 'EntityPhoneNumber',
|
||||
'select' => ['entityId'],
|
||||
'joins' => [
|
||||
[
|
||||
'phoneNumber',
|
||||
'phoneNumber',
|
||||
[
|
||||
'phoneNumber.id:' => 'phoneNumberId',
|
||||
'phoneNumber.deleted' => false,
|
||||
],
|
||||
],
|
||||
],
|
||||
'whereClause' => [
|
||||
Attribute::DELETED => false,
|
||||
'entityType' => $entityType,
|
||||
'phoneNumber.numeric' => '{value}',
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
'IN' => [
|
||||
'whereClause' => [
|
||||
'id=s' => [
|
||||
'from' => 'EntityPhoneNumber',
|
||||
'select' => ['entityId'],
|
||||
'joins' => [
|
||||
[
|
||||
'phoneNumber',
|
||||
'phoneNumber',
|
||||
[
|
||||
'phoneNumber.id:' => 'phoneNumberId',
|
||||
'phoneNumber.deleted' => false,
|
||||
],
|
||||
],
|
||||
],
|
||||
'whereClause' => [
|
||||
Attribute::DELETED => false,
|
||||
'entityType' => $entityType,
|
||||
'phoneNumber.numeric' => '{value}',
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
'NOT IN' => [
|
||||
'whereClause' => [
|
||||
'id!=s' => [
|
||||
'from' => 'EntityPhoneNumber',
|
||||
'select' => ['entityId'],
|
||||
'joins' => [
|
||||
[
|
||||
'phoneNumber',
|
||||
'phoneNumber',
|
||||
[
|
||||
'phoneNumber.id:' => 'phoneNumberId',
|
||||
'phoneNumber.deleted' => false,
|
||||
],
|
||||
],
|
||||
],
|
||||
'whereClause' => [
|
||||
Attribute::DELETED => false,
|
||||
'entityType' => $entityType,
|
||||
'phoneNumber.numeric' => '{value}',
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
'IS NULL' => [
|
||||
'leftJoins' => [['phoneNumbers', 'phoneNumbersMultiple']],
|
||||
'whereClause' => [
|
||||
'phoneNumbersMultiple.numeric=' => null,
|
||||
]
|
||||
],
|
||||
'IS NOT NULL' => [
|
||||
'whereClause' => [
|
||||
'id=s' => [
|
||||
'from' => 'EntityPhoneNumber',
|
||||
'select' => ['entityId'],
|
||||
'whereClause' => [
|
||||
Attribute::DELETED => false,
|
||||
'entityType' => $entityType,
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
40
application/Espo/Core/Utils/Database/Orm/IndexHelper.php
Normal file
40
application/Espo/Core/Utils/Database/Orm/IndexHelper.php
Normal file
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2025 EspoCRM, Inc.
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\Utils\Database\Orm;
|
||||
|
||||
use Espo\ORM\Defs\IndexDefs;
|
||||
|
||||
interface IndexHelper
|
||||
{
|
||||
/**
|
||||
* Compose an index DB name. Depending on database, the name can be unique, limited by a max length.
|
||||
*/
|
||||
public function composeKey(IndexDefs $defs, string $entityType): string;
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2025 EspoCRM, Inc.
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\Utils\Database\Orm;
|
||||
|
||||
use Espo\Core\InjectableFactory;
|
||||
use Espo\Core\Utils\Metadata;
|
||||
use RuntimeException;
|
||||
|
||||
class IndexHelperFactory
|
||||
{
|
||||
public function __construct(
|
||||
private Metadata $metadata,
|
||||
private InjectableFactory $injectableFactory
|
||||
) {}
|
||||
|
||||
public function create(string $platform): IndexHelper
|
||||
{
|
||||
/** @var ?class-string<IndexHelper> $className */
|
||||
$className = $this->metadata
|
||||
->get(['app', 'databasePlatforms', $platform, 'indexHelperClassName']);
|
||||
|
||||
if (!$className) {
|
||||
throw new RuntimeException("No Index Helper for {$platform}");
|
||||
}
|
||||
|
||||
return $this->injectableFactory->create($className);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2025 EspoCRM, Inc.
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\Utils\Database\Orm\IndexHelpers;
|
||||
|
||||
use Espo\Core\Utils\Database\Orm\IndexHelper;
|
||||
use Espo\Core\Utils\Util;
|
||||
use Espo\ORM\Defs\IndexDefs;
|
||||
|
||||
class MysqlIndexHelper implements IndexHelper
|
||||
{
|
||||
private const MAX_LENGTH = 60;
|
||||
|
||||
public function composeKey(IndexDefs $defs, string $entityType): string
|
||||
{
|
||||
$name = $defs->getName();
|
||||
$prefix = $defs->isUnique() ? 'UNIQ' : 'IDX';
|
||||
|
||||
$parts = [$prefix, strtoupper(Util::toUnderScore($name))];
|
||||
|
||||
$key = implode('_', $parts);
|
||||
|
||||
return substr($key, 0, self::MAX_LENGTH);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2025 EspoCRM, Inc.
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\Utils\Database\Orm\IndexHelpers;
|
||||
|
||||
use Espo\Core\Utils\Database\Orm\IndexHelper;
|
||||
use Espo\Core\Utils\Util;
|
||||
use Espo\ORM\Defs\IndexDefs;
|
||||
|
||||
class PostgresqlIndexHelper implements IndexHelper
|
||||
{
|
||||
private const MAX_LENGTH = 59;
|
||||
|
||||
public function composeKey(IndexDefs $defs, string $entityType): string
|
||||
{
|
||||
$name = $defs->getName();
|
||||
$prefix = $defs->isUnique() ? 'UNIQ' : 'IDX';
|
||||
|
||||
$parts = [
|
||||
$prefix,
|
||||
strtoupper(Util::toUnderScore($entityType)),
|
||||
strtoupper(Util::toUnderScore($name)),
|
||||
];
|
||||
|
||||
$key = implode('_', $parts);
|
||||
|
||||
return self::decreaseLength($key);
|
||||
}
|
||||
|
||||
private static function decreaseLength(string $key): string
|
||||
{
|
||||
if (strlen($key) <= self::MAX_LENGTH) {
|
||||
return $key;
|
||||
}
|
||||
|
||||
$list = explode('_', $key);
|
||||
|
||||
$maxItemLength = 0;
|
||||
foreach ($list as $item) {
|
||||
if (strlen($item) > $maxItemLength) {
|
||||
$maxItemLength = strlen($item);
|
||||
}
|
||||
}
|
||||
$maxItemLength--;
|
||||
|
||||
$list = array_map(
|
||||
fn ($item) => substr($item, 0, min($maxItemLength, strlen($item))),
|
||||
$list
|
||||
);
|
||||
|
||||
$key = implode('_', $list);
|
||||
|
||||
return self::decreaseLength($key);
|
||||
}
|
||||
}
|
||||
41
application/Espo/Core/Utils/Database/Orm/LinkConverter.php
Normal file
41
application/Espo/Core/Utils/Database/Orm/LinkConverter.php
Normal file
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2025 EspoCRM, Inc.
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\Utils\Database\Orm;
|
||||
|
||||
use Espo\ORM\Defs\RelationDefs;
|
||||
use Espo\Core\Utils\Database\Orm\Defs\EntityDefs;
|
||||
|
||||
/**
|
||||
* Converts link definitions to ORM definitions.
|
||||
*/
|
||||
interface LinkConverter
|
||||
{
|
||||
public function convert(RelationDefs $linkDefs, string $entityType): EntityDefs;
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2025 EspoCRM, Inc.
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\Utils\Database\Orm\LinkConverters;
|
||||
|
||||
use Espo\Core\Utils\Database\Orm\Defs\AttributeDefs;
|
||||
use Espo\Core\Utils\Database\Orm\Defs\EntityDefs;
|
||||
use Espo\Core\Utils\Database\Orm\LinkConverter;
|
||||
use Espo\ORM\Defs\RelationDefs as LinkDefs;
|
||||
use Espo\ORM\Type\AttributeType;
|
||||
use LogicException;
|
||||
|
||||
class Attachments implements LinkConverter
|
||||
{
|
||||
public function __construct(private HasChildren $hasChildren) {}
|
||||
|
||||
public function convert(LinkDefs $linkDefs, string $entityType): EntityDefs
|
||||
{
|
||||
$name = $linkDefs->getName();
|
||||
|
||||
$entityDefs = $this->hasChildren->convert($linkDefs, $entityType);
|
||||
|
||||
$entityDefs = $entityDefs->withAttribute(
|
||||
AttributeDefs::create($name . 'Types')
|
||||
->withType(AttributeType::JSON_OBJECT)
|
||||
->withNotStorable()
|
||||
);
|
||||
|
||||
$relationDefs = $entityDefs->getRelation($name);
|
||||
|
||||
if (!$relationDefs) {
|
||||
throw new LogicException();
|
||||
}
|
||||
|
||||
$relationDefs = $relationDefs->withConditions([
|
||||
'OR' => [
|
||||
['field' => null],
|
||||
['field' => $name],
|
||||
]
|
||||
]);
|
||||
|
||||
return $entityDefs->withRelation($relationDefs);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2025 EspoCRM, Inc.
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\Utils\Database\Orm\LinkConverters;
|
||||
|
||||
use Espo\Core\Utils\Database\Orm\Defs\AttributeDefs;
|
||||
use Espo\Core\Utils\Database\Orm\Defs\EntityDefs;
|
||||
use Espo\Core\Utils\Database\Orm\Defs\RelationDefs;
|
||||
use Espo\Core\Utils\Database\Orm\LinkConverter;
|
||||
use Espo\ORM\Defs\Params\AttributeParam;
|
||||
use Espo\ORM\Defs\Params\RelationParam;
|
||||
use Espo\ORM\Defs\RelationDefs as LinkDefs;
|
||||
use Espo\ORM\Type\AttributeType;
|
||||
use Espo\ORM\Type\RelationType;
|
||||
|
||||
class BelongsTo implements LinkConverter
|
||||
{
|
||||
public function convert(LinkDefs $linkDefs, string $entityType): EntityDefs
|
||||
{
|
||||
$name = $linkDefs->getName();
|
||||
$foreignEntityType = $linkDefs->getForeignEntityType();
|
||||
$foreignRelationName = $linkDefs->hasForeignRelationName() ? $linkDefs->getForeignRelationName() : null;
|
||||
$noIndex = $linkDefs->getParam('noIndex');
|
||||
$noForeignName = $linkDefs->getParam('noForeignName');
|
||||
$foreignName = $linkDefs->getParam('foreignName') ?? 'name';
|
||||
$noJoin = $linkDefs->getParam(RelationParam::NO_JOIN);
|
||||
|
||||
$idName = $name . 'Id';
|
||||
$nameName = $name . 'Name';
|
||||
|
||||
$idAttributeDefs = AttributeDefs::create($idName)
|
||||
->withType(AttributeType::FOREIGN_ID)
|
||||
->withParam('index', !$noIndex);
|
||||
|
||||
$relationDefs = RelationDefs::create($name)
|
||||
->withType(RelationType::BELONGS_TO)
|
||||
->withForeignEntityType($foreignEntityType)
|
||||
->withKey($idName)
|
||||
->withForeignKey('id')
|
||||
->withForeignRelationName($foreignRelationName);
|
||||
|
||||
if ($linkDefs->getParam(RelationParam::DEFERRED_LOAD)) {
|
||||
$relationDefs = $relationDefs->withParam(RelationParam::DEFERRED_LOAD, true);
|
||||
}
|
||||
|
||||
$nameAttributeDefs = !$noForeignName ?
|
||||
(
|
||||
$noJoin ?
|
||||
AttributeDefs::create($nameName)
|
||||
->withType(AttributeType::VARCHAR)
|
||||
->withNotStorable()
|
||||
->withParam(AttributeParam::RELATION, $name)
|
||||
->withParam(AttributeParam::FOREIGN, $foreignName) :
|
||||
AttributeDefs::create($nameName)
|
||||
->withType(AttributeType::FOREIGN)
|
||||
->withNotStorable() // Used to be false before v7.4.
|
||||
->withParam(AttributeParam::RELATION, $name)
|
||||
->withParam(AttributeParam::FOREIGN, $foreignName)
|
||||
) : null;
|
||||
|
||||
$entityDefs = EntityDefs::create()
|
||||
->withAttribute($idAttributeDefs)
|
||||
->withRelation($relationDefs);
|
||||
|
||||
if ($nameAttributeDefs) {
|
||||
$entityDefs = $entityDefs->withAttribute($nameAttributeDefs);
|
||||
}
|
||||
|
||||
return $entityDefs;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2025 EspoCRM, Inc.
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\Utils\Database\Orm\LinkConverters;
|
||||
|
||||
use Espo\Core\Utils\Database\Orm\Defs\AttributeDefs;
|
||||
use Espo\Core\Utils\Database\Orm\Defs\EntityDefs;
|
||||
use Espo\Core\Utils\Database\Orm\Defs\RelationDefs;
|
||||
use Espo\Core\Utils\Database\Orm\LinkConverter;
|
||||
use Espo\ORM\Defs\Params\AttributeParam;
|
||||
use Espo\ORM\Defs\Params\RelationParam;
|
||||
use Espo\ORM\Defs\RelationDefs as LinkDefs;
|
||||
use Espo\ORM\Type\AttributeType;
|
||||
use Espo\ORM\Type\RelationType;
|
||||
|
||||
class BelongsToParent implements LinkConverter
|
||||
{
|
||||
private const TYPE_LENGTH = 100;
|
||||
|
||||
public function convert(LinkDefs $linkDefs, string $entityType): EntityDefs
|
||||
{
|
||||
$name = $linkDefs->getName();
|
||||
|
||||
$foreignRelationName = $linkDefs->hasForeignRelationName() ?
|
||||
$linkDefs->getForeignRelationName() : null;
|
||||
|
||||
$idName = $name . 'Id';
|
||||
$nameName = $name . 'Name';
|
||||
$typeName = $name . 'Type';
|
||||
|
||||
$relationDefs = RelationDefs::create($name)
|
||||
->withType(RelationType::BELONGS_TO_PARENT)
|
||||
->withKey($idName)
|
||||
->withForeignRelationName($foreignRelationName);
|
||||
|
||||
if ($linkDefs->getParam(RelationParam::DEFERRED_LOAD)) {
|
||||
$relationDefs = $relationDefs->withParam(RelationParam::DEFERRED_LOAD, true);
|
||||
}
|
||||
|
||||
return EntityDefs::create()
|
||||
->withAttribute(
|
||||
AttributeDefs::create($idName)
|
||||
->withType(AttributeType::FOREIGN_ID)
|
||||
->withParam('index', $name)
|
||||
)
|
||||
->withAttribute(
|
||||
AttributeDefs::create($typeName)
|
||||
->withType(AttributeType::FOREIGN_TYPE)
|
||||
->withParam(AttributeParam::NOT_NULL, false) // Revise whether needed.
|
||||
->withParam('index', $name)
|
||||
->withLength(self::TYPE_LENGTH)
|
||||
)
|
||||
->withAttribute(
|
||||
AttributeDefs::create($nameName)
|
||||
->withType(AttributeType::VARCHAR)
|
||||
->withNotStorable()
|
||||
)
|
||||
->withRelation($relationDefs);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2025 EspoCRM, Inc.
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\Utils\Database\Orm\LinkConverters;
|
||||
|
||||
use Espo\Core\Utils\Database\Orm\Defs\AttributeDefs;
|
||||
use Espo\Core\Utils\Database\Orm\Defs\EntityDefs;
|
||||
use Espo\Core\Utils\Database\Orm\Defs\RelationDefs;
|
||||
use Espo\Core\Utils\Database\Orm\LinkConverter;
|
||||
use Espo\Entities\EmailAddress;
|
||||
use Espo\ORM\Defs\RelationDefs as LinkDefs;
|
||||
use Espo\ORM\Name\Attribute;
|
||||
use Espo\ORM\Type\AttributeType;
|
||||
use Espo\ORM\Type\RelationType;
|
||||
|
||||
class EmailEmailAddress implements LinkConverter
|
||||
{
|
||||
public function __construct() {}
|
||||
|
||||
public function convert(LinkDefs $linkDefs, string $entityType): EntityDefs
|
||||
{
|
||||
$name = $linkDefs->getName();
|
||||
$hasField = $linkDefs->getParam('hasField');
|
||||
|
||||
$foreignEntityType = EmailAddress::ENTITY_TYPE;
|
||||
|
||||
$key1 = lcfirst($entityType) . 'Id';
|
||||
$key2 = lcfirst($foreignEntityType) . 'Id';
|
||||
|
||||
$relationDefs = RelationDefs::create($name)
|
||||
->withType(RelationType::MANY_MANY)
|
||||
->withForeignEntityType($foreignEntityType)
|
||||
->withKey(Attribute::ID)
|
||||
->withForeignKey(Attribute::ID)
|
||||
->withMidKeys($key1, $key2);
|
||||
|
||||
return EntityDefs::create()
|
||||
->withAttribute(
|
||||
AttributeDefs::create($name . 'Ids')
|
||||
->withType(AttributeType::JSON_ARRAY)
|
||||
->withNotStorable()
|
||||
->withParam('isLinkStub', !$hasField)
|
||||
)
|
||||
->withAttribute(
|
||||
AttributeDefs::create($name . 'Names')
|
||||
->withType(AttributeType::JSON_OBJECT)
|
||||
->withNotStorable()
|
||||
->withParam('isLinkStub', !$hasField)
|
||||
)
|
||||
->withRelation($relationDefs);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2025 EspoCRM, Inc.
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\Utils\Database\Orm\LinkConverters;
|
||||
|
||||
use Espo\Core\Utils\Database\Orm\Defs\AttributeDefs;
|
||||
use Espo\Core\Utils\Database\Orm\Defs\EntityDefs;
|
||||
use Espo\Core\Utils\Database\Orm\Defs\RelationDefs;
|
||||
use Espo\Core\Utils\Database\Orm\LinkConverter;
|
||||
use Espo\Entities\User;
|
||||
use Espo\ORM\Defs\RelationDefs as LinkDefs;
|
||||
use Espo\ORM\Type\AttributeType;
|
||||
use Espo\ORM\Type\RelationType;
|
||||
|
||||
/**
|
||||
* @noinspection PhpUnused
|
||||
*/
|
||||
class EntityCollaborator implements LinkConverter
|
||||
{
|
||||
private const ENTITY_TYPE_LENGTH = 100;
|
||||
|
||||
public function convert(LinkDefs $linkDefs, string $entityType): EntityDefs
|
||||
{
|
||||
$name = $linkDefs->getName();
|
||||
$relationshipName = $linkDefs->getRelationshipName();
|
||||
|
||||
return EntityDefs::create()
|
||||
->withRelation(
|
||||
RelationDefs::create($name)
|
||||
->withType(RelationType::MANY_MANY)
|
||||
->withForeignEntityType(User::ENTITY_TYPE)
|
||||
->withRelationshipName($relationshipName)
|
||||
->withMidKeys('entityId', 'userId')
|
||||
->withConditions(['entityType' => $entityType])
|
||||
->withAdditionalColumn(
|
||||
AttributeDefs::create('entityType')
|
||||
->withType(AttributeType::VARCHAR)
|
||||
->withLength(self::ENTITY_TYPE_LENGTH)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2025 EspoCRM, Inc.
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\Utils\Database\Orm\LinkConverters;
|
||||
|
||||
use Espo\Core\Utils\Database\Orm\Defs\AttributeDefs;
|
||||
use Espo\Core\Utils\Database\Orm\Defs\EntityDefs;
|
||||
use Espo\Core\Utils\Database\Orm\Defs\RelationDefs;
|
||||
use Espo\Core\Utils\Database\Orm\LinkConverter;
|
||||
use Espo\Entities\Team;
|
||||
use Espo\ORM\Defs\RelationDefs as LinkDefs;
|
||||
use Espo\ORM\Type\AttributeType;
|
||||
use Espo\ORM\Type\RelationType;
|
||||
|
||||
class EntityTeam implements LinkConverter
|
||||
{
|
||||
private const ENTITY_TYPE_LENGTH = 100;
|
||||
|
||||
public function convert(LinkDefs $linkDefs, string $entityType): EntityDefs
|
||||
{
|
||||
$name = $linkDefs->getName();
|
||||
$relationshipName = $linkDefs->getRelationshipName();
|
||||
|
||||
return EntityDefs::create()
|
||||
->withRelation(
|
||||
RelationDefs::create($name)
|
||||
->withType(RelationType::MANY_MANY)
|
||||
->withForeignEntityType(Team::ENTITY_TYPE)
|
||||
->withRelationshipName($relationshipName)
|
||||
->withMidKeys('entityId', 'teamId')
|
||||
->withConditions(['entityType' => $entityType])
|
||||
->withAdditionalColumn(
|
||||
AttributeDefs::create('entityType')
|
||||
->withType(AttributeType::VARCHAR)
|
||||
->withLength(self::ENTITY_TYPE_LENGTH)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2025 EspoCRM, Inc.
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\Utils\Database\Orm\LinkConverters;
|
||||
|
||||
use Espo\Core\Utils\Database\Orm\Defs\AttributeDefs;
|
||||
use Espo\Core\Utils\Database\Orm\Defs\EntityDefs;
|
||||
use Espo\Core\Utils\Database\Orm\Defs\RelationDefs;
|
||||
use Espo\Core\Utils\Database\Orm\LinkConverter;
|
||||
use Espo\Entities\User;
|
||||
use Espo\ORM\Defs\RelationDefs as LinkDefs;
|
||||
use Espo\ORM\Type\AttributeType;
|
||||
use Espo\ORM\Type\RelationType;
|
||||
|
||||
class EntityUser implements LinkConverter
|
||||
{
|
||||
private const ENTITY_TYPE_LENGTH = 100;
|
||||
|
||||
public function convert(LinkDefs $linkDefs, string $entityType): EntityDefs
|
||||
{
|
||||
$name = $linkDefs->getName();
|
||||
$relationshipName = $linkDefs->getRelationshipName();
|
||||
|
||||
return EntityDefs::create()
|
||||
->withRelation(
|
||||
RelationDefs::create($name)
|
||||
->withType(RelationType::MANY_MANY)
|
||||
->withForeignEntityType(User::ENTITY_TYPE)
|
||||
->withRelationshipName($relationshipName)
|
||||
->withMidKeys('entityId', 'userId')
|
||||
->withConditions(['entityType' => $entityType])
|
||||
->withAdditionalColumn(
|
||||
AttributeDefs::create('entityType')
|
||||
->withType(AttributeType::VARCHAR)
|
||||
->withLength(self::ENTITY_TYPE_LENGTH)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2025 EspoCRM, Inc.
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\Utils\Database\Orm\LinkConverters;
|
||||
|
||||
use Espo\Core\Utils\Database\Orm\Defs\AttributeDefs;
|
||||
use Espo\Core\Utils\Database\Orm\Defs\EntityDefs;
|
||||
use Espo\Core\Utils\Database\Orm\Defs\RelationDefs;
|
||||
use Espo\Core\Utils\Database\Orm\LinkConverter;
|
||||
use Espo\ORM\Defs\RelationDefs as LinkDefs;
|
||||
use Espo\ORM\Type\AttributeType;
|
||||
use Espo\ORM\Type\RelationType;
|
||||
|
||||
class HasChildren implements LinkConverter
|
||||
{
|
||||
public function convert(LinkDefs $linkDefs, string $entityType): EntityDefs
|
||||
{
|
||||
$name = $linkDefs->getName();
|
||||
$foreignEntityType = $linkDefs->getForeignEntityType();
|
||||
$foreignRelationName = $linkDefs->hasForeignRelationName() ? $linkDefs->getForeignRelationName() : null;
|
||||
$hasField = $linkDefs->getParam('hasField');
|
||||
|
||||
$relationDefs = RelationDefs::create($name)
|
||||
->withType(RelationType::HAS_CHILDREN)
|
||||
->withForeignEntityType($foreignEntityType)
|
||||
->withForeignKey($foreignRelationName . 'Id')
|
||||
->withParam('foreignType', $foreignRelationName . 'Type')
|
||||
->withForeignRelationName($foreignRelationName);
|
||||
|
||||
return EntityDefs::create()
|
||||
->withAttribute(
|
||||
AttributeDefs::create($name . 'Ids')
|
||||
->withType(AttributeType::JSON_ARRAY)
|
||||
->withNotStorable()
|
||||
->withParam('isLinkStub', !$hasField) // Revise. Change to notExportable?
|
||||
)
|
||||
->withAttribute(
|
||||
AttributeDefs::create($name . 'Names')
|
||||
->withType(AttributeType::JSON_OBJECT)
|
||||
->withNotStorable()
|
||||
->withParam('isLinkStub', !$hasField)
|
||||
)
|
||||
->withRelation($relationDefs);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2025 EspoCRM, Inc.
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\Utils\Database\Orm\LinkConverters;
|
||||
|
||||
use Espo\Core\Utils\Database\Orm\Defs\AttributeDefs;
|
||||
use Espo\Core\Utils\Database\Orm\Defs\EntityDefs;
|
||||
use Espo\Core\Utils\Database\Orm\Defs\RelationDefs;
|
||||
use Espo\Core\Utils\Database\Orm\LinkConverter;
|
||||
use Espo\Core\Utils\Log;
|
||||
use Espo\ORM\Defs\RelationDefs as LinkDefs;
|
||||
use Espo\ORM\Type\AttributeType;
|
||||
use Espo\ORM\Type\RelationType;
|
||||
|
||||
class HasMany implements LinkConverter
|
||||
{
|
||||
public function __construct(private Log $log) {}
|
||||
|
||||
public function convert(LinkDefs $linkDefs, string $entityType): EntityDefs
|
||||
{
|
||||
$name = $linkDefs->getName();
|
||||
$foreignEntityType = $linkDefs->getForeignEntityType();
|
||||
$foreignRelationName = $linkDefs->getForeignRelationName();
|
||||
$hasField = $linkDefs->getParam('hasField');
|
||||
|
||||
$type = RelationType::HAS_MANY;
|
||||
|
||||
/*$type = $linkDefs->hasRelationshipName() ?
|
||||
RelationType::MANY_MANY : // Revise.
|
||||
RelationType::HAS_MANY;*/
|
||||
|
||||
if ($linkDefs->hasRelationshipName()) {
|
||||
$this->log->warning(
|
||||
"Issue with the link '{$name}' in '{$entityType}' entity type. Might be the foreign link " .
|
||||
"'{$foreignRelationName}' in '{$foreignEntityType}' entity type is missing. " .
|
||||
"Remove the problem link manually.");
|
||||
|
||||
return EntityDefs::create();
|
||||
}
|
||||
|
||||
return EntityDefs::create()
|
||||
->withAttribute(
|
||||
AttributeDefs::create($name . 'Ids')
|
||||
->withType(AttributeType::JSON_ARRAY)
|
||||
->withNotStorable()
|
||||
->withParam('isLinkStub', !$hasField) // Revise. Change to notExportable?
|
||||
)
|
||||
->withAttribute(
|
||||
AttributeDefs::create($name . 'Names')
|
||||
->withType(AttributeType::JSON_OBJECT)
|
||||
->withNotStorable()
|
||||
->withParam('isLinkStub', !$hasField)
|
||||
)
|
||||
->withRelation(
|
||||
RelationDefs::create($name)
|
||||
->withType($type)
|
||||
->withForeignEntityType($foreignEntityType)
|
||||
->withForeignKey($foreignRelationName . 'Id')
|
||||
->withForeignRelationName($foreignRelationName)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2025 EspoCRM, Inc.
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\Utils\Database\Orm\LinkConverters;
|
||||
|
||||
use Espo\Core\Utils\Database\Orm\Defs\AttributeDefs;
|
||||
use Espo\Core\Utils\Database\Orm\Defs\EntityDefs;
|
||||
use Espo\Core\Utils\Database\Orm\Defs\RelationDefs;
|
||||
use Espo\Core\Utils\Database\Orm\LinkConverter;
|
||||
use Espo\ORM\Defs\Params\AttributeParam;
|
||||
use Espo\ORM\Defs\RelationDefs as LinkDefs;
|
||||
use Espo\ORM\Name\Attribute;
|
||||
use Espo\ORM\Type\AttributeType;
|
||||
use Espo\ORM\Type\RelationType;
|
||||
|
||||
class HasOne implements LinkConverter
|
||||
{
|
||||
public function convert(LinkDefs $linkDefs, string $entityType): EntityDefs
|
||||
{
|
||||
$name = $linkDefs->getName();
|
||||
$foreignEntityType = $linkDefs->getForeignEntityType();
|
||||
$foreignRelationName = $linkDefs->hasForeignRelationName() ? $linkDefs->getForeignRelationName() : null;
|
||||
$noForeignName = $linkDefs->getParam('noForeignName');
|
||||
$foreignName = $linkDefs->getParam('foreignName') ?? 'name';
|
||||
$noJoin = $linkDefs->getParam('noJoin');
|
||||
|
||||
$idName = $name . 'Id';
|
||||
$nameName = $name . 'Name';
|
||||
|
||||
$idAttributeDefs = AttributeDefs::create($idName)
|
||||
->withType($noJoin ? AttributeType::VARCHAR : AttributeType::FOREIGN)
|
||||
->withNotStorable()
|
||||
->withParam(AttributeParam::RELATION, $name)
|
||||
->withParam(AttributeParam::FOREIGN, Attribute::ID);
|
||||
|
||||
$nameAttributeDefs = !$noForeignName ?
|
||||
(
|
||||
AttributeDefs::create($nameName)
|
||||
->withType($noJoin ? AttributeType::VARCHAR : AttributeType::FOREIGN)
|
||||
->withNotStorable()
|
||||
->withParam(AttributeParam::RELATION, $name)
|
||||
->withParam(AttributeParam::FOREIGN, $foreignName)
|
||||
) : null;
|
||||
|
||||
$relationDefs = RelationDefs::create($name)
|
||||
->withType(RelationType::HAS_ONE)
|
||||
->withForeignEntityType($foreignEntityType);
|
||||
|
||||
if ($foreignRelationName) {
|
||||
$relationDefs = $relationDefs
|
||||
->withForeignKey($foreignRelationName . 'Id')
|
||||
->withForeignRelationName($foreignRelationName);
|
||||
}
|
||||
|
||||
$entityDefs = EntityDefs::create()
|
||||
->withAttribute($idAttributeDefs)
|
||||
->withRelation($relationDefs);
|
||||
|
||||
if ($nameAttributeDefs) {
|
||||
$entityDefs = $entityDefs->withAttribute($nameAttributeDefs);
|
||||
}
|
||||
|
||||
return $entityDefs;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2025 EspoCRM, Inc.
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\Utils\Database\Orm\LinkConverters;
|
||||
|
||||
use Espo\Core\Utils\Database\Orm\Defs\AttributeDefs;
|
||||
use Espo\Core\Utils\Database\Orm\Defs\EntityDefs;
|
||||
use Espo\Core\Utils\Database\Orm\Defs\RelationDefs;
|
||||
use Espo\Core\Utils\Database\Orm\LinkConverter;
|
||||
use Espo\Core\Utils\Util;
|
||||
use Espo\ORM\Defs\RelationDefs as LinkDefs;
|
||||
use Espo\ORM\Name\Attribute;
|
||||
use Espo\ORM\Type\AttributeType;
|
||||
use Espo\ORM\Type\RelationType;
|
||||
|
||||
class ManyMany implements LinkConverter
|
||||
{
|
||||
public function convert(LinkDefs $linkDefs, string $entityType): EntityDefs
|
||||
{
|
||||
$name = $linkDefs->getName();
|
||||
$foreignEntityType = $linkDefs->getForeignEntityType();
|
||||
$foreignRelationName = $linkDefs->getForeignRelationName();
|
||||
$hasField = $linkDefs->getParam('hasField');
|
||||
$columnAttributeMap = $linkDefs->getParam('columnAttributeMap');
|
||||
|
||||
$relationshipName = $linkDefs->hasRelationshipName() ?
|
||||
$linkDefs->getRelationshipName() :
|
||||
self::composeRelationshipName($entityType, $foreignEntityType);
|
||||
|
||||
if ($linkDefs->hasMidKey() && $linkDefs->hasForeignMidKey()) {
|
||||
$key1 = $linkDefs->getMidKey();
|
||||
$key2 = $linkDefs->getForeignMidKey();
|
||||
} else {
|
||||
$key1 = lcfirst($entityType) . 'Id';
|
||||
$key2 = lcfirst($foreignEntityType) . 'Id';
|
||||
|
||||
if ($key1 === $key2) {
|
||||
[$key1, $key2] = strcmp($name, $foreignRelationName) > 0 ?
|
||||
['leftId', 'rightId'] :
|
||||
['rightId', 'leftId'];
|
||||
}
|
||||
}
|
||||
|
||||
$relationDefs = RelationDefs::create($name)
|
||||
->withType(RelationType::MANY_MANY)
|
||||
->withForeignEntityType($foreignEntityType)
|
||||
->withRelationshipName($relationshipName)
|
||||
->withKey(Attribute::ID)
|
||||
->withForeignKey(Attribute::ID)
|
||||
->withMidKeys($key1, $key2)
|
||||
->withForeignRelationName($foreignRelationName);
|
||||
|
||||
if ($columnAttributeMap) {
|
||||
$relationDefs = $relationDefs->withParam('columnAttributeMap', $columnAttributeMap);
|
||||
}
|
||||
|
||||
return EntityDefs::create()
|
||||
->withAttribute(
|
||||
AttributeDefs::create($name . 'Ids')
|
||||
->withType(AttributeType::JSON_ARRAY)
|
||||
->withNotStorable()
|
||||
->withParam('isLinkStub', !$hasField) // Revise.
|
||||
)
|
||||
->withAttribute(
|
||||
AttributeDefs::create($name . 'Names')
|
||||
->withType(AttributeType::JSON_OBJECT)
|
||||
->withNotStorable()
|
||||
->withParam('isLinkStub', !$hasField) // Revise.
|
||||
)
|
||||
->withRelation($relationDefs);
|
||||
}
|
||||
|
||||
private static function composeRelationshipName(string $left, string $right): string
|
||||
{
|
||||
$parts = [
|
||||
Util::toCamelCase(lcfirst($left)),
|
||||
Util::toCamelCase(lcfirst($right)),
|
||||
];
|
||||
|
||||
sort($parts);
|
||||
|
||||
return Util::toCamelCase(implode('_', $parts));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2025 EspoCRM, Inc.
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\Utils\Database\Orm\LinkConverters;
|
||||
|
||||
use Espo\Core\Utils\Database\Orm\Defs\AttributeDefs;
|
||||
use Espo\Core\Utils\Database\Orm\Defs\EntityDefs;
|
||||
use Espo\Core\Utils\Database\Orm\Defs\RelationDefs;
|
||||
use Espo\Core\Utils\Database\Orm\LinkConverter;
|
||||
use Espo\Entities\PhoneNumber;
|
||||
use Espo\ORM\Defs\RelationDefs as LinkDefs;
|
||||
use Espo\ORM\Type\AttributeType;
|
||||
use Espo\ORM\Type\RelationType;
|
||||
|
||||
class SmsPhoneNumber implements LinkConverter
|
||||
{
|
||||
public function __construct() {}
|
||||
|
||||
public function convert(LinkDefs $linkDefs, string $entityType): EntityDefs
|
||||
{
|
||||
$name = $linkDefs->getName();
|
||||
$foreignEntityType = PhoneNumber::ENTITY_TYPE;
|
||||
|
||||
$key1 = lcfirst($entityType) . 'Id';
|
||||
$key2 = lcfirst($foreignEntityType) . 'Id';
|
||||
|
||||
$relationDefs = RelationDefs::create($name)
|
||||
->withType(RelationType::MANY_MANY)
|
||||
->withForeignEntityType($foreignEntityType)
|
||||
->withKey('id')
|
||||
->withForeignKey('id')
|
||||
->withMidKeys($key1, $key2);
|
||||
|
||||
return EntityDefs::create()
|
||||
->withAttribute(
|
||||
AttributeDefs::create($name . 'Ids')
|
||||
->withType(AttributeType::JSON_ARRAY)
|
||||
->withNotStorable()
|
||||
)
|
||||
->withAttribute(
|
||||
AttributeDefs::create($name . 'Names')
|
||||
->withType(AttributeType::JSON_OBJECT)
|
||||
->withNotStorable()
|
||||
)
|
||||
->withRelation($relationDefs);
|
||||
}
|
||||
}
|
||||
272
application/Espo/Core/Utils/Database/Orm/RelationConverter.php
Normal file
272
application/Espo/Core/Utils/Database/Orm/RelationConverter.php
Normal file
@@ -0,0 +1,272 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2025 EspoCRM, Inc.
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\Utils\Database\Orm;
|
||||
|
||||
use Espo\Core\InjectableFactory;
|
||||
use Espo\Core\Utils\Database\Orm\LinkConverters\BelongsTo;
|
||||
use Espo\Core\Utils\Database\Orm\LinkConverters\BelongsToParent;
|
||||
use Espo\Core\Utils\Database\Orm\LinkConverters\HasChildren;
|
||||
use Espo\Core\Utils\Database\Orm\LinkConverters\HasMany;
|
||||
use Espo\Core\Utils\Database\Orm\LinkConverters\HasOne;
|
||||
use Espo\Core\Utils\Database\Orm\LinkConverters\ManyMany;
|
||||
use Espo\Core\Utils\Log;
|
||||
use Espo\Core\Utils\Util;
|
||||
use Espo\Core\Utils\Metadata;
|
||||
use Espo\ORM\Defs\Params\AttributeParam;
|
||||
use Espo\ORM\Defs\Params\EntityParam;
|
||||
use Espo\ORM\Defs\Params\RelationParam;
|
||||
use Espo\ORM\Defs\RelationDefs;
|
||||
use Espo\ORM\Type\AttributeType;
|
||||
use Espo\ORM\Type\RelationType;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
class RelationConverter
|
||||
{
|
||||
private const DEFAULT_VARCHAR_LENGTH = 255;
|
||||
|
||||
/** @var string[] */
|
||||
private $mergeParams = [
|
||||
RelationParam::RELATION_NAME,
|
||||
RelationParam::CONDITIONS,
|
||||
RelationParam::ADDITIONAL_COLUMNS,
|
||||
'noJoin',
|
||||
RelationParam::INDEXES,
|
||||
];
|
||||
|
||||
/** @var string[] */
|
||||
private $manyMergeParams = [
|
||||
RelationParam::ORDER_BY,
|
||||
RelationParam::ORDER,
|
||||
];
|
||||
|
||||
public function __construct(
|
||||
private Metadata $metadata,
|
||||
private InjectableFactory $injectableFactory,
|
||||
private Log $log
|
||||
) {}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @param array<string, mixed> $params
|
||||
* @param string $entityType
|
||||
* @return ?array<string, mixed>
|
||||
*/
|
||||
public function process(string $name, array $params, string $entityType): ?array
|
||||
{
|
||||
$foreignEntityType = $params[RelationParam::ENTITY] ?? null;
|
||||
$foreignLinkName = $params[RelationParam::FOREIGN] ?? null;
|
||||
|
||||
/** @var ?array<string, mixed> $foreignParams */
|
||||
$foreignParams = $foreignEntityType && $foreignLinkName ?
|
||||
$this->metadata->get(['entityDefs', $foreignEntityType, 'links', $foreignLinkName]) :
|
||||
null;
|
||||
|
||||
/** @var ?string $relationshipName */
|
||||
$relationshipName = $params[RelationParam::RELATION_NAME] ?? null;
|
||||
|
||||
if ($relationshipName) {
|
||||
$relationshipName = lcfirst($relationshipName);
|
||||
$params[RelationParam::RELATION_NAME] = $relationshipName;
|
||||
}
|
||||
|
||||
$linkType = $params[RelationParam::TYPE] ?? null;
|
||||
$foreignLinkType = $foreignParams ? $foreignParams[RelationParam::TYPE] : null;
|
||||
|
||||
if (!$linkType) {
|
||||
$this->log->warning("Link $entityType.$name has no type.");
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
$params['hasField'] = (bool) $this->metadata
|
||||
->get(['entityDefs', $entityType, 'fields', $name]);
|
||||
|
||||
$relationDefs = RelationDefs::fromRaw($params, $name);
|
||||
|
||||
$converter = $this->createLinkConverter($relationshipName, $linkType, $foreignLinkType);
|
||||
|
||||
$convertedEntityDefs = $converter->convert($relationDefs, $entityType);
|
||||
|
||||
$raw = $convertedEntityDefs->toAssoc();
|
||||
|
||||
if (isset($raw[EntityParam::RELATIONS][$name])) {
|
||||
$this->mergeParams($raw[EntityParam::RELATIONS][$name], $params, $foreignParams ?? [], $linkType);
|
||||
$this->correct($raw[EntityParam::RELATIONS][$name]);
|
||||
}
|
||||
|
||||
return [$entityType => $raw];
|
||||
}
|
||||
|
||||
private function createLinkConverter(?string $relationship, string $type, ?string $foreignType): LinkConverter
|
||||
{
|
||||
$className = $this->getLinkConverterClassName($relationship, $type, $foreignType);
|
||||
|
||||
return $this->injectableFactory->create($className);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return class-string<LinkConverter>
|
||||
*/
|
||||
private function getLinkConverterClassName(?string $relationship, string $type, ?string $foreignType): string
|
||||
{
|
||||
if ($relationship) {
|
||||
/** @var class-string<LinkConverter> $className */
|
||||
$className = $this->metadata->get(['app', 'relationships', $relationship, 'converterClassName']);
|
||||
|
||||
if ($className) {
|
||||
return $className;
|
||||
}
|
||||
}
|
||||
|
||||
if ($type === RelationType::HAS_MANY && $foreignType === RelationType::HAS_MANY) {
|
||||
return ManyMany::class;
|
||||
}
|
||||
|
||||
if ($type === RelationType::HAS_MANY) {
|
||||
return HasMany::class;
|
||||
}
|
||||
|
||||
if ($type === RelationType::HAS_CHILDREN) {
|
||||
return HasChildren::class;
|
||||
}
|
||||
|
||||
if ($type === RelationType::HAS_ONE) {
|
||||
return HasOne::class;
|
||||
}
|
||||
|
||||
if ($type === RelationType::BELONGS_TO) {
|
||||
return BelongsTo::class;
|
||||
}
|
||||
|
||||
if ($type === RelationType::BELONGS_TO_PARENT) {
|
||||
return BelongsToParent::class;
|
||||
}
|
||||
|
||||
throw new RuntimeException("Unsupported link type '$type'.");
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $relationDefs
|
||||
* @param array<string, mixed> $params
|
||||
* @param array<string, mixed> $foreignParams
|
||||
*/
|
||||
private function mergeParams(array &$relationDefs, array $params, array $foreignParams, string $linkType): void
|
||||
{
|
||||
$mergeParams = $this->mergeParams;
|
||||
|
||||
if (
|
||||
$linkType === RelationType::HAS_MANY ||
|
||||
$linkType === RelationType::HAS_CHILDREN
|
||||
) {
|
||||
$mergeParams = array_merge($mergeParams, $this->manyMergeParams);
|
||||
}
|
||||
|
||||
foreach ($mergeParams as $name) {
|
||||
$additionalParam = $this->getMergedParam($name, $params, $foreignParams);
|
||||
|
||||
if ($additionalParam === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$relationDefs[$name] = $additionalParam;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $params
|
||||
* @param array<string, mixed> $foreignParams
|
||||
* @return array<string, mixed>|scalar|null
|
||||
*/
|
||||
private function getMergedParam(string $name, array $params, array $foreignParams): mixed
|
||||
{
|
||||
$value = $params[$name] ?? null;
|
||||
$foreignValue = $foreignParams[$name] ?? null;
|
||||
|
||||
if ($value !== null && $foreignValue !== null) {
|
||||
if (!empty($value) && !is_array($value)) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
if (!empty($foreignValue) && !is_array($foreignValue)) {
|
||||
return $foreignValue;
|
||||
}
|
||||
|
||||
/** @var array<int|string, mixed> $value */
|
||||
/** @var array<int|string, mixed> $foreignValue */
|
||||
|
||||
/** @var array<string, mixed> */
|
||||
return Util::merge($value, $foreignValue);
|
||||
}
|
||||
|
||||
if (isset($value)) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
if (isset($foreignValue)) {
|
||||
return $foreignValue;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $relationDefs
|
||||
*/
|
||||
private function correct(array &$relationDefs): void
|
||||
{
|
||||
if (
|
||||
isset($relationDefs[RelationParam::ORDER]) &&
|
||||
is_string($relationDefs[RelationParam::ORDER])
|
||||
) {
|
||||
$relationDefs[RelationParam::ORDER] = strtoupper($relationDefs[RelationParam::ORDER]);
|
||||
}
|
||||
|
||||
if (!isset($relationDefs[RelationParam::ADDITIONAL_COLUMNS])) {
|
||||
return;
|
||||
}
|
||||
|
||||
/** @var array<string, array<string, mixed>> $additionalColumns */
|
||||
$additionalColumns = &$relationDefs[RelationParam::ADDITIONAL_COLUMNS];
|
||||
|
||||
foreach ($additionalColumns as &$columnDefs) {
|
||||
$columnDefs[AttributeParam::TYPE] ??= AttributeType::VARCHAR;
|
||||
|
||||
if (
|
||||
$columnDefs[AttributeParam::TYPE] === AttributeType::VARCHAR &&
|
||||
!isset($columnDefs[AttributeParam::LEN])
|
||||
) {
|
||||
$columnDefs[AttributeParam::LEN] = self::DEFAULT_VARCHAR_LENGTH;
|
||||
}
|
||||
}
|
||||
|
||||
$relationDefs[RelationParam::ADDITIONAL_COLUMNS] = $additionalColumns;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user