How empting invisible field in Drupal 8? - forms

I'm using this in node add form alter to hide field 'field_obyavlenie_ploschad_uch':
$form['field_obyavlenie_ploschad_uch']['#states'] = [
'invisible' => [
'select[name="field_obyavlenie_rubrika"]' => ['value' => '4524'],
]
];
But if user enter any value in this field (before the field was hiding) I see this value in Node view.
I'm trying empty field value, but it not working:
$form['field_obyavlenie_ploschad_uch']['#states'] = [
'empty' => [
'select[name="field_obyavlenie_rubrika"]' => ['value' => '4524'],
]
];
How empting invisible field?

Following the comment of #2pha, you won't achieve this with the Form API states.
If you actually want the field to be emptied, you will have to add a custom validation or submit handler and unset the value.
However if you're not too bothered about the persisted value in the database, then you could leverage this in a preprocess hook (function). For your use case this would be a node preprocess hook and unset the value that should be invisible when another field has a specific value:
/**
* Prepares variables for node templates.
*
* #see template_preprocess_node()
*/
function YOUR_MODULE_preprocess_node(&$variables) {
/* #var $node \Drupal\node\NodeInterface */
$node = $variables['node'];
// Look for a node field value and act accordingly.
if ($node->get('field_obyavlenie_rubrika')->value == '4524') {
unset($variables['content']['foo']['field_obyavlenie_ploschad_uch']);
}
}

Related

Symfony Forms: Modify form field options with eventListener on fields with ModelTransformer

I have a form with a field that has a ModelTransformer. In some cases (relying on the field's data) the field must be disabled (= ignored), because the underlying data is related to another Entity which cannot be linked in the current context. But that is not important for my question.
There are several questions about modifying form options and how to solve that. In short: Inside the event callback listening to PRE_SET_DATA, the field must be added again (replacing the existing field) and the old options can be read from the existing field before and readded to the new one. This is not an elegant solution from my point of view, but it is one.
Until there is a Transformer (ModelTransformer oder ViewTransformer) on the existing field, because that transformer will be removed, when the field is added again and cannot be (re)applied from inside the event callback.
In my case, I am lucky because on a disabled field, the ModelTransformer is not needed anymore, so I can do basically that:
$builder->add(
$builder
->create('myFieldWithModelTransformer', CheckboxType::class, [])
->addModelTransformer(new CallbackTransformer(
function($originalValue) {
return $originalValue; // or do some transformation
},
function($submittedValue) {
return $submittedValue; // or do some transformation
}
))
);
$builder->addEventListener(FormEvents::PRE_SET_DATA, function(FormEvent $event) use ($options) {
if (true) { //whatever condition relying on the underlying data
$event->getForm()->add('myFieldWithModelTransformer', CheckboxType::class, [
'disabled' => true,
]);
// this removed the ModelTransformer, which also cannot be added in the event context
}
);
But what If I want to change another option like required while preserving the ModelTransformer?

Add a Field to a CrudController that ONLY passes values to Store / Update Methods

I'm trying to handle how a field within a CrudController stores or updates the data on the particular model in a completely custom way. I would like the traitStore() and traitUpdate() methods to ignore this field entirely, but would like the data to still be passed in via the request. This is specifically in reference to a many-many relationship using a select2_multiple field.
I would like it so that the relationship ID's are passed via the request object to the Store or Update methods, but I DO NOT want the traitStore() or traitUpdate() methods to actually perform updates on that particular field reference.
For example...
I have this field within my crud controller
$this->crud->addField(
[
'label' => "Groups",
'type' => 'select2_multiple',
'name' => 'groups',
'entity' => 'groups',
'attribute' => 'title',
'model' => "App\Models\Group",
'pivot' => true
]
);
And I'm overriding the Store and Update Methods like so.
public function store()
{
$this->crud->setValidation(UserRequest::class);
// WOULD LIKE TO SAVE EVERYTHING BUT IGNORE THE GROUPS FIELD
$response = $this->traitStore();
// DO WHATEVER I WANT WITH GROUPS AT THIS POINT
$groups = $request->groups
return $response;
}
public function update()
{
$this->crud->setValidation(UserRequest::class);
// WOULD LIKE TO SAVE EVERYTHING BUT IGNORE THE GROUPS FIELD
$response = $this->traitUpdate();
// DO WHATEVER I WANT WITH GROUPS AT THIS POINT
$groups = $request->groups
return $response;
}
Looking at my comments I would like to get a reference to the groups and handle updating the model however I want.
I've tried to unset the groups value in the request, unset($this->request{'groups'}), but it still updates / removes the relationships when I do that.
Here is what you need to do to remove the references from being updated by the CrudController.
public function update()
{
$this->crud->setValidation(UserRequest::class);
$request = clone $this->request;
$this->crud->request->request->remove('groups');
$this->crud->removeField('groups');
$groups = $request->groups
$response = $this->traitUpdate();
return $response;
}
I found an easy way to ignore/pass form field.
Example:
In your form fields have first_name, last_name, gender and in your database is only have fullname, gender then you wanna create/update the form, it will show Column not found: 'first_name' not found...,
How to fix it:
Add $fillable in the model and fill the array data with name field that you want to store/update. In example case $fillable = ['fullname', 'gender'];
Then, just use mutators inside the model too.
public function setFullnameAttribute(){
return $this->attributes['fullname'] = \Request::input('first_name') . ' ' . \Request::input('last_name');
}
NB: You should have hidden field name 'fullname' in your CrudController.
$this->crud->addField(['name' => 'fullname', 'type' => 'hidden']);

TYPO3 Extbase: How to store NULL in DB for empty numeric fields?

In a form Viewhelper I have lots of numeric fields, which should be empty by default, not filled with '0' or '0.00'.
I need this, because there should be a different handling of the data in the saveAction, depending on left empty fields or filled in values (including 0).
For this reason I set the field properties in the database table to NULL by default, like:
CREATE TABLE IF NOT EXISTS `tx_myext_domain_model_result` (
mw double (10,2) DEFAULT NULL,
mw2 double (10,2) DEFAULT NULL,
....
);
TCA looks like this:
'mw' => array(
...
'config' => array(
'type' => 'input',
'size' => 30,
'eval' => 'double2,null'
)
),
Also, in the Result model class, the corresponding properties are initialized with NULL;
/**
* mw
*
* #var float
*/
protected $mw = null;
I did this check to assure that NULL is handled over to the getter for empty form fields:
public function setMw($mw) {
if ($mw == NULL)
$this->mw = 999;
else
$this->mw = $mw;
}
This did what I excepted: The mw field DB was set to '999' for a empty form field.
But now, switched back to normal setter,
public function setMw($mw) {
$this->mw = $mw;
}
fields in the DB table are only left NULL when all other form fields are left empty, too. I.e. as soon as I enter a value in one of the form fields, all other empty form fields are set to '0' on save.
Adding null to the TCA eval field didn't do the trick, neither.
How can I change this behaviour? I'm using TYPO3 6.2.x
step 1: in initializeAction set null for the property
step 2: the idea with checking $mw is null is preaty good idea. TYPO3 not always do what you can assume using logical thinking :) So check if(is_null($mw)) $this->mw = null
this two things should do the trick
you can also set a default value in setter param
setMw($mw = null)

Empty text field set to null in symfony2 form

I've a form with a text field. This field maps to a not nullable field in my DB (is a legacy DB and I can't change this)
The problem is that Symfony2 always set empty text field to NULL and, this make the insert fails.
Is there a way to tell Symfony2 to not set empty text fields to NULL ?
Start by setting a blank value by default in the entity
/**
* #ORM\Column(name="example", type="string", length=255, nullable=false)
*/
private $example = '';
As for your issue, unfortunately you are describing a known issue/bug with Symfony, so you'll have to override the value being set to the setter function:
public function setExample($example = null) {
if (empty($example)) {
$example = '';
}
$this->example = $example;
}
The code below works for me. I tune the form definition.
$builder->add('comment', TextareaType::class, [
'empty_data' => '',
'required' => false,
]);

