Adding a new custom Customer attribute when updating a module - php

I'm having troubles at creating new customer attribute when upgrading one of my modules.
I've created the UpgradeData.php file under /app/code/vendor/modulename/Setup/UpgradeData.php with the current code:
namespace Ucs\CustomerAttribute\Setup;
use Magento\Customer\Model\Customer;
use Magento\Framework\Setup\ModuleDataSetupInterface;
use Magento\Framework\Setup\UpgradeDataInterface;
use Magento\Framework\Setup\ModuleContextInterface;
use Magento\Customer\Setup\CustomerSetupFactory;
class UpgradeData implements UpgradeDataInterface{
private $customerSetupFactory;
public function __construct(
CustomerSetupFactory $customerSetupFactory
) {
$this->customerSetupFactory = $customerSetupFactory;
}
/**
* {#inheritdoc}
*/
public function upgrade(ModuleDataSetupInterface $setup, ModuleContextInterface $context){
$setup->startSetup();
$customerSetup = $this->customerSetupFactory->create(['setup' => $setup]);
if (version_compare($context->getVersion(), '1.0.6') < 0) {
$customerSetup->addAttribute(\Magento\Customer\Model\Customer::ENTITY, 'nome_azienda', [
'type' => 'varchar',
'label' => 'Nome azienda',
'input' => 'text',
'source' => '',
'required' => false,
'visible' => true,
'position' => 333,
'system' => false,
'backend' => ''
]);
$attribute = $customerSetup->getEavConfig()->getAttribute(\Magento\Customer\Model\Customer::ENTITY, 'nome_azienda')
->addData(['used_in_forms' => [
'adminhtml_customer',
'adminhtml_checkout',
'customer_account_create',
'customer_account_edit'
]]);
$attribute->save();
$customerSetup->addAttribute(\Magento\Customer\Model\Customer::ENTITY, 'codice_univoco', [
'type' => 'varchar',
'label' => 'Codice Univoco',
'input' => 'text',
'source' => '',
'required' => false,
'visible' => true,
'position' => 333,
'system' => false,
'backend' => ''
]);
$attribute = $customerSetup->getEavConfig()->getAttribute(\Magento\Customer\Model\Customer::ENTITY, 'codice_univoco')
->addData(['used_in_forms' => [
'adminhtml_customer',
'adminhtml_checkout',
'customer_account_create',
'customer_account_edit'
]]);
$attribute->save();
}
}
}
in short, it needs to create 2 new text (varchar) attributes. My module.xml has
setup_version="1.0.5" schema_version="1.0.5" so it should enter the version_compare condition and create the attribute, but, after running php bin/magento setup:upgrade it doesn't work. If i check in the setup_module table, the setup_version and schema_version change correctly with the version in the module.xml. It looks like for some reason the UpgradeData.php does not get executed at all.

