Initial commit: CloudOps infrastructure platform
This commit is contained in:
@@ -0,0 +1,330 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\FormBundle\Entity;
|
||||
|
||||
use ApiPlatform\Metadata\ApiResource;
|
||||
use ApiPlatform\Metadata\Delete;
|
||||
use ApiPlatform\Metadata\Get;
|
||||
use ApiPlatform\Metadata\GetCollection;
|
||||
use ApiPlatform\Metadata\Patch;
|
||||
use ApiPlatform\Metadata\Post;
|
||||
use ApiPlatform\Metadata\Put;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Mautic\ApiBundle\Serializer\Driver\ApiMetadataDriver;
|
||||
use Mautic\CoreBundle\Doctrine\Mapping\ClassMetadataBuilder;
|
||||
use Mautic\CoreBundle\Entity\UuidInterface;
|
||||
use Mautic\CoreBundle\Entity\UuidTrait;
|
||||
use Symfony\Component\Serializer\Attribute\Groups;
|
||||
use Symfony\Component\Validator\Constraints as Assert;
|
||||
use Symfony\Component\Validator\Mapping\ClassMetadata;
|
||||
|
||||
#[ApiResource(
|
||||
operations: [
|
||||
new GetCollection(security: "is_granted('form:forms:viewown')"),
|
||||
new Post(security: "is_granted('form:forms:create')"),
|
||||
new Get(security: "is_granted('form:forms:viewown')"),
|
||||
new Put(security: "is_granted('form:forms:editown')"),
|
||||
new Patch(security: "is_granted('form:forms:editother')"),
|
||||
new Delete(security: "is_granted('form:forms:deleteown')"),
|
||||
],
|
||||
normalizationContext: [
|
||||
'groups' => ['action:read'],
|
||||
'swagger_definition_name' => 'Read',
|
||||
],
|
||||
denormalizationContext: [
|
||||
'groups' => ['action:write'],
|
||||
'swagger_definition_name' => 'Write',
|
||||
]
|
||||
)]
|
||||
class Action implements UuidInterface
|
||||
{
|
||||
use UuidTrait;
|
||||
public const ENTITY_NAME = 'form_action';
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
#[Groups(['action:read', 'form:read'])]
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
#[Groups(['action:read', 'action:write', 'form:read'])]
|
||||
private $name;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
#[Groups(['action:read', 'action:write', 'form:read'])]
|
||||
private $description;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
#[Groups(['action:read', 'action:write', 'form:read'])]
|
||||
private $type;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
#[Groups(['action:read', 'action:write', 'form:read'])]
|
||||
private $order = 0;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
#[Groups(['action:read', 'action:write', 'form:read'])]
|
||||
private $properties = [];
|
||||
|
||||
/**
|
||||
* @var Form|null
|
||||
*/
|
||||
#[Groups(['action:read', 'action:write', 'form:read'])]
|
||||
private $form;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $changes;
|
||||
|
||||
public function __clone()
|
||||
{
|
||||
$this->id = null;
|
||||
$this->form = null;
|
||||
}
|
||||
|
||||
public static function loadMetadata(ORM\ClassMetadata $metadata): void
|
||||
{
|
||||
$builder = new ClassMetadataBuilder($metadata);
|
||||
|
||||
$builder->setTable('form_actions')
|
||||
->setCustomRepositoryClass(ActionRepository::class)
|
||||
->addIndex(['type'], 'form_action_type_search');
|
||||
|
||||
$builder->addIdColumns();
|
||||
|
||||
$builder->createField('type', 'string')
|
||||
->length(50)
|
||||
->build();
|
||||
|
||||
$builder->createField('order', 'integer')
|
||||
->columnName('action_order')
|
||||
->build();
|
||||
|
||||
$builder->addField('properties', 'array');
|
||||
|
||||
$builder->createManyToOne('form', 'Form')
|
||||
->inversedBy('actions')
|
||||
->addJoinColumn('form_id', 'id', false, false, 'CASCADE')
|
||||
->build();
|
||||
|
||||
static::addUuidField($builder);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares the metadata for API usage.
|
||||
*/
|
||||
public static function loadApiMetadata(ApiMetadataDriver $metadata): void
|
||||
{
|
||||
$metadata->setGroupPrefix('form')
|
||||
->addProperties(
|
||||
[
|
||||
'id',
|
||||
'name',
|
||||
'description',
|
||||
'type',
|
||||
'order',
|
||||
'properties',
|
||||
]
|
||||
)
|
||||
->build();
|
||||
}
|
||||
|
||||
public static function loadValidatorMetadata(ClassMetadata $metadata): void
|
||||
{
|
||||
$metadata->addPropertyConstraint('type', new Assert\NotBlank([
|
||||
'message' => 'mautic.core.name.required',
|
||||
'groups' => ['action'],
|
||||
]));
|
||||
}
|
||||
|
||||
private function isChanged($prop, $val): void
|
||||
{
|
||||
if ($this->$prop != $val) {
|
||||
$this->changes[$prop] = [$this->$prop, $val];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getChanges()
|
||||
{
|
||||
return $this->changes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get id.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set order.
|
||||
*
|
||||
* @param int $order
|
||||
*
|
||||
* @return Action
|
||||
*/
|
||||
public function setOrder($order)
|
||||
{
|
||||
$this->isChanged('order', $order);
|
||||
|
||||
$this->order = $order;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get order.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getOrder()
|
||||
{
|
||||
return $this->order;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set properties.
|
||||
*
|
||||
* @param array $properties
|
||||
*
|
||||
* @return Action
|
||||
*/
|
||||
public function setProperties($properties)
|
||||
{
|
||||
$this->isChanged('properties', $properties);
|
||||
|
||||
$this->properties = $properties;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get properties.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getProperties()
|
||||
{
|
||||
return $this->properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set form.
|
||||
*
|
||||
* @return Action
|
||||
*/
|
||||
public function setForm(Form $form)
|
||||
{
|
||||
$this->form = $form;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get form.
|
||||
*
|
||||
* @return Form|null
|
||||
*/
|
||||
public function getForm()
|
||||
{
|
||||
return $this->form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set type.
|
||||
*
|
||||
* @param string $type
|
||||
*
|
||||
* @return Action
|
||||
*/
|
||||
public function setType($type)
|
||||
{
|
||||
$this->isChanged('type', $type);
|
||||
$this->type = $type;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get type.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getType()
|
||||
{
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
public function convertToArray(): array
|
||||
{
|
||||
return get_object_vars($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set description.
|
||||
*
|
||||
* @param string $description
|
||||
*
|
||||
* @return Action
|
||||
*/
|
||||
public function setDescription($description)
|
||||
{
|
||||
$this->isChanged('description', $description);
|
||||
$this->description = $description;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get description.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDescription()
|
||||
{
|
||||
return $this->description;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set name.
|
||||
*
|
||||
* @param string $name
|
||||
*
|
||||
* @return Action
|
||||
*/
|
||||
public function setName($name)
|
||||
{
|
||||
$this->isChanged('name', $name);
|
||||
$this->name = $name;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\FormBundle\Entity;
|
||||
|
||||
use Mautic\CoreBundle\Entity\CommonRepository;
|
||||
|
||||
/**
|
||||
* @extends CommonRepository<Action>
|
||||
*/
|
||||
class ActionRepository extends CommonRepository
|
||||
{
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\FormBundle\Entity;
|
||||
|
||||
use Mautic\CoreBundle\Entity\CommonRepository;
|
||||
|
||||
/**
|
||||
* @extends CommonRepository<Field>
|
||||
*/
|
||||
class FieldRepository extends CommonRepository
|
||||
{
|
||||
public function fieldExistsByFormAndType(int $formId, string $type): bool
|
||||
{
|
||||
return (bool) $this->getEntityManager()->getConnection()->createQueryBuilder()
|
||||
->select('1')
|
||||
->from(MAUTIC_TABLE_PREFIX.Field::TABLE_NAME, 'f')
|
||||
->where('f.type = :type')
|
||||
->andWhere('f.form_id = :formId')
|
||||
->setParameter('type', $type)
|
||||
->setParameter('formId', $formId)
|
||||
->executeQuery()
|
||||
->fetchOne();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,940 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\FormBundle\Entity;
|
||||
|
||||
use ApiPlatform\Metadata\ApiResource;
|
||||
use ApiPlatform\Metadata\Delete;
|
||||
use ApiPlatform\Metadata\Get;
|
||||
use ApiPlatform\Metadata\GetCollection;
|
||||
use ApiPlatform\Metadata\Patch;
|
||||
use ApiPlatform\Metadata\Post;
|
||||
use ApiPlatform\Metadata\Put;
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Mautic\ApiBundle\Serializer\Driver\ApiMetadataDriver;
|
||||
use Mautic\CategoryBundle\Entity\Category;
|
||||
use Mautic\CoreBundle\Doctrine\Mapping\ClassMetadataBuilder;
|
||||
use Mautic\CoreBundle\Entity\FormEntity;
|
||||
use Mautic\CoreBundle\Entity\UuidInterface;
|
||||
use Mautic\CoreBundle\Entity\UuidTrait;
|
||||
use Mautic\CoreBundle\Helper\InputHelper;
|
||||
use Mautic\ProjectBundle\Entity\ProjectTrait;
|
||||
use Symfony\Component\Serializer\Attribute\Groups;
|
||||
use Symfony\Component\Validator\Constraints as Assert;
|
||||
use Symfony\Component\Validator\Mapping\ClassMetadata;
|
||||
|
||||
#[ApiResource(
|
||||
operations: [
|
||||
new GetCollection(security: "is_granted('form:forms:viewown')"),
|
||||
new Post(security: "is_granted('form:forms:create')"),
|
||||
new Get(security: "is_granted('form:forms:viewown')"),
|
||||
new Put(security: "is_granted('form:forms:editown')"),
|
||||
new Patch(security: "is_granted('form:forms:editother')"),
|
||||
new Delete(security: "is_granted('form:forms:deleteown')"),
|
||||
],
|
||||
normalizationContext: [
|
||||
'groups' => ['form:read'],
|
||||
'swagger_definition_name' => 'Read',
|
||||
'api_included' => ['category', 'fields', 'actions'],
|
||||
],
|
||||
denormalizationContext: [
|
||||
'groups' => ['form:write'],
|
||||
'swagger_definition_name' => 'Write',
|
||||
]
|
||||
)]
|
||||
class Form extends FormEntity implements UuidInterface
|
||||
{
|
||||
use UuidTrait;
|
||||
|
||||
use ProjectTrait;
|
||||
public const ENTITY_NAME = 'forms';
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
#[Groups(['form:read', 'download:read', 'campaign:read', 'email:read'])]
|
||||
private $id;
|
||||
|
||||
#[Groups(['form:read', 'form:write', 'download:read', 'campaign:read', 'email:read'])]
|
||||
private ?string $language = null;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
#[Groups(['form:read', 'form:write', 'download:read', 'campaign:read', 'email:read'])]
|
||||
private $name;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
#[Groups(['form:read', 'form:write', 'download:read', 'campaign:read', 'email:read'])]
|
||||
private $formAttributes;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
#[Groups(['form:read', 'form:write', 'download:read', 'campaign:read', 'email:read'])]
|
||||
private $description;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
#[Groups(['form:read', 'form:write', 'download:read', 'campaign:read', 'email:read'])]
|
||||
private $alias;
|
||||
|
||||
/**
|
||||
* @var Category|null
|
||||
**/
|
||||
#[Groups(['form:read', 'form:write', 'campaign:read', 'email:read'])]
|
||||
private $category;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
#[Groups(['form:read', 'download:read', 'campaign:read', 'email:read'])]
|
||||
private $cachedHtml;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
#[Groups(['form:read', 'form:write', 'download:read', 'campaign:read', 'email:read'])]
|
||||
private $postAction = 'message';
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
#[Groups(['form:read', 'form:write', 'download:read', 'campaign:read', 'email:read'])]
|
||||
private $postActionProperty;
|
||||
|
||||
/**
|
||||
* @var \DateTimeInterface
|
||||
*/
|
||||
#[Groups(['form:read', 'form:write', 'download:read', 'campaign:read', 'email:read'])]
|
||||
private $publishUp;
|
||||
|
||||
/**
|
||||
* @var \DateTimeInterface
|
||||
*/
|
||||
#[Groups(['form:read', 'form:write', 'download:read', 'campaign:read', 'email:read'])]
|
||||
private $publishDown;
|
||||
|
||||
/**
|
||||
* @var ArrayCollection<int, Field>
|
||||
*/
|
||||
#[Groups(['form:read', 'form:write', 'download:read', 'campaign:read', 'email:read'])]
|
||||
private $fields;
|
||||
|
||||
/**
|
||||
* @var ArrayCollection<string, Action>
|
||||
*/
|
||||
#[Groups(['form:read', 'form:write', 'download:read', 'campaign:read', 'email:read'])]
|
||||
private $actions;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
#[Groups(['form:read', 'form:write', 'download:read', 'campaign:read', 'email:read'])]
|
||||
private $template;
|
||||
|
||||
/**
|
||||
* @var bool|null
|
||||
*/
|
||||
#[Groups(['form:read', 'form:write', 'download:read', 'campaign:read', 'email:read'])]
|
||||
private $inKioskMode = false;
|
||||
|
||||
/**
|
||||
* @var bool|null
|
||||
*/
|
||||
#[Groups(['form:read', 'form:write', 'download:read', 'campaign:read', 'email:read'])]
|
||||
private $renderStyle = false;
|
||||
|
||||
/**
|
||||
* @var Collection<int, Submission>
|
||||
*/
|
||||
#[Groups(['form:read', 'download:read', 'campaign:read', 'email:read'])]
|
||||
private Collection $submissions;
|
||||
|
||||
#[Groups(['form:read', 'download:read', 'campaign:read', 'email:read'])]
|
||||
public int $submission_count = 0;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
#[Groups(['form:read', 'form:write', 'download:read', 'campaign:read', 'email:read'])]
|
||||
private $formType;
|
||||
|
||||
/**
|
||||
* @var bool|null
|
||||
*/
|
||||
#[Groups(['form:read', 'form:write', 'download:read', 'campaign:read', 'email:read'])]
|
||||
private $noIndex;
|
||||
|
||||
/**
|
||||
* @var int|null
|
||||
*/
|
||||
#[Groups(['form:read', 'form:write', 'download:read', 'campaign:read', 'email:read'])]
|
||||
private $progressiveProfilingLimit;
|
||||
|
||||
/**
|
||||
* This var is used to cache the result once gained from the loop.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
#[Groups(['form:read', 'form:write', 'download:read', 'campaign:read'])]
|
||||
private $usesProgressiveProfiling;
|
||||
|
||||
public function __clone()
|
||||
{
|
||||
$this->id = null;
|
||||
|
||||
parent::__clone();
|
||||
}
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->fields = new ArrayCollection();
|
||||
$this->actions = new ArrayCollection();
|
||||
$this->submissions = new ArrayCollection();
|
||||
$this->noIndex = true;
|
||||
$this->initializeProjects();
|
||||
}
|
||||
|
||||
public static function loadMetadata(ORM\ClassMetadata $metadata): void
|
||||
{
|
||||
$builder = new ClassMetadataBuilder($metadata);
|
||||
|
||||
$builder->setTable('forms')
|
||||
->setCustomRepositoryClass(FormRepository::class);
|
||||
|
||||
$builder->addIdColumns();
|
||||
|
||||
$builder->addField('alias', 'string');
|
||||
|
||||
$builder->createField('language', 'string')
|
||||
->columnName('lang')
|
||||
->nullable()
|
||||
->build();
|
||||
|
||||
$builder->addNullableField('formAttributes', 'string', 'form_attr');
|
||||
|
||||
$builder->addCategory();
|
||||
|
||||
$builder->createField('cachedHtml', 'text')
|
||||
->columnName('cached_html')
|
||||
->nullable()
|
||||
->build();
|
||||
|
||||
$builder->createField('postAction', 'string')
|
||||
->columnName('post_action')
|
||||
->build();
|
||||
|
||||
$builder->createField('postActionProperty', Types::TEXT)
|
||||
->columnName('post_action_property')
|
||||
->nullable()
|
||||
->build();
|
||||
|
||||
$builder->addPublishDates();
|
||||
|
||||
$builder->createOneToMany('fields', 'Field')
|
||||
->setIndexBy('id')
|
||||
->setOrderBy(['order' => 'ASC', 'id' => 'ASC'])
|
||||
->mappedBy('form')
|
||||
->cascadeAll()
|
||||
->fetchExtraLazy()
|
||||
->build();
|
||||
|
||||
$builder->createOneToMany('actions', 'Action')
|
||||
->setIndexBy('id')
|
||||
->setOrderBy(['order' => 'ASC'])
|
||||
->mappedBy('form')
|
||||
->cascadeAll()
|
||||
->fetchExtraLazy()
|
||||
->build();
|
||||
|
||||
$builder->createField('template', 'string')
|
||||
->nullable()
|
||||
->build();
|
||||
|
||||
$builder->createField('inKioskMode', 'boolean')
|
||||
->columnName('in_kiosk_mode')
|
||||
->nullable()
|
||||
->build();
|
||||
|
||||
$builder->createField('renderStyle', 'boolean')
|
||||
->columnName('render_style')
|
||||
->nullable()
|
||||
->build();
|
||||
|
||||
$builder->createOneToMany('submissions', 'Submission')
|
||||
->setOrderBy(['dateSubmitted' => 'DESC'])
|
||||
->mappedBy('form')
|
||||
->fetchExtraLazy()
|
||||
->build();
|
||||
|
||||
$builder->addNullableField('formType', 'string', 'form_type');
|
||||
|
||||
$builder->createField('noIndex', 'boolean')
|
||||
->columnName('no_index')
|
||||
->nullable()
|
||||
->build();
|
||||
|
||||
$builder->addNullableField('progressiveProfilingLimit', Types::INTEGER, 'progressive_profiling_limit');
|
||||
|
||||
static::addUuidField($builder);
|
||||
self::addProjectsField($builder, 'form_projects_xref', 'form_id');
|
||||
}
|
||||
|
||||
public static function loadValidatorMetadata(ClassMetadata $metadata): void
|
||||
{
|
||||
$metadata->addPropertyConstraint('name', new Assert\NotBlank([
|
||||
'message' => 'mautic.core.name.required',
|
||||
'groups' => ['form'],
|
||||
]));
|
||||
|
||||
$metadata->addPropertyConstraint('postActionProperty', new Assert\NotBlank([
|
||||
'message' => 'mautic.form.form.postactionproperty_message.notblank',
|
||||
'groups' => ['messageRequired'],
|
||||
]));
|
||||
|
||||
$metadata->addPropertyConstraint('postActionProperty', new Assert\NotBlank([
|
||||
'message' => 'mautic.form.form.postactionproperty_redirect.notblank',
|
||||
'groups' => ['urlRequired'],
|
||||
]));
|
||||
|
||||
$metadata->addPropertyConstraint('postActionProperty', new Assert\Url([
|
||||
'message' => 'mautic.form.form.postactionproperty_redirect.notblank',
|
||||
'groups' => ['urlRequiredPassTwo'],
|
||||
]));
|
||||
|
||||
$metadata->addPropertyConstraint('postActionProperty', new Assert\NotBlank([
|
||||
'message' => 'mautic.form.form.postactionproperty_hideform.notblank',
|
||||
'groups' => ['hideformRequired'],
|
||||
]));
|
||||
|
||||
$metadata->addPropertyConstraint('formType', new Assert\Choice([
|
||||
'choices' => ['standalone', 'campaign'],
|
||||
]));
|
||||
|
||||
$metadata->addPropertyConstraint('progressiveProfilingLimit', new Assert\GreaterThan([
|
||||
'value' => 0,
|
||||
'message' => 'mautic.form.form.progressive_profiling_limit.error',
|
||||
'groups' => ['progressiveProfilingLimit'],
|
||||
]));
|
||||
}
|
||||
|
||||
public static function determineValidationGroups(\Symfony\Component\Form\Form $form): array
|
||||
{
|
||||
$data = $form->getData();
|
||||
$groups = ['form'];
|
||||
|
||||
$postAction = $data->getPostAction();
|
||||
|
||||
if ('message' == $postAction) {
|
||||
$groups[] = 'messageRequired';
|
||||
} elseif ('redirect' == $postAction) {
|
||||
$groups[] = 'urlRequired';
|
||||
} elseif ('hideform' == $postAction) {
|
||||
$groups[] = 'hideformRequired';
|
||||
}
|
||||
|
||||
if ('' != $data->getProgressiveProfilingLimit()) {
|
||||
$groups[] = 'progressiveProfilingLimit';
|
||||
}
|
||||
|
||||
return $groups;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares the metadata for API usage.
|
||||
*/
|
||||
public static function loadApiMetadata(ApiMetadataDriver $metadata): void
|
||||
{
|
||||
$metadata->setGroupPrefix('form')
|
||||
->addListProperties(
|
||||
[
|
||||
'id',
|
||||
'name',
|
||||
'alias',
|
||||
'category',
|
||||
]
|
||||
)
|
||||
->addProperties(
|
||||
[
|
||||
'description',
|
||||
'cachedHtml',
|
||||
'publishUp',
|
||||
'publishDown',
|
||||
'fields',
|
||||
'actions',
|
||||
'template',
|
||||
'inKioskMode',
|
||||
'renderStyle',
|
||||
'formType',
|
||||
'postAction',
|
||||
'postActionProperty',
|
||||
'noIndex',
|
||||
'formAttributes',
|
||||
'language',
|
||||
]
|
||||
)
|
||||
->build();
|
||||
|
||||
self::addProjectsInLoadApiMetadata($metadata, 'form');
|
||||
}
|
||||
|
||||
protected function isChanged($prop, $val)
|
||||
{
|
||||
if ('actions' == $prop || 'fields' == $prop) {
|
||||
// changes are already computed so just add them
|
||||
$this->changes[$prop][$val[0]] = $val[1];
|
||||
} else {
|
||||
parent::isChanged($prop, $val);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
*
|
||||
* @return Form
|
||||
*/
|
||||
public function setName($name)
|
||||
{
|
||||
$this->isChanged('name', $name);
|
||||
$this->name = $name;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $description
|
||||
*
|
||||
* @return Form
|
||||
*/
|
||||
public function setDescription($description)
|
||||
{
|
||||
$this->isChanged('description', $description);
|
||||
$this->description = $description;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getDescription($truncate = false, $length = 45)
|
||||
{
|
||||
if ($truncate) {
|
||||
if (strlen($this->description) > $length) {
|
||||
return substr($this->description, 0, $length).'...';
|
||||
}
|
||||
}
|
||||
|
||||
return $this->description;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $cachedHtml
|
||||
*
|
||||
* @return Form
|
||||
*/
|
||||
public function setCachedHtml($cachedHtml)
|
||||
{
|
||||
$this->cachedHtml = $cachedHtml;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getCachedHtml()
|
||||
{
|
||||
return $this->cachedHtml;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool|null
|
||||
*/
|
||||
public function getRenderStyle()
|
||||
{
|
||||
return $this->renderStyle;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $postAction
|
||||
*
|
||||
* @return Form
|
||||
*/
|
||||
public function setPostAction($postAction)
|
||||
{
|
||||
$this->isChanged('postAction', $postAction);
|
||||
$this->postAction = $postAction;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getPostAction()
|
||||
{
|
||||
return $this->postAction;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $postActionProperty
|
||||
*
|
||||
* @return Form
|
||||
*/
|
||||
public function setPostActionProperty($postActionProperty)
|
||||
{
|
||||
$this->isChanged('postActionProperty', $postActionProperty);
|
||||
$this->postActionProperty = $postActionProperty;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getPostActionProperty(): ?string
|
||||
{
|
||||
if ('return' === $this->getPostAction()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->postActionProperty;
|
||||
}
|
||||
|
||||
public function getResultCount(): int
|
||||
{
|
||||
return count($this->submissions);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \DateTime $publishUp
|
||||
*
|
||||
* @return Form
|
||||
*/
|
||||
public function setPublishUp($publishUp)
|
||||
{
|
||||
$this->isChanged('publishUp', $publishUp);
|
||||
$this->publishUp = $publishUp;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \DateTimeInterface
|
||||
*/
|
||||
public function getPublishUp()
|
||||
{
|
||||
return $this->publishUp;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \DateTime $publishDown
|
||||
*
|
||||
* @return Form
|
||||
*/
|
||||
public function setPublishDown($publishDown)
|
||||
{
|
||||
$this->isChanged('publishDown', $publishDown);
|
||||
$this->publishDown = $publishDown;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \DateTimeInterface
|
||||
*/
|
||||
public function getPublishDown()
|
||||
{
|
||||
return $this->publishDown;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int|string $key
|
||||
*
|
||||
* @return Form
|
||||
*/
|
||||
public function addField($key, Field $field)
|
||||
{
|
||||
if ($changes = $field->getChanges()) {
|
||||
$this->isChanged('fields', [$key, $changes]);
|
||||
}
|
||||
$this->fields[$key] = $field;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int|string $key
|
||||
*/
|
||||
public function removeField($key, Field $field): void
|
||||
{
|
||||
if ($changes = $field->getChanges()) {
|
||||
$this->isChanged('fields', [$key, $changes]);
|
||||
}
|
||||
$this->fields->removeElement($field);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ArrayCollection<int, Field>
|
||||
*/
|
||||
public function getFields()
|
||||
{
|
||||
return $this->fields;
|
||||
}
|
||||
|
||||
public function getFieldAliases(): array
|
||||
{
|
||||
$aliases = [];
|
||||
$fields = $this->getFields();
|
||||
|
||||
if ($fields) {
|
||||
foreach ($fields as $field) {
|
||||
$aliases[] = $field->getAlias();
|
||||
}
|
||||
}
|
||||
|
||||
return $aliases;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loops through the form fields and returns an array of fields with mapped data.
|
||||
*
|
||||
* @return array<int, array<string, int|string>>
|
||||
*/
|
||||
public function getMappedFieldValues(): array
|
||||
{
|
||||
return array_filter(
|
||||
array_map(
|
||||
fn (Field $field): array => [
|
||||
'formFieldId' => $field->getId(),
|
||||
'mappedObject' => $field->getMappedObject(),
|
||||
'mappedField' => $field->getMappedField(),
|
||||
],
|
||||
$this->getFields()->getValues()
|
||||
),
|
||||
fn ($elem) => isset($elem['mappedObject']) && isset($elem['mappedField'])
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loops trough the form fields and returns a simple array of mapped object keys if any.
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public function getMappedFieldObjects(): array
|
||||
{
|
||||
return array_values(
|
||||
array_filter(
|
||||
array_unique(
|
||||
$this->getFields()->map(
|
||||
fn (Field $field) => $field->getMappedObject()
|
||||
)->toArray()
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $alias
|
||||
*
|
||||
* @return Form
|
||||
*/
|
||||
public function setAlias($alias)
|
||||
{
|
||||
$this->isChanged('alias', $alias);
|
||||
$this->alias = $alias;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getAlias()
|
||||
{
|
||||
return $this->alias;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Form
|
||||
*/
|
||||
public function addSubmission(Submission $submissions)
|
||||
{
|
||||
$this->submissions[] = $submissions;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function removeSubmission(Submission $submissions): void
|
||||
{
|
||||
$this->submissions->removeElement($submissions);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Collection|Submission[]
|
||||
*/
|
||||
public function getSubmissions()
|
||||
{
|
||||
return $this->submissions;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int|string $key
|
||||
*
|
||||
* @return Form
|
||||
*/
|
||||
public function addAction($key, Action $action)
|
||||
{
|
||||
if ($changes = $action->getChanges()) {
|
||||
$this->isChanged('actions', [$key, $changes]);
|
||||
}
|
||||
$this->actions[$key] = $action;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function removeAction(Action $action): void
|
||||
{
|
||||
$this->actions->removeElement($action);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all actions.
|
||||
*/
|
||||
public function clearActions(): void
|
||||
{
|
||||
$this->actions = new ArrayCollection();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ArrayCollection<string, Action>
|
||||
*/
|
||||
public function getActions()
|
||||
{
|
||||
return $this->actions;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getCategory()
|
||||
{
|
||||
return $this->category;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $category
|
||||
*/
|
||||
public function setCategory($category): void
|
||||
{
|
||||
$this->category = $category;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getTemplate()
|
||||
{
|
||||
return $this->template;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $template
|
||||
*/
|
||||
public function setTemplate($template): void
|
||||
{
|
||||
$this->template = $template;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getInKioskMode()
|
||||
{
|
||||
return $this->inKioskMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $inKioskMode
|
||||
*/
|
||||
public function setInKioskMode($inKioskMode): void
|
||||
{
|
||||
$this->inKioskMode = $inKioskMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $renderStyle
|
||||
*/
|
||||
public function setRenderStyle($renderStyle): void
|
||||
{
|
||||
$this->renderStyle = $renderStyle;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function isInKioskMode()
|
||||
{
|
||||
return $this->getInKioskMode();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getFormType()
|
||||
{
|
||||
return $this->formType;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $formType
|
||||
*
|
||||
* @return Form
|
||||
*/
|
||||
public function setFormType($formType)
|
||||
{
|
||||
$this->formType = $formType;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool|null $noIndex
|
||||
*/
|
||||
public function setNoIndex($noIndex): void
|
||||
{
|
||||
$sanitizedNoIndex = null === $noIndex ? null : (bool) $noIndex;
|
||||
$this->isChanged('noIndex', $sanitizedNoIndex);
|
||||
$this->noIndex = $sanitizedNoIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool|null
|
||||
*/
|
||||
public function getNoIndex()
|
||||
{
|
||||
return $this->noIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $formAttributes
|
||||
*
|
||||
* @return Form
|
||||
*/
|
||||
public function setFormAttributes($formAttributes)
|
||||
{
|
||||
$this->isChanged('formAttributes', $formAttributes);
|
||||
$this->formAttributes = $formAttributes;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getFormAttributes()
|
||||
{
|
||||
return $this->formAttributes;
|
||||
}
|
||||
|
||||
public function setLanguage(?string $language): self
|
||||
{
|
||||
$this->isChanged('language', $language);
|
||||
$this->language = $language;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getLanguage(): ?string
|
||||
{
|
||||
return $this->language;
|
||||
}
|
||||
|
||||
public function isStandalone(): bool
|
||||
{
|
||||
return 'campaign' != $this->formType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a form name for HTML attributes.
|
||||
*/
|
||||
public function generateFormName(): string
|
||||
{
|
||||
return $this->name ? strtolower(InputHelper::alphanum(InputHelper::transliterate($this->name))) : 'form-'.$this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if some Progressive Profiling setting is turned on on any of the form fields.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function usesProgressiveProfiling()
|
||||
{
|
||||
if (null !== $this->usesProgressiveProfiling) {
|
||||
return $this->usesProgressiveProfiling;
|
||||
}
|
||||
|
||||
// Progressive profiling must be turned off in the kiosk mode
|
||||
if (false === $this->getInKioskMode()) {
|
||||
if ('' != $this->getProgressiveProfilingLimit()) {
|
||||
$this->usesProgressiveProfiling = true;
|
||||
|
||||
return $this->usesProgressiveProfiling;
|
||||
}
|
||||
|
||||
// Search for a field with a progressive profiling setting on
|
||||
foreach ($this->fields->toArray() as $field) {
|
||||
if (false === $field->getShowWhenValueExists() || $field->getShowAfterXSubmissions() > 0) {
|
||||
$this->usesProgressiveProfiling = true;
|
||||
|
||||
return $this->usesProgressiveProfiling;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->usesProgressiveProfiling = false;
|
||||
|
||||
return $this->usesProgressiveProfiling;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $progressiveProfilingLimit
|
||||
*
|
||||
* @return Form
|
||||
*/
|
||||
public function setProgressiveProfilingLimit($progressiveProfilingLimit)
|
||||
{
|
||||
$this->isChanged('progressiveProfilingLimit', $progressiveProfilingLimit);
|
||||
$this->progressiveProfilingLimit = $progressiveProfilingLimit;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getProgressiveProfilingLimit()
|
||||
{
|
||||
return $this->progressiveProfilingLimit;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,267 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\FormBundle\Entity;
|
||||
|
||||
use Doctrine\ORM\Query\Expr\Join;
|
||||
use Doctrine\ORM\Tools\Pagination\Paginator;
|
||||
use Mautic\CoreBundle\Entity\CommonRepository;
|
||||
use Mautic\CoreBundle\Event\GlobalSearchEvent;
|
||||
use Mautic\ProjectBundle\Entity\ProjectRepositoryTrait;
|
||||
|
||||
/**
|
||||
* @extends CommonRepository<Form>
|
||||
*/
|
||||
class FormRepository extends CommonRepository
|
||||
{
|
||||
use ProjectRepositoryTrait;
|
||||
|
||||
public function getEntities(array $args = [])
|
||||
{
|
||||
$q = $this->createQueryBuilder('f');
|
||||
$q->select('f');
|
||||
|
||||
// use a subquery to get a count of submissions otherwise doctrine will not pull all of the results
|
||||
$sq = $this->_em->createQueryBuilder()
|
||||
->select('count(fs.id)')
|
||||
->from(Submission::class, 'fs')
|
||||
->where('fs.form = f');
|
||||
|
||||
$q->addSelect('('.$sq->getDql().') as submission_count');
|
||||
$q->leftJoin('f.category', 'c');
|
||||
|
||||
$args['qb'] = $q;
|
||||
|
||||
return parent::getEntities($args);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, string|array<int, array<int|string, int|string|bool|null>>> $filter
|
||||
*/
|
||||
public function getEntitiesForGlobalSearch(array $filter): Paginator
|
||||
{
|
||||
$q = $this->createQueryBuilder('f');
|
||||
$q->select('f');
|
||||
|
||||
$args = [
|
||||
'qb' => $q,
|
||||
'filter' => $filter,
|
||||
'start' => 0,
|
||||
'limit' => GlobalSearchEvent::RESULTS_LIMIT,
|
||||
'ignore_paginator' => false,
|
||||
];
|
||||
|
||||
return parent::getEntities($args);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $search
|
||||
* @param int $limit
|
||||
* @param int $start
|
||||
* @param bool $viewOther
|
||||
*/
|
||||
public function getFormList($search = '', $limit = 10, $start = 0, $viewOther = false, $formType = null): array
|
||||
{
|
||||
$q = $this->createQueryBuilder('f');
|
||||
$q->select('partial f.{id, name, alias}');
|
||||
|
||||
if (!empty($search)) {
|
||||
$q->andWhere($q->expr()->like('f.name', ':search'))
|
||||
->setParameter('search', "{$search}%");
|
||||
}
|
||||
|
||||
if (!$viewOther) {
|
||||
$q->andWhere($q->expr()->eq('f.createdBy', ':id'))
|
||||
->setParameter('id', $this->currentUser->getId());
|
||||
}
|
||||
|
||||
if (!empty($formType)) {
|
||||
$q->andWhere(
|
||||
$q->expr()->eq('f.formType', ':type')
|
||||
)->setParameter('type', $formType);
|
||||
}
|
||||
|
||||
$q->orderBy('f.name');
|
||||
|
||||
if (!empty($limit)) {
|
||||
$q->setFirstResult($start)
|
||||
->setMaxResults($limit);
|
||||
}
|
||||
|
||||
return $q->getQuery()->getArrayResult();
|
||||
}
|
||||
|
||||
protected function addCatchAllWhereClause($q, $filter): array
|
||||
{
|
||||
return $this->addStandardCatchAllWhereClause($q, $filter, [
|
||||
'f.name',
|
||||
'f.description',
|
||||
]);
|
||||
}
|
||||
|
||||
protected function addSearchCommandWhereClause($q, $filter): array
|
||||
{
|
||||
[$expr, $standardSearchParameters] = $this->addStandardSearchCommandWhereClause($q, $filter);
|
||||
if ($expr) {
|
||||
return [$expr, $standardSearchParameters];
|
||||
}
|
||||
|
||||
$command = $filter->command;
|
||||
$unique = $this->generateRandomParameterName();
|
||||
$parameters = [];
|
||||
$returnParameter = false; // returning a parameter that is not used will lead to a Doctrine error
|
||||
|
||||
switch ($command) {
|
||||
case $this->translator->trans('mautic.form.form.searchcommand.isexpired'):
|
||||
case $this->translator->trans('mautic.form.form.searchcommand.isexpired', [], null, 'en_US'):
|
||||
$expr = $q->expr()->and(
|
||||
$q->expr()->eq('f.isPublished', ":$unique"),
|
||||
$q->expr()->isNotNull('f.publishDown'),
|
||||
$q->expr()->neq('f.publishDown', $q->expr()->literal('')),
|
||||
$q->expr()->lt('f.publishDown', 'CURRENT_TIMESTAMP()')
|
||||
);
|
||||
$forceParameters = [$unique => true];
|
||||
break;
|
||||
case $this->translator->trans('mautic.form.form.searchcommand.ispending'):
|
||||
case $this->translator->trans('mautic.form.form.searchcommand.ispending', [], null, 'en_US'):
|
||||
$expr = $q->expr()->and(
|
||||
$q->expr()->eq('f.isPublished', ":$unique"),
|
||||
$q->expr()->isNotNull('f.publishUp'),
|
||||
$q->expr()->neq('f.publishUp', $q->expr()->literal('')),
|
||||
$q->expr()->gt('f.publishUp', 'CURRENT_TIMESTAMP()')
|
||||
);
|
||||
$forceParameters = [$unique => true];
|
||||
break;
|
||||
case $this->translator->trans('mautic.form.form.searchcommand.hasresults'):
|
||||
case $this->translator->trans('mautic.form.form.searchcommand.hasresults', [], null, 'en_US'):
|
||||
$sq = $this->getEntityManager()->createQueryBuilder();
|
||||
$subquery = $sq->select('count(s.id)')
|
||||
->from(Submission::class, 's')
|
||||
->leftJoin(Form::class, 'f2',
|
||||
Join::WITH,
|
||||
$sq->expr()->eq('s.form', 'f2')
|
||||
)
|
||||
->where(
|
||||
$q->expr()->eq('s.form', 'f')
|
||||
)
|
||||
->getDql();
|
||||
$expr = $q->expr()->gt(sprintf('(%s)', $subquery), 1);
|
||||
break;
|
||||
case $this->translator->trans('mautic.core.searchcommand.name'):
|
||||
case $this->translator->trans('mautic.core.searchcommand.name', [], null, 'en_US'):
|
||||
$expr = $q->expr()->like('f.name', ':'.$unique);
|
||||
$returnParameter = true;
|
||||
break;
|
||||
case $this->translator->trans('mautic.project.searchcommand.name'):
|
||||
case $this->translator->trans('mautic.project.searchcommand.name', [], null, 'en_US'):
|
||||
return $this->handleProjectFilter(
|
||||
$this->_em->getConnection()->createQueryBuilder(),
|
||||
'form_id',
|
||||
'form_projects_xref',
|
||||
$this->getTableAlias(),
|
||||
$filter->string,
|
||||
$filter->not
|
||||
);
|
||||
}
|
||||
|
||||
if ($expr && $filter->not) {
|
||||
$expr = $q->expr()->not($expr);
|
||||
}
|
||||
|
||||
if (!empty($forceParameters)) {
|
||||
$parameters = $forceParameters;
|
||||
} elseif ($returnParameter) {
|
||||
$string = ($filter->strict) ? $filter->string : "%{$filter->string}%";
|
||||
$parameters = ["$unique" => $string];
|
||||
}
|
||||
|
||||
return [
|
||||
$expr,
|
||||
$parameters,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the form results.
|
||||
*
|
||||
* @throws \Doctrine\ORM\NoResultException
|
||||
* @throws \Doctrine\ORM\NonUniqueResultException
|
||||
*/
|
||||
public function getFormResults(Form $form, array $options = []): array
|
||||
{
|
||||
$query = $this->_em->getConnection()->createQueryBuilder();
|
||||
|
||||
$query->from(MAUTIC_TABLE_PREFIX.'form_submissions', 'fs')
|
||||
->select('fr.*')
|
||||
->leftJoin('fs', $this->getResultsTableName($form->getId(), $form->getAlias()), 'fr', 'fr.submission_id = fs.id')
|
||||
->where('fs.form_id = :formId')
|
||||
->setParameter('formId', $form->getId());
|
||||
|
||||
if (!empty($options['leadId'])) {
|
||||
$query->andWhere('fs.lead_id = :leadId')
|
||||
->setParameter('leadId', $options['leadId']);
|
||||
}
|
||||
|
||||
if (!empty($options['formId'])) {
|
||||
$query->andWhere($query->expr()->eq('fs.form_id', ':id'))
|
||||
->setParameter('id', $options['formId']);
|
||||
}
|
||||
|
||||
if (!empty($options['limit'])) {
|
||||
$query->setMaxResults((int) $options['limit']);
|
||||
}
|
||||
|
||||
return $query->executeQuery()->fetchAllAssociative();
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile and return the form result table name.
|
||||
*
|
||||
* @param int $formId
|
||||
* @param string $formAlias
|
||||
*/
|
||||
public function getResultsTableName($formId, $formAlias): string
|
||||
{
|
||||
return MAUTIC_TABLE_PREFIX.'form_results_'.$formId.'_'.$formAlias;
|
||||
}
|
||||
|
||||
public function getFormTableIdViaResults(string $resultsTableName): ?string
|
||||
{
|
||||
$regexp = '/.*'.MAUTIC_TABLE_PREFIX.'form_results_([0-9]+)_(.*)/i';
|
||||
preg_match($regexp, $resultsTableName, $matches);
|
||||
|
||||
return $matches[1] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public function getSearchCommands(): array
|
||||
{
|
||||
$commands = [
|
||||
'mautic.core.searchcommand.ispublished',
|
||||
'mautic.core.searchcommand.isunpublished',
|
||||
'mautic.core.searchcommand.isuncategorized',
|
||||
'mautic.core.searchcommand.ismine',
|
||||
'mautic.form.form.searchcommand.isexpired',
|
||||
'mautic.form.form.searchcommand.ispending',
|
||||
'mautic.form.form.searchcommand.hasresults',
|
||||
'mautic.core.searchcommand.category',
|
||||
'mautic.core.searchcommand.name',
|
||||
'mautic.project.searchcommand.name',
|
||||
];
|
||||
|
||||
return array_merge($commands, parent::getSearchCommands());
|
||||
}
|
||||
|
||||
protected function getDefaultOrder(): array
|
||||
{
|
||||
return [
|
||||
['f.name', 'ASC'],
|
||||
];
|
||||
}
|
||||
|
||||
public function getTableAlias(): string
|
||||
{
|
||||
return 'f';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,337 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\FormBundle\Entity;
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Mautic\ApiBundle\Serializer\Driver\ApiMetadataDriver;
|
||||
use Mautic\CoreBundle\Doctrine\Mapping\ClassMetadataBuilder;
|
||||
use Mautic\CoreBundle\Entity\IpAddress;
|
||||
use Mautic\LeadBundle\Entity\Lead;
|
||||
use Mautic\PageBundle\Entity\Page;
|
||||
|
||||
class Submission
|
||||
{
|
||||
public const TABLE_NAME = 'form_submissions';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @var Form
|
||||
**/
|
||||
private $form;
|
||||
|
||||
/**
|
||||
* @var IpAddress|null
|
||||
*/
|
||||
private $ipAddress;
|
||||
|
||||
/**
|
||||
* @var Lead|null
|
||||
*/
|
||||
private $lead;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $trackingId;
|
||||
|
||||
/**
|
||||
* @var \DateTimeInterface
|
||||
*/
|
||||
private $dateSubmitted;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $referer;
|
||||
|
||||
/**
|
||||
* @var Page|null
|
||||
*/
|
||||
private $page;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $results = [];
|
||||
|
||||
public static function loadMetadata(ORM\ClassMetadata $metadata): void
|
||||
{
|
||||
$builder = new ClassMetadataBuilder($metadata);
|
||||
|
||||
$builder->setTable(self::TABLE_NAME)
|
||||
->setCustomRepositoryClass(SubmissionRepository::class)
|
||||
->addIndex(['tracking_id'], 'form_submission_tracking_search')
|
||||
->addIndex(['date_submitted'], 'form_date_submitted');
|
||||
|
||||
$builder->addBigIntIdField();
|
||||
|
||||
$builder->createManyToOne('form', 'Form')
|
||||
->inversedBy('submissions')
|
||||
->addJoinColumn('form_id', 'id', false, false, 'CASCADE')
|
||||
->build();
|
||||
|
||||
$builder->addIpAddress(true);
|
||||
|
||||
$builder->addLead(true, 'SET NULL');
|
||||
|
||||
$builder->createField('trackingId', 'string')
|
||||
->columnName('tracking_id')
|
||||
->nullable()
|
||||
->build();
|
||||
|
||||
$builder->createField('dateSubmitted', 'datetime')
|
||||
->columnName('date_submitted')
|
||||
->build();
|
||||
|
||||
$builder->addField('referer', 'text');
|
||||
|
||||
$builder->createManyToOne('page', Page::class)
|
||||
->addJoinColumn('page_id', 'id', true, false, 'SET NULL')
|
||||
->fetchExtraLazy()
|
||||
->build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares the metadata for API usage.
|
||||
*/
|
||||
public static function loadApiMetadata(ApiMetadataDriver $metadata): void
|
||||
{
|
||||
$metadata->setGroupPrefix('submission')
|
||||
->addProperties(
|
||||
[
|
||||
'id',
|
||||
'ipAddress',
|
||||
'form',
|
||||
'lead',
|
||||
'trackingId',
|
||||
'dateSubmitted',
|
||||
'referer',
|
||||
'page',
|
||||
'results',
|
||||
]
|
||||
)
|
||||
->setGroupPrefix('submissionEvent')
|
||||
->addProperties(
|
||||
[
|
||||
'id',
|
||||
'ipAddress',
|
||||
'form',
|
||||
'trackingId',
|
||||
'dateSubmitted',
|
||||
'referer',
|
||||
'page',
|
||||
'results',
|
||||
]
|
||||
)
|
||||
->build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get id.
|
||||
*/
|
||||
public function getId(): int
|
||||
{
|
||||
return (int) $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set dateSubmitted.
|
||||
*
|
||||
* @param \DateTime $dateSubmitted
|
||||
*
|
||||
* @return Submission
|
||||
*/
|
||||
public function setDateSubmitted($dateSubmitted)
|
||||
{
|
||||
$this->dateSubmitted = $dateSubmitted;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get dateSubmitted.
|
||||
*
|
||||
* @return \DateTimeInterface
|
||||
*/
|
||||
public function getDateSubmitted()
|
||||
{
|
||||
return $this->dateSubmitted;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set referer.
|
||||
*
|
||||
* @param string $referer
|
||||
*
|
||||
* @return Submission
|
||||
*/
|
||||
public function setReferer($referer)
|
||||
{
|
||||
$this->referer = $referer;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get referer.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getReferer()
|
||||
{
|
||||
return $this->referer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set form.
|
||||
*
|
||||
* @return Submission
|
||||
*/
|
||||
public function setForm(Form $form)
|
||||
{
|
||||
$this->form = $form;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get form.
|
||||
*
|
||||
* @return Form
|
||||
*/
|
||||
public function getForm()
|
||||
{
|
||||
return $this->form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set ipAddress.
|
||||
*
|
||||
* @return Submission
|
||||
*/
|
||||
public function setIpAddress(?IpAddress $ipAddress = null)
|
||||
{
|
||||
$this->ipAddress = $ipAddress;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return IpAddress
|
||||
*/
|
||||
public function getIpAddress()
|
||||
{
|
||||
return $this->ipAddress;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get results.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getResults()
|
||||
{
|
||||
return $this->results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get results.
|
||||
*
|
||||
* @return Submission
|
||||
*/
|
||||
public function setResults($results)
|
||||
{
|
||||
$this->results = $results;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set page.
|
||||
*
|
||||
* @return Submission
|
||||
*/
|
||||
public function setPage(?Page $page = null)
|
||||
{
|
||||
$this->page = $page;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get page.
|
||||
*
|
||||
* @return Page
|
||||
*/
|
||||
public function getPage()
|
||||
{
|
||||
return $this->page;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Lead
|
||||
*/
|
||||
public function getLead()
|
||||
{
|
||||
return $this->lead;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return $this
|
||||
*/
|
||||
public function setLead(?Lead $lead = null)
|
||||
{
|
||||
$this->lead = $lead;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getTrackingId()
|
||||
{
|
||||
return $this->trackingId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return $this
|
||||
*/
|
||||
public function setTrackingId($trackingId)
|
||||
{
|
||||
$this->trackingId = $trackingId;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is used by standard entity algorithms to check if the current
|
||||
* user has permission to view/edit/delete this item. Provide the form creator for it.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getCreatedBy()
|
||||
{
|
||||
return $this->getForm()->getCreatedBy();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $alias
|
||||
*
|
||||
* @return Field|null
|
||||
*/
|
||||
public function getFieldByAlias($alias)
|
||||
{
|
||||
foreach ($this->getForm()->getFields() as $field) {
|
||||
if ($field->getAlias() === $alias) {
|
||||
return $field;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,534 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\FormBundle\Entity;
|
||||
|
||||
use Doctrine\Common\Collections\Order;
|
||||
use Doctrine\DBAL\Query\QueryBuilder as DbalQueryBuilder;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Mautic\CoreBundle\Entity\CommonRepository;
|
||||
use Mautic\CoreBundle\Helper\DateTimeHelper;
|
||||
use Mautic\LeadBundle\Entity\TimelineTrait;
|
||||
|
||||
/**
|
||||
* @extends CommonRepository<Submission>
|
||||
*/
|
||||
class SubmissionRepository extends CommonRepository
|
||||
{
|
||||
use TimelineTrait;
|
||||
|
||||
public function saveEntity($entity, $flush = true): void
|
||||
{
|
||||
parent::saveEntity($entity, $flush);
|
||||
|
||||
// add the results
|
||||
$results = $entity->getResults();
|
||||
$results['submission_id'] = $entity->getId();
|
||||
$form = $entity->getForm();
|
||||
$results['form_id'] = $form->getId();
|
||||
|
||||
if (!empty($results)) {
|
||||
// Check that alias is SQL safe since it will be used for the column name
|
||||
$databasePlatform = $this->_em->getConnection()->getDatabasePlatform();
|
||||
$reservedWords = $databasePlatform->getReservedKeywordsList();
|
||||
foreach ($results as $alias => $value) {
|
||||
if ($reservedWords->isKeyword($alias)) {
|
||||
$results[$databasePlatform->quoteIdentifier($alias)] = $value;
|
||||
unset($results[$alias]);
|
||||
}
|
||||
}
|
||||
|
||||
$this->_em->getConnection()->insert($this->getResultsTableName($form->getId(), $form->getAlias()), $results);
|
||||
}
|
||||
}
|
||||
|
||||
public function getEntities(array $args = [])
|
||||
{
|
||||
$form = $args['form'];
|
||||
|
||||
// DBAL
|
||||
if (!isset($args['viewOnlyFields'])) {
|
||||
$args['viewOnlyFields'] = ['button', 'freetext', 'freehtml', 'pagebreak', 'captcha'];
|
||||
}
|
||||
$viewOnlyFields = array_map(
|
||||
fn ($value): string => '"'.$value.'"',
|
||||
$args['viewOnlyFields']
|
||||
);
|
||||
|
||||
// Get the list of custom fields
|
||||
$fq = $this->_em->getConnection()->createQueryBuilder();
|
||||
$fq->select('f.id, f.label, f.alias, f.type')
|
||||
->from(MAUTIC_TABLE_PREFIX.'form_fields', 'f')
|
||||
->where('f.form_id = '.$form->getId())
|
||||
->andWhere(
|
||||
$fq->expr()->notIn('f.type', $viewOnlyFields),
|
||||
$fq->expr()->eq('f.save_result', ':saveResult')
|
||||
)
|
||||
->orderBy('f.field_order, f.id', 'ASC')
|
||||
->setParameter('saveResult', true);
|
||||
$results = $fq->executeQuery()->fetchAllAssociative();
|
||||
|
||||
$fields = [];
|
||||
foreach ($results as $r) {
|
||||
$fields[$r['alias']] = $r;
|
||||
}
|
||||
unset($results);
|
||||
$fieldAliases = array_keys($fields);
|
||||
|
||||
$dq = $this->_em->getConnection()->createQueryBuilder();
|
||||
$dq->select('count(r.submission_id) as count')
|
||||
->from($this->getResultsTableName($form->getId(), $form->getAlias()), 'r')
|
||||
->innerJoin('r', MAUTIC_TABLE_PREFIX.'form_submissions', 's', 'r.submission_id = s.id')
|
||||
->leftJoin('s', MAUTIC_TABLE_PREFIX.'ip_addresses', 'i', 's.ip_id = i.id')
|
||||
->where('r.form_id = '.$form->getId());
|
||||
|
||||
$this->buildWhereClause($dq, $args);
|
||||
|
||||
// get a total count
|
||||
$result = $dq->executeQuery()->fetchAllAssociative();
|
||||
$total = $result[0]['count'];
|
||||
|
||||
// now get the actual paginated results
|
||||
$this->buildOrderByClause($dq, $args);
|
||||
$this->buildLimiterClauses($dq, $args);
|
||||
|
||||
$dq->resetQueryPart('select');
|
||||
|
||||
$databasePlatform = $this->_em->getConnection()->getDatabasePlatform();
|
||||
// Quote reserved keywords in field aliases
|
||||
$fieldAliases = array_map(fn ($alias) => $databasePlatform->quoteIdentifier($alias), $fieldAliases);
|
||||
|
||||
$fieldAliasSql = (!empty($fieldAliases)) ? ', r.'.implode(',r.', $fieldAliases) : '';
|
||||
$dq->select('r.submission_id, s.date_submitted as dateSubmitted, s.lead_id as leadId, s.referer, i.ip_address as ipAddress'.$fieldAliasSql);
|
||||
$results = $dq->executeQuery()->fetchAllAssociative();
|
||||
|
||||
// loop over results to put form submission results in something that can be assigned to the entities
|
||||
$values = [];
|
||||
$flattenResults = !empty($args['flatten_results']);
|
||||
foreach ($results as &$result) {
|
||||
$submissionId = $result['submission_id'];
|
||||
unset($result['submission_id']);
|
||||
|
||||
$values[$submissionId] = [];
|
||||
foreach ($result as $k => $r) {
|
||||
if (isset($fields[$k])) {
|
||||
if ($flattenResults) {
|
||||
$values[$submissionId][$k] = $r;
|
||||
} else {
|
||||
$values[$submissionId][$k] = $fields[$k];
|
||||
$values[$submissionId][$k]['value'] = $r;
|
||||
}
|
||||
}
|
||||
}
|
||||
$result['id'] = $submissionId;
|
||||
$result['results'] = $values[$submissionId];
|
||||
}
|
||||
|
||||
if (empty($args['simpleResults'])) {
|
||||
// get an array of IDs for ORM query
|
||||
$ids = array_keys($values);
|
||||
|
||||
if (count($ids)) {
|
||||
// ORM
|
||||
|
||||
// build the order by id since the order was applied above
|
||||
// unfortunately, can't use MySQL's FIELD function since we have to be cross-platform
|
||||
$order = '(CASE';
|
||||
foreach ($ids as $count => $id) {
|
||||
$order .= ' WHEN s.id = '.$id.' THEN '.$count;
|
||||
++$count;
|
||||
}
|
||||
$order .= ' ELSE '.$count.' END) AS HIDDEN ORD';
|
||||
|
||||
// ORM - generates lead entities
|
||||
$returnEntities = !empty($args['return_entities']);
|
||||
$leadSelect = $returnEntities ? 'l' : 'partial l.{id}';
|
||||
$q = $this
|
||||
->createQueryBuilder('s');
|
||||
$q->select('s, p, i,'.$leadSelect.','.$order)
|
||||
->leftJoin('s.ipAddress', 'i')
|
||||
->leftJoin('s.page', 'p')
|
||||
->leftJoin('s.lead', 'l');
|
||||
|
||||
// only pull the submissions as filtered via DBAL
|
||||
$q->where(
|
||||
$q->expr()->in('s.id', ':ids')
|
||||
)->setParameter('ids', $ids);
|
||||
|
||||
$q->orderBy('ORD', Order::Ascending->value);
|
||||
$results = $returnEntities ? $q->getQuery()->getResult() : $q->getQuery()->getArrayResult();
|
||||
|
||||
foreach ($results as &$r) {
|
||||
if ($r instanceof Submission) {
|
||||
$r->setResults($values[$r->getId()]);
|
||||
} else {
|
||||
$r['results'] = $values[$r['id']];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (!empty($args['withTotalCount'])) ?
|
||||
[
|
||||
'count' => $total,
|
||||
'results' => $results,
|
||||
] : $results;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $id
|
||||
*/
|
||||
public function getEntity($id = 0): ?Submission
|
||||
{
|
||||
$entity = parent::getEntity($id);
|
||||
|
||||
if (null != $entity) {
|
||||
$form = $entity->getForm();
|
||||
|
||||
// use DBAL to get entity fields
|
||||
$q = $this->_em->getConnection()->createQueryBuilder();
|
||||
$q->select('*')
|
||||
->from($this->getResultsTableName($form->getId(), $form->getAlias()), 'r')
|
||||
->where('r.submission_id = :id')
|
||||
->setParameter('id', $id);
|
||||
$results = $q->executeQuery()->fetchAllAssociative();
|
||||
|
||||
if (!empty($results)) {
|
||||
unset($results[0]['submission_id']);
|
||||
$entity->setResults($results[0]);
|
||||
}
|
||||
}
|
||||
|
||||
return $entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all submissions that derive from a landing page.
|
||||
*
|
||||
* @param array<mixed> $args
|
||||
*
|
||||
* @return array<mixed>
|
||||
*/
|
||||
public function getEntitiesByPage(array $args = []): array
|
||||
{
|
||||
$activePage = $args['activePage'];
|
||||
|
||||
$dq = $this->_em->getConnection()->createQueryBuilder();
|
||||
$dq->select('count(s.id) as count')
|
||||
->from(MAUTIC_TABLE_PREFIX.'form_submissions', 's')
|
||||
->innerJoin('s', MAUTIC_TABLE_PREFIX.'pages', 'p', 's.page_id = p.id')
|
||||
->leftJoin('s', MAUTIC_TABLE_PREFIX.'ip_addresses', 'i', 's.ip_id = i.id')
|
||||
->where($dq->expr()->eq('s.page_id', ':page'))
|
||||
->setParameter('page', $activePage->getId());
|
||||
|
||||
$this->buildWhereClause($dq, $args);
|
||||
|
||||
// get a total count
|
||||
$result = $dq->executeQuery()->fetchAllAssociative();
|
||||
|
||||
$total = $result[0]['count'];
|
||||
|
||||
// now get the actual paginated results
|
||||
$this->buildOrderByClause($dq, $args);
|
||||
$this->buildLimiterClauses($dq, $args);
|
||||
|
||||
$dq->resetQueryPart('select');
|
||||
$dq->select('s.id, s.date_submitted as dateSubmitted, s.lead_id as leadId, s.form_id as formId, s.referer, i.ip_address as ipAddress');
|
||||
$results = $dq->executeQuery()->fetchAllAssociative();
|
||||
|
||||
return [
|
||||
'count' => $total,
|
||||
'results' => $results,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param QueryBuilder|DbalQueryBuilder $q
|
||||
* @param array<mixed> $filter
|
||||
*/
|
||||
public function getFilterExpr($q, array $filter, ?string $unique = null): array
|
||||
{
|
||||
if ('s.date_submitted' === $filter['column']) {
|
||||
$date = (new DateTimeHelper($filter['value'], 'Y-m-d'))->toUtcString();
|
||||
$date1 = $this->generateRandomParameterName();
|
||||
$date2 = $this->generateRandomParameterName();
|
||||
$parameters = [$date1 => $date.' 00:00:00', $date2 => $date.' 23:59:59'];
|
||||
$expr = $q->expr()->and(
|
||||
$q->expr()->gte('s.date_submitted', ":$date1"),
|
||||
$q->expr()->lte('s.date_submitted', ":$date2")
|
||||
);
|
||||
|
||||
return [$expr, $parameters];
|
||||
}
|
||||
|
||||
return parent::getFilterExpr($q, $filter);
|
||||
}
|
||||
|
||||
protected function getDefaultOrder(): array
|
||||
{
|
||||
return [
|
||||
['s.date_submitted', 'ASC'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the base submission data from the database.
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @throws \Doctrine\ORM\NoResultException
|
||||
* @throws \Doctrine\ORM\NonUniqueResultException
|
||||
*/
|
||||
public function getSubmissions(array $options = [])
|
||||
{
|
||||
$query = $this->getEntityManager()->getConnection()->createQueryBuilder();
|
||||
$query->select('fs.id, f.name, fs.form_id, fs.page_id, fs.date_submitted AS "dateSubmitted", fs.lead_id')
|
||||
->from(MAUTIC_TABLE_PREFIX.'form_submissions', 'fs')
|
||||
->leftJoin('fs', MAUTIC_TABLE_PREFIX.'forms', 'f', 'f.id = fs.form_id');
|
||||
|
||||
if (!empty($options['leadId'])) {
|
||||
$query->andWhere('fs.lead_id = :leadId')
|
||||
->setParameter('leadId', $options['leadId']);
|
||||
}
|
||||
|
||||
if (!empty($options['id'])) {
|
||||
$query->andWhere($query->expr()->eq('fs.form_id', ':id'))
|
||||
->setParameter('id', $options['id']);
|
||||
}
|
||||
|
||||
if (isset($options['search']) && $options['search']) {
|
||||
$query->andWhere(
|
||||
$query->expr()->like('f.name', ':search')
|
||||
)->setParameter('search', '%'.$options['search'].'%');
|
||||
}
|
||||
|
||||
return $this->getTimelineResults($query, $options, 'f.name', 'fs.date_submitted', [], ['dateSubmitted'], null, 'fs.id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get list of forms ordered by it's count.
|
||||
*
|
||||
* @param DbalQueryBuilder $query
|
||||
* @param int $limit
|
||||
* @param int $offset
|
||||
*
|
||||
* @throws \Doctrine\ORM\NoResultException
|
||||
* @throws \Doctrine\ORM\NonUniqueResultException
|
||||
*/
|
||||
public function getTopReferrers($query, $limit = 10, $offset = 0): array
|
||||
{
|
||||
$query->select('fs.referer, count(fs.referer) as sessions')
|
||||
->groupBy('fs.referer')
|
||||
->orderBy('sessions', 'DESC')
|
||||
->setMaxResults($limit)
|
||||
->setFirstResult($offset);
|
||||
|
||||
return $query->executeQuery()->fetchAllAssociative();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get list of forms ordered by it's count.
|
||||
*
|
||||
* @param DbalQueryBuilder $query
|
||||
* @param int $limit
|
||||
* @param int $offset
|
||||
*
|
||||
* @throws \Doctrine\ORM\NoResultException
|
||||
* @throws \Doctrine\ORM\NonUniqueResultException
|
||||
*/
|
||||
public function getMostSubmitted($query, $limit = 10, $offset = 0, $column = 'fs.id', $as = 'submissions'): array
|
||||
{
|
||||
$asSelect = ($as) ? ' as '.$as : '';
|
||||
|
||||
$query->select('f.name as title, f.id, count(distinct '.$column.')'.$asSelect)
|
||||
->groupBy('f.id, f.name')
|
||||
->orderBy($as, 'DESC')
|
||||
->setMaxResults($limit)
|
||||
->setFirstResult($offset);
|
||||
|
||||
return $query->executeQuery()->fetchAllAssociative();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed[]
|
||||
*/
|
||||
public function getSubmissionCountsByPage($pageId, ?\DateTime $fromDate = null): array
|
||||
{
|
||||
$q = $this->_em->getConnection()->createQueryBuilder();
|
||||
$q->select('count(distinct(s.tracking_id)) as count, s.page_id as id, p.title as name, p.variant_hits as total')
|
||||
->from(MAUTIC_TABLE_PREFIX.'form_submissions', 's')
|
||||
->join('s', MAUTIC_TABLE_PREFIX.'pages', 'p', 's.page_id = p.id');
|
||||
|
||||
if (is_array($pageId)) {
|
||||
$q->where($q->expr()->in('s.page_id', $pageId))
|
||||
->groupBy('s.page_id, p.title, p.variant_hits');
|
||||
} else {
|
||||
$q->where($q->expr()->eq('s.page_id', ':page'))
|
||||
->setParameter('page', (int) $pageId);
|
||||
}
|
||||
|
||||
if (null != $fromDate) {
|
||||
$dh = new DateTimeHelper($fromDate);
|
||||
$q->andWhere($q->expr()->gte('s.date_submitted', ':date'))
|
||||
->setParameter('date', $dh->toUtcString());
|
||||
}
|
||||
|
||||
return $q->executeQuery()->fetchAllAssociative();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get submission count by email by linking emails that have been associated with a page hit that has the
|
||||
* same tracking ID as a form submission tracking ID and thus assumed happened in the same session.
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
public function getSubmissionCountsByEmail($emailId, ?\DateTime $fromDate = null): array
|
||||
{
|
||||
// link email to page hit tracking id to form submission tracking id
|
||||
$q = $this->_em->getConnection()->createQueryBuilder();
|
||||
$q->select('count(distinct(s.tracking_id)) as count, e.id, e.subject as name, e.variant_sent_count as total')
|
||||
->from(MAUTIC_TABLE_PREFIX.'form_submissions', 's')
|
||||
->join('s', MAUTIC_TABLE_PREFIX.'page_hits', 'h', 's.tracking_id = h.tracking_id')
|
||||
->join('h', MAUTIC_TABLE_PREFIX.'emails', 'e', 'h.email_id = e.id');
|
||||
|
||||
if (is_array($emailId)) {
|
||||
$q->where($q->expr()->in('e.id', $emailId))
|
||||
->groupBy('e.id, e.subject, e.variant_sent_count');
|
||||
} else {
|
||||
$q->where($q->expr()->eq('e.id', ':id'))
|
||||
->setParameter('id', (int) $emailId);
|
||||
}
|
||||
|
||||
if (null != $fromDate) {
|
||||
$dh = new DateTimeHelper($fromDate);
|
||||
$q->andWhere($q->expr()->gte('s.date_submitted', ':date'))
|
||||
->setParameter('date', $dh->toUtcString());
|
||||
}
|
||||
|
||||
return $q->executeQuery()->fetchAllAssociative();
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates lead ID (e.g. after a lead merge).
|
||||
*/
|
||||
public function updateLead($fromLeadId, $toLeadId): void
|
||||
{
|
||||
$q = $this->_em->getConnection()->createQueryBuilder();
|
||||
$q->update(MAUTIC_TABLE_PREFIX.'form_submissions')
|
||||
->set('lead_id', (int) $toLeadId)
|
||||
->where('lead_id = '.(int) $fromLeadId)
|
||||
->executeStatement();
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates that an array of submission IDs belong to a specific form.
|
||||
*/
|
||||
public function validateSubmissions($ids, $formId): array
|
||||
{
|
||||
$q = $this->_em->getConnection()->createQueryBuilder();
|
||||
$q->select('s.id')
|
||||
->from(MAUTIC_TABLE_PREFIX.'form_submissions', 's')
|
||||
->where(
|
||||
$q->expr()->and(
|
||||
$q->expr()->eq('s.form_id', (int) $formId),
|
||||
$q->expr()->in('s.id', $ids)
|
||||
)
|
||||
);
|
||||
|
||||
$validIds = [];
|
||||
$results = $q->executeQuery()->fetchAllAssociative();
|
||||
|
||||
foreach ($results as $r) {
|
||||
$validIds[] = $r['id'];
|
||||
}
|
||||
|
||||
return $validIds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare a form result value with defined value for defined lead.
|
||||
*
|
||||
* @param int $lead ID
|
||||
* @param int $form ID
|
||||
* @param string $formAlias
|
||||
* @param int $field alias
|
||||
* @param string $value to compare with
|
||||
* @param string $operatorExpr for WHERE clause
|
||||
* @param string|null $type
|
||||
*/
|
||||
public function compareValue($lead, $form, $formAlias, $field, $value, $operatorExpr, $type = null): bool
|
||||
{
|
||||
// Modify operator
|
||||
switch ($operatorExpr) {
|
||||
case 'like':
|
||||
case 'notLike':
|
||||
$value = !str_contains($value, '%') ? '%'.$value.'%' : $value;
|
||||
break;
|
||||
case 'startsWith':
|
||||
$operatorExpr = 'like';
|
||||
$value = $value.'%';
|
||||
break;
|
||||
case 'endsWith':
|
||||
$operatorExpr = 'like';
|
||||
$value = '%'.$value;
|
||||
break;
|
||||
case 'contains':
|
||||
$operatorExpr = 'like';
|
||||
$value = '%'.$value.'%';
|
||||
break;
|
||||
}
|
||||
|
||||
// use DBAL to get entity fields
|
||||
$q = $this->_em->getConnection()->createQueryBuilder();
|
||||
$q->select('s.id')
|
||||
->from($this->getResultsTableName($form, $formAlias), 'r')
|
||||
->leftJoin('r', MAUTIC_TABLE_PREFIX.'form_submissions', 's', 's.id = r.submission_id')
|
||||
->where(
|
||||
$q->expr()->and(
|
||||
$q->expr()->eq('s.lead_id', ':lead'),
|
||||
$q->expr()->eq('s.form_id', ':form')
|
||||
)
|
||||
)
|
||||
->setParameter('lead', (int) $lead)
|
||||
->setParameter('form', (int) $form);
|
||||
|
||||
match ($type) {
|
||||
'boolean', 'number' => $q->andWhere($q->expr()->$operatorExpr('r.'.$field, $value)),
|
||||
default => $q->andWhere($q->expr()->$operatorExpr('r.'.$field, ':value'))
|
||||
->setParameter('value', $value),
|
||||
};
|
||||
|
||||
$result = $q->executeQuery()->fetchAssociative();
|
||||
|
||||
return !empty($result['id']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Form $form
|
||||
*/
|
||||
public function getSubmissionCounts($form)
|
||||
{
|
||||
$query = $this->getEntityManager()->getConnection()->createQueryBuilder();
|
||||
$query->select('COUNT(fs.id) AS `total`, COUNT(DISTINCT (fs.lead_id)) AS `unique`')
|
||||
->from(MAUTIC_TABLE_PREFIX.'form_submissions', 'fs');
|
||||
$query->where($query->expr()->eq('fs.form_id', ':id'))
|
||||
->setParameter('id', $form->getId());
|
||||
|
||||
return $query->executeQuery()->fetchAssociative();
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile and return the form result table name.
|
||||
*
|
||||
* @param int $formId
|
||||
* @param string $formAlias
|
||||
*/
|
||||
public function getResultsTableName($formId, $formAlias): string
|
||||
{
|
||||
return MAUTIC_TABLE_PREFIX.'form_results_'.$formId.'_'.$formAlias;
|
||||
}
|
||||
|
||||
public function getTableAlias(): string
|
||||
{
|
||||
return 'fs';
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user