API post to store method return 500 internal server error

Published 1 month ago by boldstar

Hi, i am trying to post a form to my laravel backend but I keep getting a 500 internal server error. Im using a vue frontend with axios to send the post request.

here is my route

Route::post('/engagements', '[email protected]');

here is the EngagementsController for the store method

public function store(Request $request)
    {
        $data = $request->validate([
            'return_type' => 'required|string',
            'year' => 'required|string',
            'status' => 'required|string',
            'assigned_to' => 'required|string',
            'done' => 'required|boolean'
        ]);

        $engagement = Engagement::create([
            'client_id' => client()->id,
            'return_type' => $request->return_type,
            'year' => $request->year,
            'assigned_to' => $request->assigned_to,
            'status' => $request->status,
            'done' => $request->done,
        ]);

        return response($engagement, 201);
    }

and this is the Engagement model it references

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Engagement extends Model
{
    public function client() 
    {
        return $this->belongsTo('App\Client');
    }

    public function engagement_tasks()
    {
        return $this->belongsToMany('App\Task');
    }

}

I believe my issue is coming from the 'client_id' that is being added for the belongsTo() relationship it has with the parent model Client.

Currently my relationships are

  1. User has many Tasks
  2. Task belongs to User
  3. Client has many Engagements
  4. Engagement belongs to Client
  5. Engagement and Tasks have a many to many relationship

So I understand that when I submit data belonging to a user like this

'user_id' => auth()->user()->id,

on the store method it will reference the user model but for some reason that does not work for the client like this

'client_id' => client()->id,

I believe i am missing a step that is telling laravel which client the new engagement belongs to but Im not sure what that step is..

Any help would be greatly appreciated!!!

Best Answer (As Selected By boldstar)
boldstar

Setting all fillable fields fixed the alarm issue and as far as the EngagementsController I reverted back to this

public function store(Request $request)
    {
        $data = $request->validate([
            'client_id' => 'required|integer',
            'return_type' => 'required|string',
            'year' => 'required|string',
            'status' => 'required|string',
            'assigned_to' => 'required|string',
            'done' => 'required|boolean'
        ]);

        $engagement = Engagement::create([
            'client_id' => $request->client_id,
            'return_type' => $request->return_type,
            'year' => $request->year,
            'assigned_to' => $request->assigned_to,
            'status' => $request->status,
            'done' => $request->done,
        ]);

        return response($engagement, 201);
    }

Thank you, @Snapey, @Cronix, @Tray2

Tray2
Tray2
1 month ago (109,870 XP)

Where does the client_id come from?

Shouldn't it be $request->client_idinstead of Client()->id?

Or is the Client()method a helper that fetches it in the background?

I would pass the client id in via the Request object like all the other values.

Snapey
Snapey
1 month ago (1,036,605 XP)

look at the error.. it's usually helpful

Snapey
Snapey
1 month ago (1,036,605 XP)

ok. what is client() ?

you will have mass assignment issue with your model

You need to authenticate the request (client_id could be anything)

boldstar

Apologies for the late response, @Snapey & @Tray2

@Snapey, im not sure what you mean about 'what is client()?'

the EngagementsController references App\Engagement which is this model below

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Engagement extends Model
{
    public function client() 
    {
        return $this->belongsTo('App\Client');
    }

    public function engagement_tasks()
    {
        return $this->belongsToMany('App\Task');
    }

}

So the client_id is a foreign key on my engagements table...

on the front end i am capturing the id of the client in the URL , but this may be where I am making a mistake..

should there be more parameters being passed on the Engagement model?

Snapey
Snapey
4 weeks ago (1,036,605 XP)

here

'client_id' => client()->id,

client() is a function. To use it this way you would need to have a global client helper called that.

You answer tells me it is a relationship, but that can only be accessed from a model, as in $engagement->client()

Take it one step at a time and look at your Laravel Log file to see what is causing 500 error.

Tray2
Tray2
4 weeks ago (109,870 XP)

If you pass the client_id in the url as a query string you should use it from the request.

$request->query('client_id');

If you pass it in the header use it from the request

$request->client_id;

If you pass it in the url /model/1

You must add a parameter to your store method

public function store(Request $request, $client_id)
{
}
boldstar

Okay so I would love to view my laravel log file but I do not know where it is at.. when i try using postman to see what response I get, It does not return a stack to me except after I added the

public function store(Request $request, $client_id)

After I insert the $client_id as a parameter I do get an error response from that...

Symfony\Component\Debug\Exception\FatalThrowableError: Too few arguments to function App\Http\Controllers\EngagementsController::store(), 1 passed and exactly 2 expected in file C:\laragon\www\traxit\app\Http\Controllers\EngagementsController.php on line 53
Tray2
Tray2
4 weeks ago (109,870 XP)

Then you need to update your route

Route::post('/engagements/{client_id}', '[email protected]');

But I think it's better to pass the client_id in the request instead.

boldstar

@Tray2 , so im still struggling with this I have found an example like below..

public function store($family_id, ContactRequest $request)
{
    $family = Family::findOrFail($family_id);
    $contact = $family->contacts()->create($request->all());

    return $contact;

    //  return redirect('families/' . $family_id);
}

Where they pass the $id first before the request but what I can't grasp is the data that I am sending to the store method from vue front end. Should I be capturing the URL client_id. here is what I have happening on the front end...

This is my form and the url in vue

add-engagement/{client_id}

this is the form

