Как добавить новые поля в адрес заказчика в Prestashop

Oct 04, 2014 20:51

Еще один перевод статьи по Prestashop.
В сегодняшнем уроке мы увидим, как добавлять новые поля к адресу заказчика в Prestashop.

Используемая версия: ​​Prestashop 1.6 (совместимый с Prestashop 1.5)

План действий

Для того чтобы добавить новое поле к адресу в регистрационной форме, мы добавим пару обработчиков, а также создадим новые поля базы данных и изменим файл шаблона для адреса. Если вы не знакомы с обработчиками Prestashop, посмотрите мою статью о том, как расширить объекты PrestaShop (хотя нам придется использовать немного другую технику) - см. перевод.


Класс адреса и таблицы базы данных

Перво-наперво нам нужно нечто, с чем поиграть. Мы хотим, чтобы клиент имел возможность заполнить поля, которых в настоящее время не существует, поэтому войдите в базу данных, откройте таблицу ps_address и создайте новое поле: my_custom_field. Я сделал поле VARCHAR длиной 64.

Далее нам нужно указать объекту (классу) адреса, что у него есть новое поле для работы. Создайте новый файл в override/classes/ и назовите его Address.php. Добавьте следующее внутри php тегов:

class Address extends AddressCore
{

public $my_custom_field;

public static $definition = array(
'table' => 'address',
'primary' => 'id_address',
'fields' => array(
'id_customer' => array('type' => self::TYPE_INT, 'validate' => 'isNullOrUnsignedId', 'copy_post' => false),
'id_manufacturer' => array('type' => self::TYPE_INT, 'validate' => 'isNullOrUnsignedId', 'copy_post' => false),
'id_supplier' => array('type' => self::TYPE_INT, 'validate' => 'isNullOrUnsignedId', 'copy_post' => false),
'id_warehouse' => array('type' => self::TYPE_INT, 'validate' => 'isNullOrUnsignedId', 'copy_post' => false),
'id_country' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedId', 'required' => true),
'id_state' => array('type' => self::TYPE_INT, 'validate' => 'isNullOrUnsignedId'),
'alias' => array('type' => self::TYPE_STRING, 'validate' => 'isGenericName', 'required' => true, 'size' => 32),
'company' => array('type' => self::TYPE_STRING, 'validate' => 'isGenericName', 'size' => 64),
'lastname' => array('type' => self::TYPE_STRING, 'validate' => 'isName', 'required' => true, 'size' => 32),
'firstname' => array('type' => self::TYPE_STRING, 'validate' => 'isName', 'required' => true, 'size' => 32),
'vat_number' => array('type' => self::TYPE_STRING, 'validate' => 'isGenericName'),
'address1' => array('type' => self::TYPE_STRING, 'validate' => 'isAddress', 'required' => true, 'size' => 128),
'address2' => array('type' => self::TYPE_STRING, 'validate' => 'isAddress', 'size' => 128),
'postcode' => array('type' => self::TYPE_STRING, 'validate' => 'isPostCode', 'size' => 12),
'city' => array('type' => self::TYPE_STRING, 'validate' => 'isCityName', 'required' => true, 'size' => 64),
'other' => array('type' => self::TYPE_STRING, 'validate' => 'isMessage', 'size' => 300),
'phone' => array('type' => self::TYPE_STRING, 'validate' => 'isPhoneNumber', 'size' => 32),
'phone_mobile' => array('type' => self::TYPE_STRING, 'validate' => 'isPhoneNumber', 'size' => 32),
'dni' => array('type' => self::TYPE_STRING, 'validate' => 'isDniLite', 'size' => 16),
'deleted' => array('type' => self::TYPE_BOOL, 'validate' => 'isBool', 'copy_post' => false),
'date_add' => array('type' => self::TYPE_DATE, 'validate' => 'isDateFormat', 'copy_post' => false),
'date_upd' => array('type' => self::TYPE_DATE, 'validate' => 'isDateFormat', 'copy_post' => false),
'my_custom_field' => array('type' => self::TYPE_STRING, 'validate' => 'isGenericName', 'size' => 64),

),
);
}

