Attaching a hasOne model to another Laravel/Eloquent model without specifying id

23,869

First off, you misunderstood the relation you refer to.

Here's what you need:

// Question model
public function questionType()
{
  return $this->belongsTo('QuestionType', 'type_id');
}

// QuestionType model
public function questions()
{
  return $this->hasMany('Question', 'type_id');
}

then you can link them together like this:

$questionType = QuestionType::where(..)->first();

$question = new Question;
... // do something with it

// associate
$question->questionType()->associate($questionType);

// or the other way around - save new question and link to the type:
$questionType->questions()->save($question);

You can explicitly pass an id to associate as well:

$question->type_id = $someTypeId;
$question->save();

You can't do this:

$question->questionType = $someQuestionType;

for this way Eloquent handles model attributes, not relations.


Question 2:

$questionType = new QuestionType(['name' => 'multiple']);
$questionType->save();

$question = new Question([ ... some values ... ]);

// then either this way:
$questionType->questions()->save($question);

// or, again, the other way around:
$question->questionType()->associate($questionType);
$question->save();
Share:
23,869
olerass
Author by

olerass

Updated on October 04, 2020

Comments

  • olerass
    olerass over 3 years

    Background

    Given we have the following two tables where type_id references a row in questionType:

    question

    id | type_id | description
    ---+---------+------------
    1  | 1       | A nice question
    .. | ..      | ..
    

    questionType

    id | name 
    ---+----------------
    1  | Multiple-choice 
    .. | ..
    

    with the following Eloquent models:

    class Question extends Model {
        public function type() {
            return $this->hasOne( 'QuestionType', 'id', 'type_id' );
        }
    }
    
    class QuestionType extends Model {
    }
    

    Question 1

    How can I add a new question that references an existing question type without manually doing anything with ids? For example the following works but is ugly imo since I have to manually assign the corresponding question type id:

    $q = new Question;
    $q->type_id = 1; // Multiple-choice
    $q->description = 'This is a multiple-choice question';
    $q->save();
    

    One would think there was a way to let the ORM handle the id-assignment (isn't the point to avoid stuff like this with ORMs?), something along the lines of (this does not work in Eloquent ORM):

    $q = new Question;
    $q->type = QuestionType.where('name', '=', 'Multiple-choice');
    $q->description = 'This is a multiple-choice question';
    $q->save();
    

    Question 2

    In relation to question 1, how would I go about adding a new question that references a new question type without manually doing anything with ids? Similarly I imagine something along the lines of:

    $t = new QuestionType;
    $t->name = 'Another type';
    
    $q = new Question;
    $q->type = $t;
    $q->description = 'This is a multiple-choice question';
    $q->save();
    

    Here I'd like $q->save() to save both the new question type and question (or something similar).

    The following works, but again I'm assigning the id myself which I believe the ORM should handle:

    $t = new QuestionType;
    $t->name = 'Another type';
    $t->save();
    
    $q = new Question;
    $q->type = $t->id;
    $q->description = 'This is a multiple-choice question';
    $q->save();
    

    I've tried playing with different combinations of save(), update() methods without luck. I also looked for attach() which exists on the hasMany relationships but seem to be missing in hasOne.

  • olerass
    olerass over 9 years
    Regarding question 1: the second method is pseudocode, it does not work in Laravel (Eloquent ORM). Or at least, I do not know how to do it. Do you? Regarding question 2: you are connecting Question to $t at $q->type = $t, so I'm not sure I understand your concern about what the ORM should decide. Of course it should save $t and $q at $q->save(), and if you do not connect $t2 to anything or save it manually it is not saved.
  • olerass
    olerass over 9 years
    Thanks so much Jarek! It seems I had mixed up the semantics of hasOne and belongsTo. In my mind $this->hasOne('QuestionType') where $this is a Question means that Question has a QuestionType. After all that's what it implies when you real it aloud right?. But apparently it's the other way around -- confusing!
  • Jarek Tkaczyk
    Jarek Tkaczyk over 9 years
    @olerass It might be misleading in the beginning. But think about it - does really a question have a type? No, it rather belongs to a type and that's what Eloquent implies.
  • Andrew Mast
    Andrew Mast over 6 years
    I know this is an old thread, but I have a solution where you can directly set the question type with $question->questionType = $someQuestionType;. Code: Pastebin
  • Stanley Aloh
    Stanley Aloh over 3 years
    This helped me overcome a problem I had with user having one roles in my laravel application