Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Laravel 5.3 + Vue Reddit-like voting system

I'm learning Vuejs (not too proficient in Laravel in the first place), and I'm trying to make a simple voting system on some tasks. I managed to do adding tasks, editing them, and deleting them, but when I added the upvote/downvote stuff, the number of votes doesn't change.

So here are the Laravel routes in the routes\api.php:

Route::group(['middleware'=>'api'],function(){

Route::get('tasks', function(){
    return \App\Task::latest()->orderBy('created_at', 'desc')->get();
});

Route::get('task/{id}', function($id){
    return \App\Task::findOrFail($id);
});

Route::post('task/store', function(Request $request){
    return App\Task::create(['body' => $request->input(['body'])]);
});

Route::patch('task/{id}', function(Request $request, $id){
    return \App\Task::findOrFail($id)->update(['body' => $request->input(['body'])]);
});

Route::delete('task/{id}', function($id){
    return \App\Task::destroy($id);
});

Route::get('task/{id}/votes', function($id){
    return \App\Task::findOrFail($id)->votes->get();
});

Route::patch('task/{id}/votes', function(Request $request, $id){
    return \App\Task::findOrFail($id)->update(['votes'=> $request->input(['votes'])]);
}); 
});

The migration for the Tasks table looks like this (I used sqlite):

Schema::create('tasks', function (Blueprint $table) {
        $table->increments('id');
        $table->text('body');
        $table->integer('votes')->default(1);
        $table->timestamps();
    });

This is the Tasks component template for Vue:

<h1>My Tasks</h1>
<hr>
<h4>New Task</h4>
<form action="#" @submit.prevent="edit ? updateTask(task.id) : createTask()">
    <div class="input-group">
        <input type="text" name="body" v-model="task.body" v-el:taskinput class="form-control" autofocus>
        <span class="input-group-btn">
            <button type="submit" class="btn btn-success" v-show="!edit">New Task</button>
            <button type="submit" class="btn btn-primary" v-show="edit">Edit Task</button>
        </span>
    </div>
</form>
<hr>
<hr>
<h3>All Tasks</h3>
<ul class="list-group">
    <li class="list-group-item" v-for="task in list">
        {{ task.body }}
        <button class="btn-success btn-xs" @click="upvote(task.id)" :class="{disabled: upvoted}">Upvote</button>
        <span class="label label-primary">{{ task.votes }}</span>
        <button class="btn-danger btn-xs" @click="downvote(task.id)" :class="{disabled: downvoted}">Downvote</button>
        <span class="pull-right">
            <button class="btn-primary btn-xs" @click="showTask(task.id)">Edit Task</button>
            <button class="btn-danger btn-xs" @click="deleteTask(task.id)">Delete Task</button>
        </span>
    </li>
</ul>

And finally, the Vue script:

export default{
    data(){
        return{
            edit: false,
            list: [],
            task: {
                id: '',
                body: '',
                votes: Number
            },
            upvoted: false,
            downvoted: false
        }
    },

    ready: function () {
        this.fetchTaskList();
    },

    methods:{

        fetchTaskList: function () {
            this.$http.get('api/tasks').then(function (response) {
                this.list = response.data
            });
        },

        createTask: function () {
            this.$http.post('api/task/store', this.task);
            this.task.body = '';
            this.edit = false;
            this.fetchTaskList();
        },

        updateTask: function (id) {
            this.$http.patch('api/task/' + id, this.task);
            this.task.body='';
            this.edit = false;
            this.fetchTaskList();
        },

        showTask: function (id) {
            this.$http.get('api/task/' + id).then(function (response) {
                this.task.id = response.data.id;
                this.task.body = response.data.body;
            });
            this.$els.taskinput.focus();
            this.edit = true;
        },

        deleteTask: function (id) {
            this.$http.delete('api/task/' + id);
            this.fetchTaskList();
        },

        updateVotes: function (id, votes) {
            this.$http.patch('api/task/'+id+'/votes', votes);
        },

        upvote: function (id) {
            this.$http.get('api/task/'+id+'/votes').then(function (response) {
                this.task.id = response.data.id;
                this.task.votes = response.data.votes + 1;
                updateVotes(this.task.id, this.task.votes);
            });
            this.upvoted = !this.upvoted;
            this.downvoted = false;
        },

        downvote: function (id) {
            this.$http.get('api/task/'+id+'/votes').then(function (response) {
                this.task.id = response.data.id;
                this.task.votes = response.data.votes - 1;
                updateVotes(this.task.id, this.task.votes);
            });
            this.upvoted = false;
            this.downvoted = !this.downvoted;
        },
    }
}

