PHPFixing
  • Privacy Policy
  • TOS
  • Ask Question
  • Contact Us
  • Home
  • PHP
  • Programming
  • SQL Injection
  • Web3.0

Wednesday, April 20, 2022

[FIXED] What makes the "Add article" form in this Laravel 8 application fail?

 April 20, 2022     laravel, laravel-8, php     No comments   

Issue

I am making a blogging application with Laravel 8 and Bootstrap 5.

I run into a problem with adding an article into the articles table.

In the migration file that generated the articles table, I have:

public function up() {
 Schema::create('articles', function (Blueprint $table) {
    $table->id();
    $table->unsignedInteger('user_id');
    $table->foreign('user_id')->references('id')->on('users');
    $table->unsignedInteger('category_id');
    $table->foreign('category_id')->references('id')->on('catergories');
    $table->string('title');
    $table->string('slug');
    $table->string('short_description');
    $table->longText('content');
    $table->tinyInteger('featured')->default('0');
    $table->string('image')->nullable();
    $table->timestamps();
  });
}

In the Article model, I have:

class Article extends Model {
  use HasFactory;

    protected $fillable = [
        'user_id',
        'category_id',
        'title',
        'slug',
        'short_description',
        'content',
        'featured',
        'image',
    ];

    // Join users to articles
    public function user() {
        return $this->belongsTo(User::class);
    }

    // Join categories to articles
    public function category() {
        return $this->belongsTo(ArticleCategory::class);
    }
}

In the controller, I have:

namespace App\Http\Controllers\Dashboard;
use Illuminate\Support\Str;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Facades\Auth;
use App\Http\Controllers\Controller;
use App\Models\ArticleCategory;
use App\Models\Article;
use Illuminate\Http\Request;

class ArticleController extends Controller {
    private $rules = [
        'category_id' => 'required|exists:article_categories,id',
        'title' => 'required|string|max:255',
        'short_description' => 'required|string|max:255',
        'image' =>  'image|mimes:jpeg,png,jpg|max:2048',
        'content' => 'required|string',
        'featured' => 'required'
    ];

    private $messages = [
        'category_id.required' => 'Please pick a category for the article',
        'title.required' => 'Please provide a title for the article',
        'short_description.required' => 'The article needs a short description',
        'content.required' => 'Please add content'
    ];
    
    public function categories() {
        return ArticleCategory::all();
    }
    
    public function index() {
        $articles = Article::paginate(10);

        return view('dashboard/articles',
            ['articles' => $articles]
        );
    }

    public function create() {
        // Load the view and populate the form with categories
        return view('dashboard/add-article',
            ['categories' => $this->categories()]
        );
    }

    public function save(Request $request) {

        // Validate form (with custom messages)
        $validator = Validator::make($request->all(), $this->rules, $this->messages);

        if ($validator->fails()) {
            return redirect()->back()->withErrors($validator->errors())->withInput();
        }

        $fields = $validator->validated();

        // Upload article image
        $current_user = Auth::user();

        if (isset($request->image)) {
            $imageName = md5(time()) . $current_user->id . '.' . $request->image->extension();
            $request->image->move(public_path('images/articles'), $imageName);
        }

        // Data to be added
        $form_data = [
            'user_id' => Auth::user()->id,
            'category_id' => $fields['category_id'],
            'title' => $fields['title'],
            'slug' => Str::slug($fields['title'], '-'),
            'short_description' => $fields['short_description'],
            'content' => $fields['content'],
            'image' => $fields['image'],
            'featured' => $fields['featured']
        ];

        // Insert data in the 'articles' table
        $query = Article::create($form_data);

        if ($query) {
            return redirect()->route('dashboard.articles')->with('success', 'Article added');
        } else {
            return redirect()->back()->with('error', 'Adding article failed');
        }
    }
}

The form:

