Martin Joiner
Martin Joiner

Reputation: 3657

Eloquent methods use child class instead of parent when called from within child

I think I might have found a quirk of the code that allows Laravel Eloquent model methods to be called statically, but could do with some help.

I have a Model called FormSubmission which extends Eloquent\Model.

namespace App;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Collection;

class FormSubmission extends Model 
{

    /**
     * Should always return a Collection of FormSubmissions
     *
     * @param $formSubmissionIds
     *
     * @return Collection
     */
    protected function getFormSubmissions(array $formSubmissionIds) : Collection
    {
        return FormSubmission::whereIn('id', $formSubmissionIds)->get();
    }

}

I needed a place to put some logic that is unique to only 1 type of FormSubmission. As it is not relevant to all forms I did not want to pollute the FormSubmission model so I have made EmployeeFormSubmission which extends FormSubmission.

namespace App;

class EmployeeFormSubmission extends FormSubmission 
{

    /**
     * 
     */
    public function getTest()
    {
        $employeeFormSubmission = new EmployeeFormSubmission();

        // The resulting Collection will contain instances 
        // of EmployeeFormSubmission (not FormSubmission).
        return $employeeFormSubmission->getFormSubmissions([86]);
    }

}

Eloquent Model -> extended by FormSubmission -> extended by EmployeeFormSubmission

The problem...

The strange behaviour which I am having trouble understanding is that if I call the whereIn() method on FormSubmission from within EmployeeFormSubmission like in the getTest() example above, the resulting Collection will contain instances of EmployeeFormSubmission, even though I am explicitly calling the whereIn() method on FormSubmission model.

The behaviour is the same if I use parent::whereIn()

However, if I change FormSubmission::getFormSubmissions() method to be a public static, it works as expected and returns a Collection of instances of FormSubmission.

It's as if Laravel/PHP is substituting "FormSubmission::" for "self::" when inside a non-static method on FormSubmission.

When non-static, it's holding on to the context in which the code is executed and initialising the items within as instances of that class. But when static, it honours the model name referenced in the line of code.

Please help I am a bit stumped.

Upvotes: 1

Views: 421

Answers (1)

Jonas Staudenmeir
Jonas Staudenmeir

Reputation: 25906

The reason is that calls to undefined static methods like FormSubmission::whereIn() don't get handled by __callStatic(), but by __call() (PHP is handling incorrectly my static call).

This works:

return FormSubmission::query()->whereIn('id', $formSubmissionIds)->get();

Upvotes: 1

Related Questions