No puedo comentar todavía por falta de reputación, pero lo que sugiere Gonzalo me parece bien. Según entiendo, primero validarías con una expresión regular solo para checar que cumpla en estructura. Después validarías que el último dígito que tiene el RFC coincida con el calculado por la función.
Lo que yo hice fue esto, tengo dos reglas básicas del RFC, luego una personalizada
'rfc' => ['required', 'string', new RfcRule]
Creé una clase ValidadorRfc
con
php artisan make:class ValidadorRfc
Y ahí puse un método para validar con regex y otro para módulo 11
class ValidadorRfc
{
/**
* Valida un RFC usando una expresión regular.
*
* @param string $rfc
* @return bool
*/
public static function validarConRegex($rfc)
{
return preg_match('/[A-ZÑ&]{3,4}\d{6}[A-V1-9][A-Z1-9][0-9A]/', $rfc);
}
/**
* Valida un RFC usando módulo 11.
*
* @param string $rfc
* @return bool
*/
public static function validarConModulo11($rfc)
{
$matrizValores = [
"0" => 0, "1" => 1, "2" => 2, "3" => 3, "4" => 4,
"5" => 5, "6" => 6, "7" => 7, "8" => 8, "9" => 9,
"A" => 10, "B" => 11, "C" => 12, "D" => 13, "E" => 14,
"F" => 15, "G" => 16, "H" => 17, "I" => 18, "J" => 19,
"K" => 20, "L" => 21, "M" => 22, "N" => 23, "&" => 24,
"O" => 25, "P" => 26, "Q" => 27, "R" => 28, "S" => 29,
"T" => 30, "U" => 31, "V" => 32, "W" => 33, "X" => 34,
"Y" => 35, "Z" => 36, " " => 37, "Ñ" => 38
];
$rfc = str_split($rfc);
//Eliminamos el último dígito, ya que el dígito se calcula en base a los doce primeros numeros.
$ultimo_digito_actual = array_pop($rfc);
//Los valores se asigan del 13-2, o lo mismo poner al revés y asignar del 2-13.
$rfc = array_reverse($rfc);
$suma = 0;
$multiplicador = 2;
foreach ($rfc as $caracter) {
$suma += $matrizValores[$caracter] * $multiplicador;
$multiplicador++;
}
$modulo11 = $suma % 11;
$digito = 11 - $modulo11;
// Si $digito es 11 se convierte a 0, Si es 10 el digito es "A"
$ultimo_digito_real = $digito === 10 ? "A" : ($digito == 11 ? "0" : (string) $digito);
return $ultimo_digito_actual === $ultimo_digito_real;
}
}
Luego uso ambos en RfcRule
, primero con la regex y luego con el módulo 11
class RfcRule implements ValidationRule
{
/**
* Run the validation rule.
*
* @param \Closure(string): \Illuminate\Translation\PotentiallyTranslatedString $fail
*/
public function validate(string $attribute, mixed $value, Closure $fail): void
{
if (!ValidadorRfc::validarConRegex($value)) {
$fail("El RFC no es válido");
return;
}
if (!ValidadorRfc::validarConModulo11($value)) {
$fail("El RFC no es válido");
return;
}
}
}
El return
en cada if
va porque $fail
no hace que se salga de la función, entonces en muchos casos necesitas que las condiciones pues vayan en orden y es importante retornar la función.