inheritancecakephpcontrollercakephp-4.x

Problem with Modelless Form inheritance in Cakephp4


I'm building a CommerceManager plugin to manage purchases for online shops.

CommerceManager plugin side

To deal with the billing/delivery step I have created a Modelless Form DeliveryForm which is useful for :

// in plugin/CommerceManager/src/Form/DeliveryForm.php
class DeliveryForm extends Form
{
    /**
     * List of countries available used for <select> in delivery form
     * 
     * @var array
     */
    public $countries = [
        'AL' => ['name' => 'ALBANIA', 'code' => '008'],
        // [...]
        'FR' => ['name' => 'FRANCE', 'code' => '250'],
        // [...]
    ];

    public function validationDefault(Validator $validator): Validator
    {
        // validates form data
    }

    public function read()
    {
        $session = Session::create();
        return $session->read('Buyer') ?? [];
    }

    protected function _execute(array $data): bool
    {
        $session = Session::create();
        $session->write('Buyer', $data);

        return true;
    }
}

My CommerceManager plugin has a CheckoutController which uses the plugin's DeliveryForm :

// in plugin/CommerceManager/src/Controller/CheckoutController.php
use CommerceManager\Form\DeliveryForm; // use plugin's DeliveryForm

class CheckoutController extends AppController
{
    public function delivery()
    {
        $deliveryForm = new DeliveryForm();
        if ($this->request->is('post')) {
            if ($deliveryForm->execute($this->request->getData())) {
                return $this->redirect(['action' => 'confirm']);
            }
        }
        else {
            $deliveryForm->setData($deliveryForm->read() ?? []);
        }

        $this->set(compact('deliveryForm'));
    }

    // [...]
}

App side

I would like to make a subclass of DeliveryForm for overriding the list of countries available for delivery.

To do that I've created a subclass of CheckoutController which only inherits the actions of its parent and uses App's DeliveryForm.

// in src/Controller/CheckoutController.php
use CommerceManager\Controller\CheckoutController as BaseCheckoutController;
use App\Form\DeliveryForm; // use App's DeliveryForm 

class CheckoutController extends BaseCheckoutController
{
    // inherits actions of CommerceManager.CheckoutController
}

// in src/Form/DeliveryForm.php
use CommerceManager\Form\DeliveryForm as BaseDeliveryForm;

class DeliveryForm extends BaseDeliveryForm
{
    public $countries = [
        'FR' => ['name' => 'FRANCE', 'code' => '250'] // Only "France" is available
    ];
}

The problem comes from the fact that App still using CommerceManager's DeliveryForm.

I'm totally confused about how to deal with DeliveryForm inheritance...

How can I do for my App's CheckoutController could understand to use App's DeliveryForm subclass and not the plugin's one ?


Solution

  • // in plugin/CommerceManager/src/Controller/CheckoutController.php
    use CommerceManager\Form\DeliveryForm; // use plugin's DeliveryForm
    
    class CheckoutController extends AppController
    {
        /**
         * The class to instanciate
         */
        protected $_classnameDeliveryForm = \CommerceManager\Form\DeliveryForm::class;
    
        /**
         * The instance
         */
        protected $DeliveryForm;
    
        public function initialize(): void
        {
            parent::initialize();
            $this->DeliveryForm = new $this->_classnameDeliveryForm();
        }
    
        public function delivery()
        {
            if ($this->request->is('post')) {
                if ($this->DeliveryForm->execute($this->request->getData())) {
                    return $this->redirect(['action' => 'confirm']);
                }
            }
            else {
                $this->DeliveryForm->setData($this->DeliveryForm->read() ?? []);
            }
    
            $this->set('deliveryForm', $this->DeliveryForm);
        }
    
        // [...]
    }