Laravel PATCH works on update but validation in controller fails - laravel

I am following along with the Laracast lab, the Tweety feed and all was going good until i hit a wall.
I have a PATCH to update the user profile and it works and updates the record, but the validation doesn't. The form submits if fully complete and the record is fine but if the form validation fails server side in my controller, I get as below:
Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException
The GET method is not supported for this route. Supported methods: POST.
I have looked at the FAQ section here and scoured the net already asked and tried several steps to sort this. I have tried:
Using $user->username in my edit file template and controller.
Changing the route from PATCH TO PUT (and also POST, simply using store)
Checking the User model and ensuring I am using 'getRouteKeyName' working correctly, it is.
Checking if there is a conflict in the route, there is none and the order/VERB is fine.
Can anyone please help? Below is my code.
Routes as below:
// Snippet for seeing all the database queries.
//DB::listen(function($query){var_dump($query->sql, $query->bindings);});
use Illuminate\Support\Facades\Route;
// Home view, no sign-in required.
Route::get('/', function () {
return view('welcome');
});
// These routes require and use auth middleware.
Route::middleware('auth')->group(function(){
// TWeets main page.
Route::get('/tweets/', 'TweetsController#index')->name('home');
// Store a new tweet
Route::post('/tweets/', 'TweetsController#store');
// Profile area, view and update
// Use the 'name' attribute in route/model binding instead of the primary key = user:name
Route::get('/profile/{user:username}/', 'ProfileController#show')->name('profile');
// Show form to edit the profile
Route::post('/profile/{user:username}/edit','ProfileController#edit')->name('edit_profile');
//->middleware('can:edit,user'); // Don't forget the wildcard.
// Finally, update the users profile information.
Route::patch('/profile/{user:username}','ProfileController#update')->name('update_profile');
// Handles the follow/unfollow on a toggle method.
Route::post('/profile/{user:username}/follow/','FollowsController#store');
// Logs you out & directs to the homepage.
Route::get('/logout', '\App\Http\Controllers\Auth\LoginController#logout');
});
Auth::routes();
Controller as below:
/**
* Update the users profile after validation
*
* #param \Illuminate\Http\Request $request
* #param int $id
* #return \Illuminate\Http\Response
*/
public function update(User $user)
{
$validated = request()->validate([
'name'=>'required',
'username'=>'required',
'password'=>'required',
'password_confirmation'=>'required',
]);
$user->update($validated);
// Redirect to the tweets page which is called 'home'.
return redirect(route('profile', $user->username));
}
Template:
<x-app>
<form class="w-full" method="POST" action="{{route('update_profile',$user)}}">
#method('PATCH')
#csrf
<h2 class="text-xl mb-2 py-4 font-bold">Edit profile for {{$user->username}}</h2>
<p class="text-sm mb-5 mt-0">Below you an update your profile details.</p>
<div class="flex flex-wrap -mx-3 mb-6">
<div class="w-full px-3 mb-6 md:mb-0">
<label class="block uppercase tracking-wide text-gray-700 text-xs font-bold mb-2" for="grid-name">Name</label>
<input class="appearance-none block w-full bg-gray-200 text-gray-700 border border-red-500 rounded py-3 px-4 mb-3 leading-tight focus:outline-none focus:bg-white" id="name" type="text" placeholder="Mike Smith.." name="name" value="{{$user->name}}"/>
</div>
#error('name')
<p>{{$message}}</p>
#enderror
<div class="w-full px-3 mb-6 md:mb-0">
<label class="block uppercase tracking-wide text-gray-700 text-xs font-bold mb-2" for="grid-username">Username</label>
<input class="appearance-none block w-full bg-gray-200 text-gray-700 border border-red-500 rounded py-3 px-4 mb-3 leading-tight focus:outline-none focus:bg-white" id="grid-username" type="text" placeholder="MikeSmith1..." name="username" value="{{$user->username}}"/>
</div>
#error('username')
<p>{{$message}}</p>
#enderror
<div class="w-full px-3 mb-6 md:mb-0">
<label class="block uppercase tracking-wide text-gray-700 text-xs font-bold mb-2" for="grid-username">Email</label>
<input class="appearance-none block w-full bg-gray-200 text-gray-700 border border-red-500 rounded py-3 px-4 mb-3 leading-tight focus:outline-none focus:bg-white" id="grid-username" type="email" placeholder="MikeSmith#yahoo.com..." name="email" value="{{$user->email}}"/>
</div>
#error('email')
<p>{{$message}}</p>
#enderror
<div class="w-full px-3">
<label class="block uppercase tracking-wide text-gray-700 text-xs font-bold mb-2" for="grid-password">Password</label>
<input class="appearance-none block w-full bg-gray-200 text-gray-700 border border-gray-200 rounded py-3 px-4 mb-3 leading-tight focus:outline-none focus:bg-white focus:border-gray-500" id="grid-password" type="password" name="password"/>
<p class="mb-3 text-red-600 text-xs italic">Make it as long and as crazy as you'd like</p>
</div>
<div class="w-full px-3"> {{-- This HAS to be called "password_confirmation" --}}
<label class="block uppercase tracking-wide text-gray-700 text-xs font-bold mb-2" for="grid-password">Password Confirmation</label>
<input class="appearance-none block w-full bg-gray-200 text-gray-700 border border-gray-200 rounded py-3 px-4 mb-3 leading-tight focus:outline-none focus:bg-white focus:border-gray-500" id="grid-password_confirmation" type="password" name="password_confirmation"/>
</div>
<button type="submit" class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 ml-2 mt-4 px-4 rounded">
Update details
</button>
</div>
</form>
</x-app>
Model:User
/**
* Laravel <=6 - use this method, after v6, you can do this on the route itself.
* Using this so we can use a user name in the route, instead of the ID Laravel normally uses for route/model binding. Now you can use /profile/mikethornley
* This will still enable route/model binding to work and still find the user
* #return [type] [description]
*/
public function getRouteKeyName()
{
return 'username';
}

