lunes, 23 de noviembre de 2015

11 - Tutorial de Laravel 5 - User ResetsPasswords (Resetear el password)




En este capítulo del Tutorial de Laravel 5 continuamos con el sistema de usuarios y agregaremos una nueva funcionalidad, como es la posibilidad de que los usuarios puedan resetear el password. Laravel 5 ya nos ofrece un modelo predefinido para tal tarea llamado ResetsPasswords.

Lo primero que haremos es una serie de modificaciones sobre la clase ResetsPasswords más que nada para que los posibles estados y mensajes de error aparezcan en español.

La clase ResetsPasswords se encuentra en la siguiente ruta: vendor\laravel\framework\src\Illuminate\Foundation\Auth, abriremos el archivo, aquí se encuentra el código completo con todas las modificaciones realizadas en el capítulo.

<?php

namespace Illuminate\Foundation\Auth;

use Illuminate\Http\Request;
use Illuminate\Mail\Message;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Password;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;

trait ResetsPasswords
{
    /**
     * Display the form to request a password reset link.
     *
     * @return \Illuminate\Http\Response
     */
    public function getEmail()
    {
        return view('auth.password');
    }

    /**
     * Send a reset link to the given user.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    public function postEmail(Request $request)
    {
        
        $messages = [
            'email.required' => 'El campo es requerido',
            'email.email' => 'El formato de email es incorrecto',
        ];
        $this->validate($request, ['email' => 'required|email'], $messages);

        $response = Password::sendResetLink($request->only('email'), function (Message $message) {
            $message->subject($this->getEmailSubject());
        });

        switch ($response) {
            case Password::RESET_LINK_SENT:
                return redirect()->back()->with('status', 'Hemos enviando un link a tu cuenta de correo electrónico para que puedas resetear el password');

            case Password::INVALID_USER:
                return redirect()->back()->withErrors(['email' => trans($response)]);
        }
    }

    /**
     * Get the e-mail subject line to be used for the reset link email.
     *
     * @return string
     */
    protected function getEmailSubject()
    {
        return property_exists($this, 'subject') ? $this->subject : 'Tu link para resetear el password';
    }

    /**
     * Display the password reset view for the given token.
     *
     * @param  string  $token
     * @return \Illuminate\Http\Response
     */
    public function getReset($token = null)
    {
        if (is_null($token)) {
            throw new NotFoundHttpException;
        }

        return view('auth.reset')->with('token', $token);
    }

    /**
     * Reset the given user's password.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    public function postReset(Request $request)
    {
        
        $messages = [
            'email.required' => 'El campo es requerido',
            'email.email' => 'El formato de email es incorrecto',
            'password.required' => 'El campo es requerido',
            'password.confirmed' => 'Los passwords no coinciden',
            'password.min' => 'El mínimo de caracteres permitidos son 6',
            'password.max' => 'El máximo de caracteres permitidos son 18',
        ];
        
        $this->validate($request, [
            'token' => 'required',
            'email' => 'required|email',
            'password' => 'required|confirmed|min:6|max:18',
        ], $messages);

        $credentials = $request->only(
            'email', 'password', 'password_confirmation', 'token'
        );

        $response = Password::reset($credentials, function ($user, $password) {
            $this->resetPassword($user, $password);
        });

        switch ($response) {
            case Password::PASSWORD_RESET:
                return redirect($this->redirectPath())->with('status', 'Enhorabuena tu password ha sido reseteado con éxito');

            default:
                return redirect()->back()
                            ->withInput($request->only('email'))
                            ->withErrors(['email' => trans($response)]);
        }
    }

    /**
     * Reset the given user's password.
     *
     * @param  \Illuminate\Contracts\Auth\CanResetPassword  $user
     * @param  string  $password
     * @return void
     */
    protected function resetPassword($user, $password)
    {
        $user->password = bcrypt($password);

        $user->save();

        Auth::login($user);
    }

    /**
     * Get the post register / login redirect path.
     *
     * @return string
     */
    public function redirectPath()
    {
        if (property_exists($this, 'redirectPath')) {
            return $this->redirectPath;
        }

        return property_exists($this, 'redirectTo') ? $this->redirectTo : '/home';
    }
}



