Are you sure you are hitting the correct controller method?
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']
]);
@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();
}
}
}
@msslgomez What if you do
Survey::create($validated);
@Tray2 No difference, I even ran some commands: op:cl and composer dumpautoload but no luck
@msslgomez In your view, do you display any validation errors?
Try adding a dd($validated); after the validation and see what happens
@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":[
]
}
]
}
]
}
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();
{
"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
}
@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 I tried that as well without any luck
@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...');
}
@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.
@msslgomez So it's this method that doesn't save it's data?
addQuestionRequirement
@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'],
]);
}
}
}
@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);
@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
}
]
}
@msslgomez You still don't see where the problem is?
@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
@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"
Then you can't do nothing when the required equals null.
@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?
@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.
@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
@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.
@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...');
}
@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.
@msslgomez I would use FormObject with inheritance and leave the controller clean and neat.
@MichalOravec In this case of a FormObject would I need to create one for store and one for update?
Please or to participate in this conversation.