Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

eggplantSword's avatar

Model never gets saved to my database

I'm not sure why but I can't get a specific model to save, it's a one to many relationship for a Survey Question to a Question Requirement.

SurveyQuestion Model

    public $fillable = [
        'description',
        'response_type',
        'min',
        'max',
        'survey_section_id'
    ];

    public function requirements()
    {
        return $this->hasMany(SurveyQuestionRequirement::class, 'question_required_id');
    }

SurveyQuestionRequirement

    public $timestamps = false;

    public $fillable = [
        'survey_question_id',
        'question_required_id',
        'value'
    ];

This is how I'm trying to save this model

            if (count($parent_question) > 0) {
                if (array_key_exists('id', $parent_question)) {
                    $db_pq = SurveyQuestion::find($parent_question['id']);
                } else {
                    $db_pq = new SurveyQuestion();
                }

                $db_pq->description = $parent_question['description'];
                $db_pq->min = $parent_question['min'];
                $db_pq->max = $parent_question['max'];
                $db_pq->response_type = $parent_question['response_type'];
                $db_pq->survey_section_id = $section->id;
                $db_pq->save();

                $this->addQuestionOptions($loop_question, $question);

//here
                $req = new SurveyQuestionRequirement();
                $req->survey_question_id = $question->id;
                $req->question_required_id = $db_pq->id;
                $req->value = $loop_question['required_value'];
                $req->save();
            }

I've tried a bunch of different ways but it never gets added to the database, what am I doing wrong? What I also tried:

$req = new SurveyQuestionRequirement([
			$req->survey_question_id = $question->id;
            $req->question_required_id = $db_pq->id;
            $req->value = $loop_question['required_value']
]);
$req->save();

$question->requirements()->create([
    question_required_id => $db_pq->id,
    value => $loop_question['required_value']
]);
0 likes
28 replies
Tray2's avatar

Are you sure you are hitting the correct controller method?

eggplantSword's avatar

@Tray2 Yea the controller methods are like this, and eveything else gets saved except for the Question Requirement, (there are other methods like SurveyquestionOptions that's code I didn't add that save)

public function store(SaveSurvey $request)
    {
        $validated = $request->validated();
        DB::beginTransaction();

        $survey = new Survey($validated);
        if ($survey->required == 0) {
            $survey->hide = 0;
        }
        $survey->save();

        //sale_points
        $survey->sale_points()->attach(collect($validated['sale_points'])->pluck('id'));

        //questions
        foreach ($validated['sections'] as $section_index => $loop_section) {
            $this->addSection($survey, $loop_section, $section_index);
        }

        DB::commit();

        return redirect('surveys/surveys')->with('success', 'Encuesta registrada correctamente...');
    }

    public function addSection(Survey $survey, $loop_section, $section_index)
    {
        if (array_key_exists('id', $loop_section)) {
            $section = SurveySection::find($loop_section['id']);
        } else {
            $section = new SurveySection();
        }

        $section...
        $section->save();

        if ($section->id) {
            $question_ids = [];
            foreach ($loop_section['questions'] as $loop_question) {
                $q = $this->addQuestion($section, $loop_question, $loop_section);

                array_push($question_ids, $q->id);
            }

            SurveyQuestion::where('survey_section_id', $section->id)->whereNotIn('id', $question_ids)->delete();
        }

        return $section;
    }

    public function addQuestion(SurveySection $section, $loop_question, $loop_section)
    {
        if (array_key_exists('id', $loop_question)) {
            $question = SurveyQuestion::find($loop_question['id']);
        } else {
            $question = new SurveyQuestion();
        }

        $question->...
        $question->save();

        $this->addQuestionRequirement($section, $loop_question, $question, $loop_section);

        return $question;
    }

    public function addQuestionRequirement(SurveySection $section, $loop_question, SurveyQuestion $question, $loop_section)
    {
        if ($loop_question['required'] !== null) {
            $question->requirements()->delete();

            $parent_question = array_values(array_filter($loop_section['questions'], function ($q) use ($loop_question) {
                if (array_key_exists('ref', $q)) {
                    return $q['ref'] === $loop_question['required']['ref'];
                } else {
                    return $q['id'] === $loop_question['required']['id'];
                }
            }))[0];

            if (count($parent_question) > 0) {
                if (array_key_exists('id', $parent_question)) {
                    $db_pq = SurveyQuestion::find($parent_question['id']);
                } else {
                    $db_pq = new SurveyQuestion();
                }

                $db_pq->...
                $db_pq->save();

                $req = new SurveyQuestionRequirement();
                $req->survey_question_id = $question->id;
                $req->question_required_id = $db_pq->id;
                $req->value = $loop_question['required_value'];
                $req->save();
            }
        }
    }
