Search code examples
laraveleloquenthas-onelaravel-relations

HasOne showing either "null" or "Undefined property: Illuminate\\Database\\Eloquent\\Relations\\HasOne"


I'm trying to use hasOne to return a single value within my data set, however I can't seem to return the single column as a value without returning the full object.

What the object looks like when returned when you just return hasOne:

protected $with = ["steps"];

public function steps() {
    return $this->hasOne("App\Compares\ComparesSteps", "compare_id", "id");
}

Result of just using hasOne as the default object:

array:21 [
  "id" => 5887894545
  "steps" => array:5 [
    "id" => 21
    "compare_id" => 588789
    "steps" => array:12 [
      0 => 1
      1 => 2
      2 => 3
      3 => 4
      4 => 13
      5 => 6
      6 => 7
      7 => 17
      8 => 8
      9 => 9
      10 => 10
      11 => 12
    ]
    "created_at" => "2021-10-05 08:48:44"
    "updated_at" => "2021-10-05 08:48:44"
  ]
  "created_at" => "2021-10-05 08:48:43"
  "updated_at" => "2021-10-05 08:48:43"
  "expired_at" => "2021-10-09 08:48:43"
  "booked" => 0
  "reference" => null
  "utm" => ""
  "updates" => []
]

Returns null:

   array:21 [
      "id" => 5887894545
      "steps" => null
      "created_at" => "2021-10-05 08:48:43"
      "updated_at" => "2021-10-05 08:48:43"
      "expired_at" => "2021-10-09 08:48:43"
      "booked" => 0
      "reference" => null
      "utm" => ""
      "updates" => []
    ]

Returns Call to a member function addEagerConstraints() on array:

public function steps() {
    return $this->hasOne("App\Compares\ComparesSteps", "compare_id", "id")->value("steps");
}

Returns Undefined property: Illuminate\\Database\\Eloquent\\Relations\\HasOne::$steps:

public function steps() {
    return $this->hasOne("App\Compares\ComparesSteps", "compare_id", "id")->steps;
}

Expected Result:

   array:21 [
      "id" => 5887894545
      "steps" => array:12 [
          0 => 1
          1 => 2
          2 => 3
          3 => 4
          4 => 13
          5 => 6
          6 => 7
          7 => 17
          8 => 8
          9 => 9
          10 => 10
          11 => 12
      ]
      "created_at" => "2021-10-05 08:48:43"
      "updated_at" => "2021-10-05 08:48:43"
      "expired_at" => "2021-10-09 08:48:43"
      "booked" => 0
      "reference" => null
      "utm" => ""
      "updates" => []
    ]

Update based on conversation in comments with @MaartenDev

I want to append the $model->steps->steps to $model->steps when the model gets called. As I'm updating database tables to split certain data into tables and want to keep the structure of the data the same when calling the model.

e.g. if you was using getUserCountAttribute you can easily return just the number by doing hasMany()->Count().

So i'm wanting to append the steps array to the steps property when the model is called.


Solution

  • Would using a custom getter that uses the steps relation be an option? You could preload the values using $appends.

    class Model {
        protected $appends = ["steps"];
        protected $hidden = ['stepsRelation']
    
        public function getStepsAttribute()
        {
            return $this->stepsRelation->steps;
        }
        
        private function stepsRelation() {
            return $this->hasOne("App\Compares\ComparesSteps", "compare_id", "id");
        }
    }
    

    Checkout the Laravel docs on appends.