Пояснение: если вы читали мою статью о том, как расширить PrestaShop объекты, вы заметите, что я подошел к этой обработке немного по-другому. Я не назначаю новое поле после создания экземпляра, а я непосредственно обрабатываю его определение. Причина проста: определение Address вызывается статически при создании нового адреса в фронт-офисе, и если мы добавили новое поле в методе конструкта, мы упустили бы его из списка.

Пожалуйста, обратите внимание, что это определение взято из Prestashop 1.6.0.5, и поэтому я рекомендую взять его самостоятельно из исходного класса Address, если у вас другая версия Prestashop.

Контроллер адреса в админке

Мы уже на полпути, и теперь мы должны изменить контроллер админки, ответственный за отображение модификации адресной формы для каждого клиента.

Создайте новый файл с именем AdminAddressesController.php в override/controllers/admin/. Мы должны расширить метод renderForm(); поэтому вернитесь в основную папку controllers/admin, откройте исходный AdminAddressesController.php, найдите метод renderForm() и скопируйте его. Затем добавьте следующее внутри php тегов в нашем новом обработчике контроллера:

Class AdminAddressesController extends AdminAddressesControllerCore
{

}

И вставьте код renderForm() внутри класса. Вот как выглядит мой, взятый из Prestashop 1.6.0.5:

