Livewire validate function got stuck on unique validation - laravel

I'm trying to validate a field for unique value. The validate function throws proper validation errors When I enter incorrect/duplicate values but the execution got stuck in validate method and not coming below to dd('save called') when I enter correct/unique values.
I have tried protected $rules[], and protected function rules() but result is the same.
Note: when I remove the unique rule from validation then everything works fine.
here is component code.
public function savePaper($rowIndex)
{
$this->validate([
'rows.*.p1' => ['required', Rule::unique('dutysheets', 'p1')->ignore($this->row['id'])],
]);
dd('save called');
$row = $this->rows[$rowIndex] ?? NULL;
if (!is_null($row)) {
optional(DutySheet::find($row['id']))->update($row);
}
$this->editedRowIndex = null;
$this->editedPaper = null;
}
and here is the view code.
#foreach ($rows as $index => $row)
<tr>
<td scope="row">
<input type="number" style="max-width: 60px;"
wire:model.differ="rows.{{ $index }}.p1"
wire:click="editPaper({{ $index }}, 'p1')"
wire:keydown.enter="savePaper({{ $index }})"
/>
#if ($editedPaper === $index . '.p1')
#error('rows.' . $index . '.p1')
<div class="alert alert-danger">
{{ $message }}
</div>
#enderror
#endif
</td>

Related

Updating many-to-many relational data with attach() from multiple checkboxes in Laravel

I am creating an online bookstore in Laravel, and upon creating a new book, the administrator is able to define which warehouses that are able to stock this book, by checking the specific warehouses checkboxes.
To give insight in how it works, this is my create function:
public function create()
{
$authors = Author::all();
$selectedAuthor = Book::first()->author_id;
$publishers = Publisher::all();
$selectedPublisher = Book::first()->publisher_id;
$warehouses = Warehouse::all();
$selectedWarehouse = Book::first()->warehouse_id;
return view('books.create', compact(['authors', 'publishers', 'warehouses'],
['selectedAuthor', 'selectedPublisher', 'selectedWarehouse']
));
}
and my store method:
public function store(Request $request)
{
$request->validate([
'ISBN' => 'required',
'author_id' => 'required',
'publisher_id' => 'required',
'year' => 'required',
'title' => 'required',
'price' => 'required',
]);
try {
$book = Book::create($request->all());
foreach ($request->checked as $value){
$book->warehouses()->attach([$value]);
}
return redirect()->route('books.index')
->with('success','Book created successfully.');
} catch (\Illuminate\Database\QueryException $e) {
var_dump($e->errorInfo);
}
}
But when an administrator edits a book, the checkboxes that were checked upon creating the book, should be "checked", and the administrator should be able to attach more warehouses, and be able to "unselect" a warehouse, so if an already checked value gets unchecked and sumbitted, it should get detached from the many-to-many table.
This is what i currently have:
My edit method:
public function edit(Book $book)
{
$authors = Author::all();
$selectedAuthor = Book::first()->author_id;
$publishers = Publisher::all();
$selectedPublisher = Book::first()->publisher_id;
$warehouses = Warehouse::all();
$selectedWarehouse = Book::first()->warehouse_id;
return view('books.edit', compact(['book', 'authors', 'publishers', 'warehouses'],
['selectedAuthor', 'selectedPublisher', 'selectedWarehouse']));
}
And my update method:
public function update(Request $request, Book $book)
{
$request->validate([
'ISBN' => 'required',
'publisher_id' => 'required',
'author_id' => 'required',
'year' => 'required',
'title' => 'required',
'price' => 'required',
]);
try {
$book->update($request->all());
// TODO: Update warehouses
return redirect()->route('books.index')
->with('success','Book updated successfully.');
} catch (\Illuminate\Database\QueryException $e) {
var_dump($e->errorInfo);
}
}
And the checkboxes in my edit.blade view:
#foreach($warehouses as $warehouse)
<input type="checkbox" name="checked[]" value="{{ $warehouse->id }}">
{{ $warehouse->address }}
<br/>
#endforeach
My Book model:
public function warehouses()
{
return $this->belongsToMany(Warehouse::class);
}
And my warehouse model:
public function books()
{
return $this->belongsToMany(Book::class);
}
Any help on being able to attach / detach upon editing an existing book, would be highly appreciated!
Try this on create and update method for storing
// Your method
foreach ($request->checked as $value){
$book->warehouses()->attach([$value]);
}
// Try This
$book->warehouses()->sync($request->checked); // $request->checked must be an array
Update Blade
#foreach($warehouses as $warehouse)
<input #if($book->warehouses()->where('warehouse_id', $warehouse->id)->exists()) checked #endif type="checkbox" name="checked[]" value="{{ $warehouse->id }}">
{{ $warehouse->address }}
<br/>
#endforeach
I will left this example with a logic according your problem. In this case are roles:
public function edit(Role $role){
//get roles ids
$permission_role = [];
foreach($role->permissions as $permission){
$permission_role[] = $permission->id;
}
//get permissions
$permissions = Permission::all();
return view("role.edit", compact('role', 'permission_role', 'permissions'));
}
In the blade:
<div class="row">
<div class="col-md-8">
<div class="form-group">
<label>Select the permissions for the current role</label>
#foreach ($permissions as $permission)
<div class="valid-feedback d-block" style="font-size: 15px !important;">
<input type="checkbox" value="{{ $permission->id }}" name="permissions[]"
#if(is_array(old('permissions')) && in_array("$permission->id", old('permissions')))
checked
#elseif(is_array($permission_role) && in_array("$permission->id", $permission_role))
checked
#endif>
<strong> {{ $permission->description }} </strong>
</div>
#endforeach
</div>
<div class="invalid-feedback d-block">
#foreach ($errors->get('permissions') as $error)
{{ $error }}
#endforeach
</div>
</div>
</div>
Of this way you can also keep the old checkboxes when nothing is select. You should validate it as required.

