I create my forms via extending Zend_Form. And I use one Form for addAction() and editAction(). When I want to remove Elements within the editing process I can do so easily via $form->removeElement('x').
But what would be the best approach on removing a field from the validator?
1) Removing and Adding the newly set validator
//Controllers editAction()
$form->removeValidator('Db_NoRecordExists');
$form->addValidator('Db_NoRecordExists', true, array(
'table'=>'table',
'field'=>'field',
'exclude'=>array(
'field'=>'id',
'value'=>$this->_getParam('id')
)
));
2) Injecting editing ID into the Form
//Forms Contstructor
public function __construct($idToEdit=0, $options=null)
{
$this->setIdToEdit($idToEdit);
parent::__construct($options);
}
//within init()
$formField->addValidator('Db_NoRecordExists', true, array(
'table'=>'table',
'field'=>'field',
'exclude'=>array(
'field'=>'id',
'value'=>$this->getIdToEdit()
)
));
//Controller calling the form like this:
$form = new Custom_Form($this->_getParam('id'), $options);
3) Something else?
Maybe there is even something else I am missing, to me though somehow both ideas don't look too well to me.
For a cleaner use of SO here the answer as a post
//SOLUTION Okay, so while browsing to Zends Sourcecode (should have done that before asking...) i found the best solution (i guess). The Abstract DB Validation classes got a
function setExclude() so we can then use it in a nice flow:
//Inside Controller before valling $form->isValid()
$form->getElement('x')->getValidator('Db_NoRecordExists')->setExclude(array(
'field'=>'some_id',
'value'=>$idToEdit
))
Related
I would like to create reusable code in controller in "Cakephp way". I would like to replace always one field in few controllers before render website. For example I would like to replace string in field "body". I can do this like this in show method:
public function show($id = null) {
$site = $this->Sites->findById($id)->first();
$new_value = 'test2';
$site['body'] = str_replace('test', $new_value, $site['body']);
}
Is there any better way to do this in cakephp way for example in initalize method or beforeRender? I can't use behavior here.
EDIT:
I know about components, but how to use it to replace all $site['body] (in my code) for all controller methods (so I would like to do this automatic, like behavior for entity)?
Read about Components.
Components are packages of logic that are shared between controllers. CakePHP comes with a fantastic set of core components you can use to aid in various common tasks. You can also create your own components. If you find yourself wanting to copy and paste things between controllers, you should consider creating your own component to contain the functionality. Creating components keeps controller code clean and allows you to reuse code between different controllers.
And see Component Callbacks.
You can use component
https://book.cakephp.org/3.0/en/controllers/components.html
Don't forget to load it in appController or where your need it
After edit :
#nexequ
Maybe if you set the beforeRender in your appController
public function beforeRender()
{
debug($this->request);
}
In $this->request->data array you have your data to replace.
Exemple:
data => array(
'Reunion' => array(
'begin' => '2017-01-13 20:00:00',
'end' => '2017-01-13 20:30:00'
)
If you find the way to get the model ("Reunion" in my example.)
You can do a trick like
replace --> $this->request->data[$model]['body']
I found solution with burzum help, I can use virtual property in src/Model/Entity:
protected function _getBody() {
$new_value = 'test2';
$test = str_replace('test2', $new_value, $this->_properties['body']);
return $test;
}
It will replace for instance 'test2' with $new_value in all controller methods.
Assume for a moment that this form utilizes an imaginary Animal document object class from a ZooCollection that has only two properties ("name" and "color") in symfony2.
I'm looking for a working simple stupid solution, to pre-fill the form fields with the given object auto-magically (eg. for updates ?).
Acme/DemoBundle/Controller/CustomController:
public function updateAnimalAction(Request $request)
{
...
// Create the form and handle the request
$form = $this->createForm(AnimalType(), $animal);
// Set the data again << doesn't work ?
$form->setData($form->getData());
$form->handleRequest($request);
...
}
You should load the animal object, which you want to update. createForm() will use the loaded object for filling up the field in your form.
Assuming you are using annotations to define your routes:
/**
* #Route("/animal/{animal}")
* #Method("PUT")
*/
public function updateAnimalAction(Request $request, Animal $animal) {
$form = $this->createForm(AnimalType(), $animal, array(
'method' => 'PUT', // You have to specify the method, if you are using PUT
// method otherwise handleRequest() can't
// process your request.
));
$form->handleRequest($request);
if ($form->isValid()) {
...
}
...
}
I think its always a good idea to learn from the code generated by Symfony and doctrine console commands (doctrine:generate:crud). You can learn the idea and the way you should handle this type of requests.
Creating your form using the object is the best approach (see #dtengeri's answer). But you could also use $form->setData() with an associative array, and that sounds like what you were asking for. This is helpful when not using an ORM, or if you just need to change a subset of the form's data.
http://api.symfony.com/2.8/Symfony/Component/Form/Form.html#method_setData
The massive gotcha is that any default values in your form builder will not be overridden by setData(). This is counter-intuitive, but it's how Symfony works. Discussion:
https://github.com/symfony/symfony/issues/7141
I use a certain form in several places. In one of them I need to ignore a form element which I set programmatically after the validation.
Because it's just an exception I don't want to create a new form. So I thought, I just remove this element in the controller like:
$myForm->remove('myElement');
The problem is that the form now won't validate. I don't get any errors but the $myForm->isValid() just returns an empty value.
Any ideas what I might be doing wrong?
Ok, finally I found a solution! You can define a ValidationGroup which allows you to set the attributes you'd like to validate. The others are not validated:
$form->setValidationGroup('name', 'email', 'subject', 'message');
$form->setData($data);
if ($form->isValid()) {
...
The first thing I thought about was to remove the validator from your myElement's ValidatorChain. You could get it within the controller with:
$form->getInputFilter()->get( 'myElement' )->getValidatorChain()
It seems like you can't remove from the ValidatorChain, just add. Check this post. Matthew Weier O'Phinney, from Zend, explains why it can't be done and a possible solution for your scenario.
The way I solve this problem, is checking the 'remove condition' when I create the validator in the FormFilter class. If you use annotations I think it doesn't works for you, so Matthew suggestions is the one you should use.
Or you could try the one in this post from #Stoyan Dimov: define two forms, a kind of BasicForm and ExtendedForm. The first one have all the common form elements, the second one is an extended one of the other with the rest of fields. Depending on your condition you could use one or another.
In class ValidatorChain implements Countable, ValidatorInterface, add a new method:
public function remove($name){
foreach ($this->validators as $key => $element) {
$validator = $element['instance'];
if($validator instanceof $name){
unset($this->validators[$key]);
break;
}
}
}
Use like this:
$form->getInputFilter()->get("xxxxx")->getValidatorChain()->remove('xxxxxx');
There must be a validator defined for this particular element that you are trying to remove.
In your controller where you are adding new elements to form, there must be addValidator calling like:
$element->addValidator('alnum');
That is actually causing validation to be failed. So you have removed the element from form but you still have validation defined on that element to be checked.
If you are not able to find this validation adding function in controller, try to see if it has been defined through config file.
You can read further about form validation in zf here: http://framework.zend.com/manual/1.12/en/zend.form.elements.html
I remove the element with:
$form->get('product')->remove('version');
When I post the form I disable the validator on this element with :
$form->getInputFilter()->get('product')->get('version')->setRequired(FALSE);
I am using Symfony with propel to generate a form called BaseMeetingMeetingsForm.
In MeetingMeetingsForm.class.php I have the following configure method:
public function configure() {
$this->useFields(array('name', 'group_id', 'location', 'start', 'length'));
$this->widgetSchema['invited'] = new myWidgetFormTokenAutocompleter(array("url"=>"/user/json"));
}
In MeetingMeetings.php my save method is simply:
public function save(PropelPDO $con = null) {
$this->setOwnerId(Meeting::getUserId());
return parent::save($con);
}
However propel doesn't know about my custom field and as such doesn't do anything with it. Where and how to I put in a special section that can deal with this form field, please be aware it is not just a simple save to database, I need to deal with the input specially before it is input.
Thanks for your time and advice,
You have to define a validator (and/or create your own). The validator clean() method returns the value that needs to be persisted.
In Doctrine (I don't know Propel) the form then calls the doUpdateObject() on the form, which in turns calls the fromArray($arr) function on the model.
So if it's already a property on your model you'll only need to create the validator. If it's a more complex widget, you'll need to add some logic to the form.
I am using the symfony embedRelation method to embed forms.The code is like this:
public function configure(){
//......
$this->embedRelation('Foos as foos');
$this->getEmbeddedForm('foos')->mergePostValidator(new MenuValidatorSchema());
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
this does not work.
}
When embeding forms in Symfony, the top-level form keeps track of everything. The widget schema, validator schema, defaults, etc. of the embedded form are no longer directly used. You can see what's happening in sfForm::embedForm.
Note that in this case, since it's a post validator, it's perfectly acceptable to add it to the top-level form, i.e.:
$this->mergePostValidator(new MenuValidatorSchema());
If you want the validator schema on the embedded form and it has no current post validator, you can simply do:
$this->validatorSchema['foos']->setPostValidator(new MenuValidatorSchema());
If it has an existing one, you'll have to turn them into an sfValidatorAnd, doing something like:
$this->validatorSchema['foos']->setPostValidator(new sfValidatorAnd(array(
'validators' => array(
$this->validatorSchema['foos']->getPostValidator(),
new MenuValidatorSchema()
)
)));
The syntax of that last option is just one reason why setting post validators on the top-level form is the preferred option when available.