eggplantSword's avatar

@Tray2 No difference, I even ran some commands: op:cl and composer dumpautoload but no luck

Tray2's avatar

@msslgomez In your view, do you display any validation errors?

Try adding a dd($validated); after the validation and see what happens

eggplantSword's avatar

@Tray2 I do have validation, it's quite large actually, this is the validated data

{
   "name":"asdf",
   "description":"f",
   "init_date":"2022-06-01",
   "end_date":"2022-06-30",
   "frequency":"weekly",
   "quantity":1,
   "is_evaluation":1,
   "default":0,
   "required":0,
   "survey_type_id":1,
   "sale_points":[...],
   "sections":[
      {
         "description":"dfgsd",
         "questions":[
            {
               "ref":1,
               "description":"asdf",
               "response_type":"boolean",
               "min":0,
               "max":null,
               "options":[
                  
               ],
               "required":null,
               "required_id":null,
               "required_value":1,
               "schemas":[
                  {
                     "value":1,
                     "value2":0,
                     "points":5
                  },
                  {
                     "value":0,
                     "value2":0,
                     "points":0
                  }
               ]
            },
            {
               "ref":2,
               "description":"sfg",
               "response_type":"text",
               "min":0,
               "max":null,
               "options":[
                  
               ],
               "required":{
                  "ref":1,
                  "description":"asdf",
                  "response_type":"boolean",
                  "min":0,
                  "max":null,
                  "options":[
                     
                  ],
                  "required":null,
                  "required_id":null,
                  "required_value":1,
                  "schemas":[
                     {
                        "value":1,
                        "value2":0,
                        "points":5
                     },
                     {
                        "value":0,
                        "value2":0,
                        "points":0
                     }
                  ]
               },
               "required_id":1,
               "required_value":1,
               "schemas":[
                  
               ]
            }
         ]
      }
   ]
}
Tray2's avatar

@msslgomez

What is the value of $survey

$survey = new Survey($validated);
		dump($survey)
        if ($survey->required == 0) {
            $survey->hide = 0;
			dump('Survey isnt required');
        }
        $survey->save();
eggplantSword's avatar

@Tray2

{
   "name":"asdffgg",
   "description":"asdf",
   "init_date":"2022-06-01",
   "end_date":"2022-06-28",
   "frequency":"weekly",
   "quantity":1,
   "is_evaluation":1,
   "default":0,
   "required":0,
   "survey_type_id":1,
   "updated_at":"2022-06-20T16:35:53.000000Z",
   "created_at":"2022-06-20T16:35:53.000000Z",
   "id":9
}
Tray2's avatar

@msslgomez Ok, what happens if you remove the transaction?

public function store(SaveSurvey $request)
    {
        $validated = $request->validated();
        //DB::beginTransaction();

        $survey = new Survey($validated);
        if ($survey->required == 0) {
            $survey->hide = 0;
        }
        $survey->save();

        //sale_points
        $survey->sale_points()->attach(collect($validated['sale_points'])->pluck('id'));

        //questions
        foreach ($validated['sections'] as $section_index => $loop_section) {
            $this->addSection($survey, $loop_section, $section_index);
        }

       // DB::commit();

        return redirect('surveys/surveys')->with('success', 'Encuesta registrada correctamente...');
    }
Tray2's avatar

@msslgomez And you don't get any errors from the validation and no errors from the database?