Una de las modificaciones es el mensaje flash 'status' de la acción postReset cuando el reseteado del password ha sido realizado correctamente, la redirección es realizada a la vista 'home' que creamos en el capítulo anterior, este mensaje flash lo manejaremos en la vista home.blade.php quedando de la siguiente forma:

@extends('layouts.home')

@section('content')
<h1>Bienvenid@s a la aplicación Laravel</h1>

@if (Session::has('status'))
<div class='text-success'>
    {{Session::get('status')}}
</div>
@endif

@stop


A continuación crearemos las vistas que contendrán los formularios, password.blade.php para que el usuario pueda solicitar el link y reset.blade.php para que el usuario pueda resetear el password. Estas vistas las crearemos en la carpeta "auth" de "views".

El código para la vista password.blade.php

@extends('layouts.home')
@section('content')
 <h1>Resetear el password</h1>
 @if (Session::has('status'))
  <div class="alert alert-success">
   {{ Session::get('status') }}
  </div>
 @endif
 @if (count($errors) > 0)
  <div class="alert alert-danger">
   Los datos introducidos en el formulario son incorrectos.
  </div>
 @endif
 <hr />
 <form method="POST" action="{{url('password/email')}}">
  {{csrf_field()}}
  <div class="form-group">
   <label for="email">Email</label>
   <input type="email" class="form-control" name="email" value="{{ old('email') }}" />
   <div class="text-danger">{{$errors->first('email')}}</div>
  </div>
  <button type="submit" class="btn btn-primary">Obtener un enlace para resetear mi password</button>
 </form>
@stop


El código para la vista reset.blade.php

@extends('layouts.home')

@section('content')
 <h1>Resetear el password</h1>
 @if (count($errors) > 0)
  <div class="alert alert-danger">
   Los datos introducidos en el formulario son incorrectos.
  </div>
 @endif
 <hr />
 <form method="POST" action="{{url('password/reset')}}">
  {{csrf_field()}}
  <input type="hidden" name="token" value="{{$token}}" />

  <div class="form-group">
   <label for="email">Email:</label>
   <input type="email" class="form-control" name="email" value="{{Input::old('email')}}" />
   <div class="text-danger">{{$errors->first('email')}}</div>
  </div>

  <div class="form-group">
   <label for="password">Password:</label>
   <input type="password" class="form-control" name="password" />
   <div class="text-danger">{{$errors->first('password')}}</div>
  </div>

  <div class="form-group">
   <label for="password_confirmation">Confirmar Password:</label>
   <input type="password" class="form-control" name="password_confirmation" />
  </div>
  <button type="submit" class="btn btn-primary">Resetear Password</button>
 </form>
@stop



A continuación crearemos la plantilla de email para enviar el link al usuario, pero antes realizaremos una modificación, vamos a cambiar el nombre de la carpeta "mails" por "emails" y modificaremos en el controlador AuthController la ruta de la plantilla de email en la acción postRegister.

Mail::send('emails.register', ['data' => $data], ...


Una vez hecho estos cambios importantes, procedemos a crear la plantilla "password.blade.php" en la carpeta "emails" con el siguiente código ...

<p><strong>Haz click en el siguiente enlace para resetear el password:</strong></p>
<a href="{{url()}}/password/reset/{{$token}}">Resetear mi password</a>


Agregaremos las rutas en routes.php

Route::get('password/email', 'Auth\PasswordController@getEmail');
Route::post('password/email', 'Auth\PasswordController@postEmail');

Route::get('password/reset/{token}', 'Auth\PasswordController@getReset');
Route::post('password/reset', 'Auth\PasswordController@postReset');


Como podéis ver estas rutas se encuentran ligadas al controlador PasswordController que usa la clase ResetsPasswords y sus métodos ya predefinidos, con Auth los modificamos, pero en esta ocasión dejamos que esos métodos ya predefinidos hagan el trabajo por nosotros, PasswordController utiliza middleware para permitir sólo a los usuarios invitados (guest) a acceder a ellos.

Finalmente incluiremos un link en la vista "login.blade.php" para permitir a los usuarios que puedan resetear sus passwords:

<a href='{{url("password/email")}}'>Olvidé mi contraseña</a>


Ahora sólo queda que realices un test para ver que todo funciona correctamente.