Laravel 8 Unique form validation ignore - php

My form edit
<form action="{{ route('admin.akun.update', $akun->id_pegawai) }}" method="POST">
#csrf
#method('patch')
<input type="text" class="form-control" #if (old('nip')) value="{{old('nip')}}" #else value="{{$akun->nip}}" #endif name="nip">
My Route
Route::patch('admin/akun/{id_pegawai}', 'AkunController#update')->name('admin.akun.update');
My Controller
public function update(Request $req, $id_pegawai)
{
$user = new User;
$pegawai = new Pegawai;
$this->validate($req, [
'nip' => ["required", "unique:users,nip," . $id_pegawai],
]);
}
condition: I edit my data, I have a unique field, if I don't change the unique field it will be successfully edited, and if I replace it with the same data there will be an alert

You need to specify against which column you want to be unique
Rule::unique('users', 'nip')->ignore($id_pegawai, 'id_pegawai'),

Try it like this:
["required", "unique:users,nip,".$id_pegawai]
Documentatin: https://laravel.com/docs/8.x/validation#rule-unique
Syntax: unique:table,column,except,idColumn

Related

Laravel 8: Call to a member function notify() on string

I'm working with Laravel 8 to make a forum and now I wanted to send a notification to a user who has asked a question whenever someone answered that question.
So in order to do that, I added this to the Controller:
public function postAnswer(Request $request, $question, $asker) {
$validate_data = $request->validate([
'answer' => 'required',
]);
$answer = Answer::create([
'answer' => $request->answer,
'user_id' => auth()->user()->id,
'question_id' => $question,
]);
$asker->notify(new RepliedToThread($question));
return back();
}
So the $question is the question id and $asker is the user id of the person who has asked the question.
But now I get this error:
Call to a member function notify() on string
Now the interesting part is that, when I use auth()->user()->notify(new RepliedToThread($question));, it is working fine!
But I don't want to notify any authenticated user, I need to notify the person who has asked question, that's why I tried passding $asker...
So if you know how to solve this question, please let me know...
I would really appreciate any idea or suggestion from you guys.
Thanks in advance.
Blade:
<form action="{{ route('questions.answers', ['question' => $show->id, 'asker' => $show->user->id]) }}" method="POST" enctype="multipart/form-data">
#csrf
<div class="mb-4">
<textarea name="answer" id="answer" class="form-control BSinaBold" rows="7" placeholder="Write down answer"></textarea>
#error('answer')
<div class="text-red-500 mt-2 text-sm">
{{ $message }}
</div>
#enderror
</div>
</br>
<button type="submit" class="btn btn-primary">Send</button>
</form>
The problem is that asker is just an id, you should take the other information of the asker from the user table and after that pass your notification to the asker.
$asker = User::findOrFail($asker);
$asker->notify(new RepliedToThread($question));
Based on what you said, the problem is $asker. This parameter should be an instance from User model but the error implies it is string.
In your form change the form tag:
<form action="{{ route('questions.answers', ['question' => $show->id, 'asker' => $show->user]) }}" method="POST" enctype="multipart/form-data">
Remove the id after user.
make sure your Answer.php have use Notifiable; imported
ref link https://laravel.com/docs/8.x/notifications#using-the-notifiable-trait
then only you can call notify() function from Answer model instance
The problem is that $asker is just a string, its just the idvalue of the user.
I recommend that you use laravels route model binding. The passed parameters $querstion and $asker are automatically queried from the database.
https://laravel.com/docs/8.x/routing#route-model-binding
Afterwards you can access the question id normally and you have the notifiable user model.
public function postAnswer(Request $request, Question $question, User $asker) {
$validate_data = $request->validate([
'answer' => 'required',
]);
$answer = Answer::create([
'answer' => $request->answer,
'user_id' => auth()->user()->id,
'question_id' => $question->id,
]);
$asker = User::find($asker);
$asker->notify(new RepliedToThread($question));
return back();
}

update table with laravel

