Question

Im having a problem with the addAction in my CRUD application. On the controller the logic does not pass the $form->isValid() verification but the form does not show any error message.

I tried with this (Thanks Sam):

foreach($form->get('product')->getElements() as $el)
{
    echo $el->getName()." = ".$el->getValue()." > ".$el->getMessages()." <br/>";
}

That only show the name and value of the field, but not the error message.

I've tried letting the form totally blank and it fire "Value is required and can't be empty" error messages, but then, i fill each field one by one until i don't get more error messages but the form still invalid.

My form has a Product Fieldset as a base fieldset and a submit button. Inside Product Fieldset i have an ID field, a name field, a price field and a Brand Fieldset. Inside my Brand Fieldset i have a id field. Like this:

ProductForm:

class ProductForm extends Form
{

    public function init()
    {
        // we want to ignore the name passed
        parent::__construct('product');
        $this->setName('product');
        $this->setAttribute('method', 'post');

        $this->add(array(
               'name' => 'product',
                'type' => 'Administrador\Form\ProductFieldset',
                'options' => array(
                        'use_as_base_fieldset' => true
                ),
        ));
        $this->add(array(
                'name' => 'submit',
                'type' => 'Submit',
                'attributes' => array(
                        'value' => 'Add',
                        'id' => 'submitbutton',
                ),
        ));
    }
}

ProductFieldset:

class ProductFieldset extends Fieldset implements ServiceLocatorAwareInterface
{

    protected $serviceLocator;

    function __construct($name = null)
    {
        parent::__construct('product_fieldset');

        $this->setHydrator(new ArraySerializableHydrator());
        $this->setObject(new Product());
    }

    public function init()
    {

        $this->add(array(
                'name' => 'id',
                'type' => 'Hidden',
        ));
        $this->add(array(
                'name' => 'name',
                'type' => 'Text',
                'options' => array(
                        'label' => 'Name',
                ),
        ));
        $this->add(array(
                'name' => 'price',
                'type' => 'Text',
                'options' => array(
                        'label' => 'Price',
                ),
        ));
        $this->add(array(
                'name' => 'brand',
                'type' => 'BrandFieldset',
        ));
    }


    public function setServiceLocator(ServiceLocatorInterface $sl)
    {
        $this->serviceLocator = $sl;
    }

    public function getServiceLocator()
    {
        return $this->serviceLocator;
    }
}

BrandFieldset:

class BrandFieldset extends Fieldset
{
    function __construct(BrandTable $brandTable)
    {
        parent::__construct('brand_fieldset');

        //$this->setHydrator(new ClassMethodsHydrator(false))->setObject(new Brand());

        $this->setHydrator(new ArraySerializableHydrator());
        $this->setObject(new Brand());

        $brandSelectOptionsArray = $brandTable->populateSelectBrand();
        $this->add(array(
                'name' => 'id',
                'type' => 'Select',
                'options' => array(
                        'label' => 'Brand',
                        'empty_option' => 'Please select a brand',
                        'value_options' => $brandSelectOptionsArray,
                ),
        ));
    }
}

This is my new Form statement in the addAction:

$formManager = $this->serviceLocator->get('FormElementManager');
$form = $formManager->get('Administrador\Form\ProductForm');

Inside my model 'Product' i have the inputFilters, required filter for 'id' field, required filter for 'name' field. And for the Brand field i created other inputFilter and added it to the main inputFilter:

$brandFilter->add($factory->createInput(array(
'name'     => 'id',
'required' => true,
'filters'  => array(
    array('name' => 'Int'),
),
)));
$inputFilter->add($brandFilter, 'brand');

The weird behavior is that my editAction works fine and has the same logic.

Is it there any form of echoing an internal error message from the form, something that helps me to understand WHY the form is not valid.

EDIT 2013-06-01

Here is my full Controller:

class ProductController extends AbstractActionController
{

    protected $productTable;
    protected $brandTable;

    public function indexAction()
    {
        return new ViewModel(array(
            'products' => $this->getProductTable()->fetchAll(),
        ));
    }