<template>
  <div class="page-wrapper mt-1">
    <div class="add-engagement container">
      <div class="card-body bg-light border-primary mb-2">
        <h4 class="text-left text-primary m-0"><i class="mr-3 far fa-folder-open"></i>New Engagement</h4>
      </div>
        <form @submit.prevent="addEngagement" class="d-flex-column justify-content-center">
          <div class="form-group">
            <select class="form-control mb-3" id="type" v-model="engagement.return_type">
              <option v-for="type in types" :key="type.id" :value="type">{{ type }}</option>
            </select>
            <input type="text" class="form-control mb-3" placeholder="Year" v-model="engagement.year">
            <input type="text" class="form-control mb-3" placeholder="Assign To" v-model="engagement.assigned_to">
            <input type="text" class="form-control mb-3" placeholder="Status" v-model="engagement.status">

            <button type="submit" class="btn btn-lg btn-primary d-flex justify-content-start">Create</button>
          </div>
        </form>
      </div>  
  </div>
</template>

<script>


export default {
  name: 'add-engagement',
  data() {
    return {
      engagement: {
        return_type: null,
        year: '',
        assigned_to: '',
        status: '',
      },
      types: [ 
        'Choose Return Type...', 
        '1040', 
        '1120',
      ],
    }
  },
   methods: {
    addEngagement(e) {
      if(!this.engagement.return_type || !this.engagement.year ){
        return
      } else {
        this.$store.dispatch('addEngagement', {
          id: this.idForEngagement,
          return_type: this.engagement.return_type,
          year: this.engagement.year,
          assigned_to: this.engagement.assigned_to,
          status: this.engagement.status,
        })
        e.preventDefault();
      }
      e.preventDefault();
      this.engagement = "" 
      this.idForEngagement++
      this.$router.push('/engagements')
    },
  },
  created: function() {
    this.engagement.return_type = this.types[0]
  },

}
</script>

and this is the store.js file that is being dispatched to send the request to my laravel api route

addEngagement(context, engagement) {
      axios.post(('/engagements'), {
        return_type: engagement.return_type,
        year: engagement.year,
        assigned_to: engagement.assigned_to,
        status: engagement.status,
        done: false
      })
      .then(response => {
        context.commit('getClientEngagement', response.data)
      })
      .catch(error => {
        console.log(error)
      })
    },

Now notice that the post addEngagement method is mentioning nothing about the client_id.. does it need to be? since the client_id is a foreign_key?

Tray2
Tray2
4 weeks ago (109,870 XP)
addEngagement(e) {
      if(!this.engagement.return_type || !this.engagement.year ){
        return
      } else {
        this.$store.dispatch('addEngagement', {
          id: this.idForEngagement,
          return_type: this.engagement.return_type,
          year: this.engagement.year,
          assigned_to: this.engagement.assigned_to,
          status: this.engagement.status,

      client_id: this.engagement.client_id  //Here is where you should assign it.

        })
....

If you add it to your request you don't have to pass any extra parameters in the route. And then you can use it like any of the other parameters you pass to the controller in the request.

Snapey
Snapey
4 weeks ago (1,036,605 XP)

logs are in storage/logs

Snapey
Snapey
4 weeks ago (1,036,605 XP)

Now notice that the post addEngagement method is mentioning nothing about the client_id.. does it need to be? since the client_id is a foreign_key?

Its only a foreign key on engagements that actually exist. If you are creating a new engagement and it belongs to client then you need to tell laravel which client it should belong to.

You also need to check that the user is allowed to create engagement for the client_id mentioned since anyone can tamper with the request

boldstar

Perhaps I am getting outside my understand at the moment.. I did notice that I was using postman wrong and trying to pass the form data in the header instead of the body

So now that I am doing that correctly I will show you the error I am getting and perhaps I am headed in a better direction now

this is the error I get back in postman

Illuminate\Database\QueryException: SQLSTATE[HY000]: General error: 1364 Field &#039;return_type&#039; doesn&#039;t have a default value (SQL: insert into `engagements` (`client_id`, `updated_at`, `created_at`) values (1, 2018-09-15 18:51:13, 2018-09-15 18:51:13)) in file C:\laragon\www\traxit\vendor\laravel\framework\src\Illuminate\Database\Connection.php on line 664

This is the setup in my EngagementsController file

public function store(Request $request)
    {
        $data = $request->validate([
            'client_id' => 'required|integer',
            'return_type' => 'required|string',
            'year' => 'required|string',
            'status' => 'required|string',
            'assigned_to' => 'required|string',
            'done' => 'required|boolean'
        ]);

        $engagement = Engagement::create([
            'client_id' => $request->client_id,
            'return_type' => $request->return_type,
            'year' => $request->year,
            'assigned_to' => $request->assigned_to,
            'status' => $request->status,
            'done' => $request->done,
        ]);

        return response($engagement, 201);
    }

This is the Engagement Model

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Engagement extends Model
{
    protected $fillable =
    [
        'client_id'
    ];
    public function client() 
    {
        return $this->belongsTo('App\Client');
    }

    public function engagement_tasks()
    {
        return $this->belongsToMany('App\Task');
    }

}

At first passing the data through the body I was getting a mass assignment alarm as you suggested earlier @Snapey so i added

protected $fillable =
    [
        'client_id'
    ];

is this the correct way to handle the mass assignment issue? I did not get the alarm after applying it...

Cronix
Cronix
4 weeks ago (783,370 XP)

You need to add these fields to the fillable array as well

'return_type'
'year'
'assigned_to'
'status'
'done'

Everything you are passing to the create() method needs to be in the fillable array or they won't get saved.

You'll notice the query is only saving these fields client_id, updated_at, created_at

Snapey
Snapey
4 weeks ago (1,036,605 XP)

or if those fields are optional, you need to set then as nullable in the database

Please sign in or create an account to participate in this conversation.