Refresh same livewire component after form wire:submit.prevent

Please I'd like to refresh same component after form submit. There is an if statement that allows the the form to display in the component. I'd like to refresh the whole component so as not to show the form again after submit.
I already tried emit but I don't think it works for same component.
Livewire component
<?php
namespace App\Http\Livewire;
use App\Lesson;
use App\Question;
use App\QuestionsOption;
use App\TestsResult;
use Livewire\Component;
class LessonTest extends Component
{
public $test_result;
public $lesson;
public $test_exists;
public array $question = [];
//protected $listeners = ['testDone' => 'render'];
public function mount($test_exists, $lesson, $test_result)
{
$this->lesson = $lesson;
$this->test_exists = $test_exists;
$this->test_result = $test_result;
}
public function lessonTest()
{
$lesson = Lesson::where('slug', $this->lesson->slug)->firstOrFail();
$answers = [];
$test_score = 0;
foreach ($this->question as $question_id => $answer_id) {
$question = Question::find($question_id);
$correct = QuestionsOption::where('question_id', $question_id)
->where('id', $answer_id)
->where('correct', 1)->count() > 0;
$answers[] = [
'question_id' => $question_id,
'option_id' => $answer_id,
'correct' => $correct,
];
if ($correct) {
$test_score += $question->score;
}
/*
* Save the answer
* Check if it is correct and then add points
* Save all test result and show the points
*/
}
$test_result = TestsResult::create([
'test_id' => $this->lesson->test->id,
'user_id' => \Auth::id(),
'test_result' => $test_score,
]);
$test_result->answers()->createMany($answers);
$this->reset(['question']);
$this->emit('testDone');
}
public function render()
{
return view('livewire.lesson-test');
}
}
Livewire Blade View
<div>
#if ($test_exists)
<hr />
<h3>Test: {{ $lesson->test->title }}</h3>
#if (!is_null($test_result))
<div class="alert alert-info">Your test score: {{ $test_result->test_result }} /
{{ $lesson->test->questions->count() }}</div>
#else
<form wire:submit.prevent='lessonTest' action="{{ route('lessons.test', [$lesson->slug]) }}"
method="post">
{{ csrf_field() }}
#foreach ($lesson->test->questions as $question)
<b>{{ $loop->iteration }}. {{ $question->question }}</b>
<br />
#foreach ($question->options as $option)
<input type="radio" wire:model='question.{{ $question->id }}'
name="questions[{{ $question->id }}]" value="{{ $option->id }}" />
{{ $option->option_text }}<br />
#endforeach
<br />
#endforeach
<button class="btn btn-success btn-lg refresh" type="submit">Submit</button>
</form>
#endif
<hr />
#endif
</div>
Thank You. I got it solved, I forget that I passed the test result from the controller before, so I had to recall the test_result and also the test_exist inside the lessonTest action.
$this->test_result = TestsResult::where('test_id', $this->lesson->test->id)
->where('user_id', \Auth::id())
->first();
$this->test_exists = true;

Laravel 5.8: Validation multiple inputs

