Page 1 of 1

Creating extra Street Address Fields

Posted: Wed Aug 20, 2025 11:37 pm
by Portman
Hi,

I'm working on version 1.1.0.4

I am trying to create 2 extra street address fields for my site (entry_street_address_2 & entry_street_address_3)

I have created clones of cd_street_address.php for each and have created a new cd_modified_address.php - which is just a copy of the cd_traditional_address with the extra fields added.

A new customer can add the details no problem.
THe issues arise on editing existing addresses and creating additional addresses...

When an address is edited - it seems to update the database, but I then get an error
when I add an additional address it does not update the database and i get a very similar error

Error for update;

Code: Select all

PHP Warning:  Undefined array key "customers" in .../includes/system/versioned/1.01.00.00/customer_write.php on line 85
PHP Warning:  foreach() argument must be of type array|object, null given in .../includes/system/versioned/1.01.00.00/query.php on line 142
PHP Fatal error:  Uncaught mysqli_sql_exception: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near '' at line 1 in .../includes/system/versioned/1.0.8.1/database_core.php:67
Stack trace:
#0 .../includes/system/versioned/1.0.8.1/database_core.php(67): mysqli->query('UPDATE customer...', 0)
#1 .../includes/system/versioned/1.0.8.1/database_core.php(119): database_core->query('UPDATE customer...')
#2 .../includes/system/versioned/1.01.00.00/customer_write.php(85): database_core->perform('customers', Array, 'update', '')
#3 .../includes/system/versioned/1.0.5.1/customer_data.php(183): customer_write::update(Array, Array)
#4 .../address_book_process.php(48): customer_data->update(Array, Array, 'address_book')
#5 {main}
  thrown in .../includes/system/versioned/1.0.8.1/database_core.php on line 67
Error for additional address;

Code: Select all

PHP Fatal error:  Uncaught mysqli_sql_exception: Duplicate entry '' for key 'uq_customers_email_address' in .../includes/system/versioned/1.0.8.1/database_core.php:67
Stack trace:
#0 .../includes/system/versioned/1.0.8.1/database_core.php(67): mysqli->query('INSERT INTO cus...', 0)
#1 .../includes/system/versioned/1.0.8.1/database_core.php(119): database_core->query('INSERT INTO cus...')
#2 .../includes/system/versioned/1.01.00.00/customer_write.php(39): database_core->perform('customers', Array)
#3 .../includes/system/versioned/1.0.5.1/customer_data.php(167): customer_write::create(Array, Array)
#4 .../address_book_process.php(57): customer_data->add_address(Array)
#5 {main}
  thrown in .../includes/system/versioned/1.0.8.1/database_core.php on line 67
Obviously there is more I need to do to get this to work, could someone fill me in on what that is?

Thanks

Re: Creating extra Street Address Fields

Posted: Thu Aug 21, 2025 2:57 am
by frankl
Looks like an error here:

Code: Select all

mysqli->query('UPDATE customer...', 0)
Is the customer_id being passed? Post the whole code and it can be checked.

Re: Creating extra Street Address Fields

Posted: Fri Aug 22, 2025 6:19 am
by Portman
Hi @frankl

Im not sure which code you want me to post, so here is cd_street_address_2.php

