0
votes

I have 2 Doctrine entities (Environment and EnvironmentConfig). They have a bi-directional OneToOne relationship.

Each entity has their own Fieldset so that re-use is easy.

To create an Environment it can also have an EnvironmentConfig, however not required. To allow them to be made at the same time I have an EnvironmentForm that uses the EnvironmentFieldset and the EnvironmentConfigFieldset.

The Form renders properly. However, it saves the Environment but not the EnvironmentConfig.

Where is it that I've gone wrong in setting this up and how to fix it?

Code below, leaving out getters/setters, would be too much.

Entities

// abstract class AbstractEntity has $id + getters/setters.

class Environment extends AbstractEntity
{
    /**
     * @var string
     * @ORM\Column(name="name", type="string", length=255, nullable=false)
     */
    protected $name;

    /**
     * @var EnvironmentConfig
     * @ORM\OneToOne(targetEntity="Environment\Entity\EnvironmentConfig", inversedBy="environment")
     */
    protected $config;

    /**
     * @var EnvironmentScript
     * @ORM\OneToOne(targetEntity="EnvironmentScript")
     */
    protected $script;

    //Getters/setters
}

class EnvironmentConfig extends AbstractEntity
{
    /**
     * @var string
     * @ORM\Column(name="name", type="string", length=255, nullable=false)
     */
    protected $name;

    /**
     * @var Environment
     * @ORM\OneToOne(targetEntity="Environment\Entity\Environment", mappedBy="config")
     */
    protected $environment;

    //Getters/setters
}

Fieldsets

class EnvironmentFieldset extends AbstractFieldset
{
    /**
     * {@inheritdoc}
     */
    public function init()
    {
        //Loads id element validation
        parent::init();

        $this->add([
            'name' => 'name',
            'type' => Text::class,
            'options' => [
                'label' => _('Name'),
                'label_attributes' => [
                    'class' => 'col-xs-2 col-form-label',
                ],
            ],
            'attributes' => [
                'id' => 'name',
                'class' => 'form-control'
            ],
        ]);

        $this->add([
            'name' => 'environment_config',
            'type' => EnvironmentConfigFieldset::class,
            'options' => [
                'use_as_base_fieldset' => false,
            ],
        ]);

        $this->add([
            'type' => ObjectSelect::class,
            'name' => 'environment_script',
            'options' => [
                'object_manager' => $this->getEntityManager(),
                'target_class'   => EnvironmentScript::class,
                'property'       => 'id',
                'display_empty_item' => true,
                'empty_item_label'   => '---',
                'label_generator' => function ($targetEntity) {
                    return $targetEntity->getId() . ' - ' . $targetEntity->getName();
                },
            ],
        ]);
    }
}

class EnvironmentConfigFieldset extends AbstractFieldset
{
    /**
     * {@inheritdoc}
     */
    public function init()
    {
        //Loads id element validation
        parent::init();

        $this->add([
            'name' => 'name',
            'type' => Text::class,
            'options' => [
                'label' => _('Name'),
                'label_attributes' => [
                    'class' => 'col-xs-2 col-form-label',
                ],
            ],
            'attributes' => [
                'id' => 'name',
                'class' => 'form-control'
            ],
        ]);

    }
}

Form

class EnvironmentForm extends AbstractForm
{
    /**
     * EnvironmentForm constructor.
     * @param null $name
     * @param array $options
     */
    public function __construct($name = null, array $options)
    {
        //Also adds CSRF
        parent::__construct($name, $options);
    }

    /**
     * {@inheritdoc}
     */
    public function init()
    {
        //Call parent initializer. Adds submit button.
        parent::init();

        $this->add([
            'name' => 'environment',
            'type' => EnvironmentFieldset::class,
            'options' => [
                'use_as_base_fieldset' => true,
            ],
        ]);
    }
}

Edit: added debug data and AddAction() Debug of form and request data

Above debugging done on the persist() line in the function below.

public function addAction($formName, array $formOptions = null, $route, array $routeParams = [])
{
    if (!$this->formElementManager instanceof FormElementManagerV2Polyfill) {

        throw new InvalidArgumentException('Dependency FormElementManagerV2Polyfill not set. Please check Factory for this function.');
    }

    if (!class_exists($formName)) {

        throw new ClassNotFoundException('Given class could not be found. Does it exist?');
    }

    /** @var AbstractForm $form */
    $form = $this->getFormElementManager()->get($formName, (is_null($formOptions) ? [] : $formOptions));

    /** @var Request $request */
    $request = $this->getRequest();
    if ($request->isPost()) {
        $form->setData($request->getPost());

        if ($form->isValid()) {
            $entity = $form->getObject();

            $this->getEntityService()->getEntityManager()->persist($entity);
            $this->getEntityService()->getEntityManager()->flush();

            $this->flashMessenger()->addSuccessMessage(
                _('Successfully created object.')
            );

            $this->redirectToRoute($route, $this->getRouteParams($entity, $routeParams));
        }
    }

    return [
        'form' => $form,
        'validationMessages' => $form->getMessages() ?: '',
    ];
}
1

1 Answers

1
votes

You created a field called 'environment_config' but in class Environment you called protected $config;. Both names must be the same. Same problem for 'environment_script' field and $script attribute.

Another thing, you want to create a EnvironmentConfig dynamically so you must add in $config annotation a cascade option to be able to create a $config from Environment:

/**
 * @var EnvironmentConfig
 * @ORM\OneToOne(targetEntity="Environment\Entity\EnvironmentConfig", inversedBy="environment", cascade={"persist"})
 */
protected $config;