Laravel Livewire form, if validation fails, pivots don't work - laravel

I have a form made with Livewire in Laravel.
This is the Livewire controller
namespace App\Http\Livewire;
use Livewire\Component;
use App\Rules\Mobile;
class Form extends Component
{
public $mobile;
public $required;
public $fields;
public $showDropdown = true;
public function mount()
{
foreach($this->fields as $field){
$this->required[$field->name] = ($field->pivot->is_required == '1') ? 'required' : 'nullable';
}
}
public function submit()
{
$validatedData = $this->validate([
'mobile' => [$this->required['mobile'] ?? 'nullable', new Mobile()]
]);
}
}
This is the Livewire view
<div>
<div x-data="{ open: #entangle('showDropdown').defer, required: #js($required) }">
<span x-show="open" wire:loading.remove>
<form wire:submit.prevent="submit" style="display: flex; flex-direction: column;">
<div class="fields">
#foreach($fields as $field)
<div class="form-{{$field->name}}">
#if($field->name == 'mobile')
<input name="{{$field->name}}" type="tel" wire:model.defer="{{ $field->name }}" placeholder="{{ __($field->pivot->placeholder ?? $field->name) }}">
#endif
#error($field->name) <span class="error">{{ ucfirst($message) }}</span> #enderror
</div>
#endforeach
</div>
<button class="btn btn-primary">Send</button>
</form>
</span>
</div>
</div>
Problem is here placeholder="{{ __($field->pivot->placeholder ?? $field->name) }}"
When the form is first loaded $field->pivot->placeholder is set, but after I submit the form and the validation fails, it's not set anymore and $field->name is used instead of $field->pivot->placeholder
Have checked this way:
<input name="{{$field->name}}" type="tel" wire:model.defer="{{ $field->name }}" placeholder="{{ __($field->pivot->placeholder ?? $field->name) }}">
{{ var_dump(isset($field->pivot->placeholder)) }}
When the form is first loaded it prints bool(true) under the field, after I send the form it says bool(false)
How can I get over this? Why the pivot does not work after validation fails?
//Edit: Did a workaround, but I would still like to know why it happens
What I did is I used another property $placeholders
In the Livewire controller have added public $placeholders;, in the mount() have added $this->placeholders[$field->name] = $field->pivot->placeholder ?? $field->name;, and then used it in the view like placeholder="{{ __($placeholders[$field->name] ?? $field->name) }}"

I don't know much about livewire , but I know that mount() only called on the initial page load meaning it will only run when you refresh the whole page or visit the page. Instead of only using mount() you should also use hydrate() which will run every subsequent request after the page load.
mount() - Initial Page Load
hydrate() - Every subsequent request
You can implement the hydrate() like how you implement the mount()

Related

Error message "The GET method is not supported for this route. Supported methods: POST." in laravel 8

