Laravel hasMany method not working

2019-05-14 11:37发布

问题:

I have 2 tables, users and websites. One user can have multiple websites assigned to it.

public function websites() {
    return $this->hasMany('App\Models\Website');
}

The error I get is:

Object of class Illuminate\Database\Eloquent\Relations\HasMany could not be converted to string

If I try to output it as an array using print_r I get hundreds of lines of array which I'm assuming is caused by an error somewhere.

Also, why do I have to use App\Models\Website in the hasMany method if I specified at the top of User model use App\Models\Website;?

If I use just Website in the method, I get class not found error:

Class 'Website' not found

Not a big deal, but it would be much cleaner not to have to use the full namespace path.

The code in the controller:

<?php namespace App\Http\Controllers;

use DB;
use App\User;

class DashboardController extends Controller {

    /*------------------------------------------------------------------
        Dashboard page
    -------------------------------------------------------------------*/
    public function show() {

        //$user = auth()->user();
        $user = User::find(5);

        return $user->websites();

        //return view('dashboard');
    }

};

回答1:

The websites() function itself is not causing the error. The error is coming from wherever you are using the function.

The hasMany() function returns an Illuminate\Database\Eloquent\Relations\HasMany object. So, when you call $user->websites(), this will return the HasMany relationship object. Wherever you are using this function, you are then trying to convert that result to a string, which is the error you're seeing.

For example:

$user = User::find(1);
echo $user->websites();

In this example, if you try to echo the response from the websites() method, you will get the "could not be converted to string" error.

If this doesn't help you enough to resolve the issue, you will need to post the code that is calling the websites() function for anyone to be able to help further.

Edit

"The controller doesn't echo the websites() method, it just returns it." Anything returned by a controller method is converted to a string. This is your issue.

Instead of returning the HasMany object, you need to return the results of the HasMany object. There are two main options: call get() on the relationship to actually get the results and return that, or use the relationship attribute instead of the method.

Option 1:

public function show() {
    $user = User::find(5);

    // call "get()" to get the results
    return $user->websites()->get();
}

Option 2:

public function show() {
    $user = User::find(5);

    // use lazy loaded attribute instead of using relationship method
    return $user->websites;
}

Both of the options above will return the same thing: a Collection of the Website objects. Since you're returning this from the controller, Laravel will convert this to a string, so your output will be a json array of json Website objects.



回答2:

I have a couple of possible solutions:

First, do you have the inverse relationship defined?

User.php

public function websites() {
    return $this->hasMany('App\Models\Website');
}

Website.php

public function user()
{
    return $this->belongsTo('App\Models\User');
}

Second, have you run composer dump-autoload in terminal.

Note if you want a cleaner interface you can this if you are using >= php 5.5

User.php

public function websites() {
    return $this->hasMany(App\Models\Website::class);
}

or

use App\Models\Website;

public function user() {
    return $this->hasMany(Website::class);
}

Edit 1

Do your models have the right namespace?

namespace App\Models;

Edit 2

You could try 2 more things.

<?php namespace App\Http\Controllers;

use DB;
use App\User;

class DashboardController extends Controller {

    public function show() {
        $user = User::find(5);
        return $user->websites()->toArray();
    }

    // this one will query the database only once.

    public function newShow() {
        $user = User::with('websites')->find(5);
        return $user->websites->toArray();
    }

};