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