custom attribute displaying but is not saving in magento 2 admin customer creation form - magento2

i have created new customer attribute in my magento 2 environment
field is added the data in that filed is not saving.i am getting error as something went wrong while saving data.there is no good tutorials i could find to add new attribute.please help with this.
i have followed this code
https://magento.stackexchange.com/questions/128178/magento-2-add-custom-attribute-in-customer-registration-form

Customer Registration Custom attribute value create in admin and save
1. Text Field
2. Drop Down Field
3. Date Field
Using UpgradeSchema.php
<?php
namespace {CompanyName}\{ModuleName}\Setup;
use Magento\Customer\Model\Customer;
use Magento\Customer\Setup\CustomerSetup;
/* irrelevant */
#use Magento\Framework\Setup\UpgradeSchemaInterface;
use Magento\Framework\Setup\InstallSchemaInterface;
use Magento\Framework\Setup\ModuleContextInterface;
/* irrelevant */
#use Magento\Framework\Setup\SchemaSetupInterface;
/* add this */
use Magento\Framework\Setup\UpgradeDataInterface;
use Magento\Framework\Setup\ModuleDataSetupInterface;
class UpgradeData implements UpgradeDataInterface
{
private $customerSetupFactory;
public function __construct(\Magento\Customer\Setup\CustomerSetupFactory $customerSetupFactory)
{
$this->customerSetupFactory = $customerSetupFactory;
}
public function upgrade(ModuleDataSetupInterface $setup, ModuleContextInterface $context)
{
if (version_compare($context->getVersion(), '1.0.1', '<'))
{
// For Text field
$customerSetup->addAttribute(
\Magento\Customer\Model\Customer::ENTITY,
'attribute_title',
[
'type' => 'text',
'input' => 'text',
'label' => 'Attribute Title',
'required' => false,
'visible' => true,
'user_defined' => false,
'sort_order' => 1000,
'position' => 1000,
'system' => 0,
]
);
$attribute_title = $customerSetup->getEavConfig()->getAttribute(Customer::ENTITY, 'attribute_title')
->addData(
['used_in_forms' => ['adminhtml_customer']
]);
$attribute_title->save();
//Add field Drop Down for Yes/No
$customerSetup->addAttribute(
\Magento\Customer\Model\Customer::ENTITY,
'is_attribute',
[
'type' => 'int',
'input' => 'select',
'label' => 'Is Attribute',
'frontend' => '',
'default' => '1',
'class' => '',
'source' => 'Magento\Eav\Model\Entity\Attribute\Source\Boolean',
'backend' => 'Magento\Eav\Model\Entity\Attribute\Backend\ArrayBackend',
'global' => \Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface::SCOPE_GLOBAL,
'required' => false,
'visible' => true,
'user_defined' => false,
'sort_order' => 1000,
'position' => 1000,
'system' => 0,
]
);
$is_attribute = $customerSetup->getEavConfig()->getAttribute(Customer::ENTITY, 'is_attribute')
->addData(
['used_in_forms' => ['adminhtml_customer']
]);
$is_attribute->save();
// For Date And Time field
$customerSetup->addAttribute(
\Magento\Customer\Model\Customer::ENTITY,
'custom_date',
[
'label' => 'Custom Date',
'type' => 'datetime',
'input' => 'date',
'frontend' => 'Magento\Eav\Model\Entity\Attribute\Frontend\Datetime',
'backend' => 'Magento\Eav\Model\Entity\Attribute\Backend\Datetime',
'validate_rules' => '{"input_validation":"date"}',
'user_defined' => false,
'required' => false,
'visible' => true,
'searchable' => false,
'filterable' => false,
'comparable' => false,
'visible_on_front' => false,
'sort_order' => 1000,
'position' => 1000,
'system' => 0,
]
);
$custom_date = $customerSetup->getEavConfig()->getAttribute(Customer::ENTITY, 'custom_date')
->addData(
['used_in_forms' => ['adminhtml_customer']
]);
// more used_in_forms ['adminhtml_checkout','adminhtml_customer','adminhtml_customer_address','customer_account_edit','customer_address_edit','customer_register_address']
$commenced_business->save();
}
}
}