Class AdminAddressesController extends AdminAddressesControllerCore
{

public function renderForm()
{
$this->fields_form = array(
'legend' => array(
'title' => $this->l('Addresses'),
'icon' => 'icon-envelope-alt'
),
'input' => array(
array(
'type' => 'text_customer',
'label' => $this->l('Customer'),
'name' => 'id_customer',
'required' => false,
),
array(
'type' => 'text',
'label' => $this->l('Identification Number'),
'name' => 'dni',
'required' => false,
'col' => '4',
'hint' => $this->l('DNI / NIF / NIE')
),
array(
'type' => 'text',
'label' => $this->l('Address alias'),
'name' => 'alias',
'required' => true,
'col' => '4',
'hint' => $this->l('Invalid characters:').' <>;=#{}'
),
array(
'type' => 'text',
'label' => $this->l('Home phone'),
'name' => 'phone',
'required' => false,
'col' => '4',
'hint' => Configuration::get('PS_ONE_PHONE_AT_LEAST') ? sprintf($this->l('You must register at least one phone number.')) : ''
),
array(
'type' => 'text',
'label' => $this->l('Mobile phone'),
'name' => 'phone_mobile',
'required' => false,
'col' => '4',
'hint' => Configuration::get('PS_ONE_PHONE_AT_LEAST') ? sprintf($this->l('You must register at least one phone number.')) : ''
),
array(
'type' => 'textarea',
'label' => $this->l('Other'),
'name' => 'other',
'required' => false,
'cols' => 15,
'rows' => 3,
'hint' => $this->l('Forbidden characters:').' <>;=#{}'
),
),
'submit' => array(
'title' => $this->l('Save'),
)
);
$id_customer = (int)Tools::getValue('id_customer');
if (!$id_customer && Validate::isLoadedObject($this->object))
$id_customer = $this->object->id_customer;
if ($id_customer)
{
$customer = new Customer((int)$id_customer);
$token_customer = Tools::getAdminToken('AdminCustomers'.(int)(Tab::getIdFromClassName('AdminCustomers')).(int)$this->context->employee->id);
}

$this->tpl_form_vars = array(
'customer' => isset($customer) ? $customer : null,
'tokenCustomer' => isset ($token_customer) ? $token_customer : null
);

// Order address fields depending on country format
$addresses_fields = $this->processAddressFormat();
// we use delivery address
$addresses_fields = $addresses_fields['dlv_all_fields'];

$temp_fields = array();

foreach ($addresses_fields as $addr_field_item)
{
if ($addr_field_item == 'company')
{
$temp_fields[] = array(
'type' => 'text',
'label' => $this->l('Company'),
'name' => 'company',
'required' => false,
'col' => '4',
'hint' => $this->l('Invalid characters:').' <>;=#{}'
);
$temp_fields[] = array(
'type' => 'text',
'label' => $this->l('VAT number'),
'col' => '2',
'name' => 'vat_number'
);
}
else if ($addr_field_item == 'lastname')
{
if (isset($customer) &&
!Tools::isSubmit('submit'.strtoupper($this->table)) &&
Validate::isLoadedObject($customer) &&
!Validate::isLoadedObject($this->object))
$default_value = $customer->lastname;
else
$default_value = '';

$temp_fields[] = array(
'type' => 'text',
'label' => $this->l('Last Name'),
'name' => 'lastname',
'required' => true,
'col' => '4',
'hint' => $this->l('Invalid characters:').' 0-9!<>,;?=+()@#"�{}_$%:',
'default_value' => $default_value,
);
}
else if ($addr_field_item == 'firstname')
{
if (isset($customer) &&
!Tools::isSubmit('submit'.strtoupper($this->table)) &&
Validate::isLoadedObject($customer) &&
!Validate::isLoadedObject($this->object))
$default_value = $customer->firstname;
else
$default_value = '';

$temp_fields[] = array(
'type' => 'text',
'label' => $this->l('First Name'),
'name' => 'firstname',
'required' => true,
'col' => '4',
'hint' => $this->l('Invalid characters:').' 0-9!<>,;?=+()@#"�{}_$%:',
'default_value' => $default_value,
);
}
else if ($addr_field_item == 'address1')
{
$temp_fields[] = array(
'type' => 'text',
'label' => $this->l('Address'),
'name' => 'address1',
'col' => '6',
'required' => true,
);
}
else if ($addr_field_item == 'address2')
{
$temp_fields[] = array(
'type' => 'text',
'label' => $this->l('Address').' (2)',
'name' => 'address2',
'col' => '6',
'required' => false,
);
}
elseif ($addr_field_item == 'postcode')
{
$temp_fields[] = array(
'type' => 'text',
'label' => $this->l('Zip/Postal Code'),
'name' => 'postcode',
'col' => '2',
'required' => true,
);
}
else if ($addr_field_item == 'city')
{
$temp_fields[] = array(
'type' => 'text',
'label' => $this->l('City'),
'name' => 'city',
'col' => '4',
'required' => true,
);
}
else if ($addr_field_item == 'country' || $addr_field_item == 'Country:name')
{
$temp_fields[] = array(
'type' => 'select',
'label' => $this->l('Country'),
'name' => 'id_country',
'required' => false,
'col' => '4',
'default_value' => (int)$this->context->country->id,
'options' => array(
'query' => Country::getCountries($this->context->language->id),
'id' => 'id_country',
'name' => 'name'
)
);
$temp_fields[] = array(
'type' => 'select',
'label' => $this->l('State'),
'name' => 'id_state',
'required' => false,
'col' => '4',
'options' => array(
'query' => array(),
'id' => 'id_state',
'name' => 'name'
)
);
}
}

// merge address format with the rest of the form
array_splice($this->fields_form['input'], 3, 0, $temp_fields);

return parent::renderForm();
}

}

Мы должны отредактировать переменную $this->fields_form для добавления нового поля ввода; прямо после этого:

array(
'type' => 'text',
'label' => $this->l('Identification Number'),
'name' => 'dni',
'required' => false,
'col' => '4',
'hint' => $this->l('DNI / NIF / NIE')
),

Добавьте следующее

array(
'type' => 'text',
'label' => $this->l('My custom field'),
'name' => 'my_custom_field',
'required' => false,
'col' => '4',
'hint' => $this->l('Just a custom field!')
),

Примечание: убедитесь, что "name" соответствует одному из новых атрибутов, ранее добавленных и к базе данных, и к объекту класса Address!

И наконец, в конце метода измените:

return parent::renderForm();

на

return AdminController::renderForm();

Таким образом новый список полей не заменяется исходным.

Если класс Address или/и контроллер не были ранее переопределены, то теперь вы должны зайти в папку cache/ и стереть class_index.php, как обычно. После этого переопределение будет работать.