I can`t update my table. Maybe you can see where is problem.
Edit works fine. It brings value to the fields. If i erase {{ method_field('PUT') }} it saves values normally, but i need to UPDATE
It`s my UPDATE controller
public function update(Request $request, Radar $radar)
{
Radar::update([
'date' => $request->input('date'),
'number' => $request->input('number'),
'distance' => $request->input('distance'),
'time' => $request->input('time'),
]);
return redirect('/radars');
}
Thats how my view looks like:
<form action="{{ url('/radars')}}" method="post" >
{{ method_field('PUT') }}
{{ csrf_field() }}
Routes:
Route::put('radars/{radar}', 'RadarsController#update');
Error:
MethodNotAllowedHttpException
No message
Thank you for help.
You need to specify ID:
{{ url('/radars/') . $radar->id }}
Also, you need to use object and not just a model class. Something like:
public function update(Request $request, Radar $radar)
{
$radar->update($request->all());
return redirect('/radars');
}
Illuminate\Support\Facades\Request
Radar::update(Request::all());
Looking at it, I think it should be like the below:
<form action="{{ url('/radars/' . $radar->id )}}" method="post" >
If you look, you're posting to /radars but your route is radars/{driver}
You need to add where statement to tell which record to update and dont
Radar::where('id',$id)->update([
'date' => $request->input('date'),
'number' => $request->input('number'),
'distance' => $request->input('distance'),
'time' => $request->input('time'),
]);
return redirect('/radars');
If you are using resource route, then in your form, you need to change action from:
url('/radars')
to
route('radars.update', $radar)
and keep the following:
{{ method_field('PUT') }}

How to pass form data as route parameters in Laravel 5.2

I want to use the value of only one field of a form with two fields as a route parameter to a controller. What I achieve so far was only a mess of query string parameters appended to the url.
My form:
{{ Form::open(['route' => ['anuncio.especificar_tipo_imovel', $valorCep = 'valorCEp'], 'method' => 'GET']) }}
<input type="hidden" value="14405024" id="valorCep" name="valorCep"/>
<label for="tbCep"/>
<input autocomplete="off" id="tbCep" style="width:400px;" name="cep" type="text" />
</label>
<input type="submit" value="continuar">
{{ Form::close() }}
I have a route like this:
Route::get('anuncio/especificar_tipo_imovel/{valorCep}', [
'as' => 'anuncio.especificar_tipo_imovel',
'uses' => 'AnuncioController#especificar_tipo_imovel'
]);
and a action method like this
public function especificar_tipo_imovel(Request $request, $valorCep)
{
return view('especificar_tipo_imovel');
}
The value I want to send is the value of the hidden field: valorCep
I want a url like
http://my_route/34834839
the number beeing the value of the hidden field and the $valorCep route parameter.
my url is this way:
http://my_route/valorCEp?valorCep=14405024&cep=Rua++jardim+pedreiras14405024
Note that you are assigning $valorCep in the view with the literal 'valorCep'. You should pass it from your controller.
public function especificar_tipo_imovel(Request $request, $valorCep)
{
return view('especificar_tipo_imovel', ['valorCep' => $valorCep]);
}
and in your view:
{{ Form::open(['route' => ['anuncio.especificar_tipo_imovel', $valorCep], 'method' => 'GET']) }}
You can use this
public function especificar_tipo_imovel(Request $request, $cep)
{
$valorCep = $request->valorCep;
return view('especificar_tipo_imovel', ['valorCep' => $valorCep]);
}
You can't pass the value of the hidden input to the route like this $valorCep = 'valorCEp', that way you are just passing the string 'valorCEp' as the parameter. Check your url, is the route with the parameter being the string, plus the input's values (cause the GET method).
Unless you have the value of valorCep input in a variable, and pass this variable instead the 'valorCEp' string, you will need some javascript. Something like this:
Script (with jQuery)
$('input[type=submit]').on('click', function(event){
event.preventDefault();
var valorCep = $('#valorCep').val();
$('form').attr('action', 'anuncio/especificar_tipo_imovel/'+valorCep);
$('form').submit();
});

How to use Laravel form model binding with array as name attribute?

