add: login con username
This commit is contained in:
parent
2ca6950751
commit
46367238f5
@ -16,23 +16,29 @@
|
||||
|
||||
/**
|
||||
* Controlador de sesiones
|
||||
*
|
||||
*
|
||||
* @author Moisés Cortés C <moises.cortes@notsoweb.com>
|
||||
*
|
||||
*
|
||||
* @version 1.0.0
|
||||
*/
|
||||
class LoginController extends Controller
|
||||
{
|
||||
/**
|
||||
* Iniciar sesión
|
||||
* Permite login con username O email
|
||||
*/
|
||||
public function login(LoginRequest $request)
|
||||
{
|
||||
$user = User::where('email', $request->get('email'))->first();
|
||||
$credential = $request->get('username');
|
||||
|
||||
// Buscar por username o email
|
||||
$user = User::where('username', $credential)
|
||||
->orWhere('email', $credential)
|
||||
->first();
|
||||
|
||||
if (!$user || !$user->validateForPassportPasswordGrant($request->get('password'))) {
|
||||
return ApiResponse::UNPROCESSABLE_CONTENT->response([
|
||||
'email' => ['Usuario no valido']
|
||||
'username' => ['Credenciales inválidas']
|
||||
]);
|
||||
}
|
||||
|
||||
@ -87,7 +93,7 @@ public function forgotPassword(ForgotRequest $request)
|
||||
public function resetPassword(ResetPasswordRequest $request)
|
||||
{
|
||||
$data = $request->validated();
|
||||
|
||||
|
||||
$model = ResetPassword::with('user')->where('token', $data['token'])->first();
|
||||
|
||||
if(!$model){
|
||||
@ -142,4 +148,4 @@ private function deleteToken($token)
|
||||
{
|
||||
ResetPassword::where('token', $token)->delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,9 +7,9 @@
|
||||
|
||||
/**
|
||||
* Solicitud de login
|
||||
*
|
||||
*
|
||||
* @author Moisés Cortés C. <moises.cortes@notsoweb.com>
|
||||
*
|
||||
*
|
||||
* @version 1.0.0
|
||||
*/
|
||||
class LoginRequest extends FormRequest
|
||||
@ -30,8 +30,17 @@ public function authorize(): bool
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'email' => ['required', 'email'],
|
||||
'username' => ['required', 'string'], // Acepta username o email
|
||||
'password' => ['required', 'min:8'],
|
||||
];
|
||||
}
|
||||
|
||||
public function messages(): array
|
||||
{
|
||||
return [
|
||||
'username.required' => 'El usuario o email es requerido',
|
||||
'password.required' => 'La contraseña es requerida',
|
||||
'password.min' => 'La contraseña debe tener al menos 8 caracteres',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,13 +3,14 @@
|
||||
* @copyright (c) 2025 Notsoweb Software (https://notsoweb.com) - All rights reserved
|
||||
*/
|
||||
|
||||
use App\Models\User;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
/**
|
||||
* Almacenar usuario
|
||||
*
|
||||
*
|
||||
* @author Moisés Cortés C <moises.cortes@notsoweb.com>
|
||||
*
|
||||
*
|
||||
* @version 1.0.0
|
||||
*/
|
||||
class UserStoreRequest extends FormRequest
|
||||
@ -39,4 +40,21 @@ public function rules(): array
|
||||
'roles' => ['nullable', 'array']
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Preparar datos antes de la validación
|
||||
* Genera el username automáticamente
|
||||
*/
|
||||
protected function prepareForValidation(): void
|
||||
{
|
||||
if ($this->has('name') && $this->has('paternal')) {
|
||||
$this->merge([
|
||||
'username' => User::generateUsername(
|
||||
$this->input('name'),
|
||||
$this->input('paternal'),
|
||||
$this->input('maternal')
|
||||
)
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,4 +1,7 @@
|
||||
<?php namespace App\Models;
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
/**
|
||||
* @copyright (c) 2025 Notsoweb Software (https://notsoweb.com) - All Rights Reserved
|
||||
*/
|
||||
@ -42,6 +45,7 @@ class User extends Authenticatable
|
||||
'name',
|
||||
'paternal',
|
||||
'maternal',
|
||||
'username',
|
||||
'email',
|
||||
'phone',
|
||||
'password',
|
||||
@ -99,7 +103,7 @@ public function reports()
|
||||
public function fullName(): Attribute
|
||||
{
|
||||
return Attribute::make(
|
||||
get: fn () => $this->name . ' ' . $this->paternal . ' ' . $this->maternal,
|
||||
get: fn() => $this->name . ' ' . $this->paternal . ' ' . $this->maternal,
|
||||
);
|
||||
}
|
||||
|
||||
@ -109,7 +113,7 @@ public function fullName(): Attribute
|
||||
public function lastName(): Attribute
|
||||
{
|
||||
return Attribute::make(
|
||||
get: fn () => $this->paternal . ' ' . $this->maternal,
|
||||
get: fn() => $this->paternal . ' ' . $this->maternal,
|
||||
);
|
||||
}
|
||||
|
||||
@ -141,4 +145,90 @@ 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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
if (!Schema::hasColumn('users', 'username')) {
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->string('username')->nullable()->unique()->after('maternal');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->dropColumn('username');
|
||||
});
|
||||
}
|
||||
};
|
||||
Loading…
x
Reference in New Issue
Block a user