Code: Select all

  class cd_street_address_2 extends abstract_customer_data_module {

    const CONFIG_KEY_BASE = 'MODULE_CUSTOMER_DATA_STREET_ADDRESS_2_';

    const PROVIDES = [ 'street_address_2' ];
    const REQUIRES = [  ];

    protected function get_parameters() {
      return [
        static::CONFIG_KEY_BASE . 'STATUS' => [
          'title' => 'Enable Street Address 2 module',
          'value' => 'True',
          'desc' => 'Do you want to add the module to your shop?',
          'set_func' => "Config::select_one(['True', 'False'], ",
        ],
        static::CONFIG_KEY_BASE . 'GROUP' => [
          'title' => 'Customer data group',
          'value' => '2',
          'desc' => 'In what group should this appear?',
          'use_func' => 'customer_data_group::fetch_name',
          'set_func' => 'Config::select_customer_data_group(',
        ],
        static::CONFIG_KEY_BASE . 'REQUIRED' => [
          'title' => 'Require Street Address 2 module (if enabled)',
          'value' => 'True',
          'desc' => 'Do you want the street address 2 to be required in customer registration?',
          'set_func' => "Config::select_one(['True', 'False'], ",
        ],
        static::CONFIG_KEY_BASE . 'MIN_LENGTH' => [
          'title' => 'Minimum Length',
          'value' => '3',
          'desc' => 'Minimum length of street address',
        ],
        static::CONFIG_KEY_BASE . 'PAGES' => [
          'title' => 'Pages',
          'value' => 'address_book;checkout_new_address;create_account;customers',
          'desc' => 'On what pages should this appear?',
          'set_func' => 'Customers::select_pages(',
          'use_func' => 'abstract_module::list_exploded',
        ],
        static::CONFIG_KEY_BASE . 'SORT_ORDER' => [
          'title' => 'Sort Order',
          'value' => '4210',
          'desc' => 'Sort order of display. Lowest is displayed first.',
        ],
        static::CONFIG_KEY_BASE . 'TEMPLATE' => [
          'title' => 'Template',
          'value' => 'includes/modules/customer_data/cd_whole_row_input.php',
          'desc' => 'What template should be used to surround this input?',
        ],
      ];
    }

    public function get($field, &$customer_details) {
      switch ($field) {
        case 'street_address_2':
          if (!isset($customer_details[$field])) {
            $customer_details[$field] = $customer_details['street_address_2']
              ?? $customer_details['entry_street_address_2'] ?? null;
          }
          return $customer_details[$field];
      }
    }

    public function display_input($customer_details = null) {
      $label_text = ENTRY_STREET_ADDRESS_2;
      $input_id = 'inputStreetAddress';

      $input = new Input('street_address_2', [
        'id' => $input_id,
        'autocomplete' => 'address-line1',
        'placeholder' => ENTRY_STREET_ADDRESS_2_TEXT,
        'minlength' => $this->base_constant('MIN_LENGTH'),
      ]);

      if ($customer_details && is_array($customer_details)) {
        $input->set('value', $this->get('street_address_2', $customer_details));
      }

      if ($this->is_required()) {
        $input->require();
        $input .= FORM_REQUIRED_INPUT;
      }

      include Guarantor::ensure_global('Template')->map($this->base_constant('TEMPLATE'));
    }

    public function process(&$customer_details) {
      $customer_details['street_address_2'] = Text::input($_POST['street_address_2']);

      if ((strlen($customer_details['street_address_2']) < $this->base_constant('MIN_LENGTH'))
        && $this->is_required()
        )
      {
        $GLOBALS['messageStack']->add_classed(
          $GLOBALS['message_stack_area'] ?? 'customer_data',
          sprintf(ENTRY_STREET_ADDRESS_2_ERROR, $this->base_constant('MIN_LENGTH')));

        return false;
      }

      return true;
    }

    public function build_db_values(&$db_tables, $customer_details, $table = 'both') {
      Guarantor::guarantee_subarray($db_tables, 'address_book');
      $db_tables['address_book']['entry_street_address_2'] = $customer_details['street_address_2'];
    }

    public function build_db_aliases(&$db_tables, $table = 'both') {
      Guarantor::guarantee_subarray($db_tables, 'address_book');
      $db_tables['address_book']['entry_street_address_2'] = 'street_address_2';
    }

  }
cd_street_address_3 is identical but with 3 in all of the relevant variable names instead of 2

here is cd_modified_address.php

