Skip to content

Commit

Permalink
Added Symfony Form support commerceguys#66
Browse files Browse the repository at this point in the history
  • Loading branch information
abdfork committed Sep 30, 2016
1 parent e30e049 commit d930517
Show file tree
Hide file tree
Showing 13 changed files with 703 additions and 0 deletions.
2 changes: 2 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
"suggest": {
"commerceguys/intl": "to use it as the source of country data",
"symfony/intl": "to use it as the source of country data",
"symfony/form": "to generate symfony forms",
"symfony/translation": "to translate labels",
"symfony/validator": "to validate addresses"
},
"autoload": {
Expand Down
37 changes: 37 additions & 0 deletions resources/translations/addressLabels.en_US.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
area: 'Area'
county: 'County'
department: 'Department'
district: 'District'
do_si: 'Do'
emirate: 'Emirate'
island: 'Island'
oblast: 'Oblast'
parish: 'Parish'
prefecture: 'Prefecture'
province: 'Province'
state: 'State'

city: 'City'
district: 'District'
post_town: 'Post Town'
suburb: 'Suburb'
locality: 'Locality'

neighborhood: 'Neighborhood'
village_township: 'Village / Township'
townland: ''

postal: 'Postal Code'
zip: 'ZIP code'
pin: 'PIN code'

addressLine1: 'Street Address'
addressLine2: ''

organization: 'Company'
givenName: 'Contact Name'
additionalName: ''
familyName: ''

sortingCode: 'Cedex'
postalCode: 'Postal Code'
37 changes: 37 additions & 0 deletions resources/translations/addressLabels.es_ES.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
area: 'Área'
county: 'Condado'
department: 'Departamento'
district: 'Distrito'
do_si: 'Do'
emirate: 'Emirato'
island: 'Isla'
oblast: 'Área'
parish: 'Municipio'
prefecture: 'Prefectura'
province: 'Provincia'
state: 'Estado'

city: 'Ciudad'
district: 'Distrito'
post_town: 'Post Town'
suburb: 'Suburbio'
locality: 'Localidad'

neighborhood: 'Barrio'
village_township: 'Pueblo / Aldea'
townland: 'Localidad'

postal: 'Código Postal'
zip: 'Código ZIP'
pin: 'Código PIN'

addressLine1: 'Dirección Postal'
addressLine2: ''

organization: 'Compañía'
givenName: 'Nombre'
additionalName: ''
familyName: 'Apellidos'

sortingCode: 'Cedex'
postalCode: 'Código Postal'
5 changes: 5 additions & 0 deletions src/Address.php
Original file line number Diff line number Diff line change
Expand Up @@ -393,4 +393,9 @@ public function withLocale($locale)

return $new;
}