What I have
I have a form with 3 inputs and I want to check the following conditions:
All inputs are integers and they are required.
We do a math operation with all the numbers and we get if the operation was successfull or not.
Success: we redirect the user to a success page.
No success: we show a error message to the user with a message explaining him that the numbers aren't valid.
I resolved this with the following lines.
Controller:
function formAction(Request $request) {
$this->validate($request, [
'number1' => 'integer|required',
'number2' => 'integer|required',
'number3' => 'integer|required',
]);
$numbers = $request->all();
$isValid = MyOwnClass::checkMathOperation($numbers);
if($isValid) {
return redirect()->route('success');
} else {
$request->session()->flash('error', 'The numbers are not valid.');
return back();
}
}
View (using Bootstrap):
<form method="POST" action="{{ route('form-action') }}">
#csrf
<div class="form-group">
<label for="number1">number1</label>
<input id="number1" name="number1" class="form-control {{ $errors->has('number1') ? ' is-invalid' : '' }}" />
</div>
<div class="form-group">
<label for="number2">number2</label>
<input id="number2" name="number2" class="form-control {{ $errors->has('number2') ? ' is-invalid' : '' }}" />
</div>
<div class="form-group">
<label for="number3">number3</label>
<input id="number3" name="number3" class="form-control {{ $errors->has('number3') ? ' is-invalid' : '' }}" />
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
#if ($errors->any())
<div class="alert alert-danger">
<ul>
#foreach ($errors->all() as $error)
<li>{{ $error }}</li>
#endforeach
</ul>
</div>
#endif
What I looking for
When MyOwnClass::checkMathOperation($numbers) is false:
To highlight number1, number2 and number3 inputs.
To show an unique custom error message
To hide the number1, number2 and number3 input error messages.
How can I do that with validators?
Solution
Create a Form Request Validation called, for example, NumbersForm using:
php artisan make:request NumbersForm
The previous command creates a App/Http/Requests/NumbersForm.php file. Make authorize() returns true, put the validation rules into rules() and create a withValidatior() function.
class NumbersForm extends FormRequest
{
public function authorize() {
return true;
}
public function rules() {
return [
'number1' => 'integer|required',
'number2' => 'integer|required',
'number3' => 'integer|required',
];
}
public function withValidator($validator) {
$validator->after(function ($validator) {
$numbers = $this->except('_token'); // Get all inputs except '_token'
$isValid = MyOwnClass::checkMathOperation($numbers);
if(!$isValid) {
$validator->errors()->add('number1', ' ');
$validator->errors()->add('number2', ' ');
$validator->errors()->add('number3', ' ');
$validator->errors()->add('globalError', 'The numbers are not valid.');
}
});
}
}
Note: It's not important the text in the second param of $validator->errors()->add('number1', ' ');, but it can't be empty. If it is an empty string, $errors->has('number1') returns false, and the field won't be hightlighted.
Set the controller like this:
use App\Http\Requests\NumbersForm;
function formAction(NumbersForm $request) {
$this->validated();
return redirect()->route('success');
}
And, finally, if we want to print an unique error message, we must remove the following lines from view:
#if ($errors->any())
<div class="alert alert-danger">
<ul>
#foreach ($errors->all() as $error)
<li>{{ $error }}</li>
#endforeach
</ul>
</div>
#endif
and replace them with:
#if ($errors->has('globalError'))
<div class="alert alert-danger">
{{ $errors->first('globalError') }}
</div>
#else
#if ($errors->any())
<div class="alert alert-danger">
<ul>
#foreach ($errors->all() as $error)
<li>{{ $error }}</li>
#endforeach
</ul>
</div>
#endif
#endif
I haven't tested this but I think it can get you going in the right direction.
1 // Highlight the inputs
You can do this by accessing the error object within your view. This object is an instance of the MessageBag object.
Here is the docs: https://laravel.com/docs/5.7/validation#working-with-error-messages
Example:
// if the error exists for the input the class will be added
<input class=" {{ $error->has('number1') ? 'highlight' : '' }}" name="number1">
<input class=" {{ $error->has('number2') ? 'highlight' : '' }}" name="number2">
<input class=" {{ $error->has('number3') ? 'highlight' : '' }}" name="number3">
2 & 3 // Show a unique custom error message and hide the default messages
See the validator docs: https://laravel.com/docs/5.8/validation#custom-error-messages && https://laravel.com/docs/5.7/validation#working-with-error-messages -- this should solve both of these.
There is a validator callback and I think you can pass your second validation into that. If these numbers aren't valid then you can add your custom error messages and access them the same way as I did above.
function formAction(Request $request) {
$validator = $this->validate($request, [
'number1' => 'integer|required',
'number2' => 'integer|required',
'number3' => 'integer|required',
]);
$validator->after(function ($validator) {
$numbers = $request->all();
$isValid = MyOwnClass::checkMathOperation($numbers);
if(!$isValid) {
$validator->errors()->add('number1', 'Unique message');
$validator->errors()->add('number2', 'Unique message');
$validator->errors()->add('number3', 'Unique message');
}
});
}
Custom Validation Rules:
To add custom messages and validation you can also write a custom validation rule
Example:
class Uppercase implements Rule
{
/**
* Determine if the validation rule passes.
*
* #param string $attribute
* #param mixed $value
* #return bool
*/
public function passes($attribute, $value)
{
return strtoupper($value) === $value;
}
/**
* Get the validation error message.
*
* #return string
*/
public function message()
{
return 'The :attribute must be uppercase.';
}
}
Custom Error Messages:
You could also add custom error messages for rules within a Request:
public function messages()
{
return [
'number1.required' => 'My custom message telling the user he needs to fill in the number1 field.',
'number1.integer' => 'My custom message telling the user he needs to use an integer.',
];
}