If you only do this, what happens then?

public function store(SaveSurvey $request)
    {
        Survey::create($request->validated());
        return redirect('surveys/surveys')->with('success', 'Encuesta registrada correctamente...');
     }
eggplantSword's avatar

@Tray2 I don't get any errors, the rest of the survey, and survey questions saves correctly. Everything else except for the requirements gets saved and then I get redirected to the index where I can see the survey I just created with everything but the requirements. If I try what you say my validation fails for the sale_points and sections. If I remove the validation it saves it, but the survey part was already being saved.

Tray2's avatar

@msslgomez So it's this method that doesn't save it's data?

addQuestionRequirement

eggplantSword's avatar

@Tray2 Yes I think so, that is the only part not getting saved, I did try to do Logs of the stuff in that method and it did show an id value but it wasn't in the database either. Here is the full method

public function addQuestionRequirement(SurveySection $section, $loop_question, SurveyQuestion $question, $loop_section)
    {
        if ($loop_question['required'] !== null) {
            $question->requirements()->delete();

            $parent_question = array_values(array_filter($loop_section['questions'], function ($q) use ($loop_question) {
                if (array_key_exists('ref', $q)) {
                    return $q['ref'] === $loop_question['required']['ref'];
                } else {
                    return $q['id'] === $loop_question['required']['id'];
                }
            }))[0];

            if (count($parent_question) > 0) {
                if (array_key_exists('id', $parent_question)) {
                    $db_pq = SurveyQuestion::find($parent_question['id']);
                } else {
                    $db_pq = new SurveyQuestion();
                }

                $db_pq->description = $parent_question['description'];
                $db_pq->min = $parent_question['min'];
                $db_pq->max = $parent_question['max'];
                $db_pq->response_type = $parent_question['response_type'];
                $db_pq->survey_section_id = $section->id;
                $db_pq->save();

                $this->addQuestionOptions($loop_question, $question);

                $req = new SurveyQuestionRequirement([
                    'survey_question_id' => $question->id,
                    'question_required_id' => $db_pq->id,
                    'value' => $loop_question['required_value'],
                ]);
            }
        }
    }
Tray2's avatar

@msslgomez What is the value of the $loop_question when it is passed into the function?

Add this first in the function and show the result

        dd($loop_question);
eggplantSword's avatar

@Tray2 sure,

{
   "ref":1,
   "description":"asdffr",
   "response_type":"boolean",
   "min":0,
   "max":null,
   "options":[],
   "required":null,
   "required_id":null,
   "required_value":1,
   "schemas":[
      {
         "value":1,
         "value2":0,
         "points":5
      },
      {
         "value":0,
         "value2":0,
         "points":0
      }
   ]
}
Tray2's avatar

@msslgomez Required is null and you only go into the code if it's is not null.

if ($loop_question['required'] !== null)

So you need to set the required to something not null

1 like
eggplantSword's avatar

@tray2 @michaloravec This is the dd of the first question that pops up, which of course isn't going to have a requirements since it's the first question and can't have one, what I am noticing is that the survey that I'm saving has 2 questions where the second one has the first one as a requirement. When I put dump and than a dd at the end it really only loops over the first question and not the second one.

This code shows only the following

 public function addSection(Survey $survey, $loop_section, $section_index)
    {
        if (array_key_exists('id', $loop_section)) {
            $section = SurveySection::find($loop_section['id']);
        } else {
            $section = new SurveySection();
        }

        $section->...
        $section->save();

        if ($section->id) {
            $question_ids = [];
            dump('start');
            foreach ($loop_section['questions'] as $loop_question) {
                dump($loop_question);
                $q = $this->addQuestion($section, $loop_question, $loop_section);

                array_push($question_ids, $q->id);
            }
            dd('end');
            SurveyQuestion::where('survey_section_id', $section->id)->whereNotIn('id', $question_ids)->delete();
        }

        return $section;
    }
"start"

array:10 [▼
  "ref" => 1
  "description" => "asdffr"
  "response_type" => "boolean"
  "min" => 0
  "max" => null
  "options" => []
  "required" => null
  "required_id" => null
  "required_value" => 1
  "schemas" => array:2 [▶]
]

