0
votes

We have a Symfony2 application that allows admins to credit a user's account with a payment using an Ajax POST request. The POST request sends JSON which is handled by a Symfony controller. The controller should create a "Payment" entity, validate it and persist it. We wish to take advantage of the built-in validation of Symfony's form system.

As an example, the credit the user with id 20 with 100p, the POSTed JSON is should look like ...

{"user":"20","amount":"100","description":"test payment","paymentType":"credit"}

At the Symfony end, we have a PaymentType form;

class PaymentType extends AbstractType

public function buildForm(FormBuilderInterface $builder, array $options)
{
    $builder->add('user', 'entity', [
        'class' => 'OurAppBundle\Entity\User',
        'choice_label' => 'email',
        'multiple'     => false,
        'expanded'     => true,
    ]);
    $builder->add('paymentType', 'choice', [
        'choices' => [ 1 => 'credit', 2 => 'debit'],
        'choices_as_values' => true,

    ]);
    $builder->add('amount', 'number');
    $builder->add('description', 'textarea');
}

The Payment entity is related to the User entity and contains validation;

class Payment
{
const TYPE_CREDIT = 'credit';
const TYPE_DEBIT = 'debit';

use TimestampableTrait;

/**
 * The ID of the payment
 *
 * @var integer
 *
 * @ORM\Column(name="id", type="integer")
 * @ORM\Id
 * @ORM\GeneratedValue(strategy="AUTO")
 */
protected $id;


/**
 * the User object
 *
 * @var User
 *
 * @ORM\ManyToOne(targetEntity="User", inversedBy="payments")
 * @ORM\JoinColumn(name="user_id", referencedColumnName="id", onDelete="CASCADE", nullable=false)
 */
protected $user;

/**
 * payment type
 *
 * either 'credit' - a credit to the user's account
 * or 'debit' - money taken out their account
 *
 * @var string
 *
 * @ORM\Column(name="payment_type", type="string", length=7, nullable=false)
 *
 * @Assert\Choice(choices= {"credit", "debit"}, message="Choose a valid payment type")
 */
protected $paymentType;

/**
 * amount
 *
 * amount of payment, in pence
 *
 * @var int
 *
 * @ORM\Column(name="amount", type="integer", nullable=false)
 *
 * @Assert\Range(
 *      min = 1,
 *      max = 2000,
 *      minMessage = "Payments must be positive",
 *      maxMessage = "You cannot credit/debit more than £20"
 * )
 */
protected $amount;

/**
 * a string describing the transaction
 *
 * @var string
 *
 * @ORM\Column(name="description", type="string", length=255, nullable=false)
 *
 * @Assert\NotBlank(message="Please enter a description")
 */
protected $description;
....

We have code in a controller along the lines of;

public function creditUserAction(Request $request)
{
      $body = $request->getContent();
      $formData = json_decode($body, true);

      $payment = new Payment();
      $form = $this->createForm(new PaymentType(), $payment);
      $form->submit($data);
      if ($form->isValid()) {
          // persist and flush .....

      } ....

The problem is that when the PaymentType form is loaded by createForm, Doctrine attempts to load all Users from the database and there are tens of thousands of users.

So, my question is - is there a better way to create an entity, using a Ajax POST request, that is related to another entity using Symfony Forms, where the related entity has thousands of instances?

1

1 Answers

1
votes

You can validate your entity without creating a form

$validator = $this->get('validator');
$errors = $validator->validate($payment);
if(count($errors)) {
    //...
}