* * @version 1.0.0 */ #[ObservedBy([UserObserver::class])] class User extends Authenticatable { use Extended, HasApiTokens, HasFactory, HasRoles, HasProfilePhoto, IsNotifiable, SoftDeletes; /** * Atributos permitidos */ protected $fillable = [ 'name', 'paternal', 'maternal', 'username', 'email', 'phone', 'password', 'profile_photo_path', 'module_id' ]; /** * Atributos ocultos */ protected $hidden = [ 'password', 'remember_token', ]; /** * Atributos que se deben convertir */ protected function casts(): array { return [ 'email_verified_at' => 'datetime', 'password' => 'hashed', ]; } /** * Los accesores a añadir al modelo en su forma de array */ protected $appends = [ 'full_name', 'last_name', 'profile_photo_url', ]; /** * Un usuario puede generar muchos eventos */ public function events() { return $this->hasMany(UserEvent::class); } /** * Evento */ public function reports() { return $this->morphMany(UserEvent::class, 'reportable'); } /** * Nombre completo del usuario */ public function fullName(): Attribute { return Attribute::make( get: fn() => $this->name . ' ' . $this->paternal . ' ' . $this->maternal, ); } /** * Apellido paterno y materno del usuario */ public function lastName(): Attribute { return Attribute::make( get: fn() => $this->paternal . ' ' . $this->maternal, ); } /** * Validar la contraseña */ public function validateForPassportPasswordGrant(string $password): bool { return Hash::check($password, $this->password); } /** * Reset password */ public function resetPasswords() { return $this->hasMany(ResetPassword::class); } public function module() { return $this->belongsTo(Module::class); } /** * Módulo del cual el usuario es responsable */ public function responsibleModule() { return $this->hasOne(Module::class, 'responsible_id'); } /** * Generar username automático al crear usuario * Formato: inicial nombre + inicial segundo nombre + apellido paterno + inicial materno */ public static function generateUsername(?string $name, ?string $paternal, ?string $maternal = null): string { // Validar que al menos tengamos nombre y apellido paterno if (empty($name) || empty($paternal)) { return self::ensureUniqueUsername('user'); } $name = self::normalizeString($name); $paternal = self::normalizeString($paternal); $maternal = $maternal ? self::normalizeString($maternal) : ''; // Separar nombres y obtener iniciales $nameParts = preg_split('/\s+/', trim($name)); $firstInitial = !empty($nameParts[0]) ? substr($nameParts[0], 0, 1) : ''; $secondInitial = isset($nameParts[1]) && !empty($nameParts[1]) ? substr($nameParts[1], 0, 1) : ''; $maternalInitial = !empty($maternal) ? substr($maternal, 0, 1) : ''; // Construir username $baseUsername = $firstInitial . $secondInitial . $paternal . $maternalInitial; // Si el username queda vacío, usar fallback if (empty($baseUsername)) { $baseUsername = 'user'; } return self::ensureUniqueUsername($baseUsername); } /** * Normalizar string: quitar acentos, convertir a minúsculas, solo letras */ private static function normalizeString(string $string): string { // Convertir a minúsculas $string = mb_strtolower($string); // Reemplazar caracteres acentuados $replacements = [ 'á' => 'a', 'é' => 'e', 'í' => 'i', 'ó' => 'o', 'ú' => 'u', 'ä' => 'a', 'ë' => 'e', 'ï' => 'i', 'ö' => 'o', 'ü' => 'u', 'à' => 'a', 'è' => 'e', 'ì' => 'i', 'ò' => 'o', 'ù' => 'u', 'ñ' => 'n', 'ç' => 'c', ]; $string = strtr($string, $replacements); // Eliminar cualquier caracter que no sea letra o espacio $string = preg_replace('/[^a-z\s]/', '', $string); return $string; } /** * Asegurar que el username sea único, agregando número si es necesario */ private static function ensureUniqueUsername(string $baseUsername): string { $username = $baseUsername; $counter = 1; while (self::where('username', $username)->exists()) { $counter++; $username = $baseUsername . $counter; } return $username; } }