laravel foundation files. am I doing this correctly? - php

In Laravel 5.1 I need to pass some data to the register view.
The getRegister() function in RegistersUsers trait is responsible to return the view.
At first I modified the function to pass my data but then I realized that the modifications would be overridden in case of an update.
So I made a new controller registerController and modified the route for getRegister like this: Route::get('auth/register', 'Auth\RegisterController#getRegister')
Inside the controller I redefined the getRegister function to return the view with my additional data.
Now I am thinking.. am I doing this correctly or do I need to use some other method and use the original AuthController some other way?
Also, default auth is set to use email for post login, how do I change it to use username without touching the foundation files?
Are all these matters regarding "extending the framework" ?
Thanks

First of all, it's always a bad idea to modify vendor files as the changes would be overwritten in case of any update in vendor package.
Answering your first question:
If you want to provide some additional data in registration view, you could do 2 things.
First one is to add your own getRegister() method:
public function getRegister()
{
return view('auth.register')->with(<parameter name>, <parameter value>);
}
The drawback of this solution is that in case of any future changes in trait's getRegister method those changes will not be incorporated into your controller.
So the better approach is to reuse trait's getRegister() method in your controller and add your parameters to whatever trait returns:
In your controller do:
use RegistersUsers {
RegistersUsers::getRegister as traitGetRegister;
}
public function getRegister()
{
return $this->traitGetRegister()->with(<parameter_name>, <parameter_value>);
}
Answering your second question:
Laravel's Auth::attempt() method that is used to login users uses DatabaseUserProvider to load users from the DB and that provider is flexible enough to use any credential set you provide. However, if you want to use AuthenticatesUsers trait to login users, you have to override its postLogin method, because of the validation it does for user credentials:
$this->validate($request, [
'email' => 'required|email', 'password' => 'required',
]);
UPDATE FOR LARAVEL 5.1: Since 5.1 there is no need to override postLogin() to change the column that is used to fetch users from the database. It is enough to set username property in the controller to the name of the column:
public $username = 'login';

Related

Laravel 7 User data in __construct

Using Laravel 7.
In the controller constructor, I then hoped to get access to the current user details so I could load main site widgets (buttons links etc) and custom user widgets in to one to be displayed in the view
use Illuminate\Support\Facades\Auth;
...
$widgets = Cache::get("widgets");
$usersdata = Cache::get("userdata");
$this->middleware('auth');
$widgets = array_merge($widgets, $usersdata[Auth::user()->id]["widgets"]);
View::share([
"widgets" => json_encode($widgets)
]);
however at this stage from research the user data is not available (even after authentication ?).
Not sure of best way to access this, or better practice might be to override the middleware auth (where?) so that it could return user id or something eg:
$userid=$this->middleware('auth');
I would like this in the constructor so the same method is in place for all controllers which extend this main controller.
This is intended behavior from laravel, you can read more about it here.
Laravel collects all route specific middlewares first before running
the request through the pipeline, and while collecting the controller
middleware an instance of the controller is created, thus the
constructor is called, however at this point the request isn’t ready
yet.
You can find Taylor's reasoning behind it here:
It’s very bad to use session or auth in your constructor as no request
has happened yet and session and auth are INHERENTLY tied to an HTTP
request. You should receive this request in an actual controller
method which you can call multiple times with multiple different
requests. By forcing your controller to resolve session or auth
information in the constructor you are now forcing your entire
controller to ignore the actual incoming request which can cause
significant problems when testing, etc.
So one solution would be to create a new middleware and then apply it to all routes, something like this, where widgets is your new middleware:
Route::group(['middleware' => ['auth', 'widgets']], function () {
// your routes
});
But if you really want to keep it in the constructor you could implement the following workaround:
class YourController extends Controller
{
public function __construct(Request $request)
{
$this->middleware('auth');
$this->middleware(function ($request, $next) {
$widgets = Cache::get("widgets");
$usersdata = Cache::get("userdata");
$widgets = array_merge($widgets, $usersdata[$request->user()->id]["widgets"]);
View::share([
"widgets" => json_encode($widgets)
]);
return $next($request);
});
}
}

Laravel policy autodetect

today i was creating USER profile page with is controlled in ProfileController it returning views to profile page, profile settings, etc.
so i decide to make some Policy rules to Edit profile and etc.
so i found i should use Middleware / Gates / Policy, based on Laravel Doc i chose Policy because profil page is public but only specific part of it can author edit so i needed #can
So my steps:
php artisan make:policy ProfilePolicy ( without model )
Registered policy to AuthServiceProvider in $policies property
writed methods like edit inside ProfilePolicy
then i started thinking how i define it to my Controller hmmm, documentation doesnt helps me :/
so i tryed blade #can('edit', $user) method and it worked, but HOW ?, how to define specific policy to one Controller ? ( not Model ), how to define multiple Policy to single Controller
i m lost how laravel Magic done this maybe because of Naming ? ProfileController => ProfilePolicy ?
In the controller you can write this
public function edit(Profile $profile) {
$this->authorize('edit', $profile)
}
Laravel does this:
Check the type of $profile, and it's a Profile::class
Check policies registered for that class (your step 2)
Looks for the edit method in that policy, if not found, return false meaning user is not authorized
Executes the edit() function that returns true/false
In blade the #can directive does exactly the same thing.
Policies are meant to be tied to Models, it's a convenient way to write rules to handle single models, but they can be triggered in many ways (like the authorize() method in controllers and #can directive in blade).

