Issue 1:
Q
A

Bug report?
yes

Feature request?
no

BC Break report?
no

RFC?
no

Symfony version
4.0.3

When form is submitted, both groups "Users' and 'Strict' are validated even if there are violations in 'Users'.

When put something like "123" on username and password,expected "This value is too short. It should have 6 characters or more." but "I am passwordLegal" message appears.

class Users
{
public static function loadValidatorMetadata(ClassMetadata $metadata)
{
$metadata->addPropertyConstraint('username', new Assert\NotBlank());
$metadata->addPropertyConstraint('password', new Assert\NotBlank());
$metadata->addPropertyConstraint('username', new Assert\Length(array(
'min' => 6,
'max' => 12
)));

$metadata->addPropertyConstraint('password', new Assert\Length(array(
'min' => 6
)));

$metadata->addGetterMethodConstraint('password', 'passwordLegal', new Assert\IsTrue(array(
'message' => 'The password cannot match your username',
'groups' => array(
'Strict'
)
)));
$metadata->setGroupSequence(array(
'Users',
'Strict'
));
}

public function passwordLegal()
{
die("I am passwordLegal");
return $this->password !== $this->username;
}
}

Issue 2:
Hello,

Using the example below (smallest I could think of).

If you try the given controller, you will have the following results:

test1 : all OK [as expected]

test2 : "is not three" [as expected]

test3 : _CRASH_ ""Error: Call to a member function myBar() on a non-object in /myProject/src/Me/MeBundle/Doc/Foo.php line 31"" [_NOT_ as expected]

test4 : "bar should not be null" [as expected]

I would expect test3 to give the same result as test4 (Meaning: If the first group of the GroupSequence is violated, the others are not executed).

This is explicitly specified in the doc here : http://symfony.com/doc/current/book/validation.html#group-sequence ""In this example, it will first validate all constraints in the group User (which is the same as the Default group). Only if all constraints in that group are valid, the second group, Strict, will be validated."" (emphasis mine)

and here: #2947 (comment)

cc @bschussek

<?php

namespace ME\MEBundle\Doc;

use Symfony\Component\Validator\Constraints as Assert;

/**
* @Assert\GroupSequence({"FilledIn", "Foo"})
*/
class Foo
{
protected $bar;

protected $checkBar;

public function __construct($bar, $checkBar = false)
{
$this->bar = $bar;
$this->checkBar = $checkBar;
}

/**
* @Assert\True(groups={"Foo"}, message="is not three")
*/
public function isThree()
{
if ($this->checkBar && null === $this->bar) {
return new Bar(null);
}

return 3 == $this->getBar()->myBar();
}

/**
* @Assert\NotNull(groups={"FilledIn"}, message="bar should not be null")
*/
public function getBar()
{
return $this->bar;
}
}

<?php

namespace Me\MeBundle\Doc;

class Bar
{
protected $bar;

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

public function myBar()
{
return $this->bar;
}
}

<?php

namespace Me\MeBundle\Controller;

use Me\MeBundle\Doc;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;

use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;

use Symfony\Component\HttpFoundation\JsonResponse;

class DefaultController extends Controller
{
protected function getFooData(Foo $foo)
{
$json = [['bar' => null === $foo->getBar() ? null : (array)$foo->getBar()]];

$violations = $this->get('validator')->validate($foo);

$errors = [];

foreach($violations as $violation) {
$errors[] = $violation->getMessage();
}

$json[] = $errors;

return $json;
}

/**
* @Route("/test1", defaults={"_format"="json"})
*/
public function test1Action()
{
new Foo(null);

$foo = new Foo(new Bar(3));

return new JsonResponse($this->getFooData($foo));
}

/**
* @Route("/test2", defaults={"_format"="json"})
*/
public function test2Action()
{
$foo = new Foo(new Bar(4));

return new JsonResponse($this->getFooData($foo));
}

/**
* @Route("/test3", defaults={"_format"="json"})
*/
public function test3Action()
{
$foo = new Foo(null);

return new JsonResponse($this->getFooData($foo));
}

/**
* @Route("/test4", defaults={"_format"="json"})
*/
public function test4Action()
{
$foo = new Foo(null, true);

return new JsonResponse($this->getFooData($foo));
}
}