Livewire: create and update in two tables - laravel-livewire

First to say that I am a newbie in Laravel. I have started to develop the frontend of an APP in Laravel Nova with Livewire. The problem is that before, I was able to do operations in my controller but I don't know how to do it in the Livewire resource.
I have a simple form:
<form wire:submit.prevent="submit" class="rounded px-8 pt-6 pb-8 mb-4">
<input type="text" placeholder="Introduzca código" wire:model="code" class="md:inline-block ktext-gray-700 text-sm font-bold mb-2">
<br>
#error('code')
{{$message}}
#enderror
<br>
<input type="text" placeholder="Introduzca tipo" wire:model="access" class="md:inline-block ktext-gray-700 text-sm font-bold mb-2">
<br>
#error('access')
{{$message}}
#enderror
<br><br>
<x-jet-button type="submit">Crear Asistencia</x-jet-button>
</form>
Which inserts three data into a table:
public function submit()
{
//validate
$this->validate();
Attendance::create([
'code' => $this->code,
'ip' => $_SERVER['REMOTE_ADDR'],
'access' => $this->access,
]);
But I want that in a second table, when the "code" field matches (it is in both tables and is a boolean) the code is update in this second table.
How would you do it? Thanks in advance.

Im not a pro laravel but a think any like the code below can help with yout question:
// If you have more than one register with same code:
$infos = SecondTableModel::where('code', $this->code)->get();
foreach ($infos as $info) {
SecondTableModel::find($info->id)->update([
'ip' => $_SERVER['REMOTE_ADDR'],
'access' => $this->access,
// ... OTHERS FIELSDS
]);
}
// If you have only one register on the second table with the same code
SecondTableModel::where('code', $this->code)->first()->update([
'ip' => $_SERVER['REMOTE_ADDR'],
'access' => $this->access,
// ... OTHERS FIELSDS
]);

Related

Testing livewire component does not transfer variable to route() method

I'm testing my livewire component. In my component view I have a named route
route('add_result_bulk', ['round' => $round])
When I run the test, it gives this error:
Missing required parameter for [Route: add_results_bulk] [URI: results/add/{round}/bulk] [Missing parameter: round]. (View: /var/www/resources/views/results/livewire/add-results.blade.php) (View: /var/www/resources/views/results/livewire/add-results.blade.php)
If I put a #dd($round) right in front of the route() call, it dumps the $round variable.
Why is it not picking it up in the route then? My route is defined as:
Route::get('results/add/{round}/bulk', [ResultsController::class, 'bulk'])->name('add_results_bulk');
This is my test:
Livewire::test(AddResultsWrapper::class, ['round' => $round])
->set('results', [
[
"id" => null,
"name" => "",
"reserve" => null,
"clean" => false,
], [
"id" => null,
"name" => "Driver 2",
"reserve" => null,
"clean" => false,
],
])->call('validate')
->assertHasErrors(['results.0.name']);
}
$round is declared as a public property in my component. When I access the page through the browser, the link shows up and works correctly.
This is my view:
<div>
<form wire:submit.prevent="">
#csrf
<input type="hidden" name="form_type" value="single">
<div class="max-w-7xl mx-auto py-10">
<div class="bg-white overflow-hidden shadow-xl xl:rounded-lg overflow-x-auto">
<div class="p-6 pb-0 px-8 bg-white grid grid-cols-12 gap-x-4 gap-y-1 form-container min-w-1200px">
<div class="col-span-12">
<b>Please note:</b> When adding multi-lobby results, results need to be added using the <a
href="{{ route('add_results_bulk', ['round' => $round]) }}"
class="no-underline hover:underline text-blue-700">Bulk Loader</a>. Loading the results
here, will cause positions to be calculated incorrectly.
</div>
</div>
<div
class="p-6 px-8 bg-white border-b border-gray-200 grid grid-cols-12 gap-x-4 gap-y-1 form-container min-w-1200px">
...

Livewire checkbox preselected not working

