esorone's avatar

Update method when adding an additional field

good afternoon,

I am running into an issue, which I can't solve myself. I have an application where I have form with flexible fields. If I create these then it all works fine. Using livewire I can add and remove fields. I already made great progress thanks to MichalOravec as well. But I proceed and in the end, i failed to proceed myself.

Now I run into the following, if I want to edit a field, then I can only edit the existing fields. If I add a field, then only the already existing fields are saved. Which makes sense given the code below. I have already looked if I could solve it with the fill() method but that did not work. Also workarounds with array_diffs or nested foreach, etc did not solve it.

So my question is, how can I make sure that the extra field is saved?

As a starter my ddd(), purely to show what the fields are I have 3 fields, but only Field1 and Field 2 are being saved. (Exists already in de database, corresponding the ID) But field 3 is not saved. Again which makes sense because of my code, but How can I solve this one?

array:7 [▼
  "_token" => "BmSdIF1Xldth6G8IvJnjfs0JkDnbM4ZR9owLyQ4l"
  "_method" => "PUT"
  "id" => "1"
  "name" => "Git Push"
  "description" => "Voor Git"
  "flow_id" => "1"
  "field" => array:3 [▼
    1 => "Field 1"
    16 => "Field 2"
    17 => "Field 3"
  ]
]

```


Controller:

```

    public function update(Request $request, $id)
    {
        $command = Command::findOrFail($id);

        $command->id =  $request->id;
        $command->name =  $request->name;
        $command->description =  $request->description;
        $command->flow_id =  $request->flow_id;


      foreach ($request->field as $id => $value) {
            $test = $command->fields()->where('id', $id)->update(['field' => $value]);
        }

       $command->save();

        return redirect('/admin/commands')->with('success', 'Command is successfully updated');
    }

Form


  <input type="text" name="field[]"
              class="px-4 py-2 border focus:ring-gray-500 focus:border-gray-900 w-full sm:text-sm border-gray-300 		 
 rounded-md focus:outline-none text-gray-600"
      placeholder="Field {{('Describe ' .$field)}}"> </div>

Command model



<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Command extends Model
{
    use HasFactory;

    protected $fillable = ['name', 'description', 'flow_id'];

    public function flow()
    {
       return $this->belongsTo(Flow::class);
    }

    public function fields()
    {
        return $this->hasMany(Fields::class);
    }
}

I hope someone can point me into the right direction.

Kr

0 likes
14 replies
vincent15000's avatar

Hello,

I think the problem is the update() method. You can only update what is already in the database. What you are trying to do is to add a new field.

I suggest you another approach with the sync() method (you can also use the detach() and attach([...]) method instead).

It could be like this.

$command->fields()->sync($request->field);

Or another way.

$command->fields()->detach();
$command->fields()->attach($request->field);

Tell me if it helps ;).

esorone's avatar

Im working on it, but got the "Call to undefined method Illuminate\Database\Eloquent\Relations\HasMany::attach() and the Call to undefined method Illuminate\Database\Eloquent\Relations\HasMany::sync();

I will read the docs and comeback :-)

chaudigv's avatar

update is happening before the save. Change the order.

vincent15000's avatar

But I think that the update() method only works on datas which are already in the database, no ?

esorone's avatar

Hey,

Thanks for you answer!

The save was still at this place, because I was working on the controller. But it still saves. I just tried to change the order to see if it helps.

But for sure (i guess) it is due the fact that update() only updates existing records.

badreddine jeddab's avatar

i think the problem that you trying to use "where " for your exemple :

case id= 1 $command->fields()->where('id', $id) will return the object cause it match

case id= 16 $command->fields()->where('id', $id) will return the object cause it match

case id= 17 $command->fields()->where('id', $id) he can't find the object in the old fields so he have nothing to update

try to use updateOrCreate instead . but i recommend to use like vincent said : $command->fields()->sync($request->field); but $request->field should return arrays of ids best thing to do , go watch eloquent section the series of laravel from scratch or just click here https://laracasts.com/series/laravel-6-from-scratch/episodes/33 .

esorone's avatar

Hello,

Indeed that's the reason but I was not able to solve it. It quite frustrating that you know you own issue, but cannot solve it :-( I just watch the episode but I think there is still something different . If I do the example of the serie on my own code using Tinkerwell, I receive exact the same error.

BadMethodCallException with message 'Call to undefined method Illuminate\Database\Eloquent\Relations\HasMany::attach()'

$command = App\Models\Command::find(1);
$command->fields()->attach([1, 2]);

It looks like attach is not possible with a hasmany. So I need a kind of refactor the code. Any ideas?

Kr

badreddine jeddab's avatar

you tried updateOrCreate method ?. you can try this :

i will assume that the fields have like field (id,field,command_id)

foreach ($request->field as $id => $value) {
        $test = $command->fields()->where('id', $id)->first();;
		if($test===null)
		{
			Field::create(['field' => $value,'command_id' => $command->id]);

		}

    }

but this code will only fix the issue that you have now ,

esorone's avatar

Thanks!!.

Indeed I just had to change update to updateOrCreate.

For future reference my controller:

   public function update(Request $request, $id)
    {
        $command = Command::findOrFail($id);

        $command->id =  $request->id;
        $command->name =  $request->name;
        $command->description =  $request->description;
        $command->flow_id =  $request->flow_id;
        $command->save();

        foreach ($request->field as $id => $value) {
            $command->fields()->where('id', $id)->updateOrCreate(['field' => $value]);
        }

        return redirect('/admin/commands')->with('success', 'Command is successfully updated');
    }


```
esorone's avatar

Nearly there. But It create a new records if I change a existing field. But a non existing fields is created as it should. Hope you have some ideas left.

esorone's avatar

Sorry, It works almost,

Now new fields are being created.. So adding a field 4 will add an additional field... But when I change field 3 with an different value, a new field is being created.. I would expect that the current field 3 would be updated with a new value.. SO almost solved.

badreddine jeddab's avatar

the way that you are trying to fix this issue is wrong , you will keep jumping from an error to another like if you unselect field2 and submit ,you will find it still there , so best solution for you is to watch eloquent many to many from this video https://laracasts.com/series/laravel-6-from-scratch/episodes/31 till the end of the eloquent , in the example of this video you should assume tags like fields and articles like commands , he make only the store method and he use attach , when you try to do the same in update replace attach with sync ,

esorone's avatar

Thanks again. The reason why I did not watch the episode 31, is because this is refering to a many to many relationship, And I have just a one to many. A command got many fields, but Fields belong to one command.

But I will follow along and maybe it is wise to refactor the code into a many to many relationship.

I keep you guys posted

esorone's avatar
esorone
OP
Best Answer
Level 4

Hello All,

As promised I post my solution. The suggested series where mainly focussing on many to many.. And app is regarding on to many. But in the end i manage to update the request. Likely is this not the best way to do is, but it did the job. Unfornunately the attach() only works for many to many.

My controller

public function update(Request $request, $id)
    {

        $command = Command::findOrFail($id);
        $command->name = $request->name;
        $command->description =  $request->description;
        $command->update();

        // Remove all fields and add new
        $command->fields()->delete();
        // Save each fields

        if($request->get('field')){
            foreach($request->get('field') as $key => $field)
            {
                $fields = new Fields();
                $fields->command_id = $command->id;
                $fields->field = $field;
                $fields->id = $request->field[$key];
                $fields->save();
            };
        };

        return redirect('/admin/commands')->with('success', 'Command is successfully updated');
    }

Please or to participate in this conversation.