ZF2 - Binding Nested Fieldsets/Collections - forms

I have two entities (Metaproduct and Options) that have Many-To-Many Unidirectional relation with entity Specification.
The relation between Metaproduct and Option is One-To-Many.
The code for the Fieldsets and Forms for Metaproduct is the following:
MetaproductFieldset.php
namespace Bundle\Fieldset;
class MetaproductFieldset extends EntityUsingFieldset implements InputFilterProviderInterface{
...
public function __construct(ObjectManager $objectManager)
{
$this->add(array(
'type' => 'Zend\Form\Element\Collection',
'name' => 'options',
'options' => array(
'label' => 'Options',
'count' => 1,
'allow_add' => true,
'allow_remove' => true,
'should_create_template' => true,
'target_element' => new OptionFieldset($objectManager),
),
'attributes' => array(
'id' => 'options',
),
));
$this->add(array(
'type' => 'Zend\Form\Element\Collection',
'name' => 'specifications',
'options' => array(
'label' => 'Specifications',
'count' => 1,
'allow_add' => true,
'allow_remove' => true,
'should_create_template' => true,
'target_element' => new SpecificationFieldset($objectManager),
),
'attributes' => array(
'id' => 'specifications',
),
));
OptionFieldset.php
namespace Bundle\Fieldset;
class OptionFieldset extends EntityUsingFieldset implements InputFilterProviderInterface{
public function __construct(ObjectManager $objectManager)
{
$this->setObjectManager($objectManager);
parent::__construct('option');
$this->setHydrator(new DoctrineHydrator($objectManager))->setObject(new \Bundle\Entity\Option());
$this->add(array(
'type' => 'Zend\Form\Element\Collection',
'name' => 'specifications',
'options' => array(
'label' => 'Specifications',
'count' => 1,
'allow_add' => true,
'allow_remove' => true,
'should_create_template' => true,
'target_element' => new SpecificationFieldset($objectManager),
),
'attributes' => array(
'id' => 'specifications',
),
));
SpecificationFieldset.php
namespace Bundle\Fieldset;
use Doctrine\Common\Persistence\ObjectManager;
use DoctrineModule\Stdlib\Hydrator\DoctrineObject as DoctrineHydrator;
use Zend\InputFilter\InputFilterProviderInterface;
class SpecificationFieldset extends EntityUsingFieldset implements InputFilterProviderInterface{
public function __construct(ObjectManager $objectManager)
{
$this->setObjectManager($objectManager);
parent::__construct('specification');
$this->setHydrator(new DoctrineHydrator($objectManager))->setObject(new \Bundle\Entity\Specification());
$this->add(array(
'type' => 'Zend\Form\Element\Hidden',
'name' => 'id'
));
$this->add(array(
'type' => 'DoctrineModule\Form\Element\ObjectSelect',
'name' => 'label',
'options' => array(
'label' => 'Label',
'object_manager' => $objectManager,
'target_class' => 'Bundle\Entity\Label',
'property' => 'value',
'empty_option' => '--- please choose ---'
),
));
$this->add(array(
'type' => 'Zend\Form\Element\Text',
'name' => 'value',
'options' => array(
'label' => 'Value'
)
));
}
Metaproduct.php
namespace Bundle\Form;
...
class Metaproduct extends Form {
public function __construct(ObjectManager $objectManager){
parent::__construct('metaproduct-form');
$this->setHydrator(new DoctrineHydrator($objectManager));
$mpFieldset = new MetaproductFieldset($objectManager);
$mpFieldset->setUseAsBaseFieldset(true);
...
But when I try to print bind an object on that form, the following Expection is throwed:
File
zendframework\library\Zend\Form\Fieldset.php:439
Message
Zend\Form\Fieldset::setObject expects an object argument; received "Array"
Trace
#0 C:\projects\acuradoria-zend\vendor\zendframework\zendframework\library\Zend\Form\Element\Collection.php(549): Zend\Form\Fieldset->setObject(Array)
#1 C:\projects\acuradoria-zend\vendor\zendframework\zendframework\library\Zend\Form\Fieldset.php(601): Zend\Form\Element\Collection->extract()
#2 C:\projects\acuradoria-zend\vendor\zendframework\zendframework\library\Zend\Form\Form.php(854): Zend\Form\Fieldset->extract()
#3 C:\projects\acuradoria-zend\vendor\zendframework\zendframework\library\Zend\Form\Form.php(292): Zend\Form\Form->extract()
#4 C:\projects\acuradoria-zend\module\Bundle\src\Bundle\Controller\Plugin\FormService.php(42): Zend\Form\Form->bind(Object(Bundle\Entity\Metaproduct))

Please see this issue:
Problems in nested collections and fieldsets
https://github.com/zendframework/zf2/issues/5640

Related

How to make a field that is an entity as hidden in the form with SonataAdmin?

I need to add entity field as a hidden in my form
In the admin form with sonata I have:
protected function configureFormFields(FormMapper $formMapper)
{
if ($this->getRoot()->getSubject()->getId()) {
$formMapper
->add('driverNight', 'hidden', array(), array('admin_code' => 'cab.admin.driver'))
->add('monday', 'checkbox', array('required' => false, "attr" => array('class' => 'checkbox-day'), 'label' => 'Monday', 'value' => '0'))
->add('tuesday', 'checkbox', array('required' => false, "attr" => array('class' => 'checkbox-day'), 'label' => 'Tuesday', 'value' => '0'))
->add('wednesday', 'checkbox', array('required' => false, "attr" => array('class' => 'checkbox-day'), 'label' => 'Wednesday', 'value' => '0'))
->add('thursday', 'checkbox', array('required' => false, "attr" => array('class' => 'checkbox-day'), 'label' => 'Thursday', 'value' => '0'))
->add('friday', 'checkbox', array('required' => false, "attr" => array('class' => 'checkbox-day'), 'label' => 'Friday', 'value' => '0'))
->add('saturday', 'checkbox', array('required' => false, "attr" => array('class' => 'checkbox-day'), 'label' => 'Saturday', 'value' => '0'))
->add('sunday', 'checkbox', array('required' => false, "attr" => array('class' => 'checkbox-day'), 'label' => 'Sunday', 'value' => '0'))
}
Actually, no value in the input (see the inspect image)
How to make the driverNight field as hidden knowing that it is of type entity?
You could use a data transformer as detailed here: data transformer
I suggest that you create a transformer class which transforms the entity to an int in the view layer and reverses from int to entity in the model layer and the use the addModelTransformer method in your config form.
Create the model Transformer class which implements Symfony\Component\Form\DataTransformerInterface and contains 2 methods transform and reverseTransform. This class could be constructed by passing 2 arguments: Entity manager and the user entity object that will be transformed to int.
.
class DriverToIntTransformer implements DataTransformerInterface {
private $manager;
private $driver;
public function __construct(ObjectManager $manager, $oUser)
{
$this->manager = $manager;
$this->driver = $oUser;
}
public function transform($user)
{
if (null === $user) {
return $this->driver->getId();
}
return $user->getId();
}
public function reverseTransform($driverNumber)
{
if (!$driverNumber) {
return;
}
$oDriver = $this->manager
->getRepository('YourUserBundle:User')
// query for the driver with this id
->find($driverNumber);
if (null === $oDriver) {
throw new TransformationFailedException(sprintf(
'An user with number "%s" does not exist!',
$driverNumber
));
}
return $oDriver;
}
2 call the transform method:
if ($this->getRoot()->getSubject()->getId()) {
$driver = $this->getRoot()->getSubject()->getId();
$em = $this->container->get('doctrine.orm.entity_manager');
$oDriver = $em->getRepository('YourUserBundle:User')->find($driver);
$formMapper
->add('driverNight', 'hidden', array(), array('admin_code' => 'cab.admin.driver'));
$formBuilder = $formMapper->getFormBuilder();
$formBuilder->get('driverNight')->addModelTransformer(new DriverToIntTransformer($em, $oDriver));
$formMapper
->add('monday', 'checkbox', array('required' => false, "attr" => array('class' => 'checkbox-day'), 'label' => 'Monday', 'value' => '0'));//.....
}

Prestashop HelperFrom/List - messy layout

I'm new to prestashop and I worked the whole day on creating a back office interface that allows the user to write, edit, and delete articles. It is sort of a blog. I used Prestashop's Helpers (Form and List) and everything works great. I also added a new tab in the back office to access this tool.
The problem is that the layout is messy and doesn't look like the other forms and listing pages. The layout is really not sexy. Maybe I should look at some css file, or add any function in my controller ? You'll find the source code of the latter here (I can't insert images, not enough reputation --'):
<?php
class Article extends ObjectModel
{
/** #var string Name */
public $id_article;
public $titre;
public $contenu;
public $url_photo;
/**
* #see ObjectModel::$definition
*/
public static $definition = array(
'table' => 'article',
'primary' => 'id_article',
'fields' => array(
'titre' => array(
'type' => self::TYPE_STRING,
'validate' => 'isGenericName',
'required' => true,
'class' => 'lg'
),
'contenu' => array(
'type' => self::TYPE_STRING,
'validate' => 'isGenericName',
'required' => true
),
'url_photo' => array(
'type' => self::TYPE_STRING,
'validate' => 'isGenericName',
'required' => false,
),
),
);
}
class AdminBlogController extends AdminController{
public function initContent(){
parent::initContent();
}
public function __construct(){
$this->table = 'article';
$this->className = 'Article';
$this->lang = false;
// Building the list of records stored within the "article" table
$this->fields_list = array(
'id_article' => array(
'title' => 'ID',
'align' => 'center',
'width' => 25
),
'titre' => array(
'title' => 'Titre',
'width' => 'auto'
),
'contenu' => array(
'title' => 'Contenu',
'width' => 'auto'
)
);
// This adds a multiple deletion button
$this->bulk_actions = array(
'delete' => array(
'text' => $this->l('Delete selected'),
'confirm' => $this->l('Delete selected items?')
)
);
parent::__construct();
}
// This method generates the list of results
public function renderList(){
// Adds an Edit button for each result
$this->addRowAction('edit');
// Adds a Delete button for each result
$this->addRowAction('delete');
return parent::renderList();
}
// This method generates the Add/Edit form
public function renderForm(){
// Building the Add/Edit form
$this->fields_form = array(
'tinymce' => true,
'legend' => array(
'title' => 'Article'
),
'input' => array(
array(
'type' => 'text',
'label' => 'Titre',
'name' => 'titre',
'class' => 'lg',
'required' => true,
//'desc' => 'Nom de l\'article',
),
array(
'type' => 'textarea',
'label' => 'Contenu',
'name' => 'contenu',
'class' => 'lg',
'required' => true,
'autoload_rte' => true,
//'desc' => 'Contenu de l\'article',
),
array(
'type' => 'file',
'label' => 'Photo',
'name' => 'url_photo',
'class' => 'lg',
'required' => true,
//'desc' => 'Contenu de l\'article',
)
),
'submit' => array(
'title' => $this->l('Save'),
'class' => 'button'
)
);
return parent::renderForm();
}
}
?>
Thank you.
I just needed to set $this->bootstrap = true

How to get paramaters in controller function action in Zend 2?

this is my code:
public function registerAction(){
$this->redirect()->toRoute(null, array(
'controller' => 'user',
'action' => 'confirm',
'param1' =>'email',//$request->getPost('mail'),
'param2'=>$request->getPost('name')
));
}
public function confirmAction(){
$params = $this->params()->fromRoute('param1');
var_dump($params); exit();
return new ViewModel();
}
and this is the code from the config:
.....
'child_routes' => array(
'default' => array(
'type' => 'Segment',
'options' => array(
'route' => '[:controller[/:action]][/:param1][/:param2]',
'constraints' => array(
'controller' => '[a-zA-Z][a-zA-Z0-9_-]*',
'action' => '[a-zA-Z][a-zA-Z0-9_-]*'
),
'defaults' => array(
'action' => 'index',
'__NAMESPACE__' => 'Application\Controller'
)
)
)
)
....
I am trying to send 2 parameters in a redirect, to receive in the confirmAction function and send it tot view. But i get in the var_dump always the null value. I tried all of this:
$this->params()->fromPost('paramname'); // From POST
$this->params()->fromQuery('paramname'); // From GET
$this->params()->fromRoute('paramname'); // From RouteMatch
$this->params()->fromHeader('paramname'); // From header
$this->params()->fromFiles('paramname'); // From file being uploaded
but with no result. Can anyone help me with this ? thx
you have to set in the config the parameter name, which is in my case : param1
'child_routes' => array(
'default' => array(
'type' => 'Segment',
'options' => array(
'route' => '[:controller[/:action]][/:param1]',
'constraints' => array(
'controller' => '[a-zA-Z][a-zA-Z0-9_-]*',
'action' => '[a-zA-Z][a-zA-Z0-9_-]*'
),
'defaults' => array(
'action' => 'index',
'__NAMESPACE__' => 'Application\Controller',
'param1' => 'tralala'
)
)
)
)
After that in the controller when you do a redirect:
public function registerAction(){
$this->redirect()->toRoute(null, array(
'controller' => 'user',
'action' => 'confirm',
'param1' =>'my_email'
));
}
public function confirmAction(){
$params = $this->params()->fromRoute('param1');
var_dump($params); exit();
return new ViewModel();
}
You will receive the my_email string

ZF2 populate form collections element with child objects

Based on the Zend Framework 2 manual (here) I made a form like this:
class ParentForm extends Form
{
public function init()
{
$this->setName('parent_form')
->setAttribute('method', 'post')
->setHydrator(new ClassMethods(true))
->setInputFilter(new InputFilter());
$this->add(array(
'type' => 'Application\Form\Fieldset\Parent',
'name' => 'parent',
'options' => array(
'label' => 'Parent',
'use_as_base_fieldset' => true
)
));
$this->add(array(
'type' => 'Zend\Form\Element\Csrf',
'name' => 'csrf'
));
$this->add(array(
'name' => 'submit',
'attributes' => array(
'type' => 'submit',
'value' => 'Send'
)
));
}
}
And the 'parent' fieldset:
class ParentFieldset extends Fieldset
{
protected $count = 2;
public function init()
{
$this->setName('parent_fieldset')
->setHydrator(new ClassMethodsHydrator(false))
->setObject(new Model\Parent());
$this->add(array('type' => 'Element\MyField'));
$this->add(array(
'type' => 'collection',
'name' => 'children',
'options' => array(
'label' => 'Children',
'count' => $this->count,
'should_create_template' => true,
'allow_add' => true,
'target_element' => array(
'type' => 'Application\Form\Fieldset\Child',
'name' => 'child',
'options' => array(
'label' => child',
),
),
)
));
}
public function setCount($count)
{
$this->count = max($count, 2);
}
}
This works great to hydrate my Parent object with data obtained from my form. A var_dump of the resuting object will look like:
object(Parent)
public myField => foo
public children =>
array =>
0 => object(Child)
public field1 => value1
public field2 => value2
....
1 => object(Child)
.....
2 => object(Child)
.....
But I cannot figure out how to populate this multi-dimensional form with the above object if hydrated from the database (for editing purposes). How can I do that?
Note: the count property in the form is set to the same number of children as in the object.

ZF2 form as array

I'd like to create a simple zend form with a few fields but i wanna collect this fields into an array. I'd like to see my form names like this:
name="login[username]" name="login[password]" name="login[submit]"
I wasn't able to find any description. If somebody knows the solution please let me know!
You can try with fieldsets like that
namespace Application\Form;
use Application\Entity\Brand;
use Zend\Form\Fieldset;
use Zend\InputFilter\InputFilterProviderInterface;
class YourFieldset extends Fieldset implements InputFilterProviderInterface
{
public function __construct()
{
parent::__construct('login');
$this->add(array(
'name' => 'username',
'options' => array(
'label' => 'Username'
),
'attributes' => array(
'required' => 'required'
)
));
$this->add(array(
'name' => 'password',
'type' => 'Zend\Form\Element\Password',
'options' => array(
'label' => 'Password'
),
'attributes' => array(
'required' => 'required'
)
));
$this->add(array(
'name' => 'submit',
'type' => 'Zend\Form\Element\Submit',
'options' => array(
'label' => 'Submit'
),
'attributes' => array(
'required' => 'required'
)
));
}
/**
* #return array
*/
public function getInputFilterSpecification()
{
return array(
'name' => array(
'required' => true,
)
);
}
}