Laravel ResetsPasswords Trait - laravel

Locally, I have updated this trait to do some different redirecting after the user submits the getEmail() method to request the reset password link. When pushed to production, my editions aren't there. I'm guessing this is because the ResetsPasswords trait is in the laravel framework which is installed separately from my repository on the server.
If this is the case, what's the best way to change how this ResetsPasswords trait functions. Do I make my own and include that in the repository and just change my controller? Below is the PasswordController.
Thanks!
<?php
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\ResetsPasswords;
class PasswordController extends Controller
{
/*
|--------------------------------------------------------------------------
| Password Reset Controller
|--------------------------------------------------------------------------
|
| This controller is responsible for handling password reset requests
| and uses a simple trait to include this behavior. You're free to
| explore this trait and override any methods you wish to tweak.
|
*/
use ResetsPasswords;
protected $redirectPath = '/main';
/**
* Create a new password controller instance.
*
* #return void
*/
public function __construct()
{
$this->middleware('guest');
}
}
Update: So, in the ResetsPasswords trait, I modify the redirect getSendResetLinkEmailSuccessResponse() method. So, do I instead just put that method in my controller (with the same name) and my edited code?
protected function getSendResetLinkEmailSuccessResponse($response)
{
...modified code...
}

You should not be making changes to the Laravel vendor files for the reason you stated.
Instead, you should override any of the trait functions you need to modify in your controller.
So just add the method to your controller with your modified code like so:
<?php
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\ResetsPasswords;
class PasswordController extends Controller {
use ResetsPasswords;
protected $redirectPath = '/main';
public function __construct() {
$this->middleware('guest');
}
protected function getSendResetLinkEmailSuccessResponse($response) {
// modified code that sends an awesome flash message
}
}
Also, if all you're trying to do is change where the user is redirected to then you don't have to override the functions at all. All you have to do is change the redirectPath property in your controller.

Related

Using Sanctum with Laravel Spark conflict

My setup
Laravel 8
Laravel Spark Mollie
I'm constantly hitting a brick wall when calling API requests with Spark & Sanctum. I've installed Sanctum with no problem and migrated.
I've added use Laravel\Sanctum\HasApiTokens; to app/Models/User.php and added use HasApiTokens; to the class.
My Api.php route
Route::group([
'middleware' => 'auth:sanctum'
], function () {
Route::get('categories', [\App\Http\Controllers\categories::class, 'fetchCategories']);
});
When I call the Api I get this error
ErrorException
Declaration of Laravel\Sanctum\HasApiTokens::tokenCan(string $ability) should be compatible with Laravel\Spark\User::tokenCan($ability)
I've tried changing use Laravel\Sanctum\HasApiTokens; to Laravel\Spark\HasApiTokens on User.php. The error goes away, but whenever I try calling the Api, it returns me back to the login homepage.
Any ideas? As the Spark documentation doesn't really explain how Sanctum or Api protection work.
The problem is that your main User class extends the User class from the vendor Spark library. This User model uses the trait named HasApiTokens which is not the same as Sanctum
Since you don't want to change the file from the vendor directory, one fix I found was to copy the original SparkUser model class from the vendor and create a new one like this and remove the trait HasApiTokens since you don't want to use it anymore.
<?php
namespace App\Models\Users;
use Illuminate\Support\Str;
use Laravel\Spark\Billable;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
class SparkUser extends Authenticatable
{
use Billable, Notifiable; // HasApiTokens was removed from the original SparkUser class
/**
* Get the profile photo URL attribute.
*
* #param string|null $value
* #return string|null
*/
public function getPhotoUrlAttribute($value)
{
return empty($value) ? 'https://www.gravatar.com/avatar/'.md5(Str::lower($this->email)).'.jpg?s=200&d=mm' : url($value);
}
/**
* Make the team user visible for the current user.
*
* #return $this
*/
public function shouldHaveSelfVisibility()
{
return $this->makeVisible([
'uses_two_factor_auth',
'country_code',
'phone',
'card_brand',
'card_last_four',
'card_country',
'billing_address',
'billing_address_line_2',
'billing_city',
'billing_state',
'billing_zip',
'billing_country',
'extra_billing_information'
]);
}
/**
* Convert the model instance to an array.
*
* #return array
*/
public function toArray()
{
$array = parent::toArray();
if (! in_array('tax_rate', $this->hidden)) {
$array['tax_rate'] = $this->taxPercentage();
}
return $array;
}
}
And now all I had to change was my original User class model to use this new model like this and add the trait HasApiTokens from Sanctum!
use App\Models\SparkUser; // Modified from the original in the vendor folder
use Laravel\Sanctum\HasApiTokens;
class User extends SparkUser
{
use HasApiTokens;
...
}

Laravel actingAs guest

