Reputation: 1795
I'm brand new to Laravel and am working my way through the [Laravel 6 from Scratch][1] course over at Laracasts. The course is free but I can't afford a Laracasts membership so I can't ask questions there.
I've finished Section 6 of the course, Controller Techniques, and am having unexpected problems trying to extend the work we've done so far to add a few new features. The course has students build pages that let a user show a list of articles, look at an individual article, create and save a new article, and update and save an existing article. The course work envisioned a very simple article containing just an ID (auto-incremented in the database and not visible to the web user), a title, an excerpt and a body and I got all of the features working for that, including updating an existing article and saving it.
The update form sets method to POST but then uses a @METHOD('PUT') directive to tell the browser that it is actually supposed to do a PUT. This worked perfectly in the original code. However, now that I've added two more fields to the form, when I click Submit after editing an existing record, the save fails with this message:
Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException
The PUT method is not supported for this route. Supported methods: GET, HEAD, POST.
http://localhost:8000/articles
I don't understand why adding two fields to the form would cause this to break. Can someone enlighten me? I added the two new fields/columns to the migration and ran migrate:rollback and migrate. I've also added the new fields/columns to the fillable attribute and added validations for them in the ArticlesController.
Here is my routing:
Route::get('/articles', 'ArticlesController@index');
Route::post('/articles', 'ArticlesController@store');
Route::get('/articles/create', 'ArticlesController@create');
Route::get('/articles/{article}', 'ArticlesController@show');
Route::get('/articles/{article}/edit', 'ArticlesController@edit');
Route::put('/articles/{article}', 'ArticlesController@update');
//Route::delete('/articles/{article}', ArticlesController@destroy');
This is my ArticlesController:
<?php
namespace App\Http\Controllers;
use App\Article;
use Illuminate\Http\Request;
class ArticlesController extends Controller
{
public function index()
{
$articles = Article::latest()->get();
return view ('articles.index', ['articles' => $articles]);
}
public function show(Article $article)
{
return view('articles.show', ['article' => $article]);
}
public function create()
{
return view('articles.create');
}
public function store()
{
//Stores a NEW article
Article::create($this->validateArticle());
return redirect('/articles');
}
public function edit(Article $article)
{
return view('articles.edit', ['article' => $article]);
}
public function update(Article $article)
{
//Updates an EXISTING article
$article->update($this->validateArticle());
return redirect('/articles/', $article->id);
}
public function validateArticle()
{
return request()->validate([
'title' => ['required', 'min:5', 'max:20'],
'author' => ['required', 'min:5', 'max:30'],
'photopath' => ['required', 'min:10', 'max:100'],
'excerpt' => ['required', 'min:10', 'max:50'],
'body' => ['required', 'min:50', 'max:500']
]);
}
public function destroy(Article $article)
{
//Display existing record with "Are you sure you want to delete this? Delete|Cancel" option
//If user chooses Delete, delete the record
//If user chooses Cancel, return to the list of articles
}
}
Here's my edit form, edit.blade.php:
@extends('layout')
@section('content')
<div id="wrapper">
<div id="page" class="container">
<h1>Update Article</h1>
<form method="POST" action="/articles">
@csrf
@method('PUT')
<div class="form-group">
<label class="label" for="title">Title</label>
<div class="control">
<input class="form-control @error('title') errorborder @enderror" type="text" name="title" id="title" value="{{ $article->title }}">
@error('title')
<p class="errortext">{{ $errors->first('title') }}</p>
@enderror
</div>
</div>
<div class="form-group">
<label class="label" for="author">Author</label>
<div class="control">
<input class="form-control @error('author') errorborder @enderror" type="text" name="author" id="author" value="{{ $article->author }}">
@error('title')
<p class="errortext">{{ $errors->first('author') }}</p>
@enderror
</div>
</div>
<div class="form-group">
<label class="label" for="photopath">Path to Photo</label>
<div class="control">
<input class="form-control @error('photopath') errorborder @enderror" type="text" name="photopath" id="photopath" value="{{ $article->photopath }}">
@error('title')
<p class="errortext">{{ $errors->first('photopath') }}</p>
@enderror
</div>
</div>
<div class="form-group">
<label class="label" for="excerpt">Excerpt</label>
<div class="control">
<textarea class="form-control @error('excerpt') errorborder @enderror" name="excerpt" id="excerpt">{{ $article->excerpt }}</textarea>
@error('excerpt')
<p class="errortext">{{ $errors->first('excerpt') }}</p>
@enderror
</div>
</div>
<div class="form-group">
<label class="label" for="body">Body</label>
<div class="control">
<textarea class="form-control @error('body') errorborder @enderror" name="body" id="body">{{ $article->body }}</textarea>
@error('body')
<p class="errortext">{{ $errors->first('body') }}</p>
@enderror
</div>
</div>
<div class="control">
<button class="btn btn-primary" type="submit">Submit</button>
</div>
</form>
</div>
</div>
@endsection
Is there anything else you need to see?
[1]: https://laracasts.com/series/laravel-6-from-scratch/episodes/33?autoplay=true
Upvotes: 2
Views: 296
Reputation: 5098
Your Laravel route is:
Route::put('/articles/{article}', 'ArticlesController@update');
So your form action url should match that uri:
<form action="{{ url('/articles/'.$article->id) }}">
where the {article}
parameter is the record id (you can read more about in the docs here).
Then in your controller update()
method, you have:
return redirect('/articles/', $article->id);
which means redirect to /articles
with status code $article->id
(you can read more about in the docs here). I think you are trying to redirect to the show route, which is:
Route::get('/articles/{article}', 'ArticlesController@show');
So change the ,
(comma) to a .
(dot) to concatenate the article id with the uri:
return redirect('/articles/' . $article->id);
Upvotes: 1
Reputation: 996
The route in the form for /articles
, However your route for updating should be /articles/{article}
Try this:
<form method="POST" action="/articles/{{ $article->id }}">
Upvotes: 0