Magento – Magento 2 PHP Fatal error when saving custom attribute to customers

custom-attributescustomerfatal errormagento2.3.1

I got this error when trying to save custom data to customer,

43766 FastCGI sent in stderr: "PHP message: PHP Fatal error: Uncaught TypeError: Argument 2 passed to Magento\Eav\Model\Attribute\Data\Text::validateLength() must be of the type string, null given

This my the my code,

app/code/Test/Customer/Controller/Account/EditPost.php

    <?php
    namespace Test\Customer\Controller\Account;

    /**
     * Class EditPost
     */
    class EditPost extends \Magento\Framework\App\Action\Action
    {       

        /**
         * @var \Magento\Customer\Model\Session
         */
        protected $customerSession;

        /**
         * @var \Magento\Customer\Model\CustomerFactory
         */
        protected $customerFactory;

        /**
         * @var \Magento\Customer\Api\CustomerRepositoryInterface
         */    
        protected $customerRepository;


        public function __construct(
            \Magento\Framework\App\Action\Context $context,
            \Magento\Customer\Model\Session $customerSession,
            \Magento\Customer\Api\CustomerRepositoryInterface $customerRepository,
            \Magento\Customer\Model\CustomerFactory $customerFactory
        ) {                   
            $this->customerSession = $customerSession;
            $this->customerRepository = $customerRepository;
            $this->customerFactory = $customerFactory;
            parent::__construct($context);
        }


        public function execute()
        {       
            $resultRedirect = $this->resultRedirectFactory->create();
            $resultRedirect->setUrl('../../../customer/account/edit');  

            try {                   
                $company = $this->getRequest()->getParam('company');
                $department = $this->getRequest()->getParam('department');
                $email = $this->getRequest()->getParam('email_hidden');

                $customerId = 0;
                if($this->customerSession->isLoggedIn())
                $customerId = $this->customerSession->getCustomer()->getId();  

                $customer = $this->customerRepository->getById($customerId);


                $customer->setCustomAttribute('company', $company);
                $customer->setCustomAttribute('department', $department);
                $this->customerRepository->save($customer);
        } catch (\Exception $e) {
            $this->messageManager->addError($e->getMessage());


            return $resultRedirect;                 
        }

        return $resultRedirect;
    }

}   

And this is the code I used to created Company and Department attribute

app/code/Test/Test1/Setup/InstallData.php

<?php

namespace Test\Test1\Setup;

use Magento\Framework\Setup\InstallDataInterface;
use Magento\Framework\Setup\ModuleContextInterface;
use Magento\Framework\Setup\ModuleDataSetupInterface;
use Magento\Eav\Setup\EavSetup;
use Magento\Eav\Setup\EavSetupFactory;
use Magento\Customer\Setup\CustomerSetupFactory;
use Magento\Customer\Model\Customer;
use Magento\Sales\Setup\SalesSetupFactory;

class InstallData implements InstallDataInterface
{

    private $eavSetupFactory;

    /**
     * Customer setup factory
     *
     * @var CustomerSetupFactory
     */
    private $customerSetupFactory;

    /**
     * Constructor
     *
     * @param EavSetupFactory $eavSetupFactory
     * @param CustomerSetupFactory $customerSetupFactory
     * @param SalesSetupFactory $salesSetupFactory
     */
    public function __construct(
        EavSetupFactory $eavSetupFactory,
        CustomerSetupFactory $customerSetupFactory,
        SalesSetupFactory $salesSetupFactory
    )
    {
        $this->eavSetupFactory = $eavSetupFactory;
        $this->customerSetupFactory = $customerSetupFactory;
        $this->salesSetupFactory = $salesSetupFactory;
    }

    /**
     * {@inheritdoc}
     */
    public function install(
        ModuleDataSetupInterface $setup,
        ModuleContextInterface $context
    ) {
        $setup->startSetup();



        //company
        $customerSetup->addAttribute(
            \Magento\Customer\Model\Customer::ENTITY,
            'company',
            [
                'label' => 'Company',
                'type' => 'varchar',
                'input' => 'text',
                'visible'   => true,
                'required'  => false,
                'position'  => 2002,
                'user_defined' => false,
                'system' => false
            ]
        );

        $customerSetup->getEavConfig()->getAttribute(\Magento\Customer\Model\Customer::ENTITY, 'company')
            ->setData('used_in_forms', ['adminhtml_customer'])
            ->save();

        //department
        $customerSetup->addAttribute(
            \Magento\Customer\Model\Customer::ENTITY,
            'department',
            [
                'label' => 'Department',
                'type' => 'varchar',
                'input' => 'text',
                'visible'   => true,
                'required'  => false,
                'position'  => 2003,
                'user_defined' => false,
                'system' => false
            ]
        );

        $customerSetup->getEavConfig()->getAttribute(\Magento\Customer\Model\Customer::ENTITY, 'department')
            ->setData('used_in_forms', ['adminhtml_customer'])
            ->save();


        $setup->endSetup();

    }
}

Best Answer

We can debug which field has got the null value by placing a logger just before passing the value to validateLength function in the file vendor/magento/module-eav/Model/Attribute/Data/Text.php as follows,

public function validateValue($value)
{
    $errors = [];
    $attribute = $this->getAttribute();

    if ($value === false) {
        // try to load original value and validate it
        $value = $this->getEntity()->getDataUsingMethod($attribute->getAttributeCode());
    }

    if (!$attribute->getIsRequired() && empty($value)) {
        return true;
    }

    if (empty($value) && $value !== '0' && $attribute->getDefaultValue() === null) {
        $label = __($attribute->getStoreLabel());
        $errors[] = __('"%1" is a required value.', $label);
    }

    $writer = new \Zend\Log\Writer\Stream(BP . '/var/log/validatelog.log');
    $logger = new \Zend\Log\Logger();
    $logger->addWriter($writer);
    $logger->info($attribute->getName());

    $validateLengthResult = $this->validateLength($attribute, $value);
    $errors = array_merge($errors, $validateLengthResult);

    $validateInputRuleResult = $this->validateInputRule($value);
    $errors = array_merge($errors, $validateInputRuleResult);

    if (count($errors) == 0) {
        return true;
    }

    return $errors;
}

While checking the log file from magento2Root/var/log/validatelog.log, the last attribute will be the cause of the issue. I faced the similar issue while creating a new address programatically and found that lastname is empty which is a required field while saving in address book. Hope by this way you will find yours too.

Related Topic