Laravel Spark - redirect on login

I have a very simple problem. I just want to direct the user to somewhere other than '/home' after they login. This is not difficult if you can alter the spark software and retain those changes in every deployment. However, composer reinstalls everything when things are deployed and it is generally bad practice to make changes to core vendor software.
This seems like it should be a very basic and simple thing for the creators to work into the software. So, how do I do it?
I have tried ...
Altering the redirectTo and redirectPath variables in the auth controller and the password controller in my app.
Adding a login controller to my app - independent of spark - and then resetting the same variables.
Attempting to call the afterLoginRedirectTo and afterAuthRedirectTo functions in the Spark service provider. This returned an error indicating that the functions did not exist.
Not sure where to go from here.
After having the same issue I've done some digging and found a way of setting something other than home, I've changed a fair bit of stuff, but hopefully this works for you too!
TLDR
Spark::afterLoginRedirectTo('somenewplace');
Option 1
The variable used is: $afterLoginRedirectTo from vendor\laravel\spark\src\Configuration\ManagesAppOptions.php
You can set this within the SparkServiceProvider#boot method:
Spark::afterLoginRedirectTo('somenewplace');
Spark has its own LoginController \vendor\laravel\spark\src\Http\Controllers\Auth\LoginController.php
which has an authenticated method to handle the two factor auth settings:
if (Spark::usesTwoFactorAuth() && $user->uses_two_factor_auth) {
return $this->redirectForTwoFactorAuth($request, $user);
}
return redirect()->intended($this->redirectPath());
RedirectPath() is from the RedirectsUsers trait which is still in vendor and does the following:
return property_exists($this, 'redirectTo') ? $this->redirectTo : '/home';
redirectTo in the LoginController is set in the construct method:
$this->redirectTo = Spark::afterLoginRedirect();
Option 2
Create a new route to override the login function.
in web.php specify a new route for post login:
Route::post('/login', 'Auth\NewLoginController#login');
You can then extend the LoginController and override the authenticated method:
class LoginController extends \Laravel\Spark\Http\Controllers\Auth\LoginController
{
public function authenticated(Request $request, $user)
{
/**
* #var $user User
* Set some logic here of your own for new redirect location
*/
if ($user->last_page_accessed != null) {
$this->redirectTo = $user->last_page_accessed;
}
return parent::authenticated($request, $user);
}
}

How to check a parameter from user table when authenticating in Laravel?

I am trying to make a Laravel website that uses
Laravel's Auth package. I'm using Laravel 5.3.2.
I have created a field in user table called role.
Now I want to know how to check the users role during the authentication process and then redirect to a required view based on the role.
Please help me figure out how this would be possible.
Thank you very much in advance.
When a user logs in, this is done through your LoginController.php which is located at app\Http\Controllers\Auth
This controller uses a trait called AuthenticatesUsers.
This trait has a method called authenticated() which by default is empty. This method is called if it's not empty by the trait - after all the necessary loggin in stuff has been done.
You could override this method in your AuthenticationController.php and add the functionality you are asking for. An example would be:
// You actually get an Auth\User object passed to you by the trait!
public function authenticated(Request $request, $user)
{
if($user->role == 'admin') {
// You could do anything here
return redirect()->route('admin-dashboard');
} else {
return redirect()->route('home');
}
}
Beside solution overriding some default Laravel method. I suggest an other approach: redirect user to a route which is responsible for redirect user base on user's role
In AuthController
protected $redirectTo = '/redirect';
In routes
Route::get('redirect', function(){
switch(auth()->user()->role){
case 1:
return redirect()->to();
break;
case 2:
return redirect()->to();
break;
}
})

Validation using Sentry2 in Laravel 4

I want to use Sentry2 in my Laravel 4 application but I'm not sure how to use it to validate user submitted data before I interact with it. In my own models, I would write a $rules array to contain the validation rules and write a static validates() method that I could call in the controller.
But with Sentry2, how do I do this? Do I have to extend the User model that Sentry2 provides and use that instead? Or is there a way that Sentry allows me to add validation rules with extending it?
If I do extend the Sentry2 User model, do I extend it like so:
/app/models/User.php
class User extends \Cartalyst\Sentry\Users\Eloquent\User {
private static $rules = array(...);
public static validates($input, static::$rules) {...};
}
I then need to create my own config file instead of using the one that Sentry provides. I do the following in artisan:
php artisan config:publish cartalyst\sentry
and update the config file like so:
/app/config/packages/cartalyst/sentry/config.php
'users' => array()
'model' => 'User',
;
Is this correct? If so, do I just call the model to validate user submitted data like I normally would? So, for example, in the UsersController I would check input by doing:
$validator = User::validate(Input::all());
I believe you are on the right way. Sentry doesn't really do any validation when you're saving data (except for checking for a password and a login field), and if you want to use validation from directly within the Eloquent model, you can extend the current Cartalyst user model, exactly as you have done in your code.
In the config file, you may have to take your namespaces in account. To check that Sentry really is using your model, try getting the currently logged in user (Sentry::getUser()) and var_dump it (dd(Sentry::getUser()) to see that Sentry is really using your class).
After you've got that setup, you can use your Eloquent model and validation as you normally would, with the addition of having all Sentry methods and properties.
If you want to keep the validation logic separate from you model, you can have a look at using validation as a service: http://culttt.com/2013/07/29/creating-laravel-4-validation-services/

Categories