3

Is it possible to replace null with an empty array when no relation is found?

E.g. The customer has contacts and contracts but one of the contract has no web.

$customers = Customer::with('contacts', 'contracts.web')
        ->orderBy('company')->orderBy('prename')->get();

The result would be as following...

2 => array:21 [
  "id" => 1
  "contacts" => array:2 [
    0 => array:12 [
      "id" => 1
      "customer_id" => 1
    ]
    1 => array:12 [
      "id" => 2
      "customer_id" => 1
    ]
  ]
  "contracts" => array:2 [
    0 => array:9 [
      "id" => 1
      "customer_id" => 1
      "web" => array:7 [
        "id" => 1
        "contract_id" => 1
      ]
    ]
    1 => array:9 [
      "id" => 2
      "customer_id" => 1
      "web" => null // should be replaced with []
    ]
  ]
]

As I read in the docs (Constraining Eager Loads), it's only possible to manipulate the query with constraining eager loads.

UPDATE

Contract class

class Contract extends Model
{
    public function web()
    {
        return $this->hasOne(Web::class);
    }
}
Ilario Engler
  • 2,419
  • 2
  • 27
  • 40
  • maybe this helps you: http://stackoverflow.com/a/40192093/1595977 – Edwin May 03 '17 at 08:12
  • @Edwin thanks for your help but your post only explains how eager loading works. But thats not my question... my question is, would it be possible not to return null if no relation is found but either an empty array – Ilario Engler May 03 '17 at 08:16
  • try to use `when` and change every `web => null` to `web => array()` or just manipulate the result. – Edwin May 03 '17 at 08:44
  • @Edwin thanks I will give it a shot. Just manipulate the result is not so performant, the collection could grow really big and I have a lot of attributes which I striped out here at stackoverflow – Ilario Engler May 03 '17 at 08:52

2 Answers2

11

For further readers here's an explanation how to solve this kind of problem.

Laravel returns an empty array if no records are found on a hasMany relation. If a hasOne relation is implemented, null will be returned.

So if you need an array also if no record is found on a hasOne relation, you need to do the following.

class Contract extends Model
{

    public function web()
    {
        return $this->hasOne(Web::class)
            ->withDefault(function () {
                return new Web();
            });
    }
}

As implemented like this its not possible to just return an empty array. Why this isn't possible, check out this issue on Laravel GitHub Issue Tracker.

There is existing code that depends on the result of any Eloquent relationship to either be null, a Model instance, or a Collection of Model instances. However, the current functionality of the withDefault() method opens up the potential for returning an object that is not one of those three expected values.

If you return a new \stdClass; or an empty array, an empty instance of web is returned. To get an empty array just instanciate a new Object of the relation class. In my case new Web();.

Ilario Engler
  • 2,419
  • 2
  • 27
  • 40
-1

Your relationship method should be the one handeling this since it's the first place you can fix this

I checked this so it returns an array when the variable is null.

public class Contracts{

  public function web(){
    $collection = $this->hasMany('App\Web');
    return  $collection ? $collection : [];
  }

}
  • yeaa it's clear that I could do it this way. You could do it also a lot easier. You don't need to map all values because there are plenty. You could only do the check on the releation which is null, because there is not only web which could be null. I was just wondering if there would be a more performant solution. – Ilario Engler May 03 '17 at 11:37
  • @llario Engler I edited the answer, this should work 100% – Road2PreSchool May 03 '17 at 12:52
  • I already tried with mutators, sry didn't mentioned in my question. This not work. I think mutators work only for attributes not for relations. Correct me if I am wrong. Thanks for your help! – Ilario Engler May 03 '17 at 13:38
  • @llario Engler No problem, it was fun. Okay this now works, I tested it. Is this what you're searching for ? – Road2PreSchool May 03 '17 at 14:10
  • 1
    Laravel returns on hasMany automatically an array if there are no records found. So it's not neccessary to do the check. I use a hasOne relation and there Laravel returns null. It's just logically that null is returned and not an array, because there will be only one entry. So I figured it out how to achieve it. Will post an answer in a minute. – Ilario Engler May 03 '17 at 14:22