This is the problem:
// Use the 'name' attribute in route/model binding instead of the primary key = user:name
Route::get('/profile/{user:username}/', 'ProfileController#show')->name('profile');
// Finally, update the users profile information.
Route::patch('/profile/{user:username}','ProfileController#update')->name('update_profile');
Your routes are clashing... that post ends up in the show() method and that's why you get the error.
You need to make some change in that post route if you want to have parameters. For example
Route::patch('/profile-update/{user:username}','ProfileController#update')->name('update_profile');

Related

I am getting an error which is shown in the image below when i am trying to update information [closed]

Closed. This question is not reproducible or was caused by typos. It is not currently accepting answers.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Closed 4 days ago.
Improve this question
I have this error please help:
Too few arguments to function App\Http\Requests\ReservationStoreRequest::Illuminate\Foundation\Providers{closure}(), 0 passed in E:\XAMPP\htdocs\resto_app\vendor\laravel\framework\src\Illuminate\Macroable\Traits\Macroable.php on line 124 and exactly 1 expected
<?php
namespace App\Http\Controllers\Admin;
use App\Enums\TableStatus;
use App\Http\Controllers\Controller;
use App\Http\Requests\ReservationStoreRequest;
use App\Models\Reservation;
use App\Models\Table;
use Carbon\Carbon;
use Illuminate\Http\Request;
class ReservationController extends Controller
{
/**
* Display a listing of the resource.
*
* #return \Illuminate\Http\Response
*/
public function index()
{
$reservations = Reservation::all();
return view('admin.reservations.index', compact('reservations'));
}
/**
* Show the form for creating a new resource.
*
* #return \Illuminate\Http\Response
*/
public function create()
{
// dd($tables);
$tables = Table::where('status', TableStatus::Available)->get();
return view('admin.reservations.create', compact('tables'));
}
/**
* Store a newly created resource in storage.
*
* #param \Illuminate\Http\Request $request
* #return \Illuminate\Http\Response
*/
public function store(ReservationStoreRequest $request)
{
$table = Table::findorFail($request->table_id);
if($request->guest_number > $table->guest_number){
return back()->with('warning', 'Please choose the table base on guests.');
}
$request_date = Carbon::parse($request->res_date);
foreach ($table->reservations as $res) {
if ($res->res_date->format('Y-m-d') == $request_date->format('Y-m-d')) {
return back()->with('warning', 'This table base is reserved for this date.');
}
}
Reservation::create($request->validated());
return to_route('admin.reservations.index')->with('success', 'Reservation created successfully');
}
/**
* Display the specified resource.
*
* #param int $id
* #return \Illuminate\Http\Response
*/
public function show($id)
{
//
}
/**
* Show the form for editing the specified resource.
*
* #param int $id
* #return \Illuminate\Http\Response
*/
public function edit(Reservation $reservation)
{
$tables = Table::where('status', TableStatus::Available)->get();
return view('admin.reservations.edit', compact('reservation', 'tables'));
}
/**
* Update the specified resource in storage.
*
* #param \Illuminate\Http\Request $request
* #param int $id
* #return \Illuminate\Http\Response
*/
public function update(ReservationStoreRequest $request, Reservation $reservation)
{
$table = Table::findorFail($request->table_id);
if($request->guest_number > $table->guest_number){
return back()->with('warning', 'Please choose the table base on guests.');
}
$request_date = Carbon::parse($request->res_date);
$reservations = $table->reservations()->where('id', '!=', $reservation->id)->get();
foreach ($reservations as $res) {
if ($res->res_date->format('Y-m-d') == $request_date->format('Y-m-d')) {
return back()->with('warning', 'This table base is reserved for this date.');
}
}
$reservation->update($request->validate());
return to_route('admin.reservations.index')->with('success', 'Reservation updated successfully');
}
/**
* Remove the specified resource from storage.
*
* #param int $id
* #return \Illuminate\Http\Response
*/
public function destroy($id)
{
//
}
}
this is my view
<x-admin-layout>
<x-slot name="header">
<h2 class="font-semibold text-xl text-gray-800 dark:text-gray-200 leading-tight">
{{ __('Dashboard') }}
</h2>
</x-slot>
<div class="py-12">
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
<div class="flex m-2 p-2">
<a href = "{{ route('admin.tables.index')}}"
class="px-4 py-2 bg-indigo-500 hover:bg-indigo-700 rounded-lg text-white"
style="background-color:blue;">Tables index</a>
</div>
<div class="m-2 p-2 bg-slate-100 rounded" style="background-color:lightblue;">
<div class="space-y-8 divide-y divide-gray-200 w-1/2 mt-10">
<form method="POST" action="{{ route('admin.reservations.update', $reservation->id)}}">
#csrf
#method('PUT')
<div class="sm:col-span-6">
<label for="first_name" class="block text-sm font-medium text-gray-700">First Name</label>
<div class="mt-1">
<input type="text" id="first_name" name="first_name" value="{{ $reservation->first_name }}"
class="block w-full appearance-none bg-white border border-gray-400 rounded-md py-2 px-3 text-base leading-normal transition duration-150 ease-in-out sm:text-sm sm:leading-5" />
</div>
#error('first_name')
<div class="text-sm text-red-400">{{ $message }}</div>
#enderror
</div>
<div class="sm:col-span-6">
<label for="last_name" class="block text-sm font-medium text-gray-700">Last Name</label>
<div class="mt-1">
<input type="text" id="last_name" name="last_name" value="{{ $reservation->last_name }}"
class="block w-full appearance-none bg-white border border-gray-400 rounded-md py-2 px-3 text-base leading-normal transition duration-150 ease-in-out sm:text-sm sm:leading-5" />
</div>
#error('last_name')
<div class="text-sm text-red-400">{{ $message }}</div>
#enderror
</div>
<div class="sm:col-span-6">
<label for="email" class="block text-sm font-medium text-gray-700">Email</label>
<div class="mt-1">
<input type="email" id="email" name="email" value="{{ $reservation->email }}"
class="block w-full appearance-none bg-white border border-gray-400 rounded-md py-2 px-3 text-base leading-normal transition duration-150 ease-in-out sm:text-sm sm:leading-5" />
</div>
#error('email')
<div class="text-sm text-red-400">{{ $message }}</div>
#enderror
</div>
<div class="sm:col-span-6">
<label for="tel_number" class="block text-sm font-medium text-gray-700">Phone Number</label>
<div class="mt-1">
<input type="text" id="tel_number" name="tel_number" value="{{ $reservation->tel_number }}"
class="block w-full appearance-none bg-white border border-gray-400 rounded-md py-2 px-3 text-base leading-normal transition duration-150 ease-in-out sm:text-sm sm:leading-5" />
</div>
#error('tel_number')
<div class="text-sm text-red-400">{{ $message }}</div>
#enderror
</div>
<div class="sm:col-span-6">
<label for="res_date" class="block text-sm font-medium text-gray-700">Reservation Date</label>
<div class="mt-1">
<input type="datetime-local" id="res_date" name="res_date"
value="{{ $reservation->res_date->format('Y-m-d\TH:i:s') }}"
class="block w-full appearance-none bg-white border border-gray-400 rounded-md py-2 px-3 text-base leading-normal transition duration-150 ease-in-out sm:text-sm sm:leading-5" />
</div>
#error('res_date')
<div class="text-sm text-red-400">{{ $message }}</div>
#enderror
</div>
<div class="sm:col-span-6">
<label for="guest_number" class="block text-sm font-medium text-gray-700"> Guest Number
</label>
<div class="mt-1">
<input type="number" id="guest_number" name="guest_number" value="{{ $reservation->guest_number }}"
class="block w-full appearance-none bg-white border border-gray-400 rounded-md py-2 px-3 text-base leading-normal transition duration-150 ease-in-out sm:text-sm sm:leading-5" />
</div>
#error('guest_number')
<div class="text-sm text-red-400">{{ $message }}</div>
#enderror
</div>
<div class="sm:col-span-6 pt-5">
<label for="status" class="block text-sm font-medium text-gray-700">Table</label>
<div class="mt-1">
<select id="table_id" name="table_id" class="form-multiselect block w-full mt-1">
#foreach ($tables as $table)
<option value="{{ $table->id }}"
#selected($table->id == $reservation->table_id)>{{ $table->name }}
({{ $table->guest_number }} Guests)
</option>
#endforeach
</select>
</div>
#error('table_id')
<div class="text-sm text-red-400">{{ $message }}</div>
#enderror
</div>
<div class="mt-6 p-4">
<button type="submit"
class="px-4 py-2 bg-indigo-500 hover:bg-indigo-700 rounded-lg text-white">Update</buttom>
</div>
</form>
</div>
</div>
</div>
</div>
</x-admin-layout>
Your error is that on the update method you used $reservation->update($request->validate()) but it must be $reservation->update($request->validated()) (see validate vs validated)

