Zend Validation Db_NoRecordExists and exclude option
Asked Answered
G

6

7

I'm trying to use the "exclude" option for a Db_NoRecordExists validator, cause when I'm "editing" the element it always return me back a "duplicated" error, as usual.

What I aim to is to tell to the form to keep back the value passed to the form itself from the Controller...

This is the Controller:

public function editAction()
{
$id = $this->getRequest()->getParam('id');
$pagesMapper = new Application_Model_PagesMapper();
$form = new Application_Form_PageEdit();
$form->populate($pagesMapper->fetchId($id, true));
if ($this->getRequest()->isPost()) {
    if ($form->isValid($this->getRequest()->getPost())) {
        //... cut ...
    }
}
$this->view->form = $form;
}

This is the Form:

class Application_Form_PageEdit extends Zend_Form
{
public function init()
{
$commonFilters      = array('StringTrim');
$commonValidators = array('NotEmpty');
    $this->setMethod('post')->setAction('/admin-page/edit');

$id = new Zend_Form_Element_Hidden('id');
$pid = new Zend_Form_Element_Hidden('pid');

$keyname = new Zend_Form_Element_Text('keyname');
$keyname->setLabel('Keyname')
    ->setRequired(true)
    ->addFilters($commonFilters)
    ->addFilter('StringToLower')
    ->addFilter('Word_SeparatorToDash')
    ->addValidator('Db_NoRecordExists', false, array(
        'table'     => 'pages',
        'field'     => 'keyname',
        'exclude'   => array(
            'field' => 'id',
            'value' => $this->getValue('id)
        )
        )
    );

//... cut ...

Some advices?

Gregor answered 21/3, 2011 at 14:15 Comment(0)
N
17

I had a similar problem. My solution was to move the validator from the init to the isValid function.

public function isValid($data)
{
  $this->getElement('keyname')
       ->addValidator(
           'Db_NoRecordExists',
           false,
           array(
               'table'     => 'pages',
               'field'     => 'keyname',
               'exclude'   => array(
                   'field' => 'id',
                   'value' => $this->getValue('id')
               )
           )
       );
  return parent::isValid($data);
}
Northeastward answered 21/3, 2011 at 14:46 Comment(6)
I was thinking about it and... is stupid to think to get a value into the init() method, because the Element is not yet applied to the form, so it can't be filled out and will return false forever :D I'll try thisGregor
really, when I add the validator the "id object" exists, but is still not "connected" to the form... it's just a var inside the init method... not yet added to "$this"... I don't think it's a bug, it was my fault, cause the init method construct the whole form, but the form contain no data till the "populate()", so the validator will filter a null value (cause it's not populated so the id value is NULL)... that's why this kind of validation with dynamic value must be done in an override method. (IMHO)Gregor
Thanks for the solution but I get why is it 'value' => $this->getValue('id') and not 'value' => data['id'] ? ThanksLagunas
In his form he has created and passed an ID to an element. I imagine the value of this element does not change ergo he could retrieve the value from it. That said, data['id'] would have worked as well.Northeastward
Thanks for the solution. Question - is the "isValid" function still in the ../application/forms/MyForm.php file?Mackenzie
I had issues with $this->getValue('username') but $data['username'] worked just fine.Proven
N
2

For me only this solution works perfectly:

public function isValid($data)
{
    $clause    = $this->_dbAdapter->quoteInto('id = ?', $this->getValue('id'));
    $this->getElement('keyname')
        ->addValidator('Db_NoRecordExists', false, array(
            'adapter'   =>  $this->_dbAdapter,
            'table'     => 'employee_name',
            'field'     => 'name',
            'exclude'   => $clause
        )
    );
    return parent::isValid($data);
}
Niphablepsia answered 28/10, 2011 at 14:59 Comment(1)
$this->_dbAdapter = Zend_Db_Table::getDefaultAdapter();Merce
S
1

What I do, before the validation logic in the controller, I add the following code to check the field against the current user data. If its a match, I just remove the validator from the element like so:

    if(($this->getRequest()->isPost()) && $request->getParam('email')==$this->user->getEmail()) {
        $form_preferences->getElement('email')->removeValidator('Db_NoRecordExists');
    }

At that point it will pass the isValid logic no problem.

if(($this->getRequest()->isPost()) && $form_preferences->isValid($_POST)) {

Enjoy!

Sheryl answered 31/1, 2014 at 16:12 Comment(0)
J
1

I didn't like the solution of overriding the isValid() function. This just feels like we're not understanding how the Db_NoRecordExists validator works. You simply need to provide the validator with the actual ID of the record to exclude prior to calling isValid(). The validator even provides an accessor to set this value right within itself! The Zend instructions really don't help out as well as they could either, so it's not surprising people are struggling with this.

Here's a more elegant and work-flow oriented way to set the exclude without hacking, extending or overriding the form:

// $post is assumed to have our posted form variables // 
// $form is assumed to be our form that is already setup with validators. //

$form->getElement('username')->
        getValidator('Db_NoRecordExists')->
        setExclude([
            'field' => 'user_id',
            'value' => $post['user_id'],
        ]);

if ( $form->isValid( $post ) ) {
    // do valid stuff
    // ...
}
else {
    // do invalid stuff
    // ...
}

The exclude is actually an array of our field and value. We just tell the validator what to exclude just prior to calling isValid(). Done. Simple.

Junitajunius answered 25/9, 2016 at 8:3 Comment(1)
The only thing I would add to this is that in all of the solutions discussed here, including my own, the excluded value itself has NOT been validated. This value gets sent to a DB statement. To be vigilant about one's validation run your excluded value through an ad-hoc validator prior to assigning it to your exclude. Better safe than sorry.Junitajunius
I
0

Make this changes to the form:

class Application_Form_PageEdit extends Zend_Form
{
    public function getExcludeFromQuery($elementName)
    {
        $element = $this->getElement($elementName);
        if ($element)
        {
            return $element->getValidator('Db_NoRecordExists')->getExclude();
        } else
        {
            return NULL;
        }
    }

    public function setExcludeFromQuery($elementName, $excludeFromQuery)
    {
        $element = $this->getElement($elementName);
        if ($element)
        {
            $element->getValidator('Db_NoRecordExists')->setExclude($excludeFromQuery);
        }
        return $this;
    }


    public function init()
    {
    $commonFilters      = array('StringTrim');
    $commonValidators = array('NotEmpty');
        $this->setMethod('post')->setAction('/admin-page/edit');

    $id = new Zend_Form_Element_Hidden('id');
    $pid = new Zend_Form_Element_Hidden('pid');

    $keyname = new Zend_Form_Element_Text('keyname');
    $keyname->setLabel('Keyname')
        ->setRequired(true)
        ->addFilters($commonFilters)
        ->addFilter('StringToLower')
        ->addFilter('Word_SeparatorToDash')
        ->addValidator('Db_NoRecordExists', false, array(
            'table'     => 'pages',
            'field'     => 'keyname',
            'exclude'   => array(
                'field' => 'id',
                'value' => $this->getValue('id)
            )
            )
        );

And to the edit action:

public function editAction()
{
$duplicateElementName = 'whatever';
$duplicateElementValue = 'value from db';

$id = $this->getRequest()->getParam('id');
$pagesMapper = new Application_Model_PagesMapper();
$form = new Application_Form_PageEdit();
$form->populate($pagesMapper->fetchId($id, true));

$form->setExcludeFromQuery($duplicateElementName, $duplicateElementValue);

if ($this->getRequest()->isPost()) {
    if ($form->isValid($this->getRequest()->getPost())) {
        //... cut ...
    }
}
$this->view->form = $form;
}
Individuate answered 10/8, 2012 at 16:34 Comment(0)
M
0

There is no need to build the validator in the isValid function, just update the exclude clause:

public function isValid($data)
{
    $this->getElement('name')->getValidator('Db_NoRecordExists')->setExclude('id != ' . $data['id']);
    return parent::isValid($data);
}
Meilen answered 29/1, 2013 at 18:25 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.