<form method="POST" action="{{ route('dashboard.articles.add') }}" enctype="multipart/form-data" novalidate>
    @csrf
    <div class="row mb-2">
            <label for="title" class="col-md-12">{{ __('Title') }}</label>

            <div class="col-md-12 @error('title') has-error @enderror">
                    <input id="title" type="text" placeholder="Title" class="form-control @error('title') is-invalid @enderror" name="title" value="{{ old('title') }}" autocomplete="title" autofocus>

                    @error('title')
                        <span class="invalid-feedback" role="alert">
                            <strong>{{ $message }}</strong>
                        </span>
                    @enderror
            </div>
    </div>

    <div class="row mb-2">
            <label for="short_description" class="col-md-12">{{ __('Short description') }}</label>

            <div class="col-md-12 @error('short_description') has-error @enderror">
                    <input id="short_description" type="text" placeholder="Short description" class="form-control @error('short_description') is-invalid @enderror" name="short_description" value="{{ old('short_description') }}" autocomplete="short_description" autofocus>

                    @error('short_description')
                        <span class="invalid-feedback" role="alert">
                            <strong>{{ $message }}</strong>
                        </span>
                    @enderror
            </div>
    </div>

    <div class="row mb-2">
        <label for="category" class="col-md-12">{{ __('Category') }}</label>
    
        <div class="col-md-12 @error('category_id') has-error @enderror">
    
            <select name="category_id" id="category" class="form-control @error('category_id') is-invalid @enderror">
                <option value="0">Pick a category</option>
                @foreach($categories as $category)
                    <option value="{{ $category->id }}">{{ $category->name }}</option>
                @endforeach
            </select>
                
                @error('category_id')
                    <span class="invalid-feedback" role="alert">
                        <strong>{{ $message }}</strong>
                    </span>
                @enderror
        </div>
    </div>

    <div class="row mb-2">
        <div class="col-md-12 d-flex align-items-center switch-toggle">
                <p class="mb-0 me-3">Featured article?</p>
                <input class="mt-1" type="checkbox" name="featured" id="featured">
                <label class="px-1" for="featured">{{ __('Toggle') }}</label>
        </div>
    </div>

    <div class="row mb-2">
        <label for="image" class="col-md-12">{{ __('Article image') }}</label>
    
        <div class="col-md-12 @error('image') has-error @enderror">
            <input type="file" name="image" id="file" class="file-upload-btn">
    
            @error('image')
                <span class="invalid-feedback" role="alert">
                    <strong>{{ $message }}</strong>
                </span>
            @enderror
        </div>
    </div>

    <div class="row mb-2">
        <label for="content" class="col-md-12">{{ __('Content') }}</label>

        <div class="col-md-12 @error('content') has-error @enderror">

            <textarea name="content" id="content" class="form-control @error('content') is-invalid @enderror" placeholder="Content" cols="30" rows="6">{{ old('content') }}</textarea>

            @error('content')
                <span class="invalid-feedback" role="alert">
                    <strong>{{ $message }}</strong>
                </span>
            @enderror
        </div>
    </div>
    
    <div class="row mb-0">
            <div class="col-md-12">
                    <button type="submit" class="w-100 btn btn-primary">
                            {{ __('Save') }}
                    </button>
            </div>
    </div>
</form>

The routes:

// Article routes
Route::group(['prefix' => 'articles'], function() {
  Route::get('/', [ArticleController::class, 'index'])->name('dashboard.articles');
  Route::get('/new', [ArticleController::class, 'create'])->name('dashboard.articles.new');
  Route::post('/add', [ArticleController::class, 'save'])->name('dashboard.articles.add');
}); 

The problem:

Even though the form passes validation, and a redirect occurs, the record is not actually added to the articles table.

Laravel throws the error General error: 1366 Incorrect integer value: 'on' for column 'featured' at row 1.

What is my mistake?


Solution

When dealing with checkboxes you need to know that;

a) you cannot use required since the checkbox is not sent with the form data if it is unchecked.

b) the default value is 'on' but actually, the presence of the field in the data means it was checked so what I normally do is like;

'featured' => $request->has('featured')

this will return a boolean true or false, suitable for storing in your db

So you could write

        // Turn the 'featured' field value into a tiny integer
        //$fields['featured'] = $request->get('featured') == 'on' ? 1 : 0;

        // If no image is uploaded, use default.jpg
        $fields['image'] = $request->get('image') == '' ? 'default.jpg' : $imageName;

        // Data to be added
        $form_data = [
            'user_id' => Auth::user()->id,
            'category_id' => $fields['category_id'],
            'title' => $fields['title'],
            'slug' => Str::slug($fields['title'], '-'),
            'short_description' => $fields['short_description'],
            'content' => $fields['content'],
            'featured' => $request->has('featured'),
            'image' => $fields['image']
        ];


Answered By - Snapey
Answer Checked By - David Goodson (PHPFixing Volunteer)
  • Share This:  
  •  Facebook
  •  Twitter
  •  Stumble
  •  Digg
Newer Post Older Post Home

0 Comments:

Post a Comment

Note: Only a member of this blog may post a comment.

Total Pageviews

Featured Post

Why Learn PHP Programming

Why Learn PHP Programming A widely-used open source scripting language PHP is one of the most popular programming languages in the world. It...

Subscribe To

Posts
Atom
Posts
Comments
Atom
Comments

Copyright © PHPFixing