Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Form object refactoring #11905

Merged
merged 81 commits into from Feb 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
81 commits
Select commit Hold shift + click to select a range
20dda35
Adding mappedObject and mappedField columns to the FormField entity
escopecz Apr 15, 2020
a31c9e7
Adding Mapped Object field to the form field edit form
escopecz Apr 16, 2020
3d2aeec
Removing duplicated interface
escopecz Apr 16, 2020
6029568
Field collect event added
escopecz Apr 16, 2020
2b53028
Switch mapping field options when object changes
escopecz Apr 17, 2020
c026b9e
Set the 'data-list-type' option param similarly as previous implement…
escopecz Apr 30, 2020
fd9867c
Refactoring used mapped fields from sesssion to cache
escopecz May 5, 2020
6901d0f
Implementing removing already used mapped fields
escopecz May 6, 2020
2586bb9
Replacing clearing leadfields session with clearing mappedFieldCollector
escopecz May 21, 2020
2795dba
Fixing a countable error that happens on PHP7
escopecz May 21, 2020
8a13f28
Tests for showForContact()
escopecz May 21, 2020
6ff85aa
Refactoring FieldValueTransformer to use mapped field instead of lead…
escopecz May 22, 2020
80dcb41
Speeding up test from 11s to 8s
escopecz May 22, 2020
13c9011
Speeding up SubmissionModelTest, removing inheritance in tests, not c…
escopecz May 22, 2020
1e4944d
Removing refactored (commented out) code
escopecz May 22, 2020
e388c81
Testing populateValuesWithLead()
escopecz May 22, 2020
0ae1ce6
Replacing leadField with mappedField
escopecz May 22, 2020
38a2879
Refactoring getLeadField() with getMappedField()
escopecz May 22, 2020
2069b4a
Refactoring getLeadField() to getMappedField() for SubmissionModel
escopecz May 22, 2020
ecb31ff
Refactoring occurances of 'leadField' with 'mappedField' in templates
escopecz May 22, 2020
84f4478
Refactoring getLeadField() with getMappedField() in citrix subscriber
escopecz May 22, 2020
b53f354
Renaming collector to better describe what it stores
escopecz May 26, 2020
ab1c2bd
Adding method getMappedFieldObjects that will return all objects from…
escopecz May 26, 2020
45b42eb
Refactoring templates from contact and company fields to mapped field…
escopecz May 26, 2020
2428a99
Refactoring out of contact and company fields to mapped fields part 2
escopecz May 28, 2020
4f04b9d
Shorter, readable syntax
escopecz May 29, 2020
1c89d5a
Mapped fields must be passed to select via country template
escopecz May 29, 2020
6eeaf10
The param cannot be null, but the value can. Retyping
escopecz May 29, 2020
ae508c0
Ensure we can edit a mapped field and the original value exists
escopecz May 29, 2020
28df0b3
Renaming private props for new use
escopecz May 29, 2020
7c01b90
Use field aliases over IDs as we used to
escopecz May 29, 2020
670f890
Toggle the "ingerit choices from mapped field" option for the list fi…
escopecz May 29, 2020
4b26004
Fixing company icon
escopecz May 29, 2020
d8f2e0b
Adding object to the notice about linked field
escopecz May 29, 2020
1074aca
Storing the original mapped field to a hidden field so we could exclu…
escopecz May 29, 2020
1bf0870
Removing unnecessary question mark
escopecz Jun 1, 2020
b9d141c
CS and PHPSTAN fixes
escopecz Jun 1, 2020
955f4cf
Adding missing dependency to a test
escopecz Jun 1, 2020
9a49058
mapped field must be type of string
escopecz Jun 1, 2020
f4a8834
Functional Form API test v1 added
escopecz Jun 1, 2020
e6dc2f8
Fixing tests
escopecz Jun 1, 2020
0c4f8c3
Ensuring the leadField will be filled from the mappedField and vice v…
escopecz Jun 1, 2020
e09c2d2
Trying to test submissions
escopecz Jun 2, 2020
8626b7a
More unit tests
escopecz Jun 2, 2020
de31a37
$leadFieldMatches should contain also company fields
escopecz Jun 3, 2020
9203684
New unit tests
escopecz Jun 3, 2020
600f54b
Removing unused method
escopecz Jun 3, 2020
2257ff5
Removing unused property
escopecz Jun 4, 2020
d2c4c7a
Fixing pre-selected mapping + test
escopecz Jun 4, 2020
7292ce8
Ensure the $type is string (can be null)
escopecz Jun 4, 2020
2c4802b
Test getting form field choices for specific object
escopecz Jun 4, 2020
071b8c0
Fixing progressive profiling
escopecz Jun 4, 2020
acbdd07
Removing dead code
escopecz Jun 9, 2020
3bf24f1
Ensuring the variables are always defined
escopecz Jun 9, 2020
5aafc60
return type comment fix
escopecz Jun 9, 2020
5db0e75
Adding tests requested in CR
escopecz Jun 9, 2020
05887b7
Fixed FormApiControllerFunctionalTest
fedys Oct 6, 2020
e4f4599
Fixing adding new captcha field to a form
escopecz Feb 22, 2021
ec41155
Test fixes
escopecz Jun 9, 2022
c13b1b6
CS fixes
escopecz Jun 9, 2022
ca42695
WIP test fixes
escopecz Jun 9, 2022
793d3f8
STAN fixes
escopecz Jun 14, 2022
c25feb4
Fix the issue after rebasing
volha-pivavarchyk Jan 23, 2023
477d6b4
Fix CS Fixer issues
volha-pivavarchyk Feb 1, 2023
29d07d9
Fix PHP Stan issues
volha-pivavarchyk Feb 1, 2023
f36a2a6
Fir Rector issues
volha-pivavarchyk Feb 1, 2023
ea91688
Fix tests
volha-pivavarchyk Feb 1, 2023
909950f
Update composer
volha-pivavarchyk Feb 1, 2023
eef7046
Merge branch '5.x' into form-object-refactoring
volha-pivavarchyk Feb 1, 2023
d7ae93c
Restore composer.lock
volha-pivavarchyk Feb 2, 2023
e1f1740
Merge branch '5.x' into form-object-refactoring
volha-pivavarchyk Feb 2, 2023
a16f942
comment out a test to check
volha-pivavarchyk Feb 2, 2023
201b4aa
Fix tests
volha-pivavarchyk Feb 3, 2023
7b03bd1
Fix PHP Stan issues
volha-pivavarchyk Feb 3, 2023
87a03bf
Fix PHPStan issues
volha-pivavarchyk Feb 3, 2023
d443b19
Merge branch '5.x' into form-object-refactoring
RCheesley Feb 4, 2023
d5a9620
Removing the commented code
volha-pivavarchyk Feb 6, 2023
3f4895b
CS Fixer fixes
volha-pivavarchyk Feb 6, 2023
85b99c7
Merge branch '5.x' into form-object-refactoring
adiux Feb 7, 2023
14cec4a
Merge branch '5.x' into form-object-refactoring
adiux Feb 8, 2023
b26e912
Merge branch '5.x' into form-object-refactoring
RCheesley Feb 8, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
39 changes: 37 additions & 2 deletions app/bundles/FormBundle/Assets/js/form.js
Expand Up @@ -107,7 +107,42 @@ Mautic.formBuilderNewComponentInit = function () {
mQuery(this).val('');
mQuery(this).trigger('chosen:updated');
});
}
};