Using x-if doesn't bind the data in livewire wire:model

I have a form with several input fields and some of them render conditionally using AlpineJS x-if, those elements have wire:model data, but when the elements rendered conditionally the model binding seems not working. I tried to print the variable on the element but it doesn't work. Without x-if it works fine.
<div class="form-check" >
<input class="form-check-input appearance-none rounded-full h-4 w-4 border border-gray-300 bg-white checked:bg-[#60D619] checked:border-[#60D619] focus:outline-none transition duration-200 mt-1 align-top bg-no-repeat bg-center bg-contain float-left mr-2 cursor-pointer" type="radio" id="company" wire:model="isCompany" #click="open = true" value="true">
<label class="form-check-label inline-block px-1 text-sm text-gray-600" for="company">
Yes, I'm a company.
</label>
</div>
<div class="form-check">
<input class="form-check-input appearance-none rounded-full h-4 w-4 border border-gray-300 bg-white checked:bg-[#60D619] checked:border-[#60D619] focus:outline-none transition duration-200 mt-1 align-top bg-no-repeat bg-center bg-contain float-left mr-2 cursor-pointer" type="radio" id="person" wire:model="isCompany" #click="open = false" value="false">
<label class="form-check-label inline-block px-1 text-sm text-gray-600" for="person">
No. I'm not a company.
</label>
</div>
<div class="py-1" x-show="open" x-transition>
<span class="px-1 text-sm text-gray-600">Company Name</span>
<input wire:model.lazy="companyName" placeholder="" type="text"
class="text-md block px-3 py-2 rounded-lg w-full
bg-white border-2 border-gray-300 placeholder-gray-600 shadow-md focus:placeholder-gray-500 focus:bg-white focus:border-gray-600 focus:outline-none">
</div>
<div class="py-1" x-show="!open" x-transition>
<span class="px-1 text-sm text-gray-600">User Name</span>
<input wire:model="name" placeholder="" type="text"
class="text-md block px-3 py-2 rounded-lg w-full
bg-white border-2 border-gray-300 placeholder-gray-600 shadow-md focus:placeholder-gray-500 focus:bg-white focus:border-gray-600 focus:outline-none">
</div>
<div class="py-1" x-show="!open" x-transition>
<span class="px-1 text-sm text-gray-600">First Name</span>
<input wire:model="fullName" placeholder="" type="text"
class="text-md block px-3 py-2 rounded-lg w-full
bg-white border-2 border-gray-300 placeholder-gray-600 shadow-md focus:placeholder-gray-500 focus:bg-white focus:border-gray-600 focus:outline-none">
</div>
When x-show uses the issue goes away but the unnecessary input fields are not cleared. I don't want those data to be saved on the database. Is this because x-show only toggles the visibility of the elements and x-if completely removes the element from the DOM? Or is there a way to reset the values of the input fields when visibility is toggled using x-show?
x-if indeed removes the element, while x-show only hides it.
wire:model only works if it's loaded in with Livewire. Else Livewire doesn't "know" those fields exist, and thus can't add an event listener to detect change/input/etc. Therefore, you want to do what #Bennett already mentioned, and only hide the inputs, and clear the fields.
You can use $wire to directly clear the input. Otherwise, you will have to dispatch a change or input event on the input field (change for lazy, input for non-lazy).

