Block
1 week ago
Listing
1 month ago
RestApi
1 month ago
Templates
2 years ago
Util
2 months ago
ApiDataSanitizer.php
2 weeks ago
AssetsController.php
2 months ago
BlockStylesRenderer.php
3 years ago
BlockWrapperRenderer.php
3 years ago
BlocksRenderer.php
1 week ago
DisplayFormInWPContent.php
2 months ago
FormHtmlSanitizer.php
1 month ago
FormMessageController.php
4 years ago
FormSaveController.php
1 year ago
FormsRepository.php
1 year ago
PreviewPage.php
2 months ago
PreviewWidget.php
1 year ago
Renderer.php
2 months ago
Widget.php
2 months ago
index.php
3 years ago
FormsRepository.php
265 lines
| 1 | <?php // phpcs:ignore SlevomatCodingStandard.TypeHints.DeclareStrictTypes.DeclareStrictTypesMissing |
| 2 | |
| 3 | namespace MailPoet\Form; |
| 4 | |
| 5 | if (!defined('ABSPATH')) exit; |
| 6 | |
| 7 | |
| 8 | use MailPoet\Doctrine\Repository; |
| 9 | use MailPoet\Entities\FormEntity; |
| 10 | |
| 11 | /** |
| 12 | * @extends Repository<FormEntity> |
| 13 | */ |
| 14 | class FormsRepository extends Repository { |
| 15 | protected function getEntityClassName() { |
| 16 | return FormEntity::class; |
| 17 | } |
| 18 | |
| 19 | /** |
| 20 | * @return FormEntity[] |
| 21 | */ |
| 22 | public function findAllNotDeleted(): array { |
| 23 | return $this->entityManager |
| 24 | ->createQueryBuilder() |
| 25 | ->select('f') |
| 26 | ->from(FormEntity::class, 'f') |
| 27 | ->where('f.deletedAt IS NULL') |
| 28 | ->orderBy('f.updatedAt', 'desc') |
| 29 | ->getQuery() |
| 30 | ->getResult(); |
| 31 | } |
| 32 | |
| 33 | /** |
| 34 | * @return FormEntity[] |
| 35 | */ |
| 36 | public function findAllActive(): array { |
| 37 | return $this->entityManager |
| 38 | ->createQueryBuilder() |
| 39 | ->select('f') |
| 40 | ->from(FormEntity::class, 'f') |
| 41 | ->where('f.deletedAt IS NULL') |
| 42 | ->andWhere('f.status = :status') |
| 43 | ->setParameter('status', FormEntity::STATUS_ENABLED) |
| 44 | ->orderBy('f.updatedAt', 'desc') |
| 45 | ->getQuery() |
| 46 | ->getResult(); |
| 47 | } |
| 48 | |
| 49 | public function getNamesOfFormsForSegments(): array { |
| 50 | $allNonDeletedForms = $this->findAllNotDeleted(); |
| 51 | |
| 52 | $nameMap = []; |
| 53 | foreach ($allNonDeletedForms as $form) { |
| 54 | $blockSegmentsIds = $form->getSettingsSegmentIds(); |
| 55 | foreach ($blockSegmentsIds as $blockSegmentId) { |
| 56 | $nameMap[$blockSegmentId][] = $form->getName(); |
| 57 | } |
| 58 | } |
| 59 | |
| 60 | // Sort form names alphabetically for each segment |
| 61 | foreach ($nameMap as &$names) { |
| 62 | sort($names); |
| 63 | } |
| 64 | |
| 65 | return $nameMap; |
| 66 | } |
| 67 | |
| 68 | public function count(): int { |
| 69 | return (int)$this->doctrineRepository |
| 70 | ->createQueryBuilder('f') |
| 71 | ->select('count(f.id)') |
| 72 | ->getQuery() |
| 73 | ->getSingleScalarResult(); |
| 74 | } |
| 75 | |
| 76 | public function getActiveFormsCountByType(): array { |
| 77 | $forms = $this->findAllActive(); |
| 78 | $formBlocks = $this->getActiveFormsBlocks(); |
| 79 | |
| 80 | $counts = [ |
| 81 | 'all' => count($forms), |
| 82 | FormEntity::DISPLAY_TYPE_BELOW_POST => 0, |
| 83 | FormEntity::DISPLAY_TYPE_FIXED_BAR => 0, |
| 84 | FormEntity::DISPLAY_TYPE_POPUP => 0, |
| 85 | FormEntity::DISPLAY_TYPE_SLIDE_IN => 0, |
| 86 | FormEntity::DISPLAY_TYPE_OTHERS => 0, |
| 87 | 'with_first_name' => 0, |
| 88 | 'with_last_name' => 0, |
| 89 | 'with_custom_fields' => 0, |
| 90 | 'min_custom_fields' => 0, |
| 91 | 'max_custom_fields' => 0, |
| 92 | 'average_custom_fields' => 0, |
| 93 | 'median_custom_fields' => 0, |
| 94 | ]; |
| 95 | |
| 96 | foreach ($forms as $form) { |
| 97 | $settings = $form->getSettings(); |
| 98 | if (!is_array($settings) || !isset($settings['form_placement'])) continue; |
| 99 | |
| 100 | $hasAnyEnabled = false; |
| 101 | $formPlacement = $settings['form_placement']; |
| 102 | |
| 103 | foreach (FormEntity::FORM_DISPLAY_TYPES as $type) { |
| 104 | if (isset($formPlacement[$type]['enabled']) && $formPlacement[$type]['enabled'] === '1') { |
| 105 | $counts[$type]++; |
| 106 | $hasAnyEnabled = true; |
| 107 | } |
| 108 | } |
| 109 | |
| 110 | if (!$hasAnyEnabled) { |
| 111 | $counts[FormEntity::DISPLAY_TYPE_OTHERS]++; |
| 112 | } |
| 113 | } |
| 114 | |
| 115 | $customFieldsCounts = []; |
| 116 | foreach ($formBlocks as $blocks) { |
| 117 | if (in_array('first_name', $blocks)) { |
| 118 | $counts['with_first_name']++; |
| 119 | } |
| 120 | if (in_array('last_name', $blocks)) { |
| 121 | $counts['with_last_name']++; |
| 122 | } |
| 123 | |
| 124 | $customFieldsInForm = array_filter($blocks, function($block) { |
| 125 | return strpos($block, 'custom_') === 0; |
| 126 | }); |
| 127 | |
| 128 | if (!empty($customFieldsInForm)) { |
| 129 | $counts['with_custom_fields']++; |
| 130 | $customFieldsCounts[] = count($customFieldsInForm); |
| 131 | } |
| 132 | } |
| 133 | |
| 134 | if (!empty($customFieldsCounts)) { |
| 135 | $counts['min_custom_fields'] = min($customFieldsCounts); |
| 136 | $counts['max_custom_fields'] = max($customFieldsCounts); |
| 137 | $counts['average_custom_fields'] = round(array_sum($customFieldsCounts) / count($customFieldsCounts), 1); |
| 138 | } |
| 139 | |
| 140 | return $counts; |
| 141 | } |
| 142 | |
| 143 | /** |
| 144 | * Get active forms with their placements and blocks for analytics tracking |
| 145 | * @return array |
| 146 | */ |
| 147 | private function getActiveFormsBlocks(): array { |
| 148 | $forms = $this->findAllActive(); |
| 149 | $formBlocks = []; |
| 150 | |
| 151 | foreach ($forms as $form) { |
| 152 | $settings = $form->getSettings(); |
| 153 | if (!is_array($settings)) continue; |
| 154 | |
| 155 | $body = $form->getBody(); |
| 156 | if (!is_array($body)) continue; |
| 157 | |
| 158 | $formBlocks[] = $this->extractBlockTypes($body); |
| 159 | } |
| 160 | |
| 161 | return $formBlocks; |
| 162 | } |
| 163 | |
| 164 | /** |
| 165 | * Extract block types from form body recursively |
| 166 | * @param array $blocks |
| 167 | * @return array |
| 168 | */ |
| 169 | private function extractBlockTypes(array $blocks): array { |
| 170 | $ignoredBlocks = ['columns', 'column', 'paragraph', 'heading', 'image', 'divider', 'submit', 'html']; |
| 171 | $fieldBlockIds = ['email', 'first_name', 'last_name', 'segments']; |
| 172 | $blockTypes = []; |
| 173 | |
| 174 | foreach ($blocks as $block) { |
| 175 | if (in_array($block['id'], $fieldBlockIds)) { |
| 176 | $blockTypes[] = $block['id']; |
| 177 | } elseif (!in_array($block['type'], $ignoredBlocks)) { |
| 178 | $blockTypes[] = 'custom_' . $block['type']; |
| 179 | } |
| 180 | |
| 181 | // Recursively check nested blocks (like in columns) |
| 182 | if (isset($block['body']) && is_array($block['body'])) { |
| 183 | $blockTypes = array_merge($blockTypes, $this->extractBlockTypes($block['body'])); |
| 184 | } |
| 185 | } |
| 186 | |
| 187 | return $blockTypes; |
| 188 | } |
| 189 | |
| 190 | public function delete(FormEntity $form) { |
| 191 | $this->entityManager->remove($form); |
| 192 | $this->flush(); |
| 193 | } |
| 194 | |
| 195 | public function trash(FormEntity $form) { |
| 196 | $this->bulkTrash([$form->getId()]); |
| 197 | $this->entityManager->refresh($form); |
| 198 | } |
| 199 | |
| 200 | public function restore(FormEntity $form) { |
| 201 | $this->bulkRestore([$form->getId()]); |
| 202 | $this->entityManager->refresh($form); |
| 203 | } |
| 204 | |
| 205 | public function bulkTrash(array $ids): int { |
| 206 | if (empty($ids)) { |
| 207 | return 0; |
| 208 | } |
| 209 | |
| 210 | $result = $this->entityManager->createQueryBuilder() |
| 211 | ->update(FormEntity::class, 'f') |
| 212 | ->set('f.deletedAt', 'CURRENT_TIMESTAMP()') |
| 213 | ->where('f.id IN (:ids)') |
| 214 | ->setParameter('ids', $ids) |
| 215 | ->getQuery()->execute(); |
| 216 | |
| 217 | // update was done via DQL, make sure the entities are also refreshed in the entity manager |
| 218 | $this->refreshAll(function (FormEntity $entity) use ($ids) { |
| 219 | return in_array($entity->getId(), $ids, true); |
| 220 | }); |
| 221 | |
| 222 | return $result; |
| 223 | } |
| 224 | |
| 225 | public function bulkRestore(array $ids): int { |
| 226 | if (empty($ids)) { |
| 227 | return 0; |
| 228 | } |
| 229 | |
| 230 | $result = $this->entityManager->createQueryBuilder() |
| 231 | ->update(FormEntity::class, 'f') |
| 232 | ->set('f.deletedAt', ':deletedAt') |
| 233 | ->where('f.id IN (:ids)') |
| 234 | ->setParameter('deletedAt', null) |
| 235 | ->setParameter('ids', $ids) |
| 236 | ->getQuery()->execute(); |
| 237 | |
| 238 | // update was done via DQL, make sure the entities are also refreshed in the entity manager |
| 239 | $this->refreshAll(function (FormEntity $entity) use ($ids) { |
| 240 | return in_array($entity->getId(), $ids, true); |
| 241 | }); |
| 242 | |
| 243 | return $result; |
| 244 | } |
| 245 | |
| 246 | public function bulkDelete(array $ids): int { |
| 247 | if (empty($ids)) { |
| 248 | return 0; |
| 249 | } |
| 250 | |
| 251 | $result = $this->entityManager->createQueryBuilder() |
| 252 | ->delete(FormEntity::class, 'f') |
| 253 | ->where('f.id IN (:ids)') |
| 254 | ->setParameter('ids', $ids) |
| 255 | ->getQuery()->execute(); |
| 256 | |
| 257 | // delete was done via DQL, make sure the entities are also detached from the entity manager |
| 258 | $this->detachAll(function (FormEntity $entity) use ($ids) { |
| 259 | return in_array($entity->getId(), $ids, true); |
| 260 | }); |
| 261 | |
| 262 | return $result; |
| 263 | } |
| 264 | } |
| 265 |