Mautic.changeSelectOptions = function(selectEl, options) {
selectEl.empty();
mQuery.each(options, function(key, field) {
selectEl.append(
mQuery('<option></option>')
.attr('value', field.value)
.attr('data-list-type', field.isListType ? 1 : 0)
.text(field.label)
);
});
selectEl.trigger('chosen:updated');
};

Mautic.fetchFieldsOnObjectChange = function() {
var fieldSelect = mQuery('select#formfield_mappedField');
fieldSelect.attr('disable', true);
mQuery.ajax({
url: mauticAjaxUrl + "?action=form:getFieldsForObject",
data: {
mappedObject: mQuery('select#formfield_mappedObject').val(),
mappedField: mQuery('input#formfield_originalMappedField').val(),
formId: mQuery('input#mauticform_sessionId').val()
},
success: function (response) {
Mautic.changeSelectOptions(fieldSelect, response.fields);
},
error: function (response, textStatus, errorThrown) {
Mautic.processAjaxError(response, textStatus, errorThrown);
},
complete: function () {
fieldSelect.removeAttr('disable');
}
});
};

Mautic.updateFormFields = function () {
Mautic.activateLabelLoadingIndicator('campaignevent_properties_field');
Expand Down Expand Up @@ -344,4 +379,4 @@ Mautic.selectFormType = function(formType) {

mQuery('.form-type-modal').remove();
mQuery('.form-type-modal-backdrop').remove();
};
};
56 changes: 56 additions & 0 deletions app/bundles/FormBundle/Collection/FieldCollection.php
@@ -0,0 +1,56 @@
<?php

