Home > Software engineering >  App\Models\ must return a relationship instance
App\Models\ must return a relationship instance

Time:09-24

I am trying to run some dynamic method calls based on the value of a database field. Some context: I have a model Anniversary and I want to display all upcoming anniversaries within the next x days. An anniversary has a date and a frequency. For example, monthly, quarterly, etc. Based on the frequency, I want to check for each anniversary if it is upcoming.

Here is my code so far:

$anniversaries = auth()->user()->anniversaries()->get();

$test = $anniversaries->filter(function ($anniversary) {
    $method = Str::of($anniversary->frequency)->camel();
    return ${$anniversary->$method}() == true;
});

dd($test);

The above works, when in the actual method I dd() something. But when returning true or false, I get the error:

App\Models\Anniversary::monthly must return a relationship instance

And in my model I just have a few methods like below, for testing:

public function monthly()
{
    return true;
}

public function quarterly()
{
    return false;
}

My only question is, I want to understand why I am getting this error and ofcourse any pointers in the right direction to get what I want to work. Thanks!

CodePudding user response:

The following line creates an Illuminate\Support\Str object instead of a string. This causes the Method name must be a string error.

$method = Str::of($anniversary->frequency)->camel();

You can fix this by manually casting it to a string and invoking it directly:

$test = $anniversaries->filter(function ($anniversary) {
    $method = (string) (Str::of($anniversary->frequency)->camel());
    return $anniversary->$method() == true;
});

CodePudding user response:

Throwing in my 2 cents for this as well. The Str::of(), which are "Fluent Strings" added in Laravel 7.x return an instance of Stringable:

https://laravel.com/api/8.x/Illuminate/Support/Stringable.html

For example:

dd(Str::of('monthly')->camel());
Illuminate\Support\Stringable {#3444
  value: "monthly"
}

To get the value of this, as a string and not an object, you can cast it (as shown in MaartenDev's answer), or call the __toString() method:

dd(Str::of('monthly')->camel()->__toString());
"monthly"

In your code example, that would simply be:

$method = Str::of($anniversary->frequency)->camel()->__toString();
return $anniversary->{$method}() == true;

Alternatively, you can just use the Str::camel() function to bypass this Stringable class:

$method = Str::camel($anniversary->frequency);
return $anniversary->{$method}() == true;

https://laravel.com/docs/8.x/helpers#method-camel-case

Hope that helps clear up some confusion

  • Related