I'm guessing the error is somewhere in the Vue script, either I'm declaring votes wrong (votes: Number ?), or I can't call updateVotes function from the upvote and downvote functions like I did.

Edit: Still not working, but I have Vue.js devtools and it's definitely recognizing the votes: https://snag.gy/sE5gp6.jpg

...but in a very weird way. When I click upvote, the votes in the devtools changes to '11', not 1. When I click downvote, it goes back to 0. It doesn't change in the view, though. This is all after I've made the change from:

updateVotes(this.task.id, this.task.votes);

To:

this.updateVotes(this.task.id, this.task.votes);

..as the user Dan suggested, and after changing the:

upvote: function (id) {
        this.$http.get('api/task/'+id+'/votes')

To:

upvote: function (id) {
        this.$http.get('api/task/'+id)

as I think I was previously getting just the votes and then treating them like a Task object for the rest of the function. Same for the downvote function.

This is what comes up in console now:

vue-resource.common.js?d39b:966 PATCH http://localhost:8000/api/task/8/votes 500 (Internal Server Error)(anonymous function) @ vue-resource.common.js?d39b:966Promise$1 @ vue-resource.common.js?d39b:192xhrClient @ vue-resource.common.js?d39b:927sendRequest @ vue-resource.common.js?d39b:1060exec @ vue-resource.common.js?d39b:1017next @ vue-resource.common.js?d39b:1042before @ vue-resource.common.js?d39b:881exec @ vue-resource.common.js?d39b:1017next @ vue-resource.common.js?d39b:1042timeout @ vue-resource.common.js?d39b:920exec @ vue-resource.common.js?d39b:1017next @ vue-resource.common.js?d39b:1042method @ vue-resource.common.js?d39b:895exec @ vue-resource.common.js?d39b:1017next @ vue-resource.common.js?d39b:1042body @ vue-resource.common.js?d39b:802exec @ vue-resource.common.js?d39b:1017next @ vue-resource.common.js?d39b:1042jsonp @ vue-resource.common.js?d39b:867exec @ vue-resource.common.js?d39b:1017next @ vue-resource.common.js?d39b:1042header @ vue-resource.common.js?d39b:903exec @ vue-resource.common.js?d39b:1017next @ vue-resource.common.js?d39b:1042cors @ vue-resource.common.js?d39b:777exec @ vue-resource.common.js?d39b:1017next @ vue-resource.common.js?d39b:1042(anonymous function) @ VM56:32exec @ vue-resource.common.js?d39b:1017(anonymous function) @ vue-resource.common.js?d39b:1045Promise$1 @ vue-resource.common.js?d39b:192Client @ vue-resource.common.js?d39b:1010Http @ vue-resource.common.js?d39b:1152Http.(anonymous function) @ vue-resource.common.js?d39b:1188updateVotes @ Tasks.vue?34c5:94(anonymous function) @ vue.common.js?4a36:216(anonymous function) @ Tasks.vue?34c5:102 localhost/:1 Uncaught (in promise) Response {url: "api/task/8/votes", body: "↵↵ ↵ ↵↵↵ ↵ ↵", headers: Object, status: 500, statusText: "Internal Server Error"…}

like image 394
Miodrag Nisevic Avatar asked Oct 29 '22 19:10

Miodrag Nisevic


2 Answers

Does it work if you replace:

updateVotes(this.task.id, this.task.votes);

With

this.updateVotes(this.task.id, this.task.votes);
like image 62
Dan Avatar answered Nov 09 '22 03:11

Dan


(Posted on behalf of the OP).

The problem is solved. I didn't add protected $fillable = ['votes'] in the Task model.

like image 39
3 revs, 2 users 83% Avatar answered Nov 09 '22 04:11

3 revs, 2 users 83%