Search code examples
phpsubclassfactory-patternsuperclass

PHP Call Superclass Factory Method from Subclass Factory Method


I am writing a php app with subclasses and since I want to have multiple ways to create an object I am doing different factory methods instead of multiple constructors.

I have a User with factory methods

User::from_id
User::from_netid

I have several subclasses of User. I was previously calling the parent superconstructor, but when I switched to the factory method that constructor didn't exist.

I have Student, a subclass of User. To get it to work, I had to duplicate almost all of my superclass factory code in User::from_id to load_by_id, since in this situation the instance already existed:

// In Student.php - Student extends User
public static function from_id_and_course($id, $course){
    $instance = new self();
    $instance->load_by_id($id);
    $instance->course = $course;
    ...
}

I want to call the superclass factory method from the subclass as a starting point, and then continue to add the other fields. Something like this...

$instance = User::from_id($id);

or

$instance = Student::from_id($id);

but in these cases it gives me a User object, and I need a Student object. The only way I could accomplish this is by doing $instance = new self().

How can I call the superclass factory method from the subclass as a starting point to create a new subclass factory method?


Solution

  • Your problem is this:

    $instance = new self();
    

    self refers to the class where the method is defined, not the caller:

    • When Student::from_id() is called, if it doesn't exist, it falls back to User::from_id().
    • In User::from_id(), self refers to User, not Student.

    You'd have to use late-static bindings:

    $instance = new static();
    

    However, like I always do, I'd highly recommend against it. You're better off using the object scope than the static scope. It's easier to extend, to fake or mock and incidentally, to test.

    There's nothing wrong with:

    $user = new User;
    $user->from_id($id);
    
    $student = new Student;
    $student->from_id($id);
    

    ...it's actually better.