In Ucs\CustomerAttribute\etc\module.xml
change version to 1.0.6
then replace
if (version_compare($context->getVersion(), '1.0.6') < 0) {
with
if (version_compare($context->getVersion(), '1.0.6', '<')) {
Edit: Just to be sure.. by
I've created the UpgradeData.php file under
/app/code/vendor/modulename/Setup/UpgradeData.php
You mean app/code/Ucs/CustomerAttribute/Setup/UpgradeData.php ?
Edit2:
I assumed Your agency is called Ucs. That's why I've asked about it, beacuse that's what suggest Your module namespace.
This is not recomended practice but for purpose of installator verification, change namespace to:
namespace vendor\modulename\Setup;
What I recomend is:
Create a new module or find app/code/[YOURCOMPANYNAME]/Customer - try to corespond Magento native modules. This way You can easier manage code, design and Magento doesn't need to load separated module for each functionallity.
In UpgradeData.php try to call separate function for each version.
Like:
if (version_compare($context->getVersion(), '1.0.1', '<')) {
$this->addCustomerMyAttribute();
}
$setup->endSetup();
and then below
private function addCustomerMyAttribute($setup){
// Your code goes here
}
If it's first version of Customer module in app/code/[YOURCOMPANYNAME] remember to create InstallData.php insted of UpgradeData.php (in that case no need to check version).
After bin/magento setup:upgrade check eav_attribute table for new attribute.
If it's there remember to bin/magento indexer:reindex so it goes to flat table. If it's not there. Put ```die(var_dump('I'm running'); at the beginning of upgrade function.

Related

Why does my custom code show warnings in the logs?

I have a site on Drupal 8.6 and Bootstrap 3.3.7
I created a custom module for the customer to accept the terms and conditions of the store when placing an order.
This displays a link to a checkbox before payment to display the terms and conditions in a modal window.
When I place an order here are the warnings in the logs (sorry my code is too long to be published here):
https://pastebin.com/1p5m1Ved
https://pastebin.com/XYbqDJje
https://pastebin.com/P93bStKh
Here is the file that created the problem :
<?php
namespace Drupal\commerce_marketplace_terms_and_conditions\Plugin\Commerce\CheckoutPane;
use Drupal\Component\Serialization\Json;
use Drupal\Core\Form\FormStateInterface;
use Drupal\commerce_checkout\Plugin\Commerce\CheckoutPane\CheckoutPaneBase;
use Drupal\commerce_checkout\Plugin\Commerce\CheckoutPane\CheckoutPaneInterface;
use Drupal\Core\Link;
use Drupal\Core\Url;
/**
* Provides the completion message pane.
*
* #CommerceCheckoutPane(
* id = "marketplace_terms_and_conditions",
* label = #Translation("Marketplace Terms and Conditions"),
* default_step = "review",
* )
*/
class MarketplaceTermsAndConditions extends CheckoutPaneBase implements CheckoutPaneInterface {
/**
* {#inheritdoc}
*/
public function buildPaneForm(array $pane_form, FormStateInterface $form_state, array &$complete_form) {
$store_name = $this->order->getStore()->getName();
$store_id = $this->order->getStoreId();
$pane_form['#attached']['library'][] = 'core/drupal.dialog.ajax';
$attributes = [
'attributes' => [
'class' => 'use-ajax',
'data-dialog-type' => 'modal',
'data-dialog-options' => Json::encode([
'width' => auto
]),
],
];
$link = Link::fromTextAndUrl(
$this->t('terms and conditions of the store "#store_name"', ['#store_name' => $store_name]),
Url::fromUri("internal:/store/$store_id/cgv", $attributes)
)->toString();
$pane_form['marketplace_terms_and_conditions'] = [
'#type' => 'checkbox',
'#default_value' => FALSE,
'#title' => $this->t('I have read and accept #terms.', ['#terms' => $link]),
'#required' => TRUE,
'#weight' => $this->getWeight(),
];
return $pane_form;
}
}
What's wrong with my custom module and how to fix the problem ? Thank you
Change this to
'width' => auto
this
'width' => 'auto'
It is assuming it to be a constant, as you can see from the errors, it has to be either a variable or a string.

Changing display logic for a field in sugarcrm

I have the following situation: Contacts without a first or last name, in fact, they only have a email address.
I can work with these contacts fine, but when I use the listview anywhere (for instance to show all contacts from a company) there now is no way to click through to the contact (normally you would click on the name).
I'm looking for a way to solve this, for instance by showing a clickable text like 'name not known', but can't figure out how to do this. I've been looking at the manual and in the files in the modules directory and the sugarfields dir, but can't quite figure it out.
The closest I got was in /sugarcrm/modules/Contacts/metadata/listviewdefs.php
where this piece of code resides:
$listViewDefs['Contacts'] = array(
'NAME' => array(
'width' => '20%',
'label' => 'LBL_LIST_NAME',
'link' => true,
'contextMenu' => array('objectType' => 'sugarPerson',
'metaData' => array('contact_id' => '{$ID}',
'module' => 'Contacts',
'return_action' => 'ListView',
'contact_name' => '{$FULL_NAME}',
'parent_id' => '{$ACCOUNT_ID}',
'parent_name' => '{$ACCOUNT_NAME}',
'return_module' => 'Contacts',
'return_action' => 'ListView',
'parent_type' => 'Account',
'notes_parent_type' => 'Account')
),
'orderBy' => 'name',
'default' => true,
'related_fields' => array('first_name', 'last_name', 'salutation', 'account_name', 'account_id'),
),
Somewhere there has to be a function that joins the first and lastname together...
Edit: I found a solution:
The actual concatenation function is in /sugarcrm/include/SugarObjects/templates/person/person.php and is called _create_proper_name_field()
I can modify the output for my specific case by adding something like this to the end of the function:
if (empty(trim($full_name))){
$full_name = 'Name unknown';
}
However, I would rather have a upgrade safe solution, so that will be the next challenge.
Don't edit the core because the next upgrade will break your SugarCRM instance. Use logic hooks to be upgrade safe:
create a file 'logic_hooks.php' in /custom/modules/Contacts/
In that file, add the followin code:
<?php
$hook_array['before_save'][] = Array(1,'logic_fill_name','custom/modules/Contacts/logic_hooks/logics.php','ContactLogics','logic_fill_name');
After you have done this. create the file 'logics.php' in /custom/modules/Contacts/logic_hooks.
In the logics.php file, add something like:
<?php
require_once 'include/SugarQuery/SugarQuery.php';
/**
* Class ContactLogics
*/
class ContactLogics {
/**
* #param $bean
* #param $event
* #param $arguments
*/
public function logic_fill_name($bean, $event, $arguments) {
if (empty(trim($bean->first_name)) && empty(trim($bean->last_name))){
$bean->last_name = 'Name unknown';
}
}
}
Now some explanation. When you edited a recordview and pressed the save button, the logic hook 'before_save' will be triggered. This code will change the full name to 'Name unknown' when the full name is empty. When the 'before_save' is executed, the actual save will take place.

ZF2 Form and Doctrine 2 modify the value_options

I am using Doctrine 2 in my Zend Framework 2 Project. I have now created a Form and create one of my Dropdowns with Values from the Database. My Problem now is that I want to change which values are used and not the one which I get back from my repository. Okay, here some Code for a better understanding:
$this->add(
array(
'type' => 'DoctrineModule\Form\Element\ObjectSelect',
'name' => 'county',
'options' => array(
'object_manager' => $this->getObjectManager(),
'label' => 'County',
'target_class' => 'Advert\Entity\Geolocation',
'property' => 'county',
'is_method' => true,
'empty_option' => '--- select county ---',
'value_options'=> function($targetEntity) {
$values = array($targetEntity->getCounty() => $targetEntity->getCounty());
return $values;
},
'find_method' => array(
'name' => 'getCounties',
),
),
'allow_empty' => true,
'required' => false,
'attributes' => array(
'id' => 'county',
'multiple' => false,
)
)
);
I want to set the value for my Select to be the County Name and not the ID. I thought that I would need the 'value_options' which needs an array. I tried it like above, but get the
Error Message: Argument 1 passed to Zend\Form\Element\Select::setValueOptions() must be of the type array, object given
Is this possible at all?
I was going to suggest modifying your code, although after checking the ObjectSelect code i'm surprised that (as far as I can tell) this isn't actually possible without extending the class. This is because the value is always generated from the id.
I create all form elements using factories (without the ObjectSelect), especially complex ones that require varied lists.
Alternative solution
First create a new method in the Repository that returns the correct array. This will allow you to reuse that same method should you need it anywhere else (not just for forms!).
class FooRepository extends Repository
{
public function getCounties()
{
// normal method unchanged, returns a collection
// of counties
}
public function getCountiesAsArrayKeyedByCountyName()
{
$counties = array();
foreach($this->getCounties() as $county) {
$counties[$county->getName()] = $county->getName();
}
return $counties;
}
}
Next create a custom select factory that will set the value options for you.
namespace MyModule\Form\Element;
use Zend\Form\Element\Select;
use Zend\ServiceManager\ServiceLocatorInterface;
use Zend\ServiceManager\FactoryInterface;
class CountiesByNameSelectFactory implements FactoryInterface
{
public function createService(ServiceLocatorInterface $formElementManager)
{
$element = new Select;
$element->setValueOptions($this->loadValueOptions($formElementManager));
// set other select options etc
$element->setName('foo')
->setOptions(array('foo' => 'bar'));
return $element;
}
protected function loadValueOptions(ServiceLocatorInterface $formElementManager)
{
$serviceManager = $formElementManager->getServiceLocator();
$repository = $serviceManager->get('DoctrineObjectManager')->getRepository('Foo/Entity/Bar');
return $repository->getCountiesAsArrayKeyedByCountyName();
}
}
Register the new element with the service manager by adding a new entry in Module.php or module.config.php.
// Module.php
public function getFormElementConfig()
{
return array(
'factories' => array(
'MyModule\Form\Element\CountiesByNameSelect'
=> 'MyModule\Form\Element\CountiesByNameSelectFactory',
),
);
}
Lastly change the form and remove your current select element and add the new one (use the name that you registered with the service manager as the type key)
$this->add(array(
'name' => 'counties',
'type' => 'MyModule\Form\Element\CountiesByNameSelect',
));
It might seem like a lot more code (because it is) however you will benefit from it being a much clearer separation of concerns and you can now reuse the element on multiple forms and only need to configure it in one place.

Codeigniter an migration not working with wamp

I am following a tutorial and I have check the source code with my code to make sure there are no errors but I am unable to get codeigniter to create update or retrieve any records from my wamp sever(ver 2.4) when I enter public_html/admin/migration in the task bar codeigniter returns a message saying "Migration worked!" which is the result that is expected when codeigniter has updated the records but no changes are made to the database this is the code used in the controller
<?php
class Migration extends Admin_Controller
{
public function __construct ()
{
parent::__construct();
}
public function index ()
{
$this->load->library('migration');
if (! $this->migration->current()) {
show_error($this->migration->error_string());
}
else {
echo 'Migration worked!';
}
}
}
I have set the autoload libraries as follows $autoload['libraries'] = array('database');
is there something I am missing
here is my migration library file called '001_create_users.php'
<?php
class Migration_Create_users extends CI_Migration {
public function up()
{
$this->dbforge->add_field(array(
'id' => array(
'type' => 'INT',
'constraint' => 11,
'unsigned' => TRUE,
'auto_increment' => TRUE
),
'email' => array(
'type' => 'VARCHAR',
'constraint' => '100',
),
'password' => array(
'type' => 'VARCHAR',
'constraint' => '128',
),
'name' => array(
'type' => 'VARCHAR',
'constraint' => '100',
),
));
$this->dbforge->create_table('users');
}
public function down()
{
$this->dbforge->drop_table('users');
}
}
Not sure if you are referring to the tutorial by Free Courses on youtube about cms buildout in codeigniter.
The way I fixed the issue (after a few hours) was debugging down to the environment setup.
Replace everything in your index function of the migration controller with var_dump($this->db) and see what it returns for your username/password/hostname/database, etc. If they are not what you expected per your database library setup, then your environment is not set properly in the index.php file.
I had to fix my case statement, had an extra / or \ can not remember which one, so the switch statement was forcing it to use the production environment configuration, which were not set.
add primary key before create table
$this->dbforge->add_key('id');

Customizing layout to sfWidgetFormDoctrineChoice disable checkbox

Good morning,
In Symfony 1.4,
I tried to do what is explained here : Customizing layout to sfWidgetFormDoctrineChoice
But it doesn't work. Instead of adding a thumbnail, I just want to hide the <li> before the input, and in some condition disable/hide the checkbox input but show the label anyway.
When I add the renderer without argument, I get this error :
sfWidgetFormMySelectCheckbox requires the following options: 'choices'.
Here is my formatter code :
class sfWidgetFormMySelectCheckbox extends sfWidgetFormSelectCheckbox
{
public function configure($options = array(), $arguments = array())
{
parent::configure($options, $arguments);
}
protected function formatChoices($name, $value, $choices, $attributes)
{
.....
// new
$inputs[$id] = array(
'input' => sprintf('| test | %s',
$this->renderTag('input', array_merge($baseAttributes, $attributes))
),
'label' => $this->renderContentTag('label', self::escapeOnce($option), array('for' => $id)),
);
}
return call_user_func($this->getOption('formatter'), $this, $inputs);
}
}
And now the form where I call it :
$this->setWidget('aaa', new sfWidgetFormDoctrineChoice(array(
'model' => 'Aaa',
'expanded' => true,
'multiple' => true,
'add_empty' => false,
'query' => $query,
'renderer' => new sfWidgetFormMySelectCheckbox()
)));
Thanks for your help !
According to the docs you have to pass the choices option to the renderer object. Try something like this:
$this->setWidget('aaa', new sfWidgetFormDoctrineChoice(array(
'model' => 'Aaa',
'expanded' => true,
'multiple' => true,
'add_empty' => false,
'query' => $query,
)));
$this->widgetSchema['aaa']->setOption('renderer', new sfWidgetFormMySelectCheckbox(array(
'choices' => new sfCallable(array($this->widgetSchema['aaa'], 'getChoices'))
)));
So basically you want the renderer object get the choices from the parent widget. To do that you have to pass a sfCallable object which takes an array as the first argument in which you pass the instance of your parent widget and the name of the function getChoices.
Remember also that the expanded option is not used when you override the renderer.

Categories