"stop"
Tray2's avatar

Then you can't do nothing when the required equals null.

eggplantSword's avatar

@Tray2 I did some more testing and I figured out that it doesn't save the requirement usually but if I add a dd checking right after the requirement is saved than it does get added into my database.

 public function addQuestionRequirement(SurveySection $section, $loop_question, SurveyQuestion $question, $loop_section)
    {
        if ($loop_question['required'] !== null) {
            $question->requirements()->delete();

            $parent_question = array_values(array_filter($loop_section['questions'], function ($q) use ($loop_question) {
                if (array_key_exists('ref', $q)) {
                    return $q['ref'] === $loop_question['required']['ref'];
                } else {
                    return $q['id'] === $loop_question['required']['id'];
                }
            }))[0];

            if (count($parent_question) > 0) {
                if (array_key_exists('id', $parent_question)) {
                    $db_pq = SurveyQuestion::find($parent_question['id']);
                } else {
                    $db_pq = new SurveyQuestion();
                }

                $db_pq->description = $parent_question['description'];
                $db_pq->min = $parent_question['min'];
                $db_pq->max = $parent_question['max'];
                $db_pq->response_type = $parent_question['response_type'];
                $db_pq->survey_section_id = $section->id;
                $db_pq->save();

                $this->addQuestionOptions($loop_question, $question);

                if ($db_pq->id) {
                    $req = SurveyQuestionRequirement::create([
                        'survey_question_id' => $question->id,
                        'question_required_id' => $db_pq->id,
                        'value' => $loop_question['required_value'],
                    ]);

                    dd($req);
                }
            }
        }
    }

If I remove the dd the requirement doesn't get saved even when I send the same exact data to the backend, what could be the reason?

Tray2's avatar

@msslgomez You are aware that this line deletes all the requirements for that question.

$question->requirements()->delete();

So maybe you should put a check that it only does it the first time.

eggplantSword's avatar

@Tray2 I have that because this method is used for creating and updating so every question's requirements gets cleared and then set again on the bottom (that is the idea anyway), I'll try removing that to test and see what happens

Tray2's avatar

@msslgomez You should separate store and update, it will be a bit of possible duplication but sometimes it's worth it. At least as you are learning, when you are proficient enough you should have tests for each method and then you can refactor to make the code DRY:er.

eggplantSword's avatar

@Tray2 The store and update are separate, they do have shared methods though: addSection, addQuestion, addQuestionOptions, addQuestionRequirement, addQuestionSchema. So I should not use these shared methods?

public function store(SaveSurvey $request)
    {
        $validated = $request->validated();

        $survey = Survey::create($validated);

        if ($survey->required == 0) {
            $survey->hide = 0;
        }

        $survey->save();

        //sale_points
        $survey->sale_points()->attach(collect($validated['sale_points'])->pluck('id'));

        //questions
        foreach ($validated['sections'] as $section_index => $loop_section) {
            $this->addSection($survey, $loop_section, $section_index);
        }

        return redirect('surveys/surveys')->with('success', 'Encuesta registrada correctamente...');
    }

public function update(SaveSurvey $request, $id)
    {
        $validated = $request->validated();

        $survey = Survey::findOrFail($id);
        $survey->fill($validated);
        $survey->save();

        //sale_points
        $survey->sale_points()->sync(collect($validated['sale_points'])->pluck('id'));

        //questions
        $section_ids = [];
        foreach ($validated['sections'] as $section_index => $loop_section) {
            $s = $this->addSection($survey, $loop_section, $section_index);
            array_push($section_ids, $s->id);
        }

        SurveySection::where('survey_id', $survey->id)->whereNotIn('id', $section_ids)->delete();

        return redirect('surveys/surveys')->with('success', 'Encuesta actualizada correctamente...');
    }
Tray2's avatar

@msslgomez It depends, if the update does things that the create doesn't do, then I would isolate the the difference into their own methods.

Please or to participate in this conversation.