public function __set($property, $value)
{
$this->$property = $value;
}
}
1 change: 1 addition & 0 deletions src/AddressFormat/AddressFormatRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -410,6 +410,7 @@ protected function getDefinitions()
],
'ES' => [
'format' => "%givenName %familyName\n%organization\n%addressLine1\n%addressLine2\n%postalCode %locality %administrativeArea",
'locale' => 'es_ES',
'required_fields' => [
'addressLine1', 'locality', 'administrativeArea', 'postalCode',
],
Expand Down
236 changes: 236 additions & 0 deletions src/Form/EventListener/GenerateAddressFieldsSubscriber.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,236 @@
<?php

namespace CommerceGuys\Addressing\Form\EventListener;

use CommerceGuys\Addressing\AddressFormat\AddressField;
use CommerceGuys\Addressing\AddressFormat\AdministrativeAreaType;
use CommerceGuys\Addressing\AddressFormat\DependentLocalityType;
use CommerceGuys\Addressing\AddressFormat\LocalityType;
use CommerceGuys\Addressing\AddressFormat\PostalCodeType;
use CommerceGuys\Addressing\AddressFormat\AddressFormatHelper;
use CommerceGuys\Addressing\AddressFormat\AddressFormat;
use CommerceGuys\Addressing\AddressFormat\AddressFormatRepositoryInterface;
use CommerceGuys\Addressing\Subdivision\SubdivisionRepositoryInterface;
use CommerceGuys\Addressing\Translator\labelTranslator;
use CommerceGuys\Addressing\Translator\labelTranslatorInterface;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Form\FormInterface;


/**
* Class GenerateAddressFieldsSubscriber
* @package CommerceGuys\Addressing\Form\EventListener
*/
class GenerateAddressFieldsSubscriber implements EventSubscriberInterface
{
/**
* The address format repository.
*
* @var AddressFormatRepositoryInterface
*/
protected $addressFormatRepository;
/**
* The subdivision repository.
*
* @var SubdivisionRepositoryInterface
*/
protected $subdivisionRepository;
/**
* Creates a GenerateAddressFieldsSubscriber instance.
*
* @param AddressFormatRepositoryInterface $addressFormatRepository
* @param SubdivisionRepositoryInterface $subdivisionRepository
*/
public function __construct(AddressFormatRepositoryInterface $addressFormatRepository, SubdivisionRepositoryInterface $subdivisionRepository)
{
$this->addressFormatRepository = $addressFormatRepository;
$this->subdivisionRepository = $subdivisionRepository;
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents()
{
return [
FormEvents::PRE_SET_DATA => 'preSetData',
FormEvents::PRE_SUBMIT => 'preSubmit',
];
}

public function preSetData(FormEvent $event)
{
$address = $event->getData();

$form = $event->getForm();
if (null === $address) {
return;
}
$countryCode = $address->getCountryCode();
$administrativeArea = $address->getAdministrativeArea();
$locality = $address->getLocality();
$this->buildForm($form, $countryCode, $administrativeArea, $locality);
}

public function preSubmit(FormEvent $event)
{
$data = $event->getData();
$form = $event->getForm();

$countryCode = array_key_exists('countryCode', $data) ? $data['countryCode'] : null;
$administrativeArea = array_key_exists('administrativeArea', $data) ? $data['administrativeArea'] : null;
$locality = array_key_exists('locality', $data) ? $data['locality'] : null;
$this->buildForm($form, $countryCode, $administrativeArea, $locality);
}

/**
* Builds the address form for the provided country code.
*
* @param FormInterface $form
* @param string $countryCode The country code.
* @param string $administrativeArea The administrative area.
* @param string $locality The locality.
* @param labelTranslatorInterface $translator
*/
protected function buildForm(FormInterface $form, $countryCode, $administrativeArea, $locality, labelTranslatorInterface $translator = null)
{
$addressFormat = $this->addressFormatRepository->get($countryCode);

if(empty($translator)) {
$translator = new labelTranslator($addressFormat->getLocale());
}

// A list of needed subdivisions and their parent ids.
$subdivisions = [
AddressField::ADMINISTRATIVE_AREA => 0,
];
if (!empty($administrativeArea)) {
$subdivisions[AddressField::LOCALITY] = $administrativeArea;
}
if (!empty($locality)) {
$subdivisions[AddressField::DEPENDENT_LOCALITY] = $locality;
}
$fields = $this->getFormFields($addressFormat, $subdivisions, $translator);
foreach ($fields as $field => $fieldOptions) {
$type = isset($fieldOptions['choices']) ? ChoiceType::class : TextType::class;
$form->add($field, $type, $fieldOptions);
}
}

/**
* Gets a list of form fields for the provided address format.
*
* @param AddressFormat $addressFormat
* @param array $subdivisions An array of needed subdivisions.
*
* @param labelTranslatorInterface $translator
* @return array An array in the $field => $formOptions format.
*/
protected function getFormFields(AddressFormat $addressFormat, $subdivisions, labelTranslatorInterface $translator)
{
$fields = [];
$labels = $this->getFieldLabels($addressFormat, $translator);
$requiredFields = $addressFormat->getRequiredFields();
$format = $addressFormat->getFormat();
$groupedFields = AddressFormatHelper::getGroupedFields($format);
foreach ($groupedFields as $lineFields) {
foreach ($lineFields as $field) {
$fields[$field] = [
'label' => $labels[$field],
'required' => in_array($field, $requiredFields),
];
}
}
// Add choices for predefined subdivisions.
foreach ($subdivisions as $field => $parentId) {
// @todo Pass the form locale to get the translated values.
$children = $this->subdivisionRepository->getList(array($addressFormat->getCountryCode()));
if ($children) {
$fields[$field]['choices'] = $children;
}
}
return $fields;
}

/**
* Gets the labels for the provided address format's fields.
*
* @param AddressFormat $addressFormat
*
* @param labelTranslatorInterface $translator
* @return array An array of labels keyed by field constants.
*/
protected function getFieldLabels($addressFormat, labelTranslatorInterface $translator)
{
// All possible subdivision labels.
$subdivisionLabels = [
AdministrativeAreaType::AREA => $translator->translate(AdministrativeAreaType::AREA),
AdministrativeAreaType::COUNTY => $translator->translate(AdministrativeAreaType::COUNTY),
AdministrativeAreaType::DEPARTMENT => $translator->translate(AdministrativeAreaType::DEPARTMENT),
AdministrativeAreaType::DISTRICT => $translator->translate(AdministrativeAreaType::DISTRICT),
AdministrativeAreaType::DO_SI => $translator->translate(AdministrativeAreaType::DO_SI),
AdministrativeAreaType::EMIRATE => $translator->translate(AdministrativeAreaType::EMIRATE),
AdministrativeAreaType::ISLAND => $translator->translate(AdministrativeAreaType::ISLAND),
AdministrativeAreaType::OBLAST => $translator->translate(AdministrativeAreaType::OBLAST),
AdministrativeAreaType::PARISH => $translator->translate(AdministrativeAreaType::PARISH),
AdministrativeAreaType::PREFECTURE => $translator->translate(AdministrativeAreaType::PREFECTURE),
AdministrativeAreaType::PROVINCE => $translator->translate(AdministrativeAreaType::PROVINCE),
AdministrativeAreaType::STATE => $translator->translate(AdministrativeAreaType::STATE),
LocalityType::CITY => $translator->translate(LocalityType::CITY),
LocalityType::DISTRICT => $translator->translate(LocalityType::DISTRICT),
LocalityType::POST_TOWN => $translator->translate(LocalityType::POST_TOWN),
DependentLocalityType::DISTRICT => $translator->translate(DependentLocalityType::DISTRICT),
DependentLocalityType::NEIGHBORHOOD => $translator->translate(DependentLocalityType::NEIGHBORHOOD),
DependentLocalityType::VILLAGE_TOWNSHIP => $translator->translate(DependentLocalityType::VILLAGE_TOWNSHIP),
DependentLocalityType::SUBURB => $translator->translate(DependentLocalityType::SUBURB),
PostalCodeType::POSTAL => $translator->translate(PostalCodeType::POSTAL),
PostalCodeType::ZIP => $translator->translate(PostalCodeType::ZIP),
PostalCodeType::PIN => $translator->translate(PostalCodeType::PIN),
];

// Determine the correct administrative area label.
$administrativeAreaType = $addressFormat->getAdministrativeAreaType();
$administrativeAreaLabel = '';
if (isset($subdivisionLabels[$administrativeAreaType])) {
$administrativeAreaLabel = $subdivisionLabels[$administrativeAreaType];
}
// Determine the correct locality label.
$localityType = $addressFormat->getLocalityType();
$localityLabel = '';
if (isset($subdivisionLabels[$localityType])) {
$localityLabel = $subdivisionLabels[$localityType];
}
// Determine the correct dependent locality label.
$dependentLocalityType = $addressFormat->getDependentLocalityType();
$dependentLocalityLabel = '';
if (isset($subdivisionLabels[$dependentLocalityType])) {
$dependentLocalityLabel = $subdivisionLabels[$dependentLocalityType];
}
// Determine the correct postal code label.
$postalCodeType = $addressFormat->getPostalCodeType();
$postalCodeLabel = $subdivisionLabels[PostalCodeType::POSTAL];
if (isset($subdivisionLabels[$postalCodeType])) {
$postalCodeLabel = $subdivisionLabels[$postalCodeType];
}
// Assemble the final set of labels.
$labels = [
AddressField::ADMINISTRATIVE_AREA => $translator->translate($administrativeAreaLabel),
AddressField::LOCALITY => $translator->translate($localityLabel),
AddressField::DEPENDENT_LOCALITY => $translator->translate($dependentLocalityLabel),
AddressField::ADDRESS_LINE1 => $translator->translate(AddressField::ADDRESS_LINE1),
AddressField::ADDRESS_LINE2 => $translator->translate(AddressField::ADDRESS_LINE2),
AddressField::ORGANIZATION => $translator->translate(AddressField::ORGANIZATION),
AddressField::GIVEN_NAME => $translator->translate(AddressField::GIVEN_NAME),
AddressField::ADDITIONAL_NAME => $translator->translate(AddressField::ADDITIONAL_NAME),
AddressField::FAMILY_NAME => $translator->translate(AddressField::FAMILY_NAME),
// Google's libaddressinput provides no label for this field type,
// Google wallet calls it "CEDEX" for every country that uses it.
AddressField::SORTING_CODE => $translator->translate(AddressField::SORTING_CODE),
AddressField::POSTAL_CODE => $translator->translate($postalCodeLabel),
];
return $labels;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php

namespace CommerceGuys\Addressing\Form\EventListener;

use CommerceGuys\Addressing\AddressFormat\AddressFormat;
use Symfony\Component\Form\FormEvent;

/**
* Interface EventSubscriber knows himself what events he is interested in.
*
*/
interface GenerateAddressFieldsSusbscriberInterface
{

public function buildForm($form, $countryCode, $administrativeArea, $locality);

public function getFormFields(AddressFormat $addressFormat, $subdivisions);

public function preSetData(FormEvent $event);

public function preSubmit(FormEvent $event);

public function getFieldLabels($addressFormat);

}
Loading

0 comments on commit d930517

Please sign in to comment.