I needed to create a validation rule in Laravel that would accept either of the two inputs, but not both and at least one had to be supplied. In other words, I had to apply the XOR logic operation on them:

ABXOR
000
011
101
110

The search results did offer many solutions but I did not like any of them in particular, mostly due to their complexity or lack of clear re-usability. The closer I could get while keeping complexity at minimum was to use the required_without Laravel validation rule like this:

public function rules(): array
{
    return [
        input1 => [
            'required_without:input2',
        ],
        input2 => [
            'required_without:input1',
        ],
    ];
}

However this was still not sufficient as this is not a XOR operation, this is a plain OR operation:

ABOR
000
011
101
111

If both inputs are supplied, the validator would happily accept them. Thinking a little bit I found that there is also a prohibits Laravel validation operator, so I applied it:

public function rules(): array
{
    return [
        input1 => [
            'required_without:input2',
            'prohibits:input2',
        ],
        input2 => [
            'required_without:input1',
            'prohibits:input1',
        ],
    ];
}

This works as expected. The XOR operation is applied to both input1 and input2.

Validation messages

The only drawback was the validation message returned:

{
  "message": "The given data was invalid.",
  "errors": {
    "qrcode": ["validation.prohibits"],
    "code": ["validation.prohibits"]
  }
}

The above means that the message is simply not supplied and its "path" returned. Filling in the path validation.prohibits with a string value would return an error messages. This is a core part of the Laravel validator, see links for documentation. To reduce the code repetition, the result could look like this:

public function messages(): array
{
    return [
        ...$this->customMessage('input1', 'input2'),
        ...$this->customMessage('input2', 'input1'),
    ];
}

private function customMessage(string $input, string $otherInput): array
{
    return [
        "$input.prohibits" => "The $input field is prohibited when $otherInput is present."
    ];
}

Hope this helps. Enjoy~