How to add encrypt task before inputdata goes to dastabase?

I made contact-form which all input data goes to a MySQL database.
And when it's saved, it auto replies an email to the customer and me.
This works fine. The next step would be to encrypt that.
Where do I have to put the encrypt task and how?
I just did
php artisan key:generate
and
{{ $contact->name = \Crypt::encrypt($contact->name) }}
I can see encrypt data.
here is my code input date goes database
Contact::create($request->all());
and I would like to know how to decrypt and show all data.
Contact.php ( I fixed twice now)
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Contact extends Model
{
use EncryptsAttributes;
protected $fillable = [
'type',
'name',
'email',
'gender',
'body'
];
use EncryptsAttributes;
protected $encrypts = [
'type',
'name',
'email',
'gender',
'body'
];
static $types = [
'1',
'2',
'3',
'4'
];
}
and this is my confirm.blade.php
For checking I would like to see the encryped data
<tr>
<th>name</th>
<td>{{ $contact->name }}
(This isn't encrypt)
<br>
{{ $contact->name = \Crypt::encrypt($contact->name) }}
(I this this before I asked here. I can see name encrypeted)
</td>
</tr>
This is my name section of index.blade.php file
<div class="form-group{{ $errors->has('name') ? ' has-error' : '' }}">
{!! Form::label('name', 'YOUR NAME:', ['class' => 'col-sm-2 control-label']) !!}
<div class="col-sm-10">
{!! Form::text('name', null, ['class' => 'form-control']) !!}
#if ($errors->has('name'))
<span class="help-block">
<strong>{{ $errors->first('name') }}</strong>
</span>
#endif
</div>
</div>
now I got this error
Illuminate \ Database \ Eloquent \ MassAssignmentException
Add [_token] to fillable property to allow mass assignment on [App\Contact].
foreach ($this->fillableFromArray($attributes) as $key => $value) {
$key = $this->removeTableFromKey($key);
// The developers may choose to place some attributes in the "fillable" array
// which means only those attributes may be set through mass assignment to
// the model, and all others will just get ignored for security reasons.
if ($this->isFillable($key)) {
$this->setAttribute($key, $value);
} elseif ($totallyGuarded) {
throw new MassAssignmentException(sprintf(
'Add [%s] to fillable property to allow mass assignment on [%s].',
$key, get_class($this)
));
}
}
return $this;
}
now i got this error
htmlspecialchars() expects parameter 1 to be string, array given (View: C:\xampp\php\041951257234\resources\views\contacts\confirm.blade.php)

How to pass array to flash message?

I want to send array of additional_feature that they are exist to flash message. Now i only send one additional_feature. Any suggestion how can i do that?
if(!empty($additional_features)){
foreach($additional_features as $additional_feature){
$data = [
'name' => $additional_feature,
];
if (!Feature::where('name', '=', $additional_feature)->exists()) {
$additional = Feature::firstOrCreate($data);
$additional_ids[] = $additional->id;
}
else{
return redirect()->back()->withFlashMessage($additional_feature . ' exists!');
}
}
}
You can use session() instead of with():
session->flash('someVar', $someArray);
Another thing you could try is to seriallize array and pass it as string. Then unserilize it and use.
Also, you could save an array using simple session:
session(['someVar' => $someArray]);
Then get it and delete manually:
session('somevar');
session()->forget('someVar');
We had the same problem and forked the package. you can find it here:
Forked at first from Laracasts/Flash to use multiple message
#if (Session::has('flash_notification.message'))
#if (Session::has('flash_notification.overlay'))
#include('flash::modal', ['modalClass' => 'flash-modal', 'title' => Session::get('flash_notification.title'), 'body' => Session::get('flash_notification.message')])
#else
<div class="alert alert-{{ Session::get('flash_notification.level') }}">
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>
{!! Session::get('flash_notification.message') !!}
</div>
#endif
#endif
And the content of the include flash::modal
#if (Session::has('flash_notification.messages'))
#foreach (Session::get('flash_notification.messages') as $flashMessage)
#foreach($flashMessage as $type => $message)
<script>
$(function() {
var message = ('{{ $message }}<br>').replace(/'/g, "’");
customFlashMessage({
type: "{{ $type }}",
message: message
});
});
</script>
#endforeach
#endforeach
#endif
return redirect()->back()->with(['session1' => $value, 'session2' => $value]);
In the blade template:
{{ Session::get('session1') }}
{{ Session::get('session2') }}

Resources