It was food for thought about the flexibility granular interfaces provide, and choosing the right abstraction at the right time. First, we’ll create a course object with a Purchasable interface to indicate it can be purchased, and a Registrable interface to indicate a registration should be created after the customer has paid.
class Bundle implements Product, CreatesRegistrations public function __construct( public array $courses public function createsRegistrationsFor(): array return $this->courses; In our checkout code, we add another check for the new interface.
If we want to know wether a product will create a registration, we have to check for both the Registrable and CreatesRegistrations interfaces.
class Course implements Product, Registrable, CreatesRegistrations public function providesRegistrationsFor(): array return [$this]; And we can revert our checkout code is back to one createRegistration call.