Bind Livewire component to parent array

I have the following structure:
A parent component:
class SiteEditComponent extends Component
{
protected $listeners = ['upClicked', 'downClicked', 'childUpdated'];
public $childBlocks = [];
public function render()
{
return view('livewire.site-edit-component', ['childBlocks' => $this->childBlocks]);
}
}
Which renders that way:
<div>
<button wire:click="addBlock" type="button" class=" mb-3 inline-flex items-center px-2.5 py-1.5 border border-gray-300 shadow-sm text-xs font-medium rounded text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">Block hinzufügen</button>
#foreach($childBlocks as $key => $childBlockContent)
<livewire:templates.text-block-component wire:model.debounce="childBlocks.key" :block="$childBlockContent" :wire:key="$key">
#endforeach
</div>
And the child component(s):
class TextBlockComponent extends Component
{
public $block;
protected $rules = [
'block.title' => ''
];
public function render()
{
return view('livewire.text-block-component', [
'block' => $this->block
]);
}
Which renders that way (simplified):
<div>
<div class="bg-white shadow sm:rounded-lg mb-3">
<div class="px-4 py-5 sm:p-6">
<div
class=" w-1/3 border border-gray-300 rounded-md px-3 py-2 shadow-sm focus-within:ring-1 focus-within:ring-indigo-600 focus-within:border-indigo-600">
<label for="name" class="block text-xs font-medium text-gray-900">Titel</label>
<input wire:model.debounce="block.title" type="text" name="title" id="name"
class="block w-full border-0 p-0 text-gray-900 placeholder-gray-500 focus:ring-0 sm:text-sm"
placeholder="Blocküberschrift">
</div>
</div>
</div>
</div>
In fact both components represent a Laravel model. A site can have multiple blocks.
What would be the right way to keep track of the child components in the parent component?
I want to implement a save button which should collect all items of $childBlocks, save the Site and attach the childBlocks to it. I tried to model it by using wire:model.debounce="childBlocks.key" but the binding doesn't seem to work.
I wanted to create an associative array in which every block is identified by a randomly generated key which holds the block data.

Saved date does not show up in the form

I'm using laravel livewire and I've created a page that you can select a date for when you want the post
to publish. When I create a new post and save the date then it saves fine in the database, but if I try
to edit the post and change the date the date that I saved in the database isn't showing in the form.
Here is my code.
My EditPost.php
<?php
namespace App\Http\Livewire;
use App\Models\Post;
use Livewire\Component;
class EditPost extends Component
{
public $name;
public $publishDate;
public Post $post;
public function mount(Post $post)
{
$this->name = $post->name;
$this->publishDate = $post->publish_date;
}
public function render()
{
return view('livewire.edit-post');
}
}
and this is my edit.blade.php
<div>
<div class="mt-10 sm:mt-0">
<div class="md:grid md:grid-cols-3 md:gap-6">
<div class="md:col-span-1">
<div class="px-4 sm:px-0">
<h3 class="text-lg font-medium leading-6 text-gray-900">Edit Post</h3>
</div>
</div>
<div class="mt-5 md:mt-0 md:col-span-2">
<div class="form">
<div class="shadow overflow-hidden sm:rounded-md">
<div class="px-4 py-5 bg-white sm:p-6">
<div class="grid grid-cols-6 gap-6">
<div class="col-span-6 sm:col-span-3">
<label for="name" class="block text-sm font-medium leading-5 text-gray-700">Name</label>
<input id="name" wire:model="name" class="#error('name') border border-red-500 #enderror mt-1 form-input block w-full transition duration-150 ease-in-out sm:text-sm sm:leading-5">
#error('name')
<small>
<div class="text-red-500">{{ $message }}</div>
</small>
#enderror
</div>
<div class="col-span-6 sm:col-span-4">
<label for="publish_date" class="block text-sm font-medium leading-5 text-gray-700">Publish Date</label>
<input id="publish_date" wire:model="publishDate" type="date" class="mt-1 form-input block w-full transition duration-150 ease-in-out sm:text-sm sm:leading-5">
</div>
</div>
</div>
<div class="px-4 py-3 bg-gray-50 text-right sm:px-6">
<button wire:click="editPost" class="py-2 px-4 border border-transparent text-sm leading-5 font-medium rounded-md text-white bg-indigo-600 shadow-sm hover:bg-indigo-500 focus:outline-none focus:shadow-outline-blue active:bg-indigo-600 transition duration-150 ease-in-out">
Save
</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>

Getting value to show up using livewire

I'm using laravel and livewire and I'm trying to create an edit page. What I'm trying to do is get the value to show up in the textbox.
So for example my edit page has product info that I'm trying to edit, but my product name isn't displaying in the textbox.
Here is my code
My ProductEdit.php
<?php
namespace Modules\Products\Http\Livewire;
use App\Models\Product;
use Livewire\Component;
class ProductsEdit extends Component
{
public $name;
public $product;
public function render()
{
return view('products::livewire.edit-products', [
'name' => $this->product->name
]);
}
public function updateProduct()
{
dd($this->name);
}
}
My edit-products.blade.php
<div class="mt-1 text-sm leading-5 text-gray-900 sm:mt-0 sm:col-span-2">
<input class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
id="product_name"
type="text"
name="product_name"
value="{{ $product->name }}"
wire:model="name"
>
</div>
<button wire:click="updateProduct" class="bg-green-400 hover:bg-green-300 text-green-800 font-bold py-2 px-4 rounded inline-flex items-center">Save</button>

Resources