Search code examples
laravelormmodelforeign-keysrelationship

how to define methods for a model that has a 1: 1 self relationship


I have this relational table on my db: enter image description here

id, is referenced to: "attivitaSost" (and attivitaSpostata). The relathionship is "optional" so the foreignkey is nullable. But since the problem is the same, I will try to solve the first relationship first.

My model "cciActivities" have this 2 methods:

public function attOrig()
    {
        return $this->hasOne(CcieActivity::class,'id', 'attivitaSost');
    }

    public function attSpost(){
        return $this->belongsTo(CcieActivity::class,'attivitaSost','id');

If I set the inverse:

public function attOrig()
{
    return $this->hasOne(CcieActivity::class,'attivitaSost','id');
}

not works, and goes in a infinite loop thats goes in 500.

are well written? who needs to carry the foreign key? the children or the parent? there is a standard or I make work as was thinking:

  • save the new model,

  • pick up the id,

  • save it on the parent model,

The code:

                $ccieActPadre= CcieActivity::where('id',$ccieActivityId)->first();
                $ccieActivityNew = CcieActivity::create($data); 


                $ccieActPadre -> attivitaSost = $ccieActivityNew->id;
                $ccieActPadre->save();

I am asking this, because when i try to apply methods filters like

$ccieActivities = CcieActivity::doesntHave('attOrig')
                            ->get();

are returned not what i am expected.

When I am trying to render the resource activities, im using an api Resource like:

 return [
            'id' => $this->id,
            'project' =>new ProjectResource($this->project)  , //id, nomeEnte, name, email, ruolo
            'catAttivita' => $this->catAttivita,
            'nomeAttivita' => $this->nomeAttivita,
            'descrizione' =>  $this->descrizione,
            'dataInizioPrevista' =>  $this->dataInizioPrevista,
            'dataFinePrevista'=> $this->dataFinePrevista,
            'numNegoziAderentiPrevisti'=> $this->numNegoziAderentiPrevisti,
            'numAziendeCoinvoltePreviste'=> $this->numAziendeCoinvoltePreviste,
            'numInfluencerPartecipantiPrevisti'=> $this->numInfluencerPartecipantiPrevisti,
            'numBuyerPrevistiB2B'=> $this->numBuyerPrevistiB2B,
            'budgetTotalePrevisto'=> $this->budgetTotalePrevisto,         
            'modalitaRealizzazionePrevista'=> $this->modalitaRealizzazionePrevista,
            'attivitaSpostata' => new CcieActivityResource($this->attOrigSpost),
            'attivitaSostituitaaaaa' => new CcieActivityResource($this->attOrig),

        ];

this part

 'attivitaSostituita' => new CcieActivityResource($this->attOrig),

never works! whatever method I apply!

So I need to understand which is the right convention to menage a 1:1 optional self relationship over a laravel model, thanks.


Solution

  • The second parameters for hasOne and belongsTo are not the same.

    belongsTo is for the related model and hasOne is for the local model

    $this->hasOne(Phone::class, 'foreign_key', 'local_key');
    
    $this->belongsTo(User::class, 'foreign_key', 'owner_key');
    

    In your case, the hasOne has the wrong parameters. change it to

    public function attOrig()
    {
        return $this->hasOne(CcieActivity::class, 'attivitaSost', 'id');
    }
    

    EDIT: Never eager load by default the parent in the child model and the child in the parent model even if they are seperate Classes. It will lead to an infinite loop.