hretic hretic - 4 months ago 17
PHP Question

findOrNew returns an array instead of object when condition is not on id

i have 2 tables

users
and
user_meta


in my
users
model i have

function meta() {
return $this->hasOne('App\userMeta');
}


in my controller when i want to save user and user meta

function store( Request $request , $id = 0 ) {
$user = User::findOrNew($id);
$user->save() ;

$meta = UserMeta::findOrNew(['user_id'=>$id]);
$user->meta()->save($meta);
}


when im trying to save meta i get this error


Argument 1 passed to
Illuminate\Database\Eloquent\Relations\HasOneOrMany::save() must be
an instance of Illuminate\Database\Eloquent\Model,


here is the problem
UserMeta::findOrNew(['user_id'=>$id])
returns an array instead of object ... which is weird .. while
User::findOrNew($id)
itself returns and object .... i'm guessing it's becuz of my conditions (its the only difference between 2 query)

right now i have to do something like this

$meta = UserMeta::findOrNew(['user_id'=>$id]);
if(is_array($meta)) {
$user->meta()->save($meta[0]);
} else {
$user->meta()->save($meta);
}


which i dont like and looks wrong !

Answer

The function findOrNew() is probably not what your are looking for. It is intended to be used as follows:

// the array in the second parameter specifies which columns are 
// included in the resulting model (if found)
// it is NOT used as a WHERE clause
$meta = UserMeta::findOrNew($id, ['user_id'=>$id]);

What you are looking for:

// adds where clause (returns new model if it does not exist)
$meta = UserMeta::firstOrNew(['user_id' => $id]);

// same as above but persists new model to database
$meta = UserMeta::firstOrCreate(['user_id' => $id]);

Reason why firstOrNew() returns no single model in your case:

This is the implementation of findOrNew Illuminate\Database\Eloquent\Builder.php :

public function findOrNew($id, $columns = ['*'])
{
    if (! is_null($model = $this->find($id, $columns))) {
        return $model;
    }

    return $this->model->newInstance();
}

As you can see, the first parameter is not intended for the columns.

If you have a look at the find method in $this->find($id, $columns) you will see:

public function find($id, $columns = ['*'])
{
    if (is_array($id)) {
        return $this->findMany($id, $columns);
    }

    $this->query->where($this->model->getQualifiedKeyName(), '=', $id);

    return $this->first($columns);
}

As your passed an array as $id the result of $this->findMany() is returned, which is a Collection.