<?php // phpcs:ignore SlevomatCodingStandard.TypeHints.DeclareStrictTypes.DeclareStrictTypesMissing
namespace MailPoet\Form;
if (!defined('ABSPATH')) exit;
use MailPoet\Doctrine\Repository;
use MailPoet\Entities\FormEntity;
/**
* @extends Repository<FormEntity>
*/
class FormsRepository extends Repository {
protected function getEntityClassName() {
return FormEntity::class;
}
/**
* @return FormEntity[]
*/
public function findAllNotDeleted(): array {
return $this->entityManager
->createQueryBuilder()
->select('f')
->from(FormEntity::class, 'f')
->where('f.deletedAt IS NULL')
->orderBy('f.updatedAt', 'desc')
->getQuery()
->getResult();
}
/**
* @return FormEntity[]
*/
public function findAllActive(): array {
return $this->entityManager
->createQueryBuilder()
->select('f')
->from(FormEntity::class, 'f')
->where('f.deletedAt IS NULL')
->andWhere('f.status = :status')
->setParameter('status', FormEntity::STATUS_ENABLED)
->orderBy('f.updatedAt', 'desc')
->getQuery()
->getResult();
}
public function getNamesOfFormsForSegments(): array {
$allNonDeletedForms = $this->findAllNotDeleted();
$nameMap = [];
foreach ($allNonDeletedForms as $form) {
$blockSegmentsIds = $form->getSettingsSegmentIds();
foreach ($blockSegmentsIds as $blockSegmentId) {
$nameMap[$blockSegmentId][] = $form->getName();
}
}
// Sort form names alphabetically for each segment
foreach ($nameMap as &$names) {
sort($names);
}
return $nameMap;
}
public function count(): int {
return (int)$this->doctrineRepository
->createQueryBuilder('f')
->select('count(f.id)')
->getQuery()
->getSingleScalarResult();
}
public function getActiveFormsCountByType(): array {
$forms = $this->findAllActive();
$formBlocks = $this->getActiveFormsBlocks();
$counts = [
'all' => count($forms),
FormEntity::DISPLAY_TYPE_BELOW_POST => 0,
FormEntity::DISPLAY_TYPE_FIXED_BAR => 0,
FormEntity::DISPLAY_TYPE_POPUP => 0,
FormEntity::DISPLAY_TYPE_SLIDE_IN => 0,
FormEntity::DISPLAY_TYPE_OTHERS => 0,
'with_first_name' => 0,
'with_last_name' => 0,
'with_custom_fields' => 0,
'min_custom_fields' => 0,
'max_custom_fields' => 0,
'average_custom_fields' => 0,
'median_custom_fields' => 0,
];
foreach ($forms as $form) {
$settings = $form->getSettings();
if (!is_array($settings) || !isset($settings['form_placement'])) continue;
$hasAnyEnabled = false;
$formPlacement = $settings['form_placement'];
foreach (FormEntity::FORM_DISPLAY_TYPES as $type) {
if (isset($formPlacement[$type]['enabled']) && $formPlacement[$type]['enabled'] === '1') {
$counts[$type]++;
$hasAnyEnabled = true;
}
}
if (!$hasAnyEnabled) {
$counts[FormEntity::DISPLAY_TYPE_OTHERS]++;
}
}
$customFieldsCounts = [];
foreach ($formBlocks as $blocks) {
if (in_array('first_name', $blocks)) {
$counts['with_first_name']++;
}
if (in_array('last_name', $blocks)) {
$counts['with_last_name']++;
}
$customFieldsInForm = array_filter($blocks, function($block) {
return strpos($block, 'custom_') === 0;
});
if (!empty($customFieldsInForm)) {
$counts['with_custom_fields']++;
$customFieldsCounts[] = count($customFieldsInForm);
}
}
if (!empty($customFieldsCounts)) {
$counts['min_custom_fields'] = min($customFieldsCounts);
$counts['max_custom_fields'] = max($customFieldsCounts);
$counts['average_custom_fields'] = round(array_sum($customFieldsCounts) / count($customFieldsCounts), 1);
}
return $counts;
}
/**
* Get active forms with their placements and blocks for analytics tracking
* @return array
*/
private function getActiveFormsBlocks(): array {
$forms = $this->findAllActive();
$formBlocks = [];
foreach ($forms as $form) {
$settings = $form->getSettings();
if (!is_array($settings)) continue;
$body = $form->getBody();
if (!is_array($body)) continue;
$formBlocks[] = $this->extractBlockTypes($body);
}
return $formBlocks;
}
/**
* Extract block types from form body recursively
* @param array $blocks
* @return array
*/
private function extractBlockTypes(array $blocks): array {
$ignoredBlocks = ['columns', 'column', 'paragraph', 'heading', 'image', 'divider', 'submit', 'html'];
$fieldBlockIds = ['email', 'first_name', 'last_name', 'segments'];
$blockTypes = [];
foreach ($blocks as $block) {
if (in_array($block['id'], $fieldBlockIds)) {
$blockTypes[] = $block['id'];
} elseif (!in_array($block['type'], $ignoredBlocks)) {
$blockTypes[] = 'custom_' . $block['type'];
}
// Recursively check nested blocks (like in columns)
if (isset($block['body']) && is_array($block['body'])) {
$blockTypes = array_merge($blockTypes, $this->extractBlockTypes($block['body']));
}
}
return $blockTypes;
}
public function delete(FormEntity $form) {
$this->entityManager->remove($form);
$this->flush();
}
public function trash(FormEntity $form) {
$this->bulkTrash([$form->getId()]);
$this->entityManager->refresh($form);
}
public function restore(FormEntity $form) {
$this->bulkRestore([$form->getId()]);
$this->entityManager->refresh($form);
}
public function bulkTrash(array $ids): int {
if (empty($ids)) {
return 0;
}
$result = $this->entityManager->createQueryBuilder()
->update(FormEntity::class, 'f')
->set('f.deletedAt', 'CURRENT_TIMESTAMP()')
->where('f.id IN (:ids)')
->setParameter('ids', $ids)
->getQuery()->execute();
// update was done via DQL, make sure the entities are also refreshed in the entity manager
$this->refreshAll(function (FormEntity $entity) use ($ids) {
return in_array($entity->getId(), $ids, true);
});
return $result;
}
public function bulkRestore(array $ids): int {
if (empty($ids)) {
return 0;
}
$result = $this->entityManager->createQueryBuilder()
->update(FormEntity::class, 'f')
->set('f.deletedAt', ':deletedAt')
->where('f.id IN (:ids)')
->setParameter('deletedAt', null)
->setParameter('ids', $ids)
->getQuery()->execute();
// update was done via DQL, make sure the entities are also refreshed in the entity manager
$this->refreshAll(function (FormEntity $entity) use ($ids) {
return in_array($entity->getId(), $ids, true);
});
return $result;
}
public function bulkDelete(array $ids): int {
if (empty($ids)) {
return 0;
}
$result = $this->entityManager->createQueryBuilder()
->delete(FormEntity::class, 'f')
->where('f.id IN (:ids)')
->setParameter('ids', $ids)
->getQuery()->execute();
// delete was done via DQL, make sure the entities are also detached from the entity manager
$this->detachAll(function (FormEntity $entity) use ($ids) {
return in_array($entity->getId(), $ids, true);
});
return $result;
}
}