Hello stackoverflow geeks, I'm in my final stages of the laravel learning curve all thanks to you guys.
However, i need to generate a warning message like "You cannot delete a role assigned to a user" every time a user tries to delete a role assigned to a user.
instead it loads a page with an sql error. how to i do it?
And how do i avoid a password that has been already been stored from being hashed again. eg:- $2y$10$p8JwI5P4yE2UFo2.vHP99.0dP2jU7ll/9w73IzUa9/yegKOSTHJWq is always hashed every time i edit a user's information.
Thanks you all who've made learning laravel easy for me by answering in time
code
public function destroy(Request $request,$id)
{
// delete
// $role = Role::find($id);
//$role->delete();
$role = Role::find ($id);
if ($role->users() !=null) {
return redirect()->back()->withInput(['warning' => 'Not allowed']);
}
$role->delete();
// redirect
Session::flash('message', 'Record successfully deleted!');
Session::flash('alert-type', 'success');
return Redirect::to('role');
}
This highly depends on how you want to handle the errors. You can either catch the sql exception and display your custom error OR what is probably better for you is to handle the incoming request, validate it and return an error if validation fails.
Here are the validation docs : https://laravel.com/docs/5.3/validation
You have multiple options on how to validate a request. Simple example to validate a title is unique in the table posts and is maximum 255 chars long:
$this->validate($request, [
'title' => 'required|unique:posts|max:255'
]);
If you cannot find a rule that is helping you simply define your own validation rule https://laravel.com/docs/5.3/validation#custom-validation-rules
Ofcourse you can also do the validation manually. In your request or in your controller (depends on your setup) just check for it
// assuming you want to delete an entry
public function delete(Request $request, $id)
{
$role = App\Role::findOrFail($id);
if ($role->users() != null) {
return redirect()->back()->withInput(['message' => 'Not allowed']);
// now you can output $message
}
$role->delete();
return ...
}
Related
I have this custom function for atempting to login in Laravel 8
protected function attemptLogin(Request $request)
{
$credentials = $this->credentials($request);
$credentials['estado']=1;
return $this->guard()->attempt(
$credentials, $request->filled('remember')
);
}
How I can make to accept the login atempt when $credentials['estado'] also has 2 as value.
Don't know how to make it accept multiple values.
I managed to make the custom function accept the value of 1 but dunno how to make it accept multiple $credentials['estado'] values.
You don't need to change anything in attemptLogin() method, instead you can customize the crededentials() method in LoginController like this:
// login, if user have like a following described data in array
protected function credentials(Request $request)
{
$username = $this->username();
return [
$username => $request->get($username),
'password' => $request->get('password'),
'estado' => [ 1, 2 ], // OR condition
];
}
Answer for comments:
Honestly in my experience I didn't have that case, but if you want to redirect to the another view on failed login (for specific field 'estado'), you can customize the "sendFailedLoginResponse" method, and add some additional if-condition for checking the 'estado'.
As the "sendFailedLoginResponse" method will be called only for getting failed login response instance, then you can check: is that fail comes from 'estado' field actually. Something like this:
protected function sendFailedLoginResponse(Request $request)
{
// custom case, when login failed and estado is 2
if ($request->get('estado') == 2) {
return view('some.specific.view');
}
// laravel by default implementation
else {
throw ValidationException::withMessages([
$this->username() => [trans('auth.failed')],
]);
}
}
Remember, in this case (when we're redirecting the user to some page) we actually not redirecting as for always, but instead we're just returning a view. We do that because I think you don't want to let the users to open that specific view page anytime their want, as you need to let them see that page only for specific case. But when you'll do the actual redirect, then you will let the users to visit that page with some static URL.
Of course, you can do some additional stuff (add something in DB or the Session, and check is the request comes actually from 'estado' fails, but not from any user), but this could be a headeche for you, and in my opinion that will not be a real laravel-specific code.
Anyway, this is the strategy. I don't think, that this is mandatory, but this can be do your work easy and secure.
Note: I've got this deafault implementations from "AuthenticatesUsers" trait (use use Illuminate\Foundation\Auth\AuthenticatesUsers;). In any time you can get some available methods from there and override them in your LoginController, as the LoginController used that as a trait.
I'm trying to use the password reset ability of Laravel's authentication. After running make:auth command, inside my ResetPasswordController, I have overridden rules function of Illuminate\Foundation\Auth\ResetsPasswords trait as the following:
protected function rules()
{
return [
'token' => 'required',
'email' => 'required|email',
'password' => 'required|confirmed|min:4',
];
}
So, I am trying to change the minimum length value to 4. But when I try to reset my password, a rule of minimum of 8 characters is still being applied instead of 4.
Here is the reset function of laravel in the same file:
public function reset(Request $request)
{
$request->validate($this->rules(), $this->validationErrorMessages());
// Here we will attempt to reset the user's password. If it is successful we
// will update the password on an actual user model and persist it to the
// database. Otherwise we will parse the error and return the response.
$response = $this->broker()->reset(
$this->credentials($request), function ($user, $password) {
$this->resetPassword($user, $password);
}
);
// If the password was successfully reset, we will redirect the user back to
// the application's home authenticated view. If there is an error we can
// redirect them back to where they came from with their error message.
return $response == Password::PASSWORD_RESET
? $this->sendResetResponse($request, $response)
: $this->sendResetFailedResponse($request, $response);
}
And the $response being returned is Illuminate\Support\Facades\Password::INVALID_PASSWORD. I don't understand where this rule is coming from.
Actually the validation behavior is like this: When I enter less than 4 characters, my own rule is applied (correctly). However, entering 4 to less than 8 characters is also an error by some other rule.
The reason that you're getting the error back is because the PasswordBroker expects a password with a minimum length of 8 characters so even though your form validation is passing, the validation in the PasswordBroker isn't.
One way to get around this would be to override the broker() method in your ResetPasswordController and pass your own validator to it:
public function broker()
{
$broker = Password::broker();
$broker->validator(function ($credentials) {
return $credentials['password'] === $credentials['password_confirmation'];
});
return $broker;
}
The above is essentially the same as what's going on in the PasswordBroker itself, just without the string length check as well.
Don't forget to import the Password facade into your controller:
use Illuminate\Support\Facades\Password;
This isn't essential, but for good measure I would then suggest updating the password error message in your resources/lang/en/passwords.php file as well.
I wonder if I should do form validation before retrieving input values or vice versa.
I usually do validation first as I see no benefit in trying to access input values that might not be valid. However, a coworker looked at my code recently and found it strange. Is there any correct order for these steps?
public function createGroups(Request $request)
{
$this->validate($request, [
'courses' => 'required_without:sections',
'sections' => 'required_without:courses',
'group_set_name' => 'required',
'group_number' => 'required|integer|min:1'
]);
$courses = $request->input('courses');
$sections = $request->input('sections');
$group_set_name = $request->input('group_set_name');
$group_number = $request->input('group_number');
Positioning the validation for your controller logic at the beginning of a method is probably the way to go here, as you have required parameters defined. If you receive data that does not fully satisfy the requirements, you produce a validation error back to the user. This follows the productive "Fail Fast" line of thinking: https://en.wikipedia.org/wiki/Fail-fast
It's also important that you're not using any data that hasn't passed your stringent requirements from validation. Data that fails validation should no longer be trusted. Unless there's some other reason you need to be, say, logging any incoming data from the frontend, the order here looks good to me.
I totally agree with #1000Nettles response, to elaborate a little bit more on his/her answer (who should be the accepted one): There isn't any need to continue with your business logic when the data doens't comply with your specifications. Let's say you expected a string of a N characters long, because you defined your database with that limitation (in order to optimize the db desing), will you try to persist it even when it'll throw an exception? Not really.
Besides, Laravel has a particular way to extract validation classes: Form Request. This are injected in controllers. When a call reach the controller it means that already passed the validation, if not, an 422error be returned.
Create a custom request and keep the mess out of your controller, it doesn't even hit your controller function if validation failed and can just grab the data in your controller if validation passed.
php artisan make:request GroupRequest
In app/Http/Requests/GroupRequest.php:
public function authorize()
{
// return true;
return request()->user()-isAdmin; // <-- example, but true if anyone can use this form
}
public function rules()
{
return [
'courses' => ['required_without:sections'],
'sections' => ['required_without:courses'],
'group_set_name' => ['required'],
'group_number' => ['required', 'integer', 'min:1'],
];
}
The best part is you can even manipulate the data in here (GroupRequest.php) after it has been validated:
public function validated()
{
$validated = $this->getValidatorInstance()->validate();
// EXAMPLE: hash password here then just use new hashed password in controller
$validated['password'] = Hash::make($validated['password']);
return $validated;
}
In your controller:
public function createUser(UserRequest $request) // <- in your case 'GroupRequest'
{
$validated = $request->validated(); // <-- already passed validation
$new_user = User::create($validated); // <-- password already hashed in $validated
return view('dashboard.users.show')->with(compact('user'));
}
In your case, if you use my GroupRequest block above, you can return to view in 1 line of code:
public function createGroups(GroupRequest $request)
{
return view('example.groups.show')->with($request->validated()); // <-- already an array
}
In you blade view file, you can then use your variables like {{ $group_set_name }} and {{ $group_number }}
Before anything, thank you for your help
I've been having a problem for days with Laravel login. My only credential to login users is their document (it's quite a small system) but, recently, I'm starting to have trouble when logging in with documents larger than 10 chars. Whenever I try to log in with a document like that, it sends me back to the login view with no errors, it just doesn't let me log in.
I'm using the Auth::loginUsingId($request->document) (I already checked that the user had $primaryKey = 'document')
I tried to refresh all migrations, show the output of every single line in the login method, but everything seems to be fine, the Auth::loginUsingId() returns the corresponding User instance but it's just not redirecting to the right view. I really hope you can help me with this one
Here is the code of my login method in the AdminController.php
public function login(Request $request) {
// validacion
$rules = [
'document' => 'required|numeric|exists:users,document'
];
$validator = Validator::make($request->all(), $rules);
if($validator->fails()){
return redirect()
->route('admin.login')
->withErrors($validator)
->withInput();
}
// login
$document = $request->document;
if(Auth::loginUsingId($document)){
return redirect()->route('home');
}else{
return redirect()
->route('admin.login')
->with('status', 'Login Failed');
}
}
In laravel 5.2, i want to add the condition so that only users where their expiry date is greater than today's date to login.
protected function getCredentials(Request $request)
{
return ['email' => $request->{$this->loginUsername()}, 'password' => $request->password];
}
The code does not accept adding:
'expires' => gte(Carbon::now())
Any help is appreciated
I don't think this is possible, even in Laravel 5.5. Taking a look at the retrieveByCredentials method in Illuminate\Auth\EloquentUserProvider which is used to get the user from the database, you can see that the query passes simple key/value combinations to the where method on the $query object, which equate to where key = value. This is from 5.5:
public function retrieveByCredentials(array $credentials)
{
if (empty($credentials) ||
(count($credentials) === 1 &&
array_key_exists('password', $credentials))) {
return;
}
// First we will add each credential element to the query as a where clause.
// Then we can execute the query and, if we found a user, return it in a
// Eloquent User "model" that will be utilized by the Guard instances.
$query = $this->createModel()->newQuery();
foreach ($credentials as $key => $value) {
if (! Str::contains($key, 'password')) {
$query->where($key, $value);
}
}
return $query->first();
}
To achieve what you are after I would recommend doing this check after the user has logged in, in your controller for instance:
// Imagine this is the controller method where you're dealing with user logins
public function login(array $credentials)
{
if (! auth()->attempt($credentials)) {
// Handle what happens if the users credentials are incorrect.
}
$user = auth()->user();
if (Carbon::now()->gte($user->expires)) {
// User's account has expired, lets log them out.
auth()->logout();
// Return a redirect with a message or something...
}
// Handle a successful login.
}
I'm not sure if the auth() helper is available in 5.2, but you should be able to use the Auth facade to do the same thing, e.g. Auth::attempt(...).