Шаблон фронтенда адреса

В качестве самого последнего шага давайте теперь добавим настраиваемое поле в шаблон фронт-офиса, чтобы он мог быть заполнен клиентами, когда они регистрируются. Откройте address.tpl, расположенный в папке темы. Я использую дефолтный шаблон, так что ваш файл может выглядеть по-другому, если у вас свой собственный шаблон! Найдите:

{if $field_name eq 'vat_number'}








{/if}

И прямо после этого добавьте:

{if $field_name eq 'my_custom_field'}








{/if}

Сохраните и обновите, мы сделали это!

СОВЕТ: если вы не видите никаких изменений во фронтенде, убедитесь, что включены перекомпиляции в Advanced Parameters > Performance, а также очистите Smarty кэш.

Дополнительно: Создание нового обязательного поля

Если вам очень нужно, чтобы ваши клиентам вводили что-то в этом новом поле, необходим другой обработчик. Создайте новый файл в override/controllers/front и назовите его его AddressController.php. Вставьте как обычно следующее внутри php тегов:

Class AddressController extends AddressControllerCore
{

}

Мы должны переопределить метод processSubmitAddress(). Для этого, если ваша версия Prestashop не 1.6.0.5, пойдите и скопируйте исходный метод в этот новый обработчик; в противном случае вы можете использовать следующее:

protected function processSubmitAddress()
{
$address = new Address();
$this->errors = $address->validateController();
$address->id_customer = (int)$this->context->customer->id;

// Check page token
if ($this->context->customer->isLogged() && !$this->isTokenValid())
$this->errors[] = Tools::displayError('Invalid token.');

// Check phone
if (Configuration::get('PS_ONE_PHONE_AT_LEAST') && !Tools::getValue('phone') && !Tools::getValue('phone_mobile'))
$this->errors[] = Tools::displayError('You must register at least one phone number.');
if ($address->id_country)
{
// Check country
if (!($country = new Country($address->id_country)) || !Validate::isLoadedObject($country))
throw new PrestaShopException('Country cannot be loaded with address->id_country');

if ((int)$country->contains_states && !(int)$address->id_state)
$this->errors[] = Tools::displayError('This country requires you to chose a State.');

$postcode = Tools::getValue('postcode');
/* Check zip code format */
if ($country->zip_code_format && !$country->checkZipCode($postcode))
$this->errors[] = sprintf(Tools::displayError('The Zip/Postal code you\'ve entered is invalid. It must follow this format: %s'), str_replace('C', $country->iso_code, str_replace('N', '0', str_replace('L', 'A', $country->zip_code_format))));
elseif(empty($postcode) && $country->need_zip_code)
$this->errors[] = Tools::displayError('A Zip/Postal code is required.');
elseif ($postcode && !Validate::isPostCode($postcode))
$this->errors[] = Tools::displayError('The Zip/Postal code is invalid.');

// Check country DNI
if ($country->isNeedDni() && (!Tools::getValue('dni') || !Validate::isDniLite(Tools::getValue('dni'))))
$this->errors[] = Tools::displayError('The identification number is incorrect or has already been used.');
else if (!$country->isNeedDni())
$address->dni = null;
}
// Check if the alias exists
if (!$this->context->customer->is_guest && !empty($_POST['alias']) && (int)$this->context->customer->id > 0)
{
$id_address = Tools::getValue('id_address');
if(Configuration::get('PS_ORDER_PROCESS_TYPE') && (int)Tools::getValue('opc_id_address_'.Tools::getValue('type')) > 0)
$id_address = Tools::getValue('opc_id_address_'.Tools::getValue('type'));

if (Db::getInstance()->getValue('
SELECT count(*)
FROM '._DB_PREFIX_.'address
WHERE `alias` = \''.pSql($_POST['alias']).'\'
AND id_address != '.(int)$id_address.'
AND id_customer = '.(int)$this->context->customer->id.'
AND deleted = 0') > 0)
$this->errors[] = sprintf(Tools::displayError('The alias "%s" has already been used. Please select another one.'), Tools::safeOutput($_POST['alias']));
}

// Check the requires fields which are settings in the BO
$this->errors = array_merge($this->errors, $address->validateFieldsRequiredDatabase());

// Don't continue this process if we have errors !
if ($this->errors && !$this->ajax)
return;

// If we edit this address, delete old address and create a new one
if (Validate::isLoadedObject($this->_address))
{
if (Validate::isLoadedObject($country) && !$country->contains_states)
$address->id_state = 0;
$address_old = $this->_address;
if (Customer::customerHasAddress($this->context->customer->id, (int)$address_old->id))
{
if ($address_old->isUsed())
$address_old->delete();
else
{
$address->id = (int)($address_old->id);
$address->date_add = $address_old->date_add;
}
}
}

if ($this->ajax && Tools::getValue('type') == 'invoice' && Configuration::get('PS_ORDER_PROCESS_TYPE'))
{
$this->errors = array_unique(array_merge($this->errors, $address->validateController()));
if (count($this->errors))
{
$return = array(
'hasError' => (bool)$this->errors,
'errors' => $this->errors
);
die(Tools::jsonEncode($return));
}
}

// Save address
if ($result = $address->save())
{
// Update id address of the current cart if necessary
if (isset($address_old) && $address_old->isUsed())
$this->context->cart->updateAddressId($address_old->id, $address->id);
else // Update cart address
$this->context->cart->autosetProductAddress();

if ((bool)(Tools::getValue('select_address', false)) == true OR (Tools::getValue('type') == 'invoice' && Configuration::get('PS_ORDER_PROCESS_TYPE')))
$this->context->cart->id_address_invoice = (int)$address->id;
elseif (Configuration::get('PS_ORDER_PROCESS_TYPE'))
$this->context->cart->id_address_invoice = (int)$this->context->cart->id_address_delivery;
$this->context->cart->update();

if ($this->ajax)
{
$return = array(
'hasError' => (bool)$this->errors,
'errors' => $this->errors,
'id_address_delivery' => (int)$this->context->cart->id_address_delivery,
'id_address_invoice' => (int)$this->context->cart->id_address_invoice
);
die(Tools::jsonEncode($return));
}

// Redirect to old page or current page
if ($back = Tools::getValue('back'))
{
if ($back == Tools::secureReferrer(Tools::getValue('back')))
Tools::redirect(html_entity_decode($back));
$mod = Tools::getValue('mod');
Tools::redirect('index.php?controller='.$back.($mod ? '&back='.$mod : ''));
}
else
Tools::redirect('index.php?controller=addresses');
}
$this->errors[] = Tools::displayError('An error occurred while updating your address.');
}

Перед комментарием // CHeck Phone добавьте:

if ( !Tools::getValue('my_custom_field'))
$this->errors[] = Tools::displayError('The custom field is mandatory!');

Теперь выскочит ошибка, если ваши клиенты оставят это поле пустым.

Не забудьте снова стереть class_index.php, конечно же.

Добавление нового поля в формате адреса в Prestashop

В качестве примечания, знайте, что для того, чтобы отобразить новое поле, которое мы добавили, нам нужно изменить формат адреса для каждой страны. Мне сейчас не известно, как применить изменения адреса для всех стран сразу простым способом. Поэтому перейдите в Localization > Countries, нажмите на название страны и добавьте новое поле в блок адреса:


Как насчет новых полей для формы клиентов?

В этом уроке мы добавили новое поле в объект Address. Тем не менее, это также возможно для создания нового основного поля клиентов, такого же, как имя, фамилия и адрес электронной почты в самой базовой регистрации (даже без адреса). Чтобы сделать это, вы можете просто применить тот же процесс для класса Customer, и относящихся к нему контроллеров. Только имейте ввиду, что вам нужно будет отредактировать пару других файлов шаблонов: identity.tpl и authentication.tpl, а также order-opc-new-account.tpl если вы используете одностраничное оформление!

перевод, работа, кодинг, prestashop

Previous post Next post
Up