I have collection of permission_types which i can access in my view. I am looping the permission_types to show each one in check box. all the checkboxes are binded to permission_resource, which is defined as a public property array. I want all the checkboxes to be preselected for which i used checked attribute but it won't work, as soon as i remove the wire:model attribute from input all the checkboxes are preselected. which narrows down the problem to wire:model binding.
What i am trying to achieve:
All i want to do is preselect the checkboxes binded to public property $permission_resource. Can anyone please help me out. I really cannot figure out what am i doing wrong here. Appreciate any effort to solve this problem in advance.
Relevent Component Code:
public $permission_types;
public $permission_resource = [];
public function render()
{
$this->permission_types = PermissionType::all();
// dd($this->permission_types->toArray());
return view('livewire.permissions.create-permission-component')
->extends('layouts.app')
->section('content');
}
Relevant View Code:
<div class="row">
#foreach($this->permission_types as $permissionType)
<div class="col-md-3">
<input wire:model="permission_resource" type="checkbox" class="filled-in chk-col-green form-control" id="{{$permissionType['name']}}" value="{{ $permissionType['name'] }}" checked />
<label for="{{ $permissionType['name'] }}" class="p-r-30">{{ ucfirst($permissionType['name']) }} Resource</label>
</div>
#endforeach
</div>
Permission Types Dump
What i have tried so far:
so far i have tried following but none of it worked for me.
1: defining public $permission_resource; instead of public $permission_resource = [];
2: wire:model="permission_resource.{{$permissionType["id"]}}"
3: wire:model="permission_resource.{{$permissionType["name"]}}"
4: wire:model.defer="permission_resource.{{$permissionType["id"]}}"
5: wire:model.defer="permission_resource.{{$permissionType["name"]}}"
6: name="permission_resource.{{$permissionType["id"]}}"
You are binding all the inputs to the same (array) variable, what you want to do is bind each to an element in that array, not to the array itself.
So you would need to prepopulate that array, and then bind each input using $loop->index.
Not sure if you have a good reason for populating permission_types in the render method, better in mount if it is not highly dynamic (likely to change from render to render).
Populating permission_resource in mount might look like this:
public function mount() {
$this->permission_types = PermissionType::all();
$this->permission_resource = $this->permission_types->pluck('name');
}
Then in the blade, bind to elements of the array, and don't set checked yourself, wire:model will always override this anyway, checking your checkbox if the thing you have bound to is truthy, and unchecking if bound var is falsey (this is why everything is unchecked for you with the wire:model binding them to the array, because empty($array) == false, if you just fix the casing of permission_type in the blade you will find the checkbox all come on when you check one, because they are all bound to the same thing).
<input wire:model="permission_resource.{{$loop->index}}" type="checkbox" class="filled-in chk-col-green form-control" id="{{$permission_type['name']}}" value="{{$permission_type['name']}}" />
PS: That will at least get to what you wanted, depending on what you are ultimately trying to do with permission_resource, it may not be a very good/useful representation for the purpose.
try this
public $permission = [];
protected $rules = [
...
'permission' => 'required|array',
];
public function create()
{
if ($this->validate()) {
$data = [
'name' => $this->name,
....
];
$model = Model::create($data);
if (!empty($this->permission)) {
$permissionId = PermissionType::whereIn('id', $this->permission)->pluck('id');
$model->permission()->attach($permissionId); // add name relation instead 'permission' , of create 'attach' of update 'sync'
}
}
}
public function render()
{
return view('livewire.permissions.create-permission-component', [
'permissions' => PermissionType::select('id', 'name')->get()
])
->extends('layouts.app')
->section('content');
}
in View
<div class="row">
#foreach($permissions as $permission)
<div class="col-md-3">
<input wire:model.defer="permission" wire:key="permission{{$permission->id}}" type="checkbox" value="{{$permission->id}}" class="filled-in chk-col-green form-control" />
<label for="{{ $permission->name }}" class="p-r-30">{{ ucfirst($permission->name) }} Resource</label>
</div>
#endforeach
</div>

How to hook event of validation error in livewire 2?