declare(strict_types=1);

namespace Mautic\FormBundle\Collection;

use Mautic\FormBundle\Crate\FieldCrate;
use Mautic\FormBundle\Exception\FieldNotFoundException;

/**
* @extends \ArrayIterator<int,FieldCrate>
*/
final class FieldCollection extends \ArrayIterator
{
/**
* @return array<string,string>
*/
public function toChoices(): array
{
$choices = [];

/** @var FieldCrate $field */
foreach ($this as $field) {
$choices[$field->getName()] = $field->getKey();
}

return $choices;
}

public function getFieldByKey(string $key): FieldCrate
{
/** @var FieldCrate $field */
foreach ($this as $field) {
if ($key === $field->getKey()) {
return $field;
}
}

throw new FieldNotFoundException("Field with key {$key} was not found.");
}

/**
* @param string[] $keys
*/
public function removeFieldsWithKeys(array $keys, string $keyToKeep = null): FieldCollection
{
return new self(
array_filter(
$this->getArrayCopy(),
function (FieldCrate $field) use ($keys, $keyToKeep) {
return ($keyToKeep && $field->getKey() === $keyToKeep) || !in_array($field->getKey(), $keys, true);
}
)
);
}
}
12 changes: 12 additions & 0 deletions app/bundles/FormBundle/Collection/MappedObjectCollection.php
@@ -0,0 +1,12 @@
<?php

declare(strict_types=1);

namespace Mautic\FormBundle\Collection;

/**
* @extends \ArrayIterator<string,FieldCollection>
*/
final class MappedObjectCollection extends \ArrayIterator
{
}
28 changes: 28 additions & 0 deletions app/bundles/FormBundle/Collection/ObjectCollection.php
@@ -0,0 +1,28 @@
<?php

declare(strict_types=1);

namespace Mautic\FormBundle\Collection;

use Mautic\FormBundle\Crate\ObjectCrate;

/**
* @extends \ArrayIterator<int,ObjectCrate>
*/
final class ObjectCollection extends \ArrayIterator
{
/**
* @return array<string,string>
*/
public function toChoices(): array
{
$choices = [];

/** @var ObjectCrate $object */
foreach ($this as $object) {
$choices[$object->getName()] = $object->getKey();
}

return $choices;
}
}
81 changes: 81 additions & 0 deletions app/bundles/FormBundle/Collector/AlreadyMappedFieldCollector.php
@@ -0,0 +1,81 @@
<?php

declare(strict_types=1);

namespace Mautic\FormBundle\Collector;

use Mautic\CacheBundle\Cache\CacheProviderInterface;

/**
* We need to store mapped fields in the form field builder so we could remove the used ones from the select box.
*/
final class AlreadyMappedFieldCollector implements AlreadyMappedFieldCollectorInterface
{
private const EXPIRATION_IN_SECONDS = 18000; // 5 hours

private CacheProviderInterface $cacheProvider;

public function __construct(CacheProviderInterface $cacheProvider)
{
$this->cacheProvider = $cacheProvider;
}

public function getFields(string $formId, string $object): array
{
$cacheItem = $this->cacheProvider->getItem($this->buildCacheKey($formId, $object));

return json_decode($cacheItem->get() ?? '[]', true);
}

public function addField(string $formId, string $object, string $fieldKey): void
{
$this->fetchAndSave($formId, $object, function (array $fields) use ($fieldKey) {
if (!in_array($fieldKey, $fields, true)) {
$fields[] = $fieldKey;
}

return $fields;
});
}

public function removeField(string $formId, string $object, string $fieldKey): void
{
$this->fetchAndSave($formId, $object, function (array $fields) use ($fieldKey) {
$cacheKey = array_search($fieldKey, $fields, true);

if (false !== $cacheKey) {
unset($fields[$cacheKey]);

// Reset indexes.
$fields = array_values($fields);
}

return $fields;
});
}

public function removeAllForForm(string $formId): void
{
$this->cacheProvider->invalidateTags([$this->buildCacheTag($formId)]);
}

private function fetchAndSave(string $formId, string $object, callable $callback): void
{
$cacheItem = $this->cacheProvider->getItem($this->buildCacheKey($formId, $object));
$fields = json_decode($cacheItem->get() ?? '[]', true);
$cacheItem->set(json_encode($callback($fields)));
$cacheItem->expiresAfter(self::EXPIRATION_IN_SECONDS);
$cacheItem->tag($this->buildCacheTag($formId));
$this->cacheProvider->save($cacheItem);
}

private function buildCacheKey(string $formId, string $object): string
{
return sprintf('mautic.form.%s.object.%s.fields.mapped', $formId, $object);
}

private function buildCacheTag(string $formId): string
{
return sprintf('mautic.form.%s.fields.mapped', $formId);
}
}
@@ -0,0 +1,24 @@
<?php

