Skip to content

Commit

Permalink
Merge pull request mautic#13319 from dadarya0/manage-length-of-custom…
Browse files Browse the repository at this point in the history
…-field-values

Manage length of custom field values
  • Loading branch information
escopecz authored Feb 8, 2024
2 parents 55d8db2 + 7e9d000 commit 1ff2536
Show file tree
Hide file tree
Showing 39 changed files with 1,567 additions and 113 deletions.
88 changes: 64 additions & 24 deletions app/bundles/CoreBundle/Doctrine/Helper/IndexSchemaHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use Doctrine\DBAL\Schema\Table;
use Doctrine\DBAL\Types\TextType;
use Mautic\CoreBundle\Exception\SchemaException;
use Mautic\LeadBundle\Entity\LeadField;

class IndexSchemaHelper
{
Expand Down Expand Up @@ -77,26 +78,31 @@ public function allowColumn($name): void
}

/**
* @param array $options
* @param string $name
* @param array $options
*
* @return $this
*
* @throws \Doctrine\DBAL\Schema\SchemaException
*/
public function addIndex($columns, $name, $options = [])
{
$columns = $this->getTextColumns($columns);
$textColumns = $this->getTextColumns($columns);

if (!empty($columns)) {
$index = new Index($this->prefix.$name, $columns, false, false, $options);
if (empty($textColumns)) {
return $this;
}

if ($this->table->hasIndex($this->prefix.$name)) {
$this->changedIndexes[] = $index;
} else {
$this->addedIndexes[] = $index;
}
$index = new Index($this->prefix.$name, $textColumns, false, false, $options);

if ($this->table->hasIndex($this->prefix.$name)) {
$this->changedIndexes[] = $index;

return $this;
}

$this->addedIndexes[] = $index;

return $this;
}

Expand All @@ -113,8 +119,8 @@ public function dropIndex($columns, $name, $options = [])
{
$textColumns = $this->getTextColumns($columns);

$index = new Index($name, $textColumns, false, false, $options);
if ($this->table->hasIndex($name)) {
$index = new Index($this->prefix.$name, $textColumns, false, false, $options);
if ($this->table->hasIndex($this->prefix.$name)) {
$this->dropIndexes[] = $index;
}

Expand All @@ -129,23 +135,17 @@ public function executeChanges(): void
$platform = $this->db->getDatabasePlatform();

$sql = [];
if (count($this->changedIndexes)) {
foreach ($this->changedIndexes as $index) {
$sql[] = $platform->getDropIndexSQL($index, $this->table);
$sql[] = $platform->getCreateIndexSQL($index, $this->table);
}
foreach ($this->changedIndexes as $index) {
$sql[] = $platform->getDropIndexSQL($index, $this->table);
$sql[] = $platform->getCreateIndexSQL($index, $this->table);
}

if (count($this->dropIndexes)) {
foreach ($this->dropIndexes as $index) {
$sql[] = $platform->getDropIndexSQL($index, $this->table);
}
foreach ($this->dropIndexes as $index) {
$sql[] = $platform->getDropIndexSQL($index, $this->table);
}

if (count($this->addedIndexes)) {
foreach ($this->addedIndexes as $index) {
$sql[] = $platform->getCreateIndexSQL($index, $this->table);
}
foreach ($this->addedIndexes as $index) {
$sql[] = $platform->getCreateIndexSQL($index, $this->table);
}

if (count($sql)) {
Expand All @@ -159,6 +159,46 @@ public function executeChanges(): void
}

/**
* @throws SchemaException
*/
public function hasIndex(LeadField $leadField): bool
{
$alias = $leadField->getAlias();
$this->setName($leadField->getCustomFieldObject());

return $this->table->hasIndex($this->prefix."{$alias}_search");
}

/**
* @param array<mixed> $uniqueIdentifierColumns
*/
public function hasMatchingUniqueIdentifierIndex(LeadField $leadField, array $uniqueIdentifierColumns): bool
{
$this->setName($leadField->getCustomFieldObject());

$index = $this->table->getIndex($this->prefix.'unique_identifier_search');

$columns = $index->getColumns();

asort($columns);
asort($uniqueIdentifierColumns);

return $columns === $uniqueIdentifierColumns;
}

/**
* @throws SchemaException
*/
public function hasUniqueIdentifierIndex(LeadField $leadField): bool
{
$this->setName($leadField->getCustomFieldObject());

return $this->table->hasIndex($this->prefix.'unique_identifier_search');
}

/**
* @param mixed $columns
*
* @throws \Doctrine\DBAL\Schema\SchemaException
*/
private function getTextColumns($columns): array
Expand Down
1 change: 1 addition & 0 deletions app/bundles/CoreBundle/Translations/en_US/messages.ini
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,7 @@ mautic.core.icon_tooltip.preference_center="Is preference center page"
mautic.core.icon_tooltip.translation="Has translations"
mautic.core.id="ID"
mautic.core.insert="Insert"
mautic.core.indexable="Indexable"
mautic.core.ip_lookup.fetch_data="Fetch IP Lookup Data Store"
mautic.core.ip_lookup.remote_fetch_error="Automatically fetching the IP lookup data failed. Download %remoteUrl%, extract if necessary, and upload to %localPath%."
mautic.core.ip_lookup.remote_fetch_error_generic="Automatically fetching IP lookup data failed."
Expand Down
2 changes: 1 addition & 1 deletion app/bundles/LeadBundle/Assets/js/lead.js
Original file line number Diff line number Diff line change
Expand Up @@ -834,7 +834,7 @@ Mautic.updateLeadFieldProperties = function(selectedVal, onload) {
html = mQuery('#field-templates .default_template_text').html();
tempType = 'text';

if (selectedVal == 'number' || selectedVal == 'tel' || selectedVal == 'url' || selectedVal == 'email') {
if (html != undefined && (selectedVal == 'number' || selectedVal == 'tel' || selectedVal == 'url' || selectedVal == 'email')) {
var replace = 'type="text"';
var regex = new RegExp(replace, "g");
html = html.replace(regex, 'type="' + selectedVal + '"');
Expand Down
4 changes: 4 additions & 0 deletions app/bundles/LeadBundle/Controller/Api/FieldApiController.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
use Mautic\CoreBundle\Translation\Translator;
use Mautic\LeadBundle\Entity\LeadField;
use Mautic\LeadBundle\Field\Exception\AbortColumnCreateException;
use Mautic\LeadBundle\Field\Exception\AbortColumnUpdateException;
use Mautic\LeadBundle\Model\FieldModel;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Form\FormFactoryInterface;
Expand Down Expand Up @@ -74,6 +75,9 @@ protected function saveEntity($entity, int $statusCode): int
} catch (AbortColumnCreateException) {
// Field has been queued
return Response::HTTP_ACCEPTED;
} catch (AbortColumnUpdateException) {
// Field has been queued
return Response::HTTP_OK;
}
}

Expand Down
16 changes: 14 additions & 2 deletions app/bundles/LeadBundle/Controller/FieldController.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use Mautic\CoreBundle\Exception\SchemaException;
use Mautic\LeadBundle\Entity\LeadField;
use Mautic\LeadBundle\Field\Exception\AbortColumnCreateException;
use Mautic\LeadBundle\Field\Exception\AbortColumnUpdateException;
use Mautic\LeadBundle\Helper\FieldAliasHelper;
use Mautic\LeadBundle\Model\FieldModel;
use Symfony\Component\Form\FormError;
Expand Down Expand Up @@ -289,10 +290,20 @@ public function editAction(Request $request, $objectId, $ignorePost = false)
}

if ($valid) {
$flashMessage = 'mautic.core.notice.updated';

// form is valid so process the data
$model->saveEntity($field, $this->getFormButton($form, ['buttons', 'save'])->isClicked());
try {
$model->saveEntity($field, $this->getFormButton($form, ['buttons', 'save'])->isClicked());
} catch (AbortColumnUpdateException) {
$flashMessage = $this->translator->trans('mautic.lead.field.pushed_to_background');
} catch (SchemaException $e) {
$flashMessage = $e->getMessage();
$form['alias']->addError(new FormError($e->getMessage()));
$valid = false;
}

$this->addFlashMessage('mautic.core.notice.updated', [
$this->addFlashMessage($flashMessage, [
'%name%' => $field->getLabel(),
'%menu_link%' => 'mautic_contactfield_index',
'%url%' => $this->generateUrl('mautic_contactfield_action', [
Expand Down Expand Up @@ -361,6 +372,7 @@ public function cloneAction(Request $request, FieldAliasHelper $fieldAliasHelper
}

$clone = clone $entity;
$clone->setId(null);
$clone->setIsPublished(false);
$clone->setIsFixed(false);
$fieldAliasHelper->makeAliasUnique($clone);
Expand Down
42 changes: 42 additions & 0 deletions app/bundles/LeadBundle/Entity/LeadField.php
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,8 @@ class LeadField extends FormEntity implements CacheInvalidateInterface
*/
private $isUniqueIdentifier = false;

private ?int $charLengthLimit = 64;

/**
* @var int|null
*/
Expand All @@ -105,6 +107,8 @@ class LeadField extends FormEntity implements CacheInvalidateInterface
*/
private $properties = [];

private ?bool $isIndex = false;

/**
* The column in lead_fields table was not created yet if this property is true.
* Entity cannot be published and we cannot work with it until column is created.
Expand Down Expand Up @@ -187,6 +191,12 @@ public static function loadMetadata(ORM\ClassMetadata $metadata): void
->build();

$builder->addNullableField('isUniqueIdentifer', 'boolean', 'is_unique_identifer');
$builder->addNullableField('isIndex', 'boolean', 'is_index');

$builder->createField('charLengthLimit', 'integer')
->columnName('char_length_limit')
->nullable()
->build();

$builder->createField('order', 'integer')
->columnName('field_order')
Expand Down Expand Up @@ -270,6 +280,11 @@ public static function loadApiMetadata(ApiMetadataDriver $metadata): void
->build();
}

public function setId(?int $id = null): void
{
$this->id = $id;
}

/**
* Get id.
*
Expand Down Expand Up @@ -495,6 +510,19 @@ public function getObject()
return $this->object;
}

public function setCharLengthLimit(?int $charLengthLimit): LeadField
{
$this->isChanged('charLengthLimit', $charLengthLimit);
$this->charLengthLimit = $charLengthLimit;

return $this;
}

public function getCharLengthLimit(): ?int
{
return $this->charLengthLimit;
}

public function getCustomFieldObject(): string
{
if (!$this->customFieldObject) {
Expand Down Expand Up @@ -618,6 +646,10 @@ public function getIsUniqueIdentifer()
*/
public function setIsUniqueIdentifer($isUniqueIdentifer)
{
if ($isUniqueIdentifer) {
$this->isIndex = true;
}

$this->isUniqueIdentifer = $this->isUniqueIdentifier = $isUniqueIdentifer;

return $this;
Expand Down Expand Up @@ -785,4 +817,14 @@ public function getCacheNamespacesToDelete(): array
{
return [self::CACHE_NAMESPACE];
}

public function isIsIndex(): bool
{
return $this->isIndex;
}

public function setIsIndex(bool $indexable): void
{
$this->isIndex = $indexable;
}
}
2 changes: 1 addition & 1 deletion app/bundles/LeadBundle/Entity/LeadFieldRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public function getAliases($exludingId, $publishedOnly = false, $includeEntityFi

if ($includeEntityFields) {
// add lead main column names to prevent attempt to create a field with the same name
$leadRepo = $this->_em->getRepository(\Mautic\LeadBundle\Entity\Lead::class)->getBaseColumns(\Mautic\LeadBundle\Entity\Lead::class, true);
$leadRepo = $this->_em->getRepository(Lead::class)->getBaseColumns(Lead::class, true);
$aliases = array_merge($aliases, $leadRepo);
}

Expand Down
20 changes: 10 additions & 10 deletions app/bundles/LeadBundle/EventListener/DoctrineSubscriber.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,28 +42,27 @@ public function postGenerateSchema(GenerateSchemaEventArgs $args): void

// get a list of fields
$fields = $args->getEntityManager()->getConnection()->createQueryBuilder()
->select('f.alias, f.is_unique_identifer as is_unique, f.type, f.object')
->select('f.alias, f.is_unique_identifer as is_unique, f.is_index, f.type, f.object')
->from(MAUTIC_TABLE_PREFIX.'lead_fields', 'f')
->where("f.object = '$object'")
->orderBy('f.field_order', 'ASC')
->executeQuery()
->fetchAllAssociative();

// Compile which ones are unique identifiers
// Email will always be included first
$uniqueFields = ('lead' === $object) ? ['email' => 'email'] : ['companyemail' => 'companyemail'];
foreach ($fields as $f) {
if ($f['is_unique'] && 'email' !== $f['alias']) {
$uniqueFields[$f['alias']] = $f['alias'];
$uniqueFields = [];
foreach ($fields as $field) {
if ($field['is_unique']) {
$uniqueFields[$field['alias']] = $field['alias'];
}
$columnDef = SchemaDefinition::getSchemaDefinition($f['alias'], $f['type'], !empty($f['is_unique']));
$columnDef = SchemaDefinition::getSchemaDefinition($field['alias'], $field['type'], !empty($field['is_unique']));

if (!$table->hasColumn($f['alias'])) {
if (!$table->hasColumn($field['alias'])) {
$table->addColumn($columnDef['name'], $columnDef['type'], $columnDef['options']);
}

if ('text' != $columnDef['type']) {
$table->addIndex([$columnDef['name']], MAUTIC_TABLE_PREFIX.$f['alias'].'_search');
if (!empty($field['is_unique']) || !empty($field['is_index'])) {
$table->addIndex([$columnDef['name']], MAUTIC_TABLE_PREFIX.$field['alias'].'_search');
}
}

Expand All @@ -81,6 +80,7 @@ public function postGenerateSchema(GenerateSchemaEventArgs $args): void

if (count($uniqueFields) > 1) {
// Only use three to prevent max key length errors
asort($uniqueFields);
$uniqueFields = array_slice($uniqueFields, 0, 3);
$table->addIndex($uniqueFields, MAUTIC_TABLE_PREFIX.'unique_identifier_search');
}
Expand Down
24 changes: 24 additions & 0 deletions app/bundles/LeadBundle/Field/BackgroundService.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use Mautic\LeadBundle\Exception\NoListenerException;
use Mautic\LeadBundle\Field\Dispatcher\FieldColumnBackgroundJobDispatcher;
use Mautic\LeadBundle\Field\Exception\AbortColumnCreateException;
use Mautic\LeadBundle\Field\Exception\AbortColumnUpdateException;
use Mautic\LeadBundle\Field\Exception\ColumnAlreadyCreatedException;
use Mautic\LeadBundle\Field\Exception\CustomFieldLimitException;
use Mautic\LeadBundle\Field\Exception\LeadFieldWasNotFoundException;
Expand Down Expand Up @@ -68,4 +69,27 @@ public function addColumn(int $leadFieldId, ?int $userId): void

$this->customFieldNotification->customFieldWasCreated($leadField, $userId);
}

/**
* @throws AbortColumnUpdateException
* @throws DriverException
* @throws LeadFieldWasNotFoundException
* @throws SchemaException
* @throws \Mautic\CoreBundle\Exception\SchemaException
*/
public function updateColumn(int $leadFieldId, int $userId): void
{
$leadField = $this->fieldModel->getEntity($leadFieldId);
if (null === $leadField) {
throw new LeadFieldWasNotFoundException('LeadField entity was not found');
}

try {
$this->fieldColumnBackgroundJobDispatcher->dispatchPreUpdateColumnEvent($leadField);
} catch (NoListenerException) {
}

$this->customFieldColumn->processUpdateLeadColumn($leadField);
$this->customFieldNotification->customFieldWasUpdated($leadField, $userId);
}
}
Loading

0 comments on commit 1ff2536

Please sign in to comment.