Data save will not happen by itself you need to set data into attribute.
If its part of customer Interface
For example
$attribute = $customer->getCustomAttribute('client_dn');
if ($attribute)
{
$customer->setValue("hi");
}
if you are saving using customer object
$customer->setData('client_dn', 'Hi');

Related

Validation error "This value should not be blank" when submitting a form on production website

I'm developing a website using php 7.4, symfony 5.4 and twig. This website is deployed on several servers.
On one of the servers (RedHat), a form cannot be submitted. I get the following error 4 times : "This value should not be blank.".
The messages appear on top of the form and aren't attached to a particular field.
I can't reproduce this error on another server, nor on my development environment...
The problem might comes from a validator but I'm not sure whether it's a symfony or a doctrine error.
The POST data is identical on production server and dev environment :
report_selection[iFrame]: 1
report_selection[dteFrom]: 2023-01-30 07:00
report_selection[dteTo]: 2023-01-31 07:00
report_selection[reportType]: 1
report_selection[size]: 200
report_selection[product]: 1
report_selection[submit]:
I assume that the empty field submit is not a problem since other forms work fine while having the same field empty.
The database structure is the same on all servers.
Here is the form's code :
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$bDisplaySize = $options['bDisplaySize'];
$bDisplayReportType = $options['bDisplayReportType'];
$bDisplayProduct = $options['bDisplayProduct'];
$defaultValue = $options['defaultValue'];
$em = $options['entity_manager'];
list($H, $m) = explode(":", $iShiftStart);
$initialFromDate = (new DateTime())->modify('-'.$H.' hour');
$initialFromDate = $initialFromDate->modify('-1 day');
$initialFromDate->setTime((int)$iShiftStart, (int)$m, 0);
$initialToDate = clone $initialFromDate;
$initialToDate = $initialToDate->modify('+1 day');
$builder->add(
'iFrame',
ChoiceType::class,
array(
'label' => 'master.preselection',
'choices' => [
'master.yesterday' => false,
'master.today' => false,
'master.thisWeek' => false,
'master.lastWeek' => false,
'master.thisMonth' => false,
'master.lastMonth' => false,
'master.memomryDate' => false,
],
'attr' => ['onchange' => 'refreshPreselectedChoices()'],
'choice_attr' => [
'master.yesterday' => [],
'master.today' => ['selected' => 'selected'],
'master.thisWeek' => [],
'master.lastWeek' => [],
'master.thisMonth' => [],
'master.lastMonth' => [],
'master.memomryDate' => ['disabled' => true],
],
)
);
$builder->add(
'dteFrom',
TextType::class,
array(
'label' => 'form.from',
'data' => $initialFromDate->format('Y-m-d H:i'),
'attr' => array(
'style' => 'width:150px;',
'oninput' => 'dteFromToCustom()',
'onchange' => 'dteFromToCustom()',
),
)
);
$builder->add(
'dteTo',
TextType::class,
array(
'label' => 'form.to',
'data' => $initialToDate->format('Y-m-d H:i'),
'attr' => array(
'label' => 'form.to',
'style' => 'width:150px;',
'oninput' => 'dteFromToCustom()',
'onchange' => 'dteFromToCustom()',
),
)
);
if ($bDisplayReportType) {
$builder->add(
'reportType',
ChoiceType::class,
array(
'label' => 'summaryReport.data',
'choices' => array(
'summaryReport.type1' => '1',
'summaryReport.type2' => '2',
),
)
);
}
if ($bDisplaySize) {
$builder->add(
'size',
EntityType::class,
array(
'class' => ProductsSizeSpecs::class,
'choice_label' => 'rSize',
'choice_value' => 'rSize',
'placeholder' => '',
'label' => 'form.size',
'required' => false,
'mapped' => false,
'query_builder' => function (EntityRepository $er) {
return $er->createQueryBuilder('e')
->groupBy('e.rSize')
->orderBy('e.rSize', 'ASC');
},
)
);
}
if ($bDisplayProduct) {
$builder->add(
'product',
EntityType::class,
array(
'class' => Products::class,
'choice_label' => 'sNumber',
'choice_value' => 'sNumber',
'placeholder' => '',
'label' => 'master.product',
'required' => false,
'query_builder' => function (EntityRepository $er) {
return $er->createQueryBuilder('e')
->groupBy('e.sNumber')
->orderBy('e.sNumber', 'ASC');
},
)
);
}
$builder->add(
'submit',
SubmitType::class,
array(
'label' => 'form.submit',
'attr' => array('class' => 'btn btn-primary'),
)
);
}
Other forms use the exact same code with more or less options.
I search a way to debug this on the production server (list/dump of 'blank' fields?).
Any hint will be appreciated, thanks !
Indeed I had some #Assert\NotBlank on several columns of an Entity.
Why the error was only on this server :
An instance (db record) of this Entity was NULL on those columns (which is an anormal behavior).
All the instances where retrieved to populate the form's dropdowns (as 'default' data).
It looks like the validator is checking the submitted 'data' AND those 'default' values since they are part of the form.
There were 4 asserted columns, so that's why I had 4 errors messages.
What I've done to find this out :
Added a dump($this->form->getErrors()) instruction on the callback processing the submitted form. It displayed the 4 entity's columns giving me hard time.
Went into db to see the corrupted record, and deleted it.
To prevent this in the future I might change the default values of these columns from NULL to something else, a basic string or a 0 value, and search the process that led to this corrupted record in db.
Thanks for your hints guys !

