Related
So in summary I'm trying to override a third party module to include an extra dropdown menu on the admin Ui
Heres the third party file at
Prince/Productattach/Block/Adminhtml/Productattach/Edit/Tab/Main.php
namespace Prince\Productattach\Block\Adminhtml\Productattach\Edit\Tab;
/**
*Class Main
*#package Prince\Productattach\Block\Adminhtml\Productattach\Edit\Tab
*/
class Main extends \Magento\Backend\Block\Widget\Form\Generic implements
\Magento\Backend\Block\Widget\Tab\TabInterface
{
/**
* #var \Magento\Store\Model\System\Store
*/
private $systemStore;
/**
* #var \Magento\Customer\Model\ResourceModel\Group\Collection
*/
private $customerCollection;
/**
* Main constructor.
* #param \Magento\Backend\Block\Template\Context $context
* #param \Magento\Framework\Registry $registry
* #param \Magento\Framework\Data\FormFactory $formFactory
* #param \Magento\Store\Model\System\Store $systemStore
* #param \Magento\Customer\Model\ResourceModel\Group\Collection $customerCollection
* #param array $data
*/
public function __construct(
\Magento\Backend\Block\Template\Context $context,
\Magento\Framework\Registry $registry,
\Magento\Framework\Data\FormFactory $formFactory,
\Magento\Store\Model\System\Store $systemStore,
\Magento\Customer\Model\ResourceModel\Group\Collection $customerCollection,
array $data = []
) {
$this->_systemStore = $systemStore;
$this->_customerCollection = $customerCollection;
parent::__construct($context, $registry, $formFactory, $data);
}
/**
* Prepare form
*
* #return $this
*/
public function _prepareForm()
{
$model = $this->_coreRegistry->registry('productattach');
/*
* Checking if user have permissions to save information
*/
if ($this->_isAllowedAction('Prince_Productattach::save')) {
$isElementDisabled = false;
} else {
$isElementDisabled = true;
}
/** #var \Magento\Framework\Data\Form $form */
$form = $this->_formFactory->create();
$form->setHtmlIdPrefix('productattach_main_');
$fieldset = $form->addFieldset(
'base_fieldset',
['legend' => __('Attachment Information')]
);
$customerGroup = $this->customerCollection->toOptionArray();
if ($model->getId()) {
$fieldset->addField('productattach_id', 'hidden', ['name' => 'productattach_id']);
}
$fieldset->addField(
'name',
'text',
[
'name' => 'name',
'label' => __('Attachment Name'),
'title' => __('Attachment Name'),
'required' => true,
'disabled' => $isElementDisabled
]
);
$fieldset->addField(
'description',
'textarea',
[
'name' => 'description',
'label' => __('Description'),
'title' => __('Description'),
'disabled' => $isElementDisabled
]
);
$fieldset->addField(
'files',
'file',
[
'name' => 'file',
'label' => __('File'),
'title' => __('File'),
'required' => false,
'note' => 'File size must be less than 2 Mb.', // TODO: show ACCTUAL file-size
'disabled' => $isElementDisabled
]
);
$fieldset->addType(
'uploadedfile',
\Prince\Productattach\Block\Adminhtml\Productattach\Renderer\FileIconAdmin::class
);
$fieldset->addField(
'file',
'uploadedfile',
[
'name' => 'uploadedfile',
'label' => __('Uploaded File'),
'title' => __('Uploaded File'),
]
);
$fieldset->addField(
'url',
'text',
[
'name' => 'url',
'label' => __('URL'),
'title' => __('URL'),
'required' => false,
'disabled' => $isElementDisabled,
'note' => 'Upload file or Enter url'
]
);
$fieldset->addField(
'customer_group',
'multiselect',
[
'name' => 'customer_group[]',
'label' => __('Customer Group'),
'title' => __('Customer Group'),
'required' => true,
'value' => [0,1,2,3], // todo: preselect ALL customer groups, not just 0-3
'values' => $customerGroup,
'disabled' => $isElementDisabled
]
);
$fieldset->addField(
'store',
'multiselect',
[
'name' => 'store[]',
'label' => __('Store'),
'title' => __('Store'),
'required' => true,
'value' => [0],
'values' => $this->systemStore->getStoreValuesForForm(false, true),
'disabled' => $isElementDisabled
]
);
$fieldset->addField(
'active',
'select',
[
'name' => 'active',
'label' => __('Active'),
'title' => __('Active'),
'value' => 1,
'options' => ['1' => __('Yes'), '0' => __('No')],
'disabled' => $isElementDisabled
]
);
$this->_eventManager->dispatch('adminhtml_productattach_edit_tab_main_prepare_form', ['form' => $form]);
if ($model->getId()) {
$form->setValues($model->getData());
}
$this->setForm($form);
return parent::_prepareForm();
}
/**
* Prepare label for tab
*
* #return string
*/
public function getTabLabel()
{
return __('Attachment Information');
}
/**
* Prepare title for tab
*
* #return string
*/
public function getTabTitle()
{
return __('Attachment Information');
}
/**
* {#inheritdoc}
*/
public function canShowTab()
{
return true;
}
/**
* {#inheritdoc}
*/
public function isHidden()
{
return false;
}
/**
* Check permission for passed action
*
* #param string $resourceId
* #return bool
*/
public function _isAllowedAction($resourceId)
{
return $this->_authorization->isAllowed($resourceId);
}
}
I have my module setup and my di.xml configured with the usual plus below to override the class.
<preference for="Prince\Productattach\Block\Adminhtml\Productattach\Edit\Tab" type="Vendor\Filecategory\Block\Adminhtml\Productattach\Edit\Tab"/>
then and exact replica of the class with namespace and my extra field added at
Vendor/Filecategory/Block/Adminhtml/Productattach/Edit/Tab/Main.php
namespace Vendor\Filecategory\Block\Adminhtml\Productattach\Edit\Tab;
use \Prince\Productattach\Block\Adminhtml\Productattach\Edit\Tab\Main as Main;
class MainExt extends Main
{
/**
* #var \Magento\Store\Model\System\Store
*/
private $systemStore;
/**
* #var \Magento\Customer\Model\ResourceModel\Group\Collection
*/
private $customerCollection;
/**
* Main constructor.
* #param \Magento\Backend\Block\Template\Context $context
* #param \Magento\Framework\Registry $registry
* #param \Magento\Framework\Data\FormFactory $formFactory
* #param \Magento\Store\Model\System\Store $systemStore
* #param \Magento\Customer\Model\ResourceModel\Group\Collection $customerCollection
* #param array $data
*/
public function __construct(
\Magento\Backend\Block\Template\Context $context,
\Magento\Framework\Registry $registry,
\Magento\Framework\Data\FormFactory $formFactory,
\Magento\Store\Model\System\Store $systemStore,
\Magento\Customer\Model\ResourceModel\Group\Collection $customerCollection,
array $data = []
) {
$this->systemStore = $systemStore;
$this->customerCollection = $customerCollection;
parent::__construct($context, $registry, $formFactory, $data, $systemStore);
}
/**
* Prepare form
*
* #return $this
*/
public function _prepareForm()
{
$model = $this->_coreRegistry->registry('productattach');
/*
* Checking if user have permissions to save information
*/
if ($this->_isAllowedAction('Prince_Productattach::save')) {
$isElementDisabled = false;
} else {
$isElementDisabled = true;
}
/** #var \Magento\Framework\Data\Form $form */
$form = $this->_formFactory->create();
$form->setHtmlIdPrefix('productattach_main_');
$fieldset = $form->addFieldset(
'base_fieldset',
['legend' => __('Attachment Information')]
);
$customerGroup = $this->customerCollection->toOptionArray();
if ($model->getId()) {
$fieldset->addField('productattach_id', 'hidden', ['name' => 'productattach_id']);
}
$fieldset->addField(
'name',
'text',
[
'name' => 'name',
'label' => __('Attachment Name'),
'title' => __('Attachment Name'),
'required' => true,
'disabled' => $isElementDisabled
]
);
$fieldset->addField(
'Category',
'select',
[
'name' => 'Category',
'label' => __('Category'),
'title' => __('Category'),
'value' => 0,
'options' => ['0' => __('Technical Specification'), '1' => __('Installation Instructions')],
]
);
$fieldset->addField(
'description',
'textarea',
[
'name' => 'description',
'label' => __('Description'),
'title' => __('Description'),
'disabled' => $isElementDisabled
]
);
$fieldset->addField(
'files',
'file',
[
'name' => 'file',
'label' => __('File'),
'title' => __('File'),
'required' => false,
'note' => 'File size must be less than 2 Mb.', // TODO: show ACCTUAL file-size
'disabled' => $isElementDisabled
]
);
$fieldset->addType(
'uploadedfile',
\Prince\Productattach\Block\Adminhtml\Productattach\Renderer\FileIconAdmin::class
);
$fieldset->addField(
'file',
'uploadedfile',
[
'name' => 'uploadedfile',
'label' => __('Uploaded File'),
'title' => __('Uploaded File'),
]
);
$fieldset->addField(
'url',
'text',
[
'name' => 'url',
'label' => __('URL'),
'title' => __('URL'),
'required' => false,
'disabled' => $isElementDisabled,
'note' => 'Upload file or Enter url'
]
);
$fieldset->addField(
'customer_group',
'multiselect',
[
'name' => 'customer_group[]',
'label' => __('Customer Group'),
'title' => __('Customer Group'),
'required' => true,
'value' => [0,1,2,3], // todo: preselect ALL customer groups, not just 0-3
'values' => $customerGroup,
'disabled' => $isElementDisabled
]
);
$fieldset->addField(
'store',
'multiselect',
[
'name' => 'store[]',
'label' => __('Store'),
'title' => __('Store'),
'required' => true,
'value' => [0],
'values' => $this->systemStore->getStoreValuesForForm(false, true),
'disabled' => $isElementDisabled
]
);
$fieldset->addField(
'active',
'select',
[
'name' => 'active',
'label' => __('Active'),
'title' => __('Active'),
'value' => 1,
'options' => ['1' => __('Yes'), '0' => __('No')],
'disabled' => $isElementDisabled
]
);
$this->_eventManager->dispatch('adminhtml_productattach_edit_tab_main_prepare_form', ['form' => $form]);
if ($model->getId()) {
$form->setValues($model->getData());
}
$this->setForm($form);
return parent::_prepareForm();
}
/**
* Prepare label for tab
*
* #return string
*/
public function getTabLabel()
{
return __('Attachment Information');
}
/**
* Prepare title for tab
*
* #return string
*/
public function getTabTitle()
{
return __('Attachment Information');
}
/**
* {#inheritdoc}
*/
public function canShowTab()
{
return true;
}
/**
* {#inheritdoc}
*/
public function isHidden()
{
return false;
}
/**
* Check permission for passed action
*
* #param string $resourceId
* #return bool
*/
public function _isAllowedAction($resourceId)
{
return $this->_authorization->isAllowed($resourceId);
}
}
However I keep getting Error
Incompatible argument type: Required type: \Magento\Store\Model\System\Store. Actual type: array;
I have tried flush cache and reindex but no luck. Please can someone tell me what im doing wrong here? Also happy to listen to any alternative ways of completing the same thing.
Thanks in advance.
I have managed to complete this by creating a plugin class instead of overriding the whole class. I have attached the post I followed for any one interested in adding a form field to n existing form attached to a third part module.
https://magento.stackexchange.com/questions/174209/magento-2-add-new-field-to-magento-user-admin-form
I have an registration form User\UserForm which contains a fieldset User\UserFieldset. In User\UserFieldset I put an field called "passwordVerify" which should be identical with another field in the fieldset called "password".
This works fine.
However, if an admin wants to modify an user account within the Admin\UserForm, which also contains the User\UserFieldset, the field "passwordVerify" in the fieldset User\UserFieldset should be removed. Therefore I call the following within the Admin\UserForm:
$this->get('user')->remove('passwordVerify');
$this->getInputFilter()->get('user')->remove('passwordVerify')
As expected, the form lacks the field "passwordVerify" now.
If I save the form after editing some stuff, my custom filter "PasswordFilter" cannot retrieve the bound object of the fieldset anymore ($this->getOption('object'); returns an User-object) - but all properties of the bound object are nulled. If I use the Admin\UserForm without removing "passwordVerify"-field and "passwordVerify"-inputfilter everything works fine and the bound object is passed to "PasswordFilter" with populated properties (in respect of values, inserted by the user in the Admin\UserForm). The line which breaks everything is $this->getInputFilter()->get('user')->remove('passwordVerify'). So this leads to my assumption, that by removing an inputfilter, the hydrated object gets somehow nulled / emptied. Below are my some excerpts of my code, if needed I can provide more information about factories, etc.
Admin\UserForm:
class UserForm extends Form
{
/**
* #var EntityManager
*/
protected $entityManager = null;
/**
* #var Translator
*/
protected $translator = null;
public function __construct(EntityManager $entityManager, Translator $translator)
{
$this->entityManager = $entityManager;
$this->translator = $translator;
parent::__construct("userForm");
$this->setHydrator(new DoctrineHydrator($entityManager));
}
public function init()
{
// Adding UserFieldset
$this->add(array(
'name' => 'user',
'type' => \User\Form\UserFieldset::class,
'options' => array(
'use_as_base_fieldset' => true,
),
));
$this->get('user')->remove('passwordVerify');
$this->getInputFilter()->get('user')->remove('passwordVerify');
$this->add(array(
'type' => 'Zend\Form\Element\Csrf',
'name' => 'csrf',
));
$this->add(array(
'type' => 'submit',
'name' => 'submit',
'options' => array(
'label' => $this->translator->translate('Btn.submit.user', 'Form')
),
));
}
}
User\UserFieldset:
class UserFieldset extends Fieldset implements InputFilterProviderInterface
{
/**
* #var EntityManager
*/
protected $entityManager = null;
/**
* #var Translator
*/
protected $translator = null;
public function __construct(EntityManager $entityManager, Translator $translator)
{
$this->entityManager = $entityManager;
$this->translator = $translator;
parent::__construct("userFieldset");
$this->setHydrator(new DoctrineHydrator($entityManager))->setObject(new User());
}
public function init()
{
$this->add(array(
'type' => 'text',
'name' => 'firstName',
'options' => array(
'label' => $this->translator->translate('label.firstName', 'Form'),
'label_attributes' => array(
'class' => 'col-sm-3',
),
'column-size' => 'sm-5',
),
'attributes' => array(
'id' => 'firstName',
),
));
/* ... */
$this->add(array(
'type' => 'text',
'name' => 'password',
'options' => array(
'label' => $this->translator->translate('label.password', 'Form'),
'label_attributes' => array(
'class' => 'col-sm-3',
),
'column-size' => 'sm-5',
),
'attributes' => array(
'id' => 'password',
),
));
$this->add(array(
'type' => 'password',
'name' => 'passwordVerify',
'options' => array(
'label_attributes' => array(
'class' => 'col-sm-3 control-label'
),
'label' => $this->translator->translate('label.verifyPassword', 'Form'),
'column-size' => 'sm-8',
),
'attributes' => array(
'class' => 'form-control',
'id' => 'password'),
));
/* ... */
// Adding AddressFieldset
$this->add(array(
'name' => 'address',
'type' => \User\Form\AddressFieldset::class,
));
/* ... */
$this->add(array(
'type' => 'datetime',
'name' => 'created',
'options' => array(
'label' => $this->translator->translate('label.created', 'Form'),
'format' => 'd.m.Y H:i',
'label_attributes' => array(
'class' => 'col-sm-3',
),
'column-size' => 'sm-5',
),
'attributes' => array(
'id' => 'created',
),
));
}
public function getInputFilterSpecification()
{
return array(
'firstName' => array(
'required' => true,
'filters' => array(
array('name' => 'StringTrim'),
array('name' => 'StripTags'),
),
'validators' => array(),
),
/* ... */
'password' => array(
'required' => true,
'filters' => array(
array('name' => 'StringTrim'),
array('name' => 'StripTags'),
[
'name' => PasswordFilter::class,
'options' => [
'object' => $this->getObject(),
'field' => 'password'
]
]
),
'validators' => array(),
),
'passwordVerify' => array(
'required' => true,
'filters' => [
[
'name' => PasswordFilter::class,
'options' => [
'object' => $this->getObject(),
'field' => 'password'
]
]
],
'validators' => array(
array(
'name' => 'StringLength',
'options' => array(
'min' => 6
)
),
array(
'name' => 'Identical',
'options' => array(
'token' => 'password'
)
)
)
),
/* ... */
'created' => array(
'required' => false,
'filters' => array(
array('name' => 'StringTrim'),
),
'validators' => array(
array(
'name' => 'Date',
'options' => array('format' => 'd.m.Y H:i')
),
),
)
);
}
}
PasswordFilter:
class PasswordFilter extends AbstractFilter
{
/** #var EntityManager */
protected $entityManager;
/** #var PasswordInterface */
protected $passwordManager;
/**
* PasswordFilter constructor.
* #param EntityManager $entityManager
* #param PasswordInterface $passwordManager
* #param array $options
*/
public function __construct(EntityManager $entityManager, PasswordInterface $passwordManager, $options = [])
{
$this->entityManager = $entityManager;
$this->passwordManager = $passwordManager;
$this->options = $options;
}
public function filter($value)
{
$object = $this->getOption('object');
$field = $this->getOption('field');
$getter = 'get'.ucfirst($field);
if (!$object || !$field) {
throw new \Exception('Options "object" and "field" are required.');
}
if ($object->getId()) {
$dbObject = $this->entityManager->getRepository(get_class($object))->find($object->getId());
if ($value === $dbObject->{$getter}()) {
return $value;
}
}
// hash password here...
return $this->passwordManager->create($value);
}
private function getOption($option)
{
if (array_key_exists($option, $this->options)) {
return $this->options[$option];
}
return false;
}
}
Any clues? Do I call remove inputfilter of "passwordVerify" to early in the process of instantiation?
I also tested to remove the inputFilter and field after "$this->form->bind($user)" in my controller, which also works. Why it does not work then if I remove it in Admin\UserForm, which is in my opinion the cleaner way of managing the "passwordVerify"-stuff?
If you call $this->getInputFilter(), the InputProviderInterface::getInputSpecification method in your UserForm is being called.
If you did not attached the object already, it cannot retrieve it.
But I dont get why you would even need that. You are hashing the password if the value does not fit to the database value, but obviously the database value seems to be plain text as the input or why would you compare it?
IMHO you just should hash the password, no matter what the current value in your database is.
If you are using doctrine and the password wont change, it wont execute an UPDATE query anyway.
I have a problem with my ZF2 form element (select). When i bind my doctrine entity to this form, all my select options get the selected attribute instead of just the one that should. The entity just got a connected object and the Hydrator is set in the for aswell.
Here is some of my code. Hope i just missed something small.
AddressEntity.php
<?php
namespace Application\Entity;
use Doctrine\ORM\Mapping as ORM;
use ZF2Core\Entity\AbstractEntity;
/**
* #ORM\Entity
* #ORM\Table(name="`address`")
*/
class Address extends AbstractEntity
{
/**
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
* #ORM\Column(type="bigint", options={"unsigned":true})
*/
protected $addressId;
/**
* #ORM\ManyToOne(targetEntity="SNOrganisation\Entity\Organisation", inversedBy="organisationId")
* #ORM\JoinColumn(name="organisationId", referencedColumnName="organisationId", nullable=false)
*/
protected $organisation;
/**
* #ORM\ManyToOne(targetEntity="AddressType")
* #ORM\JoinColumn(name="addressTypeId", referencedColumnName="addressTypeId", nullable=false)
*/
protected $addressType;
/** #ORM\Column(type="string", nullable=true) */
protected $otys;
/** #ORM\Column(type="string") */
protected $address;
/** #ORM\Column(type="string", nullable=true) */
protected $postalcode;
/** #ORM\Column(type="string") */
protected $city;
/** #ORM\Column(type="string", nullable=true) */
protected $email;
/**
* #ORM\ManyToOne(targetEntity="ZF2Country\Entity\Country")
* #ORM\JoinColumn(name="countryId", referencedColumnName="countryId", nullable=false)
*/
protected $country;
/** #ORM\Column(type="datetime") */
protected $created;
/** #ORM\Column(type="datetime", nullable=true) */
protected $deleted;
public function getAddressId()
{
return $this->addressId;
}
public function getOrganisation()
{
return $this->organisation;
}
public function getAddressType()
{
return $this->addressType;
}
public function getOtys()
{
return $this->otys;
}
public function getAddress()
{
return $this->address;
}
public function getPostalcode()
{
return $this->postalcode;
}
public function getCity()
{
return $this->city;
}
public function getEmail()
{
return $this->email;
}
public function getCreated()
{
return $this->created;
}
public function getDeleted()
{
return $this->deleted;
}
public function setAddressId($addressId)
{
$this->addressId = $addressId;
return $this;
}
public function setOrganisation($organisation)
{
$this->organisation = $organisation;
return $this;
}
public function setAddressType($addressType)
{
$this->addressType = $addressType;
return $this;
}
public function setOtys($otys)
{
$this->otys = $otys;
return $this;
}
public function setAddress($address)
{
$this->address = $address;
return $this;
}
public function setPostalcode($postalcode)
{
$this->postalcode = $postalcode;
return $this;
}
public function setCity($city)
{
$this->city = $city;
return $this;
}
public function setEmail($email)
{
$this->email = $email;
return $this;
}
public function setCreated($created)
{
$this->created = $created;
return $this;
}
public function setDeleted($deleted)
{
$this->deleted = $deleted;
return $this;
}
public function getCountry()
{
return $this->country;
}
public function setCountry($country)
{
$this->country = $country;
return $this;
}
}
AddressForm.php
<?php
namespace SNOrganisation\Form;
use Zend\Form\Form;
use DoctrineModule\Stdlib\Hydrator\DoctrineObject as DoctrineHydrator;
use Application\Entity\Address;
use Zend\ServiceManager\ServiceManager;
class AddressForm extends Form
{
public function __construct($name = null, $entityManager, ServiceManager $serviceManager)
{
parent::__construct($name);
$this->setAttribute('method', 'post');
$this->setAttribute('class', 'form-horizontal');
$this->setHydrator(new DoctrineHydrator($entityManager, 'Application\Entity\Address'));
$this->setObject(new Address());
$this->add(array(
'name' => 'addressType',
'type' => 'select',
'options' => array(
'label' => _('Type'),
'label_attributes' => array(
'class' => 'col-lg-2 control-label'
),
'value_options' => $this->getAddressTypeOptions($serviceManager),
),
'attributes' => array(
'class' => 'form-control',
),
));
$this->add(array(
'name' => 'address',
'type' => 'text',
'options' => array(
'label' => _('Address'),
'label_attributes' => array(
'class' => 'col-lg-2 control-label'
),
),
'attributes' => array(
'class' => 'form-control',
'placeholder' => 'Abbey Road 1',
),
));
$this->add(array(
'name' => 'postalcode',
'type' => 'text',
'options' => array(
'label' => _('Postalcode'),
'label_attributes' => array(
'class' => 'col-lg-2 control-label'
),
),
'attributes' => array(
'class' => 'form-control',
'placeholder' => '1234 AB',
),
));
$this->add(array(
'name' => 'city',
'type' => 'text',
'options' => array(
'label' => _('City'),
'label_attributes' => array(
'class' => 'col-lg-2 control-label'
),
),
'attributes' => array(
'class' => 'form-control',
'placeholder' => 'Amsterdam',
),
));
$this->add(array(
'name' => 'country',
'type' => 'select',
'options' => array(
'label' => _('Country'),
'label_attributes' => array(
'class' => 'col-lg-2 control-label'
),
'value_options' => $this->getCountryOptions($serviceManager),
),
'attributes' => array(
'class' => 'form-control',
),
));
$this->add(array(
'name' => 'email',
'type' => 'email',
'options' => array(
'label' => _('Email'),
'label_attributes' => array(
'class' => 'col-lg-2 control-label'
),
),
'attributes' => array(
'class' => 'form-control',
'placeholder' => 'name#domain.tld',
),
));
$this->add(array(
'name' => 'submit',
'type' => 'submit',
'options' => array(
'label' => _('Save'),
),
'attributes' => array(
'class' => 'btn btn-large btn-primary',
),
));
}
protected function getAddressTypeOptions($serviceManager)
{
$data = array();
$addressTypeService = $serviceManager->get('application_service_addresstype');
$addressTypeCollection = $addressTypeService->getAddressTypes()->getResult();
foreach($addressTypeCollection as $addressType)
{
$data[$addressType->getAddressTypeId()] = $addressType->getName();
}
return $data;
}
protected function getCountryOptions($serviceManager)
{
$data = array();
$countryService = $serviceManager->get('zf2country_service_country');
$countryCollection = $countryService->getCountries()->getResult();
foreach($countryCollection as $country)
{
$data[$country->getCountryId()] = $country->getName();
}
return $data;
}
}
AddressController.php
<?php
namespace SNOrganisation\Controller;
use ZF2Core\Controller\AbstractController;
use Zend\View\Model\ViewModel;
use Application\Entity\Address;
class AddressController extends AbstractController
{
public function editAction()
{
$organisationId = (int)$this->params()->fromRoute('id');
$addressId = (int)$this->params()->fromRoute('addressId');
$request = $this->getRequest();
$address = $this->getEntityManager()->getRepository('Application\Entity\Address')->find($addressId);
if ($address)
{
$addressForm = $this->getServiceLocator()->get('snorganisation_form_address');
$addressForm->bind($address);
}
else
{
$this->resultMessenger()->addFatalMessage($this->getTranslator()->translate('The address could not be found'));
$this->redirect()->toRoute('organisation');
}
return new ViewModel(array(
'addressForm' => $addressForm,
));
}
}
Entity Dump
<?php
object(Application\Entity\Address)[700]
protected 'addressId' => string '487956' (length=6)
protected 'organisation' =>
object(DoctrineORMModule\Proxy\__CG__\SenetOrganisation\Entity\Organisation)[701]
public '__initializer__' =>
object(Closure)[583]
public '__cloner__' =>
object(Closure)[584]
public '__isInitialized__' => boolean false
protected 'organisationId' => string '412705' (length=6)
protected 'ownerPerson' => null
protected 'otys' => null
protected 'name' => null
protected 'paymentInterval' => null
protected 'vatNumber' => null
protected 'debtor' => null
protected 'invoiceByEmail' => null
protected 'active' => null
protected 'reasonInactive' => null
protected 'created' => null
protected 'addressCollection' => null
protected 'personCollection' => null
protected 'orderCollection' => null
protected 'serviceManager' => null
protected 'addressType' =>
object(DoctrineORMModule\Proxy\__CG__\Application\Entity\AddressType)[714]
public '__initializer__' =>
object(Closure)[704]
public '__cloner__' =>
object(Closure)[705]
public '__isInitialized__' => boolean false
protected 'addressTypeId' => string '2' (length=1)
protected 'name' => null
protected 'serviceManager' => null
protected 'otys' => null
protected 'address' => string 'Langebrug 87 b' (length=14)
protected 'postalcode' => string '2311 TJ' (length=7)
protected 'city' => string 'Leiden' (length=6)
protected 'email' => null
protected 'country' =>
object(DoctrineORMModule\Proxy\__CG__\ZF2Country\Entity\Country)[724]
public '__initializer__' =>
object(Closure)[711]
public '__cloner__' =>
object(Closure)[710]
public '__isInitialized__' => boolean false
protected 'countryId' => string '157' (length=3)
protected 'nameIso' => null
protected 'name' => null
protected 'iso' => null
protected 'iso3' => null
protected 'serviceManager' => null
protected 'created' =>
object(DateTime)[698]
public 'date' => string '2014-03-22 16:05:49' (length=19)
public 'timezone_type' => int 3
public 'timezone' => string 'Europe/Amsterdam' (length=16)
protected 'deleted' => null
protected 'serviceManager' => null
Let me investigate, what is the reason. Sometimes it is convenient to use Zend\Form\Select element, not Doctrine element. But Zend element sometimes can not deal with doctrine Entities. The reason is in the following code of file Zend\Form\View\Helper\FormSelect.php at method renderOptions.
if (ArrayUtils::inArray($value, $selectedOptions)) {
$selected = true;
}
This piece of code makes every option selected. But $selectedOptions is not entity id, it is Entity object. This object is transformed into array via magic method, so we have wrong $selectedOptions.
So I've decided to change form element type from 'Select' to DoctrineModule\Form\Element\ObjectSelect and inject entityManager.
'type' => 'DoctrineModule\Form\Element\ObjectSelect',
'options' => array(
'object_manager' => $entityManager,
'target_class' => 'Telecom\Entity\Name',
)
I dont know, why sometimes it is not a problem. Probably I should take a look at Proxy object, generated by Doctrine. I will update the answer if I understand something.
The DoctrineModule\Form\Element\ObjectSelect did the trick
So I've a problem that made me crazy :(, I create a form and when I use the class form in my controller I got this error:
Fatal error: Class 'Application\forms\ArticleForm' not found in C:\wamp2\www\test\module\Application\src\Application\Controller\BlogController.php
and even when I try to use Application\forms\ArticleForm this path doesn't found, this is a part of my action : Update the code :
public function addAction()
{
$form = new ArticleForm();// here the class doesn't found !!
//var_dump($form);die;
$form->initForm();
$request = $this->getRequest();
$form->setData($request->getPost());
And this is my ArticleForm :
class ArticleForm extends Form {
public function __construct()
{
parent::__construct('UserEntry');
$this->setAttribute('method', 'post');
$this->setAttribute('enctype', 'multipart/form-data');
$this->setAttribute('class', 'contact_form');
}
public function initForm()
{
$this->addFormFields(); //function where we added all fields
$articleInputFilter = new ArticleInputFilter();
$this->setInputFilter($articleInputFilter->getInputFilter()); //Asign input Filter to form
}
protected function addFormFields()
{
$this->addSubmit();
$this->addTitle();
$this->addContent();
$this->addDate();
$this->addPublication();
$this->addImage();
}
/**
*
*/
protected function addTitle()
{
$this->add(array(
'name' => 'title',
'attributes' => array(
'type' => 'text',
),
'options' => array(
'label' => _('Title')
),
));
}
/**
*
*/
protected function addContent()
{
$this->add(array(
'name' => 'content',
'attributes' => array(
'type' => 'text',
),
'options' => array(
'label' => _('Content')
),
));
}
/**
*
*/
protected function addDate()
{
$this->add(array(
'name' => 'date',
'attributes' => array(
'type' => 'date',
),
'options' => array(
'label' => _('Date'),
'id' => 'datepicker',
),
));
}
/**
*
*/
protected function addPublication()
{
$this->add(array(
'name' => 'publication',
'attributes' => array(
'type' => 'checkbox',
),
'options' => array(
'label' => _('Publication'),
'use_hidden_element' => true,
'checked_value' => 1,
'unchecked_value' => 'no',
),
));
}
/**
*
*/
protected function addImage()
{
$this->add(array(
'name' => 'Image',
'attributes' => array(
'type' => new ImageForm(),
),
'options' => array(
'label' => _('Image')
),
));
}
/**
*
*/
protected function addSubmit()
{
$this->add(array(
'name' => 'submit',
'attributes' => array(
'type' => 'submit',
'value' => _('Add'),
'class' => 'submit',
),
));
}
}
Finally this is my ArticleInputFilter :
class ArticleInputFilter extends InputFilter implements InputFilterAwareInterface
{
/**
* #var string
*/
public $title;
/**
* #var int
*/
public $image;
/**
* #var string
*/
public $content;
/**
* #var Date
*/
public $date;
/**
* #var Boolean
*/
public $publication;
/**
* #param $data
*/
public function exchangeArray($data)
{
$this->title = (isset($data['title'])) ? $data['title'] : $this->title;
$this->image = (isset($data['image'])) ? $data['image'] : $this->image;
$this->content = (isset($data['content'])) ? $data['content'] : $this->content;
$this->publication = (isset($data['publication'])) ? $data['publication'] : $this->publication;
$this->date = (isset($data['date'])) ? $data['date'] : $this->date;
}
/**
* #param InputFilterInterface $inputFilter
* #return void|InputFilterAwareInterface
* #throws \Exception
*/
public function setInputFilter(InputFilterInterface $inputFilter)
{
throw new \Exception("Not used");
}
/**
* #return InputFilter|InputFilterInterface
*/
public function getInputFilter()
{
if (!$this->inputFilter) {
$inputFilter = new InputFilter();
$factory = new InputFactory();
$inputFilter->add($factory->createInput(array(
'name' => 'title',
'required' => true,
'filters' => array(
array('name' => 'StripTags'),
array('name' => 'StringTrim'),
),
'validators' => array(
array(
'name' => 'StringLength',
'options' => array(
'encoding' => 'UTF-8',
'min' => 6,
'max' => 100,
),
),
),
)));
$inputFilter->add($factory->createInput(array(
'name' => 'content',
'required' => true,
'filters' => array(
array('name' => 'StripTags'),
array('name' => 'StringTrim'),
),
'validators' => array(
array(
'name' => 'StringLength',
'options' => array(
'encoding' => 'UTF-8',
'min' => 10,
),
),
),
)));
$inputFilter->add($factory->createInput(array(
'name' => 'publication',
'required' => false,
)));
$inputFilter->add($factory->createInput(array(
'name' => 'date',
'required' => true,
)));
$inputFilter->add($factory->createInput(array(
'name' => 'image',
'required' => true,
)));
$this->inputFilter = $inputFilter;
}
return $this->inputFilter;
}
}
So please if someone has any idea or solution to my problem I will be very appreciative.
Probably an autoloading problem.
Where is defined your ArticleForm ?
Note : you'd better use the form element manager to get form instance. You can read more on this subject here
I had the same problem. The answer is pretty simple. The problem is in the autoloader so you need to "refresh" or "sincronyze everything" before you get started. So at the top of the function addAction() or the file just require the autoload.php file that is inside of the vendor folder to "update" your project. Something like this:
require 'vendor/autoload.php';
Obviously, you need to write your own path.
I hope that would help.
So i work in a Zend Framework project and i am using Doctrine, i create my Form, Controller and Entities, but when i run my project i got this error :
Object provided to Escape helper, but flags do not allow recursion
This is my Entity :
namespace Application\Entity;
use Doctrine\ORM\Mapping as ORM;
use Zend\Form\Annotation;
/**
* Article
*
* #ORM\Table()
* #ORM\Entity(repositoryClass="Application\Entity\ArticleRepository")
*/
class Article
{
/**
* #ORM\Column(name="publication", type="boolean")
*/
private $publication;
public function __construct()
{
$this->date = new \Datetime();
}
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="title", type="string", length=255)
*/
private $title;
/**
* #var \DateTime
*
* #ORM\Column(name="date", type="date")
*/
private $date;
/**
* #var string
*
* #ORM\Column(name="content", type="text")
*/
private $content;
/**
* #ORM\OneToOne(targetEntity="Application\Entity\Image", cascade={"persist","remove"})
*/
private $image;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set title
*
* #param string $title
* #return Article
*/
public function setTitle($title)
{
$this->title = $title;
return $this;
}
/**
* Get title
*
* #return string
*/
public function getTitle()
{
return $this->title;
}
/**
* Set date
*
* #param \DateTime $date
* #return Article
*/
public function setDate($date)
{
$this->date = $date;
return $this;
}
/**
* Get date
*
* #return \DateTime
*/
public function getDate()
{
return $this->date;
}
/**
* Set content
*
* #param string $content
* #return Article
*/
public function setContent($content)
{
$this->content = $content;
return $this;
}
/**
* Get content
*
* #return string
*/
public function getContent()
{
return $this->content;
}
/**
* Set publication
*
* #param boolean $publication
* #return Article
*/
public function setPublication($publication)
{
$this->publication = $publication;
return $this;
}
/**
* Get publication
*
* #return boolean
*/
public function getPublication()
{
return $this->publication;
}
/**
* Set image
*
* #param \Application\Entity\Image $image
* #return Article
*/
public function setImage(\Application\Entity\Image $image = null)
{
$this->image = $image;
return $this;
}
/**
* Get image
*
* #return \Application\Entity\Image
*/
public function getImage()
{
return $this->image;
}
}
And this is Form with fields validation:
class ArticleForm extends Form implements ObjectManagerAwareInterface
{
/**
* #var EntityManager
*/
protected $em;
public function init()
{
$this->add(array(
'name' => 'title',
'attributes' => array(
'type' => 'text',
),
'options' => array(
'label' => 'Title'
),
));
$this->add(array(
'name' => 'id',
'attributes' => array(
'type' => 'hidden',
),
));
$this->add(array(
'name' => 'content',
'attributes' => array(
'type' => 'textera',
),
'options' => array(
'label' => 'Content'
),
));
$this->add(array(
'name' => 'date',
'attributes' => array(
'type' => 'text',
'class' => 'datepicker',
),
'options' => array(
'label' => 'Date',
),
));
$this->add(array(
'name' => 'publication',
'attributes' => array(
'type' => 'Checkbox',
),
));
$this->add(array(
'name' => 'url',
'attributes' => array(
'type' => 'file',
'id' => 'files',
'class'=> 'upload'
),
'options' => array(
'label' => 'Url'
),
));
$this->add(array(
'name' => 'alt',
'attributes' => array(
'type' => 'text',
),
'options' => array(
'label' => 'Alt'
),
));
$this->add(array(
'name' => 'submit',
'attributes' => array(
'type' => 'submit',
'value' => 'Go',
'class' => 'submit',
),
));
$this->setInputFilter($this->createInputFilter());
}
public function __construct($name = null, $options = array())
{
parent::__construct($name, $options);
}
public function createInputFilter()
{
if (!$this->inputFilter) {
$inputFilter = new InputFilter();
$factory = new InputFactory();
$inputFilter->add($factory->createInput(array(
'name' => 'title',
'required' => true,
'filters' => array(
array('name' => 'StripTags'),
array('name' => 'StringTrim'),
),
'validators' => array(
array(
'name' => 'StringLength',
'options' => array(
'encoding' => 'UTF-8',
'min' => 6,
'max' => 100,
),
),
),
)));
$inputFilter->add($factory->createInput(array(
'name' => 'content',
'required' => true,
'filters' => array(
array('name' => 'StripTags'),
array('name' => 'StringTrim'),
),
'validators' => array(
array(
'name' => 'StringLength',
'options' => array(
'encoding' => 'UTF-8',
'min' => 10,
),
),
),
)));
$inputFilter->add($factory->createInput(array(
'name' => 'publication',
'required' => false,
)));
$inputFilter->add($factory->createInput(array(
'name' => 'date',
'required' => true,
)));
$inputFilter->add($factory->createInput(array(
'name' => 'image',
'required' => true,
)));
$this->inputFilter = $inputFilter;
}
return $this->inputFilter;
}
public function setObjectManager(ObjectManager $objectManager) {
$this->objectManager = $objectManager;
}
/**
* Get the object manager
*
* #return ObjectManager
*/
public function getObjectManager() {
return $this->objectManager;
}
}
Then my Action :
public function addAction()
{
$form = new ArticleForm($this->getObjectManager());
$article = new Article();
$request = $this->getRequest();
$hydrator = new DoctrineHydrator($this->getObjectManager(), get_class($article));
$form->setHydrator($hydrator);
$form->bind($article);
if ($this->zfcUserAuthentication()->hasIdentity()) {
if ($request->isPost())
{
$form->setData($request->getPost());
if ($form->isValid()) {
$this->getObjectManager()->persist($article);
$this->getObjectManager()->flush();
return $this->redirect()->toRoute('blog');
}
}
}
else
{
return $this->redirect()->toRoute('user');
}
return array('form' => $form);
}
Finally my View where i think i have an error :
<?php
$form = $this->form;
$form->setAttribute('action', $this->url('add', array('action' => 'add')));
$form->prepare();
?>
<?php
echo $this->form()->openTag($form);
?>
<ul>
<li>
<?php echo $this->formHidden($form->get('id'));?>
<li>
<li>
<label>Publication:</label>
<?php echo $this->formInput($form->get('publication'));?>
</li>
<li>
<label>Title:</label>
<?php echo $this->formInput($form->get('title'));?>
</li>
// ....
<li>
<?php echo $this->formSubmit($form->get('submit'));?></li>
</ul>
<?php
echo $this->form()->closeTag();
?>
That it's, this is almost my codes, i tried everything and i didn't found any solution, and i think the error in my view, so please if someone has any idea i will be very appreciative
its propably because of the date-object.
try to change the type of the form element to date:
$this->add(array(
'name' => 'date',
'type' => 'Date',
'attributes' => array(
'type' => 'text',
'class' => 'datepicker',
),
'options' => array(
'label' => 'Date',
),
));
You can get this error when you set an object as attribute instead of a string:
$element->setAttribute('class', $object)
where $element can be a Form, Fieldset or Element
In general, Object provided to Escape helper, but flags do not allow recursion means that the Escape view helper was expecting, but did not get, either a scalar or an object with a __toString() method. It's a fancy way of saying "dude, I can't print this."
The solution is to either do your own rendering (without using the form view helpers), or ensure that the form element value is something you can echo.
You have an init function. Comment out your __construct function and write it like this:
public function __construct(ObjectManager $em, $name = null, $options = array())
{
$this->setObjectManager($em);
parent::__construct($name, $options);
//here you add all the form elements
//(meaning: just put all the content of your init() function here)
//if that's a construct function, you also need to add:
//private $inputFilter;
//at the top of your class, it is not declared in your code
}
Here's the construct function you provided:
public function __construct($name = null, $options = array())
{
parent::__construct($name, $options);
}
The error was thrown, because you wrote:
$form = new ArticleForm($this->getObjectManager());
So the first parameter you passed to the __construct function was an instance of objectManager, and when it was processed in Zend\View\Helper\Escaper\AbstractHelper, there was something wrong. I can't tell you exactly what's going on there, but if you declare the __construct function as I demonstrated, everything works fine.