I want to use form model binding in Laravel. The following (simplified) example works fine:
{{ Form::model($user, array('class'=>'form-horizontal')) }}
{{ Form::token() }}
{{ Form::label('email', 'Email Address') }}
{{ Form::text('email') }}
{{ Form::close() }}
However, I want to use arrays in the name attributes, as is pretty standard nowadays. In other words, have user[email] as the field name, so that I get all the form elements in one array in the backend.
Is this possible with model binding? When I use {{ Form::text('user[email]') }} the email does not get filled in. I tried adding array('user'=>$user) in the Form::model function in case it needed a nested value, but no luck.
Form::model(array('user' => $user)) is the correct solution, BUT unfortunately the implementation of form model binding is pretty bad as it does not easily work on a nested, mixed set of arrays and objects. See https://github.com/laravel/framework/pull/5074
You could try Form::model(array('user' => $user->toArray())) or Form::model((object) array('user' => $user)).
You could do something like this assuming that you would have a single $user and multiple $types
Form::macro('userTypes', function($user,$types)
{
foreach ($types as $type) {
$concat = $user . "_" . $type;
return '<input type="{$type}" name="{$concat}">';
}
});
And customize the output with your form style, even adding more complexity to the function might be required.
And then simply calling it for example
$user = "johndoe";
$types = array("email","text");
Form::userTypes($user,$types);
That would result in
<input type="email" name="johndoe_email">
<input type="text" name="johndoe_phone">
If you want to do it in a single line and assuming that you would have a single $user and a single$type you could do something like
Form::macro('userType', function($user,$type)
{
return '<input type="{$type}" name="{$user[$type]}">';
});
And with the call
$user = [ "mail" => "some_mail_value" ];
Form::userType($user,"mail");
Would result in
<input type="mail" name="some_mail_value">
Or perhaps you'd like something that would work with a single $user key-value array as :
Form::macro('userType', function($user)
{
$keys = array_keys($user);
foreach ($keys as $key => $value) {
return '<input type="{$key}" name="{$value}">';
}
});
And with the call
$user = ["mail" => "mail_value" , "text" => "text_value"];
Form::userType($user);
That would result in
<input type="mail" name="mail_value">
<input type="text" name="text_value">
And finally I didn't find a direct way to do it with default form model binding, as It requires the field name to be the same as the model attribute, but you could do a workaround as
Form::macro('customBind', function($user_model,$type)
{
return '<input type={$type} name="user[{$type}]" value="{$user_model->$type}">';
});
And then
$user = new User();
$user->email = "johndoe#gmail.com";
Form::customBind($user,"email");
Which would produce something like
<input type="email" name="user[email]" value="johndoe#gmail.com">
The takeaway point is that basically the solution to your problem is creating a Macro, I have provided some workarounds on this but you will need to refine this to your specific needs.

Persist Form Data Across Multiple Steps in Laravel