declare(strict_types=1);

namespace Mautic\FormBundle\Collector;

interface AlreadyMappedFieldCollectorInterface
{
/**
* @param string $formId can be a string hash for new forms
*
* @return mixed[]
*/
public function getFields(string $formId, string $object): array;

public function addField(string $formId, string $object, string $fieldKey): void;

public function removeField(string $formId, string $object, string $fieldKey): void;

/**
* Removes all mapped fields for the specified form.
*/
public function removeAllForForm(string $formId): void;
}
41 changes: 41 additions & 0 deletions app/bundles/FormBundle/Collector/FieldCollector.php
@@ -0,0 +1,41 @@
<?php

declare(strict_types=1);

namespace Mautic\FormBundle\Collector;

use Mautic\FormBundle\Collection\FieldCollection;
use Mautic\FormBundle\Event\FieldCollectEvent;
use Mautic\FormBundle\FormEvents;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;

final class FieldCollector implements FieldCollectorInterface
{
private EventDispatcherInterface $dispatcher;

/**
* @var FieldCollection[]
*/
private array $fieldCollections = [];

public function __construct(EventDispatcherInterface $dispatcher)
{
$this->dispatcher = $dispatcher;
}

public function getFields(string $object): FieldCollection
{
if (!isset($this->fieldCollections[$object])) {
$this->collect($object);
}

return $this->fieldCollections[$object];
}

private function collect(string $object): void
{
$event = new FieldCollectEvent($object);
$this->dispatcher->dispatch($event, FormEvents::ON_FIELD_COLLECT);
$this->fieldCollections[$object] = $event->getFields();
}
}
12 changes: 12 additions & 0 deletions app/bundles/FormBundle/Collector/FieldCollectorInterface.php
@@ -0,0 +1,12 @@
<?php

declare(strict_types=1);

namespace Mautic\FormBundle\Collector;

use Mautic\FormBundle\Collection\FieldCollection;

interface FieldCollectorInterface
{
public function getFields(string $object): FieldCollection;
}
30 changes: 30 additions & 0 deletions app/bundles/FormBundle/Collector/MappedObjectCollector.php
@@ -0,0 +1,30 @@
<?php

declare(strict_types=1);

namespace Mautic\FormBundle\Collector;

use Mautic\FormBundle\Collection\MappedObjectCollection;

final class MappedObjectCollector implements MappedObjectCollectorInterface
{
private FieldCollectorInterface $fieldCollector;

public function __construct(FieldCollectorInterface $fieldCollector)
{
$this->fieldCollector = $fieldCollector;
}

public function buildCollection(string ...$objects): MappedObjectCollection
{
$mappedObjectCollection = new MappedObjectCollection();

foreach ($objects as $object) {
if ($object) {
$mappedObjectCollection->offsetSet($object, $this->fieldCollector->getFields($object));
}
}

return $mappedObjectCollection;
}
}
@@ -0,0 +1,12 @@
<?php

declare(strict_types=1);

namespace Mautic\FormBundle\Collector;

use Mautic\FormBundle\Collection\MappedObjectCollection;

interface MappedObjectCollectorInterface
{
public function buildCollection(string ...$objects): MappedObjectCollection;
}
37 changes: 37 additions & 0 deletions app/bundles/FormBundle/Collector/ObjectCollector.php
@@ -0,0 +1,37 @@
<?php

declare(strict_types=1);

namespace Mautic\FormBundle\Collector;

use Mautic\FormBundle\Collection\ObjectCollection;
use Mautic\FormBundle\Event\ObjectCollectEvent;
use Mautic\FormBundle\FormEvents;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;

final class ObjectCollector implements ObjectCollectorInterface
{
private EventDispatcherInterface $dispatcher;
private ?ObjectCollection $objects = null;

public function __construct(EventDispatcherInterface $dispatcher)
{
$this->dispatcher = $dispatcher;
}

public function getObjects(): ObjectCollection
{
if (null === $this->objects) {
$this->collect();
}

return $this->objects;
}

private function collect(): void
{
$event = new ObjectCollectEvent();
$this->dispatcher->dispatch($event, FormEvents::ON_OBJECT_COLLECT);
$this->objects = $event->getObjects();
}
}