Code: Select all

 class cd_modified_address extends abstract_module {

    const CONFIG_KEY_BASE = 'MODULE_CUSTOMER_DATA_MODIFIED_ADDRESS_';

    const PROVIDES = [ 'address' ];
    const REQUIRES = [ 'name', 'street_address', 'street_address_2', 'street_address_3', 'postcode', 'city', 'country' ];
    const FIELDS = [ 'name', 'street_address', 'street_address_2', 'street_address_3', 'postcode', 'city', 'country_id', 'company', 'suburb', 'state' ];

    private $active_fields;

    protected function get_parameters() {
      return [
        static::CONFIG_KEY_BASE . 'STATUS' => [
          'title' => 'Enable Address module - Modified for CA',
          'value' => 'True',
          'desc' => 'Do you want to add the module to your shop?',
          'set_func' => "Config::select_one(['True', 'False'], ",
        ],
      ];
    }

    public function get($field, &$customer_details) {
      switch ($field) {
        case 'address':
          if (!isset($customer_details[$field])) {
            $customer_details[$field] = array_combine(
              $this->get_fields(),
              array_map(
                function ($v) use (&$customer_details) {
                  return $GLOBALS['customer_data']->get($v, $customer_details);
                },
                $this->get_fields()
              )
            );
          }

          return $customer_details[$field];
      }
    }

    public function process(&$customer_details) {
      $results = $GLOBALS['customer_data']->process($this->get_fields());
      $customer_details = array_merge($customer_details, $results);

      return !empty($results);
    }

    public function get_fields() {
      if (is_null($this->active_fields)) {
        global $customer_data;

        $customer_data->has(self::FIELDS);
        $this->active_fields = array_diff(self::FIELDS, $customer_data->get_last_missing_abilities());
      }

      return $this->active_fields;
    }

    public function get_purveyors() {
      return array_map([$GLOBALS['customer_data'], 'get_module'], $this->get_fields());
    }

    public function build_db_values(&$db_tables, $customer_details, $table = 'both') {
      foreach ($this->get_purveyors() as $purveyor) {
        $purveyor->build_db_values($db_tables, $customer_details, $table);
      }
    }

    public function build_db_aliases(&$db_tables, $table = 'both') {
      foreach ($this->get_purveyors() as $purveyor) {
        $purveyor->build_db_aliases($db_tables, $table);
      }
    }

    function format($address, $html, $boln, $eoln) {
      $address_format_id = $address['format_id'] ?? $address['address_format_id'] ?? $this->get_address_format_id($address['country_id'] ?? $address['country']['id'] ?? null);
      $address_format = $GLOBALS['db']->query("SELECT address_format AS format FROM address_format WHERE address_format_id = " . (int)$address_format_id)->fetch_assoc();

      $company = htmlspecialchars($address['company'] ?? '');
      $name = htmlspecialchars($GLOBALS['customer_data']->get('name', $address) ?? '');

      $street = htmlspecialchars($address['street_address']);
      $street_2 = htmlspecialchars($address['street_address_2'] ?? '');
	  $street_3 = htmlspecialchars($address['street_address_3'] ?? '');
      $suburb = htmlspecialchars($address['suburb'] ?? '');
      $city = htmlspecialchars($address['city']);
      $state = htmlspecialchars($address['state'] ?? '');
      if (!empty($address['country_id'])) {
        $country = Country::fetch_name($address['country_id']);

        if (!empty($address['zone_id'])) {
          $state = Zone::fetch_code($address['zone_id'], $address['country_id'], $state);
        }
      } elseif (!empty($address['country']) && is_array($address['country'])) {
        $country = htmlspecialchars($address['country']['title']);
      } else {
        $country = '';
      }
      $postcode = htmlspecialchars($address['postcode']);
      $zip = $postcode;

      if ($html) {
        // HTML Mode
        $HR = '<hr>';
        $hr = '<hr>';
        if ( ($boln == '') && ($eoln == "\n") ) { // Values not specified, use rational defaults
          $CR = '<br>';
          $cr = '<br>';
          $eoln = $cr;
        } else { // Use values supplied
          $CR = $eoln . $boln;
          $cr = $CR;
        }
      } else {
        // Text Mode
        $CR = $eoln;
        $cr = $CR;
        $HR = '----------------------------------------';
        $hr = '----------------------------------------';
      }

      $statecomma = '';
      $streets = $street;
	  if ('' != $street_2) {
        $streets .= $cr . $street_2;
      }
	  if ('' != $street_3) {
        $streets .= $cr . $street_3;
      }
      if ('' != $suburb) {
        $streets .= $cr . $suburb;
      }
      if ('' != $state) {
        $statecomma = $state . ', ';
      }

      $fmt = $address_format['format'];
      eval("\$address = \"$fmt\";");

      if (!empty($company)) {
        $address = $company . $cr . $address;
      }

      return $address;
    }

    function get_address_format_id($country_id) {
      $address_format = $GLOBALS['db']->query("SELECT address_format_id AS format_id FROM countries WHERE countries_id = " . (int)$country_id)->fetch_assoc();

      return $address_format['format_id'] ?? '1';
    }

  }
I hope that helps

Re: Creating extra Street Address Fields

Posted: Fri Aug 22, 2025 1:10 pm
by ecartz
I doubt the problem is in the code you changed for this. Did you change anything else? Note that it is complaining that it is trying to insert a blank email into the customers table. Why? It's not clear. If you are editing an address, it should not try to modify the customers table at all.

Two possible problems:

1. You're missing the customer ID on the edit address page.
2. You have some field in customers on the edit address page. Perhaps customers_email_address or customers_telephone.

If you make a new vanilla test store and put only your street address changes in it, does it work? If so, it must be some other change that's causing the problem.

Re: Creating extra Street Address Fields

Posted: Sat Aug 23, 2025 4:25 am
by Portman
thanks @ecartz ,

Yes I had some cusomer details listed on the address edit page - thanks for this I would have never picked that up!