Radio button: input was not found in the haystack?

Whenever I submit the form I get this message:
The input was not found in the haystack.
This is for the shipping-method element (radio button). Can't figure out what it means, the POST data for that element is not null.
public function getInputFilter()
{
if (!$this->inputFilter) {
$inputFilter = new InputFilter();
// Some other basic filters
$inputFilter->add(array(
'name' => 'shipping-method',
'required' => true,
'filters' => array(
array('name' => 'StripTags'),
array('name' => 'StringTrim')
),
'validators' => array(
array(
'name' => 'StringLength',
'options' => array(
'encoding' => 'UTF-8',
'max' => 20,
),
),
array(
'name' => 'Db\RecordExists',
'options' => array(
'table' => 'shipping',
'field' => 'shipping_method',
'adapter' => $this->dbAdapter
)
),
),
));
$inputFilter->get('shipping-address-2')->setRequired(false);
$inputFilter->get('shipping-address-3')->setRequired(false);
$this->inputFilter = $inputFilter;
}
return $this->inputFilter;
}
I only keep finding solutions for <select>.
Here's the sample POST data:
object(Zend\Stdlib\Parameters)#143 (1) {
["storage":"ArrayObject":private] => array(9) {
["shipping-name"] => string(4) "TEST"
["shipping-address-1"] => string(4) "test"
["shipping-address-2"] => string(0) ""
["shipping-address-3"] => string(0) ""
["shipping-city"] => string(4) "TEST"
["shipping-state"] => string(4) "TEST"
["shipping-country"] => string(4) "TEST"
["shipping-method"] => string(6) "Ground"
["submit-cart-shipping"] => string(0) ""
}
}
UPDATE:
form.phtml
<div class="form-group">
<?= $this->formRow($form->get('shipping-method')); ?>
<?= $this->formRadio($form->get('shipping-method')
->setValueOptions(array(
'Ground' => 'Ground',
'Expedited' => 'Expedited'))
->setDisableInArrayValidator(true)); ?>
</div>
ShippingForm.php
$this->add(array(
'name' => 'shipping-method',
'type' => 'Zend\Form\Element\Radio',
'options' => array(
'label' => 'Shipping Method',
'label_attributes' => array(
'class' => 'lbl-shipping-method'
),
)
));
The problem lies with when you use the setValueOptions() and the setDisableInArrayValidator(). You should do this earlier within your code as it is never set before validating your form and so the inputfilter still contain the defaults as the InArray validator. As after validation, which checks the inputfilter, you set different options for the shipping_methods.
You should move the setValueOptions() and the setDisableInArrayValidator() before the $form->isValid(). Either by setting the right options within the form itsself or doing this in the controller. Best way is to keep all of the options in one place and doing it inside the form class.
$this->add([
'name' => 'shipping-method',
'type' => 'Zend\Form\Element\Radio',
'options' => [
'value_options' => [
'Ground' => 'Ground',
'Expedited' => 'Expedited'
],
'disable_inarray_validator' => true,
'label' => 'Shipping Method',
'label_attributes' => [
'class' => 'lbl-shipping-method',
],
],
]);
Another small detail you might want to change is setting the value options. They are now hardcoded but your inputfilter is checking against database records whether they exist or not. Populate the value options with the database records. If the code still contains old methods but the database has a few new ones, they are not in sync.
class ShippingForm extends Form
{
private $dbAdapter;
public function __construct(AdapterInterface $dbAdapter, $name = 'shipping-form', $options = [])
{
parent::__construct($name, $options)
// inject the databaseAdapter into your form
$this->dbAdapter = $dbAdapter;
}
public function init()
{
// adding form elements to the form
// we use the init method to add form elements as from this point
// we also have access to custom form elements which the constructor doesn't
$this->add([
'name' => 'shipping-method',
'type' => 'Zend\Form\Element\Radio',
'options' => [
'value_options' => $this->getDbValueOptions(),
'disable_inarray_validator' => true,
'label' => 'Shipping Method',
'label_attributes' => [
'class' => 'lbl-shipping-method',
],
],
]);
}
private function getDbValueOptions()
{
$statement = $this->dbAdapter->query('SELECT shipping_method FROM shipping');
$rows = $statement->execute();
$valueOptions = [];
foreach ($rows as $row) {
$valueOptions[$row['shipping_method']] = $row['shipping_method'];
}
return $valueOptions;
}
}
Just had this happen yesterday.
The select and multi select ZF2+ elements have a built in in_array validator.
Remember filters occur before validators.
You may be doing too much here -- it is very rare to need to filter or add validators ot select and multi select form elements in ZF2 forms. The built in element validator is robust, ZF does a lot of work for us.
Try removing both filter and validator for the element, such as:
$inputFilter->add(array(
'name' => 'shipping-method',
'required' => true,
));
There is another edge case that I have seen: changing the select element's valueOptions somewhere in the controller (or view) resulting in different valueOptions used in view vs form validation (in our case it was replacing the element with a new one before validation).
I think your problem lies in the fact you are adding your value options after the InArray validator has been set, hence the validator has no haystack.
Try this
$this->add(array(
'name' => 'shipping-method',
'type' => 'Zend\Form\Element\Radio',
'options' => array(
'label' => 'Shipping Method',
'label_attributes' => array(
'class' => 'lbl-shipping-method'
),
'value_options' => array(
'Ground' => 'Ground',
'Expedited' => 'Expedited'
),
'disable_inarray_validator' => TRUE,
)
));
and remove setValueOptions and setDisableInArrayValidator from your view.
Hope this works.

Magento 2 Get Category list in admin tab/main.php

I have created a custom module.Now I want to get categories in drop down in admin.
The file is on the following path,
app/code/vendor/theme/block/adminhtml/catbanner/edit/tab/Main.php
The html is for dropdown is,
$fieldset->addField(
'banner_category',
'select',
[
'label' => __('Select Category'),
'title' => __('Select Category'),
'name' => 'banner_category',
'required' => true,
'options' => \vendor\module\Block\Adminhtml\Catbanner\Grid::getOptionArray1(),
'disabled' => $isElementDisabled
]
);
I want the options to be populated by the categories.Kindly help how can i do that?
Use below code for fieldset
$fieldset->addField(
'category',
'select',
[
'name' => 'category',
'label' => __('Category'),
'id' => 'category',
'title' => __('Category'),
'values' => \vendor\module\Block\Adminhtml\Catbanner\Grid::getOptionArray1(),
'class' => 'category',
'required' => true,
]);
In your grid block use below code:
public function getOptionArray1()
{
$categoryCollection = $this->_categoryCollectionFactory->create()
->addAttributeToSelect(array('id','name'))
->addAttributeToFilter('is_active','1');
$options = array();
foreach($categoryCollection as $category){
$options[] = array(
'label' => $category->getName(),
'value' => $category->getId()
);
}
return $options;
}
Hope this will work for you...

Magento "visible" config key not recognized in EAV custom attribute

I am using the getDefaultEntities() function which is run from my installer script. It mostly works, almost all of the attribute config keys reflect properly in the Attributes section of the admin. However the "visible," "visible_on_front" and some of the other properties do not work at all. My custom attribute is always set to not visible, not visible on front end. Can anyone spot what I am doing wrong?
class Ia_AdvancedShipping_Model_Resource_Eav_Mysql4_Setup extends Mage_Eav_Model_Entity_Setup
{
/**
* #return array
*/
public function getDefaultEntities()
{
return array(
'catalog_product' => array(
'entity_model' => 'catalog/product',
'attribute_model' => 'catalog/resource_eav_attribute',
'table' => 'catalog/product',
'additional_attribute_table' => 'catalog/eav_attribute',
'entity_attribute_collection' => 'catalog/product_attribute_collection',
'attributes' => array(
'iaadvancedshipping_profile' => array(
'group' => 'Advanced Shipping',
'label' => 'Shipping Profiles',
'type' => 'varchar',
'input' => 'select',
'source' => 'iaadvancedshipping/product_attribute_source_profiles',
'default' => '0',
'class' => '',
'backend' => '',
'frontend' => '',
'global' => Mage_Catalog_Model_Resource_Eav_Attribute::SCOPE_STORE,
'visible' => true,
'required' => false,
'user_defined' => false,
'searchable' => false,
'filterable' => false,
'comparable' => false,
'visible_on_front' => true,
'visible_in_advanced_search' => false,
'unique' => false
),
)
)
);
}
}

How to pre-select a form radio item with Symfony 2?

I'm working on a language choice form:
$currentLocale = "en_US"; // This is indeed sent to the formType
$langs = array(
'fr_FR' => 'fr',
'en_US' => 'en'
);
$builder->add('language', 'language', array(
'choices' => $langs,
'expanded' => true,
'multiple' => false,
'required' => false,
'label' => false,
));
The HTML code looks like this (simplified):
<div id="languageForm_language">
<input type="radio" value="fr_FR">
<input type="radio" value="en_US">
</div>
How could I get the second item pre-selected, according to the $currentLocale value ?
In your $langs array you can specify key value pairs like this:
array(
0 => 'value1',
1 => 'value2'
)
Now, e.g. you want to preselect value2, you can set the data attribute to the key from value2:
$builder->add('language', 'choice', array(
'choices' => $langs,
'expanded' => true,
'multiple' => false,
'required' => false,
'label' => false,
'data' => 1
));
According to this, you can set your data attribute to your $currentLocale variable to preselect it. Your code should look like this:
$currentLocale = "en_US"; // This is indeed sent to the formType
$langs = array(
'fr_FR' => 'fr',
'en_US' => 'en'
);
$builder->add('language', 'choice', array(
'choices' => $langs,
'expanded' => true,
'multiple' => false,
'required' => false,
'label' => false,
'data' => $currentLocale
));
Note: the second parameter from the add() method should be choice not language.
If the form is used with a model object, just set the language on the object itself before passing it to the form:
$object->setLanguage($currentLocale);
$form = $this->createForm('some_form_type', $object);
Otherwise, set the data option to the default language key:
$builder->add('language', 'language', array(
'choices' => $langs,
'data' => $currentLocale,
// ...
));