When I've made multistep forms in the past I would generally store the form data in the session before returning it to the view, that way the data persists if the user refreshes the page or clicks the browser's native back buttons.
Transferring my past logic to Laravel I built the following form consisting of three stages:
[Input -> Confirm -> Success]
Routes.php
Route::group(array('prefix' => 'account'), function(){
Route::get('register', array(
'before' => 'guest',
'as' => 'account-create',
'uses' => 'AccountController#getCreate'
));
Route::post('register', array(
'before' => 'guest|csrf',
'as' => 'account-create-post',
'uses' => 'AccountController#postCreate'
));
Route::get('register/confirm', array(
'before' => 'guest',
'as' => 'account-create-confirm',
'uses' => 'AccountController#getCreateConfirm'
));
Route::post('register/confirm', array(
'before' => 'guest|csrf',
'as' => 'account-create-confirm-post',
'uses' => 'AccountController#postCreateConfirm'
));
Route::get('register/complete', array(
'before' => 'guest',
'as' => 'account-create-complete',
'uses' => 'AccountController#getCreateComplete'
));
});
AccountController.php
<?php
class AccountController extends BaseController {
private $form_session = 'register_form';
public function getCreate()
{
if(Session::has($this->form_session))
{
// get forms session data
$data = Session::get($this->form_session);
// clear forms session data
Session::forget($this->form_session);
// load the form view /w the session data as input
return View::make('account.create')->with('input',$data);
}
return View::make('account.create');
}
public function postCreate()
{
// set the form input to the session
Session::set($this->form_session, Input::all());
$validation_rules = array(
'email' => 'required|max:50|email|unique:users',
'password' => 'required|max:60|min:6',
'password_conf' => 'required|max:60|same:password'
);
$validator = Validator::make(Input::all(), $validation_rules);
// get forms session data
$data = Session::get($this->form_session);
// Return back to form w/ validation errors & session data as input
if($validator->fails()) {
return Redirect::back()->withErrors($validator);
}
// redirect to the confirm step
return Redirect::route('account-create-confirm');
}
public function getCreateConfirm()
{
// prevent access without filling out step1
if(!Session::has($this->form_session)) {
return Redirect::route('account-create');
}
// get forms session data
$data = Session::get($this->form_session);
// retun the confirm view w/ session data as input
return View::make('account.create-confirm')->with('input', $data);
}
public function postCreateConfirm()
{
$data = Session::get($this->form_session);
// insert into DB
// send emails
// etc.
// clear forms session data
Session::forget($this->form_session);
// redirect to the complete/success step
return Redirect::route('account-create-complete');
}
public function getCreateComplete() {
return View::make('account.create-complete');
}
}
create.blade.php
<form action="{{ URL::route('account-create-post') }}" method="post">
Email: <input type="text" name="email" value="{{ (isset($input['email'])) ? e($input['email']) : '' }}">
#if($errors->has('email'))
{{ $errors->first('email') }}
#endif
<br />
Password: <input type="text" name="password" value="">
#if($errors->has('password'))
{{ $errors->first('password') }}
#endif
<br />
Password Confirm: <input type="text" name="password_conf" value="">
#if($errors->has('password_conf'))
{{ $errors->first('password_conf') }}
#endif
<br />
{{ Form::token() }}
<input type="submit" value="Confirm">
</form>
create-confirm.blade.php
Email: {{ $input['email']; }}
Password: {{ $input['password']; }}
<form action="{{ URL::route('account-create-confirm-post') }}" method="post">
{{ Form::token() }}
return
<input type="submit" name="submit_forward" value="Submit">
</form>
The above works fine, however I am wondering if this is the best way to approach multi-step forms in Laravel?
When I have created multi-part forms, I have always done it in a way so that the user can always come back and finish the form later, by making each form persist what it has to the database.
For instance
Step 1 - Account Creation
I would have the user create their authentication details at this step, create the user account (with password) here and also log the user in, redirecting to the dashboard. There I can do a check to see if the user has a profile and if they don't, redirect them to the profile creation form.
Step 2 - Profile Creation
Because we have an authenticated user, the profile creation form can save its data to the currently logged in user. Subsequent sections follow the same process but check the existence of the previous step.
Your question seems to be about confirming whether a user wishes to create an account. What I would do in your situation would be, on the form you created to confirm the user account, I would keep the user's data in hidden input fields.
Email: {{ $input['email'] }}
Password: {{ $input['password'] }}
<form action="{{ URL::route('account-create-confirm-post') }}" method="post">
<input type="hidden" name="email" value="{{ $input['email'] }}">
<input type="hidden" name="password" value="{{ $input['password'] }}">
{{ Form::token() }}
return
<input type="submit" name="submit_forward" value="Submit">
</form>
Although displaying the user's chosen password back to them on this page seems to be a bit superfluous when you ask them to confirm their password on the previous page, plus some users might question why their password is being shown in plaintext on the screen, especially if they are accessing the site from a public computer.
The third option I would suggest would be to create the user account and soft-delete it (Laravel 4.2 Docs / Laravel 5 Docs), returning the user's account number to the new form:
Email: {{ $input['email'] }}
Password: {{ $input['password'] }}
<form action="{{ URL::route('account-create-confirm-post') }}" method="post">
<input type="hidden" name="id" value="{{ $user_id }}">
{{ Form::token() }}
return
<input type="submit" name="submit_forward" value="Submit">
</form>
then undo the soft-delete when the user confirms their account. This has the added bonus that you could track people trying to sign up multiple times for an account and not completing the process and see if there's a problem with your UX.
Conclusion
Of course, you could also still do it the way you always have with a session, all I have tried to do here is show you some other ways you can approach it, as with everything to do with the best way of doing something, this is a highly opinionated subject and is likely to get many opposing views on how it should be done. The best way to do it is the way that works best for you and your users... mainly your users.
There are two ways to do it (that i can think of). I prefer second one.
Client side - everything can be handled by javascript. Basic validation (if field is email, if field has enough characters etc.) would be checked with javascript. After confirmation, AJAX request would go through server side validation and if anything went wrong you could highlight invalid inputs. "check if email is available" button (via AJAX) would be great too.
Server side - pretty much what you did but I would move it to service - it would make it much cleaner.
public function getCreate() {
if ($this->formRememberService->hasData()) {
return View::make('account.create')
->with('input', $this->formRememberService->getData());
}
return View::make('account.create');
}
public function postCreate() {
$this->formRememberService->saveData(Input::all());
// ...
}
public function postCreateConfirm() {
// ...
$this->formRememberService->clear();
return Redirect::route('account-create-complete');
}
Adding "forget me" action would be nice (especially if form requires more private data).
Why getCreate() has Session::forget()? If someone goes back to change something and accidently leaves your site his data will be lost.
1st) Create a custom hidden field in the form containing a random md5 character set to submit it with the form... (it can be the timestamp, the user ip address, and country concatenated together as 3 md5 strings separated by whatever character , or #, so it can be working as a token of the form)
2nd) pass the hidden field into your controller and validate it after getting the user input from the form by generating the same values in your controller, encrypting these values as md5 too, then concatenate them all together, and compare the values that is coming from the user input form with the values you are generating in your controller.
3rd) Put the values of the form in your controller in a session then regenerate the session id every visit to every view the user is going to visit.
4th) update the timestamp in your session according the timestamp the user is visiting every page.
Just because you know Laravel, does not mean you have to do everything in Laravel.
Multi-step forms should never involve server-side magic. The best and easiest you can do is to hide certain steps with display:none; and switch to the next step using javascript toggling visibilities only.

Categories