Skip to content

Instantly share code, notes, and snippets.

@geerteltink
Last active February 13, 2022 21:25
Show Gist options
  • Save geerteltink/c6d08b6fda54b04cd397416e35b9ba1a to your computer and use it in GitHub Desktop.
Save geerteltink/c6d08b6fda54b04cd397416e35b9ba1a to your computer and use it in GitHub Desktop.
Symfony 3 forms validation constraints without an entity
<?php
// ...
use Doctrine\Common\Collections\ArrayCollection;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\PasswordType;
use Symfony\Component\Form\Extension\Core\Type\RepeatedType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Validator\Constraints\Callback;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
class SubscriptionUserType extends AbstractType
{
/**
* @var ArrayCollection<DataSubscription>
*/
private $dataSubscriptions;
public function buildForm(FormBuilderInterface $builder, array $options)
{
$this->dataSubscriptions = $options['data_subscriptions'];
// Shared user
$builder->add('username', TextType::class, [
'attr' => [
'pattern' => '[a-z][a-z0-9\.\_\-]{1,29}[a-z0-9]',
],
'required' => true,
]);
$builder->add('subscriptionId', ChoiceType::class, [
'label' => 'Subscription',
'choices' => $this->buildSubscriptionChoices($this->dataSubscriptions),
'required' => true,
]);
// Email data
$builder->add('hasEmail', CheckboxType::class, [
'label' => 'Create email address?',
'required' => false,
]);
$builder->add('emailPassword', RepeatedType::class, [
'type' => PasswordType::class,
'invalid_message' => 'The passwords must match.',
'first_options' => [
'label' => 'Password',
'attr' => [
'pattern' => '^(?=.*[a-z]+.*)(?=.*[A-Z]+.*)(?=.*[0-9]+.*)(?=.*[\@\#\$\%\^\*\~\-\=\_\+\[\]\\\{\}\:\,\/\?]+.*)[0-9a-zA-Z\@\#\$\%\^\*\~\-\=\_\+\[\]\\\{\}\:\,\/\?]{8,12}$',
],
],
'second_options' => [
'label' => 'Confirm password',
],
]);
// Webspace data
$builder->add('hasWebspace', CheckboxType::class, [
'label' => 'Create webspace?',
'required' => false,
]);
$builder->add('webspacePassword', RepeatedType::class, [
'type' => PasswordType::class,
'invalid_message' => 'The passwords must match.',
'first_options' => [
'label' => 'Password',
'attr' => [
'pattern' => '^(?=.*[a-z]+.*)(?=.*[A-Z]+.*)(?=.*[0-9]+.*)(?=.*[\@\#\$\%\^\*\~\-\=\_\+\[\]\\\{\}\:\,\/\?]+.*)[0-9a-zA-Z\@\#\$\%\^\*\~\-\=\_\+\[\]\\\{\}\:\,\/\?]{8,12}$',
],
],
'second_options' => [
'label' => 'Confirm password',
],
]);
// Save button
$builder->add('save', SubmitType::class);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => SubscriptionUser::class,
'constraints' => [
new Callback([
'callback' => [$this, 'checkSubscription'],
]),
new Callback([
'callback' => [$this, 'checkSelectedOptions'],
]),
new Callback([
'callback' => [$this, 'checkEmailPassword'],
]),
new Callback([
'callback' => [$this, 'checkWebspacePassword'],
]),
],
'csrf_protection' => true,
'csrf_field_name' => '_token',
'intention' => 'subscription_user',
'data_subscriptions' => [],
]);
}
public function buildSubscriptionChoices($dataSubscriptions)
{
$choices = [];
foreach ($dataSubscriptions as $dataSubscription) {
if ($dataSubscription->hasEmailAddressAvailable() || $dataSubscription->hasWebspaceAvailable()) {
$label = sprintf(
'#%s - %s [%s/%s]',
$dataSubscription->getId(),
$dataSubscription->getMacAddress(),
$dataSubscription->getEmailAddressesAvailable(),
$dataSubscription->getWebspacesAvailable()
);
$choices[$label] = $dataSubscription->getId();
}
}
return $choices;
}
public function checkSubscription($data, ExecutionContextInterface $context)
{
$dataSubscription = $this->dataSubscriptions->get($data->getSubscriptionId());
if (!$dataSubscription) {
$context->buildViolation('Invalid subscription selected!')
->atPath('subscriptionId')
->addViolation();
return;
}
if ($data->hasEmail() && !$dataSubscription->hasEmailAddressAvailable()) {
$context->buildViolation('The selected subscription has no email address available!')
->atPath('subscriptionId')
->addViolation();
}
if ($data->hasWebspace() && !$dataSubscription->hasWebspaceAvailable()) {
$context->buildViolation('The selected subscription has no webspace available!')
->atPath('subscriptionId')
->addViolation();
}
}
public function checkSelectedOptions($data, ExecutionContextInterface $context)
{
if ($data->hasEmail() === false && $data->hasWebspace() === false) {
$context->buildViolation('At least one of the options create email or webspace must be selected!')
->addViolation();
}
}
public function checkEmailPassword($data, ExecutionContextInterface $context)
{
if ($data->hasEmail() === true && !$data->getEmailPassword()) {
$context->buildViolation('Email password is not set!')
->atPath('emailPassword')
->addViolation();
}
}
public function checkWebspacePassword($data, ExecutionContextInterface $context)
{
if ($data->hasWebspace() === true && !$data->getWebspacePassword()) {
$context->buildViolation('Webspace password is not set!')
->atPath('webspacePassword')
->addViolation();
}
}
public function getBlockPrefix()
{
return 'selfcare_subscription_user';
}
}
@COil
Copy link

COil commented Oct 3, 2018

👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment