Using Magento 2.3.0
Whenever trying to save customer I get errors that newly created attributes are required, even when I set their values.
etc/extend_attributes.xml
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Api/etc/extension_attributes.xsd">
<extension_attributes for="Magento\Customer\Api\Data\CustomerInterface">
<attribute code="customershipping_enabled" type="string" />
<attribute code="customershipping_price" type="string" />
</extension_attributes>
</config>
Setup/InstallData.php
<?php
namespace <vendor>\<module_name>\Setup;
use Magento\Eav\Model\Entity\Attribute\Source\Boolean;
use Magento\Framework\Setup\InstallDataInterface;
use Magento\Framework\Setup\ModuleContextInterface;
use Magento\Framework\Setup\ModuleDataSetupInterface;
class InstallData implements InstallDataInterface {
private $customerSetupFactory;
public function __construct(
\Magento\Customer\Setup\CustomerSetupFactory $customerSetupFactory
) {
$this->customerSetupFactory = $customerSetupFactory;
}
public function install(ModuleDataSetupInterface $setup, ModuleContextInterface $context) {
$customerSetup =$this->customerSetupFactory->create(['setup'=>$setup]);
$setup->startSetup();
$customerSetup->addAttribute('customer', 'customershipping_enabled', [
'label'=>'Customer Shipping Enabled',
'type' => 'int',
'input' => 'select',
'source' => Boolean::class,
'required'=>true,
'visible'=>true,
'default' => 0,
'position' => 198,
]);
$customerSetup->addAttribute('customer', 'customershipping_price', [
'label'=>'Customer Shipping Price',
'type'=>'decimal',
'input' => 'text',
'required'=>true,
'visible'=>true,
'default' => 0,
'position' => 199,
]);
$enabledAttribute = $customerSetup->getEavConfig()->getAttribute('customer', 'customershipping_enabled');
$enabledAttribute->setData('used_in_forms', ['adminhtml_customer']);
$enabledAttribute->save();
$priceAttribute = $customerSetup->getEavConfig()->getAttribute('customer', 'customershipping_price');
$priceAttribute->setData('used_in_forms', ['adminhtml_customer']);
$priceAttribute->save();
$setup->endSetup();
}
}
I have read many tutorials and documentation on this, and I believe this should be working correctly, am I missing something?
Whenever I try to add new customer or update existing customer, it says that these 2 attributes are required values, save fails.
Also looks identical to this post:
mage2gen.com/snippets/customerattribute
I had similar issue recently, try to add this in 'used_in_forms'.
You might have to remove the attribute and reinstall it:
'used_in_forms' => ['adminhtml_customer', 'customer_account_edit', 'customer_account_create']
edit
Oh I think this should solve the issue, just checked my installData and upgradeData scripts and they all have system => 0. Just add it in.
$customerSetup->addAttribute('customer', 'customershipping_enabled', [
'label'=>'Customer Shipping Enabled',
'type' => 'int',
'input' => 'select',
'source' => Boolean::class,
'required'=>true,
'visible'=>true,
'default' => 0,
'position' => 198,
'system' => 0
]);
It'll be related to this issue:
https://apiworks.net/magento2/magento-2-is-not-saving-the-customer-attribute/
The function getCustomAttributesMetadata is looping through all EAV
attributes and checking if the attribute is marked as “is_system”
inside the “customer_eav_attribute” table, which was the case with my
custom attribute.
Solution:
By default, Magento flagged my custom attribute as is_system = 1, so I
just needed to add ‘system’ => false in my upgrade script and execute
it again (after I removed the original attribute directly from the
database. ).
Root cause of this issue is design behavior of magento 2.
If custom attribute is set as a required one than it must be configured to be shown on storefront and to be shown in all the forms.
If you wants a custom attribute to be required only on some certain forms, then an extension attribute should be used instead with 'required'=>false.
Extension attributes are used to extend functionality of custom attributes.
You just need to replace
'required'=>true,
with
'required'=>false,
For more details please refer the link:
Click here
Related
In my project I have 3 entities; User, Module, and UserModule.
User has properties such as id, username, email along with a UserModules property that is a collection of UserModules.
Module has properties id, name and description.
Each UserModule object has properties User(reference to corresponding User), Module (reference to corresponding module) and Access (boolean for whether or not access is permitted)
My issue is I do not know how to use Symfony's Form types to show all the modules when creating a User.
I have a number of modules made already (i.e. 'admin', 'ticket', 'help'). In my UserType class my buildForm method looks like:
{
$builder
->add('username')
->add('email')
->add('modules', CollectionType::class, [
'entry_type' => UserModuleType::class,
'entry_options' => [
'label' => false
],
]);
}
my UserModuleType class is
{
$builder
->add('access', ChoiceType::class, [
'choices' => [
'No Access' => false,
'Full Access' => true,
]
]);
}
This shows the UserModules assigned to the User, but when creating a User, I want it to already have UserModules already added for each module.
How can I set this up so that the form will use all possible modules to create default UserModules?
I hope you will find some help here :
https://symfony.com/doc/current/form/form_collections.html
in the example : use tags as your modules
In this case, as you are only using true and false for defining if the user has access to a module, I would use the EntityType with the multiple attribute instead of the CollectionType.
This way you only set which modules have access.
Link to documentation
The title is rather self explanatory, but what i would like to have is a dynamic default value.
The idea behind it is to get the biggest number from a column in the database and then add one to the result. This result should be saved as the default value.
Lets take for example this code:
$GLOBALS['TCA'][$modelName]['columns']['autojobnumber'] = array(
'exclude' => true,
'label' => 'LLL:EXT:path/To/The/LLL:tx_extension_domain_model_job_autojobnumber',
'config' => [
'type' => 'input',
'size' => 10,
'eval' => 'trim,int',
'readOnly' =>1,
'default' => $result,
]
);
The SQL looks like this:
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('tx_extension_domain_model_job');
$getBiggestNumber = $queryBuilder
->select('autojobnumber')
->from('tx_extension_domain_model_job')
->groupBy('autojobnumber')
->orderBy('autojobnumber', 'DESC')
->setMaxResults(1)
->execute()
->fetchColumn(0);
$result = $getBiggestNumber + 1;
So how can i do that "clean"?
I thought about processCmdmap_preProcess but i dont know how to pass the value to the coorisponding TCA field. Plus i do not get any results on my backend when i use the DebuggerUtility like i get them when i use processDatamap_afterAllOperations after saving the Object.
Can someone point me to the right direction?
I don't think it is supported to create a dynamic default value, see default property of input field.
What you can do however, is to create your own type (use this instead of type="input"). You can use the "user" type. (It might also be possible to create your own renderType for type="input", I never did this, but created custom renderTypes for type= "select").
You can look at the code of InputTextElement, extend that or create your own from scratch.
core code: InputTextElement
documentation for user type
more examples in FormEngine documentation
Example
(slightly modified, from documentation)
ext_localconf.php:
$GLOBALS['TYPO3_CONF_VARS']['SYS']['formEngine']['nodeRegistry'][<current timestamp>] = [
'nodeName' => 'customInputField',
'priority' => 40,
'class' => \T3docs\Examples\Form\Element\CustomInputElement::class,
];
CustomInputElement
<?php
declare(strict_types = 1);
namespace Myvendor\MyExtension\Backend\FormEngine\Element\CustomInputElement;
use TYPO3\CMS\Backend\Form\Element\AbstractFormElement;
// extend from AbstractFormElement
// alternatively, extend from existing Type and extend it.
class CustomInputElement extends AbstractFormElement
{
public function render():array
{
$resultArray = $this->initializeResultArray();
// add some HTML
$resultArray['html'] = 'something ...';
// ... see docs + core for more info what you can set here!
return $resultArray;
}
}
When I try to add the wizard named wizard_geo_selector in TCA ,there arised an error "module not registered".Please tell me how to register the wizard properly in the TCA.?
In TYPO3 Version 7.6 new wizards are added like this:
Inside your extension create the directory Configuration/Backend/
In the new directory create a file Routes.php, it will be found automatically, no mentioning in ext_localconf.php or ext_tables.php is required. If you still need Ajax you can add the file AjaxRoutes.php in the same folder.
Content for Routes.php:
return array(
'my_wizard_element' => array(
'path' => '/wizard/tx_geoselecotor/geo_selector_wizard',
'target' => \Path\To\your\class\WizardGeoSelector::class . '::WizardAction'
),
);
Content for AjaxRoutes.php
<?php
/**
* Definitions for routes provided by EXT:backend
* Contains all AJAX-based routes for entry points
*
* Currently the "access" property is only used so no token creation + validation is made
* but will be extended further.
*/
return array('my_ajax_element' => array(
'path' => 'tx_geoselecotor/my_ajax_route',
'target' => \Path\To\your\class\MyAjaxController::class .'::myAjaxFunction'
));
If you're unsure about the notation you can compare with existing entries in the Global Variables in the Backend:
Navigate to System -> Configuration -> Backend Routes
The route of the paths is handled different, for Ajax it's always "ajax" prepended, so you've never to add it to the path, else it's twice in the route. For the common route there is no change concerning the defined string.
Now the wizard can be used and even it never has to be defined in ext_tables.php it has to be mentioned there from any table-field in the configuration-area (module[name]):
'table_field_for_wizard' => array(
'label' => 'LLL:EXT:my_extension/Resources/Private/Language/locallang.xml:table_name.tx_myextension_wizard',
'config' => array (
'type' => 'user',
'userFunc' => 'Path/to/class/without/wizard->renderForm',
'wizards' => array(
'my_wizard' => array(
'type' => 'popup',
'title' => 'MyTitle',
'JSopenParams' => 'height=700,width=780,status=0,menubar=0,scrollbars=1',
'icon' => 'EXT:' . $_EXTKEY . '/Resources/Public/img/link_popup.gif',
'module' => array(
'name' => 'my_wizard_element',
'urlParameters' => array(
'mode' => 'wizard',
'ajax' => '0',
'any' => '... parameters you need'
),
),
),
'_VALIGN' => 'middle',
'_PADDING' => '4',
),
# Optional
#'softref'=>'something',
),
),
In the userFunc Path/to/class/without/wizard->renderForm you've to create a button which is linking to the wizard and onClick the wizard will open with the route you defined in Routes.php and the optional urlParameters.
Currently I never found this whole item explained in the core-documentation.
Edit:
Details about routing can be found here: Routing
The rendering process can be found here: Rendering / NodeFactory
You should probably read also the outer context of the linked paragraph.
Edit 2:
An example extension can be found here, some things never work 100% but the wizard is working. The extension is for TYPO3 Version 7:
https://github.com/DavidBruchmann/imagemap_wizard
Ricky's answer doesn't really work anymore, since addModulePath ist deprecated since version 7.
Also, just registering the module like this still give's you said error.
The only thing that keeps the wizard going again is this:
\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addModule('wizard','pbsurvey_answers',"",\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::extPath($_EXTKEY).'wizard/');
But when you add this, the module appears as a new point in your TYPO3 backend.
IN TCA add the wizard like follows:
'module' => array(
'name' => 'wizard_geo_selector',
),
In ext_tables.php register the wizard.
\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addModulePath(
'wizard_geo_selector',
\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::extPath($_EXTKEY) . 'Modules/Wizards/Yourwizardname/'
);
Keep in mind this is deprecated since Typo3 7 and removed in Typo3 8.So you can use this method upto Typo3 7.For Typo3 8 do use the method specified by David below.
I have created a custom field sugarfield_ast_rec_link_c.php in custom/Extension/modules/Calls/Ext/Vardefs with such content:
`
<?php
$dictionary['Calls']['fields']['ast_rec_link_c'] = array
('name' => 'ast_rec_link_c',
'vname' => 'LBL_AST_REC_LINK_C',
'type' => 'varchar',
'len' => '255',
'source' => 'non-db',
'function' => array('name'=>'getRecordLink',
'returns'=>'html',
'include'=>'custom/modules/Calls/CustomLogic.php')
);
?>
`
Also aded language file in custom/Extension/modules/Calls/Ext/Language. After quick repair my custom field doesn't appear in Studio -> Calls -> Fields. So I can't put it on views. Can anyone help?
You should change
<?php
$dictionary['Calls']['fields']['ast_rec_link_c'] = array(...);
to
<?php
$dictionary['Call']['fields']['ast_rec_link_c'] = array(...);
Remember you should always use bean name (not module one!) as a $dictionary array key while defining new custom fields.
In my opinion best way to check if everything is OK with your custom vardefs is to compare your own ones with existing in cache/modules/<module_name>/BEAN_NAMEvardefs.php
I'm using a class form in Symfony2 Beta3 as follows:
namespace Partners\FrontendBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilder;
class ConfigForm extends AbstractType
{
public function buildForm(FormBuilder $builder, array $options)
{
$builder->add('no_containers', 'choice', array('choices' => array(1 => 'yes', 0 => 'no')));
...
I want to translate the 'yes' and 'no' options, but I don't know how to use the translator here.
You can use the translation resources as usual. This worked for me:
$builder->add('sex', 'choice', array(
'choices' => array(
1 => 'profile.show.sex.male',
2 => 'profile.show.sex.female',
),
'required' => false,
'label' => 'profile.show.sex.label',
'translation_domain' => 'AcmeUserBundle'
));
And then add your translations to the Resources->translations directory of your Bundle.
Update from #CptSadface:
In symfony 2.7, using the choice_label argument, you can specify the translation domain like this:
'choice_label' => 'typeName',
'choice_translation_domain' => 'messages',
Without specifying the domain, options are not translated.
I searched a while to find an answer, but finally I found out how Symfony translates form content. The easiest way in your case seems to be to just add a translation for "yes" and "no" by adding a YAML or XLIFF translation file to your application (e.g. app/Resources/translations/messages.de.yml) or your bundle. This is described here:
http://symfony.com/doc/current/book/translation.html
The problem - in my opinion - is that you don't seem to be able to use custom translation keys. The guys from FOSUserBundle solve this (or a similar) problem with "Form Themes" (http://symfony.com/doc/2.0/cookbook/form/form_customization.html). Here are two significant lines of code to achieve the usage of the form element id as translation key:
https://github.com/FriendsOfSymfony/FOSUserBundle/blob/master/Resources/views/Registration/register_content.html.twig#L1 / https://github.com/FriendsOfSymfony/FOSUserBundle/blob/50ab4d8fdfd324c1e722cb982e685abdc111be0b/Resources/views/form.html.twig#L4
By adding a Form Theme you're able to modify pretty much everything of the forms in the templates - this seems to be the right way of doing this.
(Sorry, I had to split two of the links b/c I don't have enough reputation to post more than two links. Sad.)
In symfony 2.7, using the choice_label argument, you can specify the translation domain like this:
'choice_label' => 'typeName',
'choice_translation_domain' => 'messages',
Without specifying the domain, options are not translated.
CptSadface's answer was what helped me with translating my entity choices.
$builder
->add(
'authorizationRoles',
null,
[
'label' => 'app.user.fields.authorization_roles',
'multiple' => true,
'choice_label' => 'name', // entity field storing your translation key
'choice_translation_domain' => 'messages',
]
);