BentCoder
BentCoder

Reputation: 12750

Applying a correct logic after successful validation in Symfony

First of I all, I created the whole example below specifically for this question because the actual example is very big so if it looks stupid then assume that it is not for now!

I'm trying to come up with a solution so that I can call a correct private method (bankA() or bankB()) in controller if the validation successfully passes. As you can see in the custom validation constraint, I only check the $bank->code property however the condition is not actually that simple (there is repository checks so on) - (as I said above, it is trimmed down version). So, could please someone tell me, how will I know that which private method I should call in controller after successful validation? I'm happy to create dedicated validators if necessary so open for suggestions and examples.

Note: I looked into symfony group validation documentation but didn't really get the picture how I could apply to my scenario.

EXAMPLE REQUEST

CONTROLLER

use Symfony\Component\Validator\Validator\ValidatorInterface;

/**
 * @Route("bank", service="application_frontend.controller.bank")
 */
class BankController extends Controller
{
    private $validator;

    public function __construct(
        ValidatorInterface $validator
    ) {
        $this->validator = $validator;
    }

    /**
     * @param Request $request
     *
     * @Route("")
     * @Method({"POST"})
     *
     * @throws Exception
     */
    public function indexAction(Request $request)
    {
        $content = $request->getContent();
        $content = json_decode($content, true);

        $bank = new Bank();
        $bank->id = $content['id'];
        $bank->code = $content['code'];

        $errors = $this->validator->validate($bank);
        if (count($errors)) {
            throw new Exception($errors[0]->getMessage());
        }

        // OK, validation has passed so which one do I call now ?!?!
        $this->bankA($bank);
        $this->bankB($bank);
    }

    private function bankA(Bank $bank)
    {
        // Do something nice with Bank
    }

    private function bankB(Bank $bank)
    {
        // Do something bad with Bank
    }
}

BANK MODEL

use Application\FrontendBundle\Validator\Constraint as BankAssert;

/**
 * @BankAssert\Bank
 */
class Bank
{
    /**
     * @var int
     */
    public $id;

    /**
     * @var string
     */
    public $code;
}

CUSTOM VALIDATOR

use Symfony\Component\Validator\Constraint;

/**
 * @Annotation
 */
class Bank extends Constraint
{
    public function getTargets()
    {
        return self::CLASS_CONSTRAINT;
    }

    public function validatedBy()
    {
        return get_class($this).'Validator';
    }
}

use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;

class BankValidator extends ConstraintValidator
{
    public function validate($bank, Constraint $constraint)
    {
        if ($bank->code == 'A') {
            return;
        }

        if ($bank->code == 'B') {
            return;
        }

        $this->context->buildViolation('Bank error')->addViolation();
    }
}

Upvotes: 1

Views: 118

Answers (1)

qooplmao
qooplmao

Reputation: 17759

Depending on how many codes there are you could either do...

if ('A' === $bank->getCode()) {
    $this->bankA($bank);
} else {
    $this->bankB($bank);
}

Or..

$method = 'bank'.$bank->getCode();

if (!method_exists($this, $method)) {
    throw new \Exception('Method "'.$method.'" does not exist');
}

$this->$method();

All of that being said, it would be advisable to move all of this work into a dedicated service rather than in your controller. Then in your controller use something like...

$this->container->get('do_something_to_bank.service')->processAction($bank);

Upvotes: 1

Related Questions