Reading how validations work at https://laravel-livewire.com/docs/2.x/input-validation
I did not find if there is a way in livewire 2 when I have
validation error to hook event as I need to send dispatchBrowserEvent event
to show message with toastr ?
My form is rather big and val;idation field can be out of screen
and I want to pay attention of user that there are validation errors...
Updated Block # 1:
You propose to get rid of livewire validate method and use laravel validate methods, which are written here
https://laravel.com/docs/8.x/validation, like :
$validator = Validator::make($request->all(), [
'title' => 'required|unique:posts|max:255',
'body' => 'required',
]);
if ($validator->fails()) {
...
In my conmponent I define all variables under 1 variable $form :
public $form
= [
'name' => '',
'state_id' => '',
...
public function firstStepSubmit()
{
$rules = [
'name' => 'required',
'state_id' => 'required',
];
$validation = Validator::make( $this->form, $rules, Hostel::getValidationMessagesArray());
$failed = $validation->fails();
if ($failed) {
$errorMsg = $validation->getMessageBag();
$focus_field = array_key_first($errorMsg->getMessages());
$this->dispatchBrowserEvent('PersonalPageMessageWarning', [
'title' => 'Personal Hostel',
'message' => 'Your hostel has invalid data !',
'focus_field' => str_replace('form.', '', $focus_field ),
]);
$validation->validate(); // What for this line ? Looks like we really need it ?
return;
}
$this->currentStep = 2;
} // public function firstStepSubmit()
// What for is
$validation->validate();
line inside of failed block ? Do we really need it ?
2) moving to laravel original methods sems for me as step back... Are there some livewire hooks/methods for it ?
Thanks in advance!
I got a solution to that. Let's say you have next:
// in blade
<div class="col-md-6 flex-row">
<label for="name">Name</label>
<input id="name" type="text" class="form-control flex-row" wire:model="name">
#error('name') <span class="error" style="color: #ff0000">{{ $message }}</span> #enderror
</div>
<div class="col-md-6 flex-row">
<label for="last_name">Name</label>
<input id="last_name" type="text" class="form-control flex-row" wire:model="last_name">
#error('last_name') <span class="error" style="color: #ff0000">{{ $message }}</span> #enderror
</div>
.... // all the rest of elements
//in component
public $name, $last_name;
// the rest of properies
public function store()
{
$validation = Validator::make([
'name' => $this->name,
'last_name' => $this->last_name,
.....
], $this->rules());
if ($validation->fails()) {
$errorMsg = $validation->getMessageBag();
$this->dispatchBrowserEvent('focusErrorInput',['field' => array_key_first($errorMsg->getMessages())]);
$validation->validate();
}
//... other code
now, if validation fails, the above validation checks dispatch and event with all the non-validated fields and in the blade's script tag handle the element focus in order of the error bag field. So, if the element is out of windows this will be focused
<script>
window.addEventListener('focusErrorInput', event => {
var $field = '#' + event.detail.field;
$($field).focus()
})
</script>

I have problem with id increasing in laravel

I have the methods finishSave and numbers, when I call numbers from finishSave, and put dd() I took increasing id, so what about problem?
I tried dd($this->prefix_id); in PhoneNumber class Method numbers
protected function finishSave(array $options)
{
$this->numbers();
Cache::tags([config('app.env') . '_responsecache'])->flush();
parent::finishSave($options);
}
and numbers method
public function numbers()
{
dd($this->prefix_id);
}
I expect each time from DB the ID of prefix, but I took increasing id, and how I can get this I cannot understand, so help me
thank you all, occasionly it was my fault, I changed blade file from null to model than it was working, sorry.
I have done like this:
<div class="form-group">
{!! Form::label('region_id', 'Регион:', ['class' => 'text-semibold']) !!}
{!! Form::select('region_id', $regions, null, ['class'=>'form-control']) !!}
</div>
and changed to that:
<div class="form-group">
{!! Form::label('region_id', 'Регион:', ['class' => 'text-semibold']) !!}
{!! Form::select('region_id', $regions, isset($phoneNumber) ? $phoneNumber->phonePrefix->region_id : null, ['class'=>'form-control']) !!}
</div>

How to autofocus an input field in semantic-ui-react?

I'm having a difficult time autofocusing an input field with semantic-ui-react. The documentation doesn't seem to include an autoFocus prop and the focus prop doesn't place the cursor inside the input field as would be expected.
<Form onSubmit={this.handleFormSubmit}>
<Form.Field>
<Form.Input
onChange={e => this.setState({ username: e.target.value })}
placeholder='Enter your username'
fluid />
</Form.Field>
</Form>
EDIT: This code works:
<Form onSubmit={this.handleFormSubmit}>
<Form.Input
onChange={e => this.setState({ username: e.target.value })}
placeholder="Enter your username"
autoFocus
fluid />
</Form>
The focus prop is purely to add a focus effect on the input's appareance, it does not actually set the focus.
Any props unused by Semantic are passed down to the DOM element, so if you set an autoFocus prop, it should go down to the input.
However, as explained in the Form documentation:
Form.Input
Sugar for <Form.Field control={Input} />.
So your code should rather be:
const yourForm = (
<Form onSubmit={this.handleFormSubmit}>
<Form.Input
onChange={e => this.setState({ username: e.target.value })}
onSelect={() => this.setState({ usernameErr: false })}
placeholder="Enter your username"
error={usernameErr}
iconPosition="left"
name="username"
size="large"
icon="user"
fluid
autoFocus
/>
</Form>
)
Note that this only works if you want the focus to happen right when the wrapper component is mounted. If you want to focus the input after it has been mounted, you have to use a ref and call the focus() method on it, just as showed in the documentation, like so:
class InputExampleRefFocus extends Component {
handleRef = (c) => {
this.inputRef = c
}
focus = () => {
this.inputRef.focus()
}
render() {
return (
<div>
<Button content='focus' onClick={this.focus} />
<Input ref={this.handleRef} placeholder='Search...' />
</div>
)
}
}
Hope that helps!
I would have assumed that semantic UI would pass all unknown props to the root element, the input. So if it does, you should be able to add the autoFocus attribute to it, if not, you will have to control which input is being focused in your state.
<Input placeholder='Search...' focus={this.state.focusedElement === "search"}/>
In order to tell the input field to focus, you need to create a reference (ref) to the input field as follows:
import React, { useState, useRef } from 'react';
import { Input, Button } from 'semantic-ui-react';
const SearchInputExample = () => {
const [searchValue, setSearchValue] = useState('');
// Create reference to the input field
const searchRef = useRef(null);
const handleSearchValueChange = event => setSearchValue(event.target.value);
return (
<div>
<Input
placeholder="Search..."
// Assign the ref created to a ref attribute
ref={searchRef}
value={searchValue}
onChange={handleSearchValueChange}
/>
<Button
onClick={() => {
setSearchValue('');
// Use the ref assigned to put the focus inside the input
searchRef.current.focus();
}}
>
Clear search (and focus)
</Button>
</div>
);
};
export default SearchInputExample;
You can read more about the useRef() hook here