Following is my controller. Method ProjectsView is to view all the listings. The second method BackendProjectSearch is for searching of projects. The first page of search result is displayed properly, but when we click on next page it gives the error "The GET method is not supported for this route. Supported methods: POST."
What should I do ?
public function ProjectsView(){
$projects = projects::orderBy('id','ASC')->paginate(15);
return view('backend.projects.projects_view',compact('projects')); }
public function BackendProjectSearch(Request $request){
$request->validate(["search" => "required"]);
$item = $request->search;
$projects = Projects::where('project_name','LIKE',"%$item%")->paginate(15);
return view('backend.projects.projects_view',compact('projects')); }
Following are the routes for both the methods :
Route::post('/backend/project/search', [ProjectsController::class, 'BackendProjectSearch'])->name('backend.project.search');
Route::get('/view', [ProjectsController::class, 'projectsView'])->name('projects.view');
View code :
<div class="col-md-8">
<div class="header-navsearch">
<form class="form-inline mr-auto" method="post" action="{{route('backend.project.search')}}">
#csrf
<div class="nav-search">
<input type="search" name="search" class="form-control header-search" placeholder="Search projects…" aria-label="Search">
<button class="btn btn-primary" type="submit"><i class="fa fa-search"></i></button>
</div>
</form>
</div>
</div>
The route for this request should be post, not get.
Example
Route::post(-----);
Also, make sure to insert a CSRF token while making the request
it means you now cannot do something like the following.
Instead, you have to do something like...
<form action="{{ route('example') }}" method="POST">
#csrf {{-- According to the version of laravel --}}
{{-- Your other codes --}}
<button type="submit" class="">Submit</button>
</form>
You have to use the below code
return redirect()->route('projects.view')->with(['projects' => $projects]);
Your route might be little change
Route::get('/view/{projects?}', [ProjectsController::class, 'projectsView'])->name('projects.view');
and In your controller, you have to change function like this
public function ProjectsView($projects = null){
if($projects!=null){
return view('backend.projects.projects_view',compact('projects'));
}
else{
$projects = projects::orderBy('id','ASC')->paginate(15);
return view('backend.projects.projects_view',compact('projects'));
}

property does not refresh in the internal components of Livewire

Take a look at the following examples:
showPost.blade.php:
<div>
<livewire:content-box :content="$post"/>
<button wire:click="nextPost" >Next Post >></button>
</div>
and
content-box.blade.php :
<div>
<h1>{{ $content->title }}</h1>
<p>{{ $content->content }}</p>
</div>
So far, it is completely clear what is going to happen ...: First, the information of the content to be viewed is received through showPost and passed to the contentBox, and everything is OK ..
Well now I want to get the information of the next content via the account through the button I put and calling the nextPost method:
class ShowPost extends Component
{
public Post $post;
public function render()
{
return view('livewire.show-post');
}
public function nextPost()
{
$id = $this->post->id;
$nextPost = Post::where('id', '>', $id)->first();
$this->post = $nextPost;
}
...
But nothing happens and the contentBox component has no reaction .... Has anyone had this problem ???!
I'm not sure livewire works well with nested components. could use the pagination instead. The livewire docs suggest you should not use them for little snippets or use blade components for that kind of nesting.
You can achieve what you're doing at the moment with some simple pagination.
<?php
namespace App\Http\Livewire;
use App\Models\User;
use Livewire\Component;
use Livewire\WithPagination;
class SomeContent extends Component
{
use WithPagination;
public function render()
{
// Using simplePaginate(1) instead of paginate(1).
// simplePaginate only shows "<- Previous" and "Next ->" links
// paginate shows those 2 buttons but also page numbers which you don't seem to want.
return view('livewire.some-content', [
'users' => User::simplePaginate(1),
]);
}
}
<div>
{{-- This might look wrong, but essentially it's looping through an array of length 1 because we're paginating --}}
#foreach ($users as $user)
<h1>{{ $user->name }}</h1>
<h2>{{ $user->email }}</h2>
#endforeach
{!! $users->links() !!}
</div>
EDIT
I can confirm blade components work.
Here, nextUser is the same implementation you gave.
public function nextUser()
{
$id = $this->user->id;
$nextUser = User::where('id', '>', $id)->first();
$this->user = $nextUser;
}
<div class="container">
<div class="content">
{{-- These two have the exact same template --}}
<livewire:child :user="$user" />{{-- Doesn't update when clicking Next --}}
<x-blade-child :user="$user" />{{-- Updates when clicking Next --}}
</div>
<div>
<button wire:click="nextUser">Next</button>
</div>
</div>
When clicking nextUser, the blade component updates but the livewire one doesn't.
Livewire doesn't like nested components. In your case, we can use basic blade component:
<div>
<x-content-box :content="$post"/>
<button wire:click="nextPost" >Next Post >></button>
</div>
And then:
Move content-box.blade.php to resources/views/components/
Remove component_name.php file in app/Http/Livewire
Most of the time, we can change 2 nested livewire components to livewire(parent) + basic blade component(child),

Laravel 2 submit buttons in the same form

I am building a CRUD with Laravel. Each category hasMany attachments and each attachment belongsTo a category.
In the category.edit view I want to give the user the possibility of deleting the attachments (singularly) from the Category. I tried this method but it did not work:
Registering route for the attachment:
Route::group(['middleware' => ['auth']], function () {
Route::delete('attachment/{id}', 'AttachmentController#delete')->name('attachment');
});
Handling the delete building the AttachmentController#delete method:
class AttachmentController extends Controller
{
public function delete($id) {
$toDelete = Attachment::findOrFail($id);
$toDelete->delete();
return redirect()->back();
}
}
In the CategoryController (edit method), I fetch the attachments linked to each category to be rendered inside the view:
public function edit($category)
{
$wildcard = $category;
$category = Category::findOrFail($wildcard);
$attachments = App\Category::findOrFail($wildcard)->attachments()->get()->toArray();
return view('category.edit', [
'category' => $category,
'attachments' => $attachments
]);
}
In the view, I render the attachment and the button to delete. I am fully aware of the error of having a form inside another form, nevertheless I do not know antoher approach to submit this delete request.
// update Category form
#foreach ($attachments as $attachment)
<div class="row">
<div class="col-4">
<img style="width: 100%;" src={{ $attachment['url'] }} alt="">
</div>
<div class="col-4">
<div>
<p class="general_par general_url">{{ $attachment['url'] }}</p>
</div>
</div>
<div class="col-4">
<form action="{{ route('attachment', $attachment['id']) }}" method="POST">
#csrf
#method('DELETE')
<button type="submit" class="btn btn-danger">Delete Image</button>
</form>
</div>
</div>
<hr>
#endforeach
// end of update Category form
Should I build a deleteAttachment method inside the CategoryController? If so, how can I still submit the Delete request? Also, if any other Model in the future will have attachments, should I build a deleteAttachment method inside each controller? That is cumbersome. Thanks in advance
if you don't like to use form, then use tag:
<a class="btn btn-danger" href="{{ route('attachment', $attachment['id']) }}">Delete Image</a>
And redefine the route to Route::get(...)
(or maybe use ajax for POST method if that is required)

GET Data empty in Laravel

Hi I am new to Laravel and so far I made good progress. Right now my head is blocked now and I need some direction or help, please.
I am getting data from radio button but GET Data is empty. I need to fill-in this data (pay) into an exist DB and I am getting "Creating default object from empty value" and I agree with Laravel :) I guess, my lack of knowledge is blocking me here.
Thanks.
This is the GET and POST data
GET Data empty
POST Data
_token = "72nrnI7Y7xuIQJe6LZPLGLzNsAv6ZZbY29zkjcIr"
pay = "CC"`
This is the Model
namespace App;
use Illuminate\Database\Eloquent\Model;
use DB;
use Auth;
class DAddress extends Model
{
protected $table='dAddress';
protected $fillable = ['payment_method'];
public function createPay()
{
$user = Auth::user();
$order = $user->daddress()->create([
'payment_method' => paymentMethod()
]);
}
}
This is the Controller
public function paymentMethod(Request $request) {
$address->payment_method = $request->pay;
DAddress::createPay();
Cart::destroy();
return redirect('abc');
}
This is where I get the HTML data
<form action="{{url('/paymentMethod')}}" method="post">
<input type="hidden" value="{{csrf_token()}}" name="_token"/>
<div class="form-group">
<div class="col-md-6">
<!-- First name -->
<input type="radio" class="form-control" name="pay"
value="CC"><i class="fa fa-credit-card"></i> Credit Card
<br> <br>
<input type="radio" class="form-control" name="pay"
value="PP"><i class="fa fa-paypal"></i> Paypal
<br> <br>
<input type="radio" class="form-control" name="pay"
value="BT"><i class="fa fa-university"></i> Bank Transfer
</div>
</div>
<input type="submit" class="btn btn-primary" value="Move to Last Page" />
This is the User.php I added below function.
public function daddress()
{
return $this->hasMany(DAddress::class);
}
i do not understand what you are trying to do with this line $address->payment_method = $request->pay;
when you say $address->payment_method, what is the $address object holding and where is payment_method you are trying to access, why not do something like this $payment_method = $request->pay, except is you are setting $payment_method as a global variable
if you are using the laravel create method you could do something like this $payment_method= App\createPay::create(['payment_method' => $request->pay]);
else you could instantiate you model like
$pay =createPay();
$pay->payment_method=$request->pay;
$pay->save();
i would have love you logic to be in the controller, i would have done something like this in my controller
public function createPay(Request $request){
$user = Auth::user();
$id=$user->id;
$payment=createPay::find($id);
$payment->payment_method=$request->pay;
$payment->save();
return redirect('abc');
}
i used laravel eloquent here, that is if you want to update a record, i don't know if this is close to what you want.

How to pass variable to view without using url in Laravel?

guys. I'm a newbie of Laravel.
I'm just wondering that if I need to pass some sensitive info, which is been wrote on a form, to controller, then to another view.
How could I pass the info to the view without using URL?
I have made some small test, but the result is all the same (url would include the data).
Here is the code.
Html(a):
<form action="/data" method="GET" class="form-horizontal">
<div class="form-group">
<input type="text" class="form-control" name="Test">
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary">test</button>
</div>
web.php:
Route::get('/data', 'DataController#show');
DataController.php:
public function show(Request $request) {
return view('testShow');
}
Html(b):
<div class="ShowDataHere">
#if(!empty(request()->all()))
{{ request()->Test }}
#endif
Simply change your form to use POST instead of GET.
On the form...
<form action="/data" method="POST" class="form-horizontal">
In your route ...
Route::post('/data', 'DataController#show');
This will send all of the form fields to the server in a way that is less visable (not in the URL) but to make the form secure, you'll want to ensure it's served using HTTPS as well.
In Laravel when you use Route::post('/data', 'DataController#show') the Laravel expect a GET, because of the show function.
So you can use Route::post('/data', 'DataController#someFunction') and in your laravel controller get with:
public function someFunction(Request $request)
{
$array = $request->all(); //your fields
}
In this function you can get the attributes with $array = $request->all();

Resources