A set of fields for one property entity in Symfony 2

My Product entity has the following structure:
private $id;
private $title;
/**
* #ManyToOne(targetEntity="Category")
* #JoinColumn(name="cat_id", referencedColumnName="id")
*/
private $category;
Category have nested structure. And each level of nesting is shown in 5 separate fields:
In class form code, I solve it in this way:
$builder
->add('cat_1', 'entity', array(
...
'query_builder' => function() { return someSelectLogic1(); }
))
->add('cat_2', 'entity', array(
...
'query_builder' => function() { return someSelectLogic2(); }
))
->add('cat_3', 'entity', array(
...
'query_builder' => function() { return someSelectLogic3(); }
))
->add('cat_4', 'entity', array(
...
'query_builder' => function() { return someSelectLogic4(); }
))
->add('cat_5', 'entity', array(
...
'query_builder' => function() { return someSelectLogic5(); }
))
Now I need to know which field is filled in the last turn and pass the value of that field in the entity property.
In all that I do not like:
complex logic to determine which field with category was filled at the end
each of these fields is not tied to the entity 'mapped' => false
1) What the right way to organize code of my form?
2) And is there a way to bring these fields into a separate class which will deal with the logic of determining which category was chosen in the end?
I would suggest the following:
1) Create a new custom form field type and put all those entity in there.
This process is not much different from ordinary creation of form type. Just enclose those fields in it's own buildForm() and that should do the trick. Docs.
2) Mark all those entity fields with property "property_path => false".
Clearly you wont be storing these values inside your model.
3) Add two more fields: chosen and lastOne.
Now, this might be tricky: I would either set the chosen to text type (basically, generic type) or would use entity as well. If you go for entity you would need to include all possible answers from all entity fields. As for the lastOne set it to text as it will reflect which field (by name) was selected last.
Either way, those two fields will be invisible. Don't forget to set property_path to false for lastOne field.
4) Finally, add ValueTransformer (docs) which will contain logic to "see" which field was selected last.
Now, I dealt with it only once and don't understand it just quite yet, so your best bet would be trial and error with examples from official docs, unfortunately.
What basically you should do is to, within value-transformer, read the value of field lastOne. This will give you the name of field which was selected last. Then, using that value, read the actual last value selected. Last, set that value (object, if you've went for entity type, or it's ID otherwise) to chosen field.
That should basically do the thing.
As for the JS, I don't know if you're using any framework but I will assume jQuery. You will need to set lastOne field as your selecting items in your form.
$(function(){
$('#myform').find('select').on('change', function(){
var $this = $(this);
$this.closest('form').find('#__ID_OF_YOUR_LASTONE_FIELD').val($this.attr('name'));
});
});
I'm sorry I cannot provide you with code samples for PHP right now. It's a bit late here and will do my best to further update this answer tomorrow.