Henry
Henry

Reputation: 1795

PUT/POST in Laravel

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

Answers (2)

porloscerros Ψ
porloscerros Ψ

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

Mohamed Abdallah
Mohamed Abdallah

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

Related Questions