zend-frameworkzend-framework2zend-controller

ZF2: change view script from controller factory


In ZF2, I have 2 helpers. A helper has a form, and a mapper to handle the database interaction. I pass these helpers to the controller using a controller factory. The controller handles phones and addresses of a party that is either a person or an organization. Because a party is a person or an organization, it has different data, so the controller factory also passes the object: PersonObject or OrganizationObject with the party-specific data into the controller.

The 2 helpers are the same for both party types. But in the view script, I want to show the party-specific data, and here is my problem: I need to change the view script based on the object that the controller factory passes to the controller. I thought of having two different controllers, but it's an overkill: the view script is 90% the same, except for that 10% party-specific info that comes from the database into the party object.

How to change the view script from the controller factory? By changing here, I mean slightly different html layout with the party specific data.

EDIT:

@Saeven suggested posting some code. At the moment, I decided to create ViewModel in the controller factory, prepare it accordingly, and inject it into the controller. But I'm not sure it's a good.

The Helper:

class ContactMechanismRegistrationViewHelper extends AbstractRegistrationViewHelper
{
    public function __construct(
        FormInterface $form,
        ContactMechanismMapperInterface $contactMechanismMapper
    ) {
        $this->form = $form;
        $this->mapper = $contactMechanismMapper;
    }

    public function saveToDb()
    {
        $this->mapper->save(
            $this->form->get('contactMechanismFieldset')->getObject(),
            $this->form->get('partyFieldset')->getObject()
        );
    }
}

The helper factory:

class ContactMechanismRegistrationViewHelperFactory implements FactoryInterface, MutableCreationOptionsInterface
{
    use MutableCreationOptionsTrait;

    public function createService(ServiceLocatorInterface $serviceLocator)
    {
        $serviceManager = $serviceLocator->getServiceLocator();
        $formElementManager = $serviceManager->get('FormElementManager');

        if (in_array('phone', $this->creationOptions)) {
            return new ContactMechanismRegistrationViewHelper(
                $formElementManager
                    ->get('Parties\Forms\Forms\ContactMechanisms\PhoneRegistrationForm'),
                $serviceManager
                    ->get('Parties\Mappers\ContactMechanisms\PhoneMapper')
            );
        } elseif (in_array('address', $this->creationOptions)) {
            return new ContactMechanismRegistrationViewHelper(
                $formElementManager
                    ->get('Parties\Forms\Forms\ContactMechanisms\AddressRegistrationForm'),
                $serviceManager
                    ->get('Parties\Mappers\ContactMechanisms\AddressMapper')
            );
        } else {
            throw new ServiceNotCreatedException('wrong option type specified');
        }
    }
}

Controller that uses the helper:

class PartyDetailsController extends AbstractActionController
{
    protected $phoneViewHelper;
    protected $addressViewHelper;
    protected $partyViewModel;

    public function __construct(
        ContactMechanismRegistrationViewHelper $phoneViewHelper,
        ContactMechanismRegistrationViewHelper $addressViewHelper,
        ModelInterface $viewModel
    )
    {
        $this->phoneViewHelper = $phoneViewHelper;
        $this->addressViewHelper = $addressViewHelper;
        $this->viewModel = $viewModel;
    }

    public function indexAction()
    {
        $request = $this->getRequest();
        if ($request->isPost()) {
            $viewHelperForFormSubmission = $this->getViewHelperForFormSubmission(
                $request->getPost('submitButton')
            );
            $viewHelperForFormSubmission->getForm()->setData($request->getPost());
            $viewHelperForFormSubmission->getForm()->setIsSubmitted(true);
            if ($viewHelperForFormSubmission->getForm()->isValid()) {
                try {
                    $viewHelperForFormSubmission->saveToDb();
                    $viewHelperForFormSubmission->getForm()->resetForm();
                } catch (\Exception $e) {
                    die($e->getMessage());
                }
            } else {
                $viewHelperForFormSubmission->getForm()->highlightInvalidElements();
            }
        }

        return $this->viewModel->setVariables([
            'phoneForm' => $this->phoneViewHelper->getForm(),
            'addressForm' => $this->addressViewHelper->getForm(),
        ]);
    }

    protected function getViewHelperForFormSubmission($submitValue)
    {
        if ($submitValue == 'phone') {
            return $this->phoneViewHelper;
        } elseif ($submitValue == 'address') {
            return $this->addressViewHelper;
        } else {
            throw new \Exception('invalid submit argument');
        }
    }
}

Solution

  • After revisiting my question a little less than a year later, decided to share my experience, perhaps it would be of use to someone.

    I went for different view scripts depending on the party type. What I did is I created a factory that creates an instance of a view model based on the type of party. It's absolutely OK to inject the view model from the controller factory into the controller. In fact, if you create a factory for the view model, you can reuse it as a part of some other module's view model by attaching it as a child of the main ViewModel. It is sometimes very useful if you need cross-module interaction.

    I found out that the idea of FormViewHelpers isn't really great in my case because it adds unnecessary complexity and hierarchy because all what my FormViewHelpers did was to pass the call straight to the service or mapper. Just dependency-injected service (or mapper) and form into the controller did just fine for me. FormViewHelpers, are in principle not a bad idea IF(!) there's quite some logic to be put in.

    Ah, and by the way, there's a great article by @yourcommonsense on www.phpdelusions.net that describes (im)proper use of try and catch blocks. It's main message is that use try... catch... when you have a certain scenario for recovering the error. Otherwise PHP itself is quite good with error reporting.

    That's it. Good luck to someone who is in my previous shoes. :-)