Skip to content

Commit a92cf11

Browse files
authored
Auto-assign the sole visible category in StartRegistration when no category field is shown (Case 212902) (#33)
When only one newsletter category existed, TypeHasCategoriesElementTrait skipped adding the categories field to the form. As a result, the model transformer created a PendingOptIn with no categories, and the confirmed Recipient ended up stored with no subscriptions. StartRegistration\Type now detects this case inside the model transformer's reverseTransform and injects the single visible category into the form data before passing it to the PendingOptInFactory. As part of this, addCategoriesElementToForm() was refactored: the trait no longer calls findVisible() itself or owns the categoryRepository property. Instead, it receives the pre-fetched choices as a parameter and unconditionally adds the field. The decision of whether to call addCategoriesElementToForm() at all now rests with the calling types (StartRegistration\Type and EditRegistration\Type), which avoids a second findVisible() call and separates the concerns of fetching categories and rendering the form field.
1 parent c5f2d44 commit a92cf11

4 files changed

Lines changed: 49 additions & 13 deletions

File tree

src/EditRegistration/Type.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ class Type extends AbstractType
1212
use TypeHasCategoriesElementTrait;
1313

1414
public const ELEMENT_CATEGORIES = 'categories';
15+
protected CategoryRepositoryInterface $categoryRepository;
1516

1617
public function __construct(CategoryRepositoryInterface $categoryRepository)
1718
{
@@ -20,7 +21,11 @@ public function __construct(CategoryRepositoryInterface $categoryRepository)
2021

2122
public function buildForm(FormBuilderInterface $builder, array $options): void
2223
{
23-
$this->addCategoriesElementToForm($builder, false);
24+
$choices = $this->categoryRepository->findVisible();
25+
26+
if (\count($choices) > 1) {
27+
$this->addCategoriesElementToForm($builder, $choices, false);
28+
}
2429

2530
// We need at least one element in addition to the categories above, so that Symfony recognizes the form being
2631
// submitted even if no categories where chosen.

src/EditRegistration/TypeHasCategoriesElementTrait.php

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,11 @@
55
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
66
use Symfony\Component\Form\FormBuilderInterface;
77
use Symfony\Component\Validator\Constraints\Choice;
8-
use Webfactory\NewsletterRegistrationBundle\Entity\CategoryRepositoryInterface;
98

109
trait TypeHasCategoriesElementTrait
1110
{
12-
protected CategoryRepositoryInterface $categoryRepository;
13-
14-
protected function addCategoriesElementToForm(FormBuilderInterface $builder, bool $recipientHasToChooseAtLeastOne)
11+
protected function addCategoriesElementToForm(FormBuilderInterface $builder, array $choices, bool $recipientHasToChooseAtLeastOne): void
1512
{
16-
// add category choices, if there is more than one
17-
$choices = $this->categoryRepository->findVisible();
18-
if (\count($choices) < 2) {
19-
return;
20-
}
21-
2213
$constraints = [];
2314
if (true === $recipientHasToChooseAtLeastOne) {
2415
$constraints[] = new Choice(choices: $choices, multiple: true, min: 1);

src/StartRegistration/Type.php

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ class Type extends AbstractType
1919
public const ELEMENT_HONEYPOT = 'url';
2020

2121
protected PendingOptInFactoryInterface $pendingOptInFactory;
22+
protected CategoryRepositoryInterface $categoryRepository;
2223

2324
public function __construct(CategoryRepositoryInterface $categoryRepository, PendingOptInFactoryInterface $pendingOptInFactory)
2425
{
@@ -30,7 +31,11 @@ public function buildForm(FormBuilderInterface $builder, array $options): void
3031
{
3132
$builder->add(static::ELEMENT_EMAIL_ADDRESS, EmailAddressType::class);
3233

33-
$this->addCategoriesElementToForm($builder, true);
34+
$choices = $this->categoryRepository->findVisible();
35+
36+
if (\count($choices) > 1) {
37+
$this->addCategoriesElementToForm($builder, $choices, true);
38+
}
3439

3540
// fake field for spam protection
3641
$builder->add(static::ELEMENT_HONEYPOT, HoneypotType::class);
@@ -47,7 +52,14 @@ function (?PendingOptInInterface $pendingOptIn): array {
4752
static::ELEMENT_CATEGORIES => $pendingOptIn->getCategories(),
4853
];
4954
},
50-
function (array $formData) use ($that): ?PendingOptInInterface {
55+
function (array $formData) use ($that, $choices): ?PendingOptInInterface {
56+
if (!isset($formData[self::ELEMENT_CATEGORIES]) && 1 === \count($choices)) {
57+
// if the field 'categories' is not in the form because you could choose only one anyway, we need to
58+
// set that one category here.
59+
$singleCategory = $choices[0];
60+
$formData[self::ELEMENT_CATEGORIES] = [$singleCategory];
61+
}
62+
5163
return $that->pendingOptInFactory->fromRegistrationFormData($formData);
5264
}
5365
));

tests/StartRegistration/TypeTest.php

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,34 @@ function (array $formData) {
240240
$this->assertEquals($pendingOptIn, $form->getData());
241241
}
242242

243+
#[Test]
244+
public function provides_PendingOptIn_with_single_category_auto_assigned_when_only_one_exists(): void
245+
{
246+
$this->setUpOneCategory();
247+
248+
$pendingOptIn = new PendingOptIn(null, new EmailAddress('webfactory@example.com', 'secret'), [$this->category1]);
249+
$this->pendingOptInFactory
250+
->method('fromRegistrationFormData')
251+
->with(
252+
$this->callback(
253+
function (array $formData) {
254+
return \array_key_exists(StartRegistrationType::ELEMENT_CATEGORIES, $formData)
255+
&& $formData[StartRegistrationType::ELEMENT_CATEGORIES] === [$this->category1];
256+
}
257+
)
258+
)
259+
->willReturn($pendingOptIn);
260+
261+
$form = $this->factory->create(StartRegistrationType::class);
262+
$form->submit([
263+
StartRegistrationType::ELEMENT_EMAIL_ADDRESS => 'webfactory@example.com',
264+
StartRegistrationType::ELEMENT_HONEYPOT => '',
265+
]);
266+
267+
$this->assertTrue($form->isValid());
268+
$this->assertEquals($pendingOptIn, $form->getData());
269+
}
270+
243271
#[Test]
244272
public function provides_PendingOptIn_if_submitted_with_valid_data_and_category_choices()
245273
{

0 commit comments

Comments
 (0)