I'm working on a Laravel project with a few models. My main model Product has a few many to many relationships with some secondary models on the app. I created the required methods to setup the relationships on the respective models, the thing is, that now I need a clean way to add or remove the related secondary models. Having my Product defined as:
class Product extends Model
{
public function equipments()
{
return $this->belongsToMany('App\Equipment', 'product_has_equipment');
}
public function rawmaterials()
{
return $this->belongsToMany('App\RawMaterial', 'product_has_raw_material')->withPivot('composition');
}
public function indirectcosts()
{
return $this->belongsToMany('App\IndirectCost', 'product_has_indirect_cost');
}
public function presentations()
{
return $this->belongsToMany('App\Presentation', 'product_has_presentation');
}
}
I tried to do the following:
public function addRelated(string $type, array $items)
{
$this->__call($type, [])->attach($items);
}
I thougth that way it would be possible to just specify a string matching one of the relationships names, and with __call it could be called dinamically, so if I pass 'equipments' as $type on the above function, I hoped the inner call would be:
$this->equipments()->attach($items);
But instead I got the error
This seems strange, given that the "undefined method" is exactly the same as the one adviced by laravel in same page.
Take into account that I DO know that this approach may be insecure code-wise, but this project will be an API running on a local server only to be consumed by an electron app, so security is not my priority in this case.
check the source code of Laravel model
/**
* Handle dynamic method calls into the model.
*
* @param string $method
* @param array $parameters
* @return mixed
*/
public function __call($method, $parameters)
{
if (in_array($method, ['increment', 'decrement'])) {
return $this->$method(...$parameters);
}
return $this->forwardCallTo($this->newQuery(), $method, $parameters);
}
and forwardCall
says
/**
* Forward a method call to the given object.
*
* @param mixed $object
* @param string $method
* @param array $parameters
* @return mixed
*
* @throws \BadMethodCallException
*/
protected function forwardCallTo($object, $method, $parameters)
{
try {
return $object->{$method}(...$parameters);
} catch (Error | BadMethodCallException $e) {
$pattern = '~^Call to undefined method (?P<class>[^:]+)::(?P<method>[^\(]+)\(\)$~';
if (! preg_match($pattern, $e->getMessage(), $matches)) {
throw $e;
}
if ($matches['class'] != get_class($object) ||
$matches['method'] != $method) {
throw $e;
}
static::throwBadMethodCallException($method);
}
}
since the $this->newQuery()
returns a Illuminate\Database\Eloquent\Builder;
,which doesn't have wanted methods