    public function addAction()
    {
        $formManager = $this->serviceLocator->get('FormElementManager');
        $form = $formManager->get('Administrador\Form\ProductForm');
        $form->get('submit')->setValue('Add');

        $request = $this->getRequest();
        if ($request->isPost()) {
            $product = new Product();
            $product->brand = new Brand();
            $form->setInputFilter($product->getInputFilter());
            $form->setData($request->getPost());

            if ($form->isValid()) {
                $product->exchangeArray($form->getData());
                $this->getProductTable()->saveProduct($product);

                // Redirect to list of products
                return $this->redirect()->toRoute('product');
            }
        }
        return new ViewModel(array(
            'form' => $form,
        ));
    }

    public function editAction()
    {
        $id = (int) $this->params()->fromRoute('id', 0);
        if (!$id) {
            return $this->redirect()->toRoute('product', array(
                    'action' => 'add'
            ));
        }

        // Get the Product with the specified id.  An exception is thrown
        // if it cannot be found, in which case go to the index page.
        try {
            $product = $this->getProductTable()->getProduct($id);
        }
        catch (\Exception $ex) {
            return $this->redirect()->toRoute('product', array(
                    'action' => 'index'
            ));
        }
        $formManager = $this->serviceLocator->get('FormElementManager');
        $form = $formManager->get('Administrador\Form\ProductForm');
        $brand = $this->getBrandTable()->getBrand($product->brand);
        $product->brand = $brand;
        $form->bind($product);
        $form->get('submit')->setAttribute('value', 'Edit');

        $request = $this->getRequest();
        if ($request->isPost()) {
            $form->setInputFilter($product->getInputFilter());
            $form->setData($request->getPost());

            if ($form->isValid()) {
                $this->getProductTable()->saveProduct($form->getData());

                // Redirect to list of products
                return $this->redirect()->toRoute('product');
            }
        }

        return array(
                'id' => $id,
                'form' => $form,
        );
    }

    public function deleteAction()
    {
        $id = (int) $this->params()->fromRoute('id', 0);
        if (!$id) {
            return $this->redirect()->toRoute('product');
        }

        $request = $this->getRequest();
        if ($request->isPost()) {
            $del = $request->getPost('del', 'No');

            if ($del == 'Yes') {
                $id = (int) $request->getPost('id');
                $this->getProductTable()->deleteProduct($id);
            }

            // Redirect to list of products
            return $this->redirect()->toRoute('product');
        }

        return array(
                'id'    => $id,
                'product' => $this->getProductTable()->getProduct($id)
        );
    }


    public function getProductTable()
    {
        if (!$this->productTable) {
            $sm = $this->getServiceLocator();
            $this->productTable = $sm->get('Administrador\Model\ProductTable');
        }
        return $this->productTable;
    }

    public function getBrandTable()
    {
        if (!$this->brandTable) {
            $sm = $this->getServiceLocator();
            $this->brandTable = $sm->get('Administrador\Model\BrandTable');
        }
        return $this->brandTable;
    }
}
Was it helpful?

Solution 2

Well, I got the answer :D

This is how the addAction should be:

public function addAction()
{
    $formManager = $this->serviceLocator->get('FormElementManager');
    $form = $formManager->get('Administrador\Form\ProductForm');
    $form->get('submit')->setValue('Add');

    $product = new Product();
    $product->brand = new Brand();

    $form->bind($product);  // I need to bind the product to the form to pass the isValid() validation

    $request = $this->getRequest();
    if ($request->isPost()) {
        $form->setInputFilter($product->getInputFilter());
        $form->setData($request->getPost());

        if ($form->isValid()) {
            $product = $form->getData();
            $this->getProductTable()->saveProduct($product);

            // Redirect to list of products
            return $this->redirect()->toRoute('product');
        }
    }
    return new ViewModel(array(
        'form' => $form,
    ));
}

Apparently i needed to bind and empty product object to the form to be able to pass the isValid() validation. After that i retrieve a product object from the $form->getData().

OTHER TIPS

My case was I passed wrong input filter. isValid returns false, but $form->getMessages() is empty. Form OrderForm had the following:

$form->setInputFilter(new \Application\Form\UserInputFilter($er));

When I changed UserInputFilter to OrderInputFilter it works.

You can also do: $form->setBindOnValidate(false);

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top