Laravel provides a way to authenticate a given user during HTTP testing with
$this->actingAs($user);
Is there a way to unauthenticate that $user within the same test?
Yes, you can unauthenticate using this:
Auth::logout();
https://laravel.com/docs/7.x/authentication#logging-out
Warning: above does far more than just forgetting (acting as if the login did not happen), for example, when using JWT above should invalidate token.
Yes, define new actingAsGuest method in base TestCase class
in file tests/TestCase.php
<?php
namespace Tests;
use Illuminate\Auth\RequestGuard;
use Illuminate\Foundation\Testing\TestCase as BaseTestCase;
abstract class TestCase extends BaseTestCase
{
use CreatesApplication;
protected function setUp(): void
{
parent::setUp();
// add logout method to RequestGuard
RequestGuard::macro('logout', function() {
$this->user = null;
});
}
// add method to base TestCase class
public function actingAsGuest(): void
{
$this->app['auth']->logout();
}
}
And then in your test class you can use it:
<?php
namespace Tests\Feature;
use App\Models\User;
use Tests\TestCase;
class ExampleTest extends TestCase
{
/**
* A basic test example.
*
* #return void
*/
public function test_example()
{
// acting as authenticated user
$this->actingAs(User::factory()->create());
$this->assertAuthenticated();
// acting as unauthenticated user
$this->actingAsGuest();
$this->assertGuest();
}
}
I had same requirements as OP did, but wanted actingAsGuest() to completely reset everything, except Database state.
Full App reset (except DB)
For Laravel 7 (and maybe newer or older)
I toke a look at Laravel's tearDown() and setUp() methods.
And came up with helper method like:
use Illuminate\Support\Facades\Facade;
// ...
function actingAsGuest()
{
// Backup database state.
/** #var \Illuminate\Database\MySqlConnection $connection */
$connection = app('db.connection');
// Reset everything else.
/** #var \Illuminate\Foundation\Application $app */
$app = $this->app;
$app->flush();
$this->app = null;
Facade::clearResolvedInstances();
$this->refreshApplication();
// Restore database state.
app('db')->extend($connection->getName(), function () use ($connection) {
return $connection;
});
}
WARNING !!
Above works fine unless your test's logic caches any of above discarded objects somewhere.
For example, Laravel's DatabaseTransactions trait did cache db facade (in their App-Destroyed-listener).
Which we fixed by overriding said trait's logic.
Like we changed:
// ...
$this->beforeApplicationDestroyed(function () use ($database) {
// ...
Into:
// ...
$this->beforeApplicationDestroyed(function () {
$database = app('db');
// ...

Hot to use custom Request class instead NovaRequest (FormRequest) for creating resuorce in Laravel Nova?

I make:
php artisan make:request DiscoverRequest
I want use DiscoverRequest instead default NovaRequest for create new entity for specific resource.
In Laravel Nova exist unified ResourceStoreController for all resources.
public function handle(CreateResourceRequest $request)
Route::post('/{resource}', 'ResourceStoreController#handle');
I want to override Request only for one resource.
How this can be implemented?
Can you elaborate what you mean by "override Request for only one resource"?
Normally you can just type hint the new Request in the method like this:
<?php
namespace App\Http\Controllers;
use App\Http\Requests\StoreItineraryRequest;
class ResourceStoreController extends Controller
{
public function handle(DiscoverRequest $request) {
...
}
}

Separating methods into many controllers?

I noticed that many of the examples in the Laravel docs seem to have Controllers where the class has only one use/method.
For example, in this part of the doc, they have a UpdatePasswordController class with a single method, update():
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
use App\Http\Controllers\Controller;
class UpdatePasswordController extends Controller
{
/**
* Update the password for the user.
*
* #param Request $request
* #return Response
*/
public function update(Request $request)
{
// Validate the new password length...
$request->user()->fill([
'password' => Hash::make($request->newPassword)
])->save();
}
}
Normally, I would put a method called updatePassword() in my UserController class (along with signIn(), signUp(), resetPassword(), etc.), but I'm wondering if it's better to create multiple classes, each with a single action?
Normally class is defined for a single purpose. In laravel , for authentication there is a Illuminate base bundle which is optimized for years.
As an example UpdatePasswordController only responsible for updating password,
AuthController only responsible for authentication.
I prefer you to reserch some MVC best practices

How do I translate the subject of my password reset email in laravel 5

I am new to laravel and I am currently building a multilingual app. I am implementing password reset using laravels shipped methods. After looking at this method in ResetsPasswords trait:
protected function getEmailSubject()
{
return isset($this->subject) ? $this->subject : 'Your Password Reset Link';
}
I noticed that I can specify a variable for my subject in the PasswordController like so:
protected $subject = 'Password Reset';
How do I get this value from a language file and assign to the variable?
Use the trans() helper function in the contructor
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\ResetsPasswords;
class PasswordController extends Controller
{
use ResetsPasswords;
/**
* Create a new password controller instance.
*
* #return void
*/
public function __construct()
{
$this->subject = trans('passwords.subject');
$this->middleware($this->guestMiddleware());
}
}
After doing some digging I found the answer as shown below.
protected function getEmailSubject(){
return Lang::has('passwords.password_reset')
? Lang::get('passwords.password_reset')
: 'Your Password Reset Link.';
}
Using method overriding, I overrode the getEmailSubject method in the ResetsPasswords trait and provided the necessary implementation as shown in the body of the email. passwords.password_reset is a key for a text in my language file.

Resources