2 /***************************************************************************
3 * Copyright (C) 2003-2010 Polytechnique.org *
4 * http://opensource.polytechnique.org/ *
6 * This program is free software; you can redistribute it and/or modify *
7 * it under the terms of the GNU General Public License as published by *
8 * the Free Software Foundation; either version 2 of the License, or *
9 * (at your option) any later version. *
11 * This program is distributed in the hope that it will be useful, *
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14 * GNU General Public License for more details. *
16 * You should have received a copy of the GNU General Public License *
17 * along with this program; if not, write to the Free Software *
19 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
20 ***************************************************************************/
22 /** Class Address is meant to perform most of the access to the table profile_addresses.
24 * profile_addresses describes an Address, which can be related to either a
25 * Profile, a Job or a Company:
27 * - `type` is set to 'home'
28 * - `pid` is set to the related profile pid (in profiles)
29 * - `id` is the id of the address in the list of those related to that profile
30 * - `jobid` is set to 0
33 * - `type` is set to 'hq'
35 * - `jobid` is set to the id of the company (in profile_job_enum)
36 * - `id` is set to 0 (only one address per Company)
39 * - `type` is set to 'job'
40 * - `pid` is set to the pid of the Profile of the related Job (in both profiles and profile_job)
41 * - `id` is the id of the job to which we refer (in profile_job)
42 * - `jobid` is set to 0
44 * Thus an Address can be linked to a Company, a Profile, or a Job.
48 const LINK_JOB
= 'job';
49 const LINK_COMPANY
= 'hq';
50 const LINK_PROFILE
= 'home';
52 // List of all available postal formattings.
53 private static $formattings = array('FRANCE' => 'FR');
55 // Abbreviations to be used to format French postal addresses.
56 private static $streetAbbreviations = array(
61 'CENTRE COMMERCIAL' => 'CCAL',
66 'LOTISSEMENT' => 'LOT',
70 'ROND-POINT' => 'RPT',
74 'ZONE D\'ACTIVITE' => 'ZA',
75 'ZONE D\'AMENAGEMENT CONCERTE' => 'ZAC',
76 'ZONE D\'AMENAGEMENT DIFFERE' => 'ZAD',
77 'ZONE INDUSTRIELLE' => 'ZI'
79 private static $otherAbbreviations = array(
81 'AERODROME' => 'AERD',
83 'AERONAUTIQUE' => 'AERN',
86 'AGRICOLE' => 'AGRIC',
88 'ANCIENNEMENT' => 'ANC',
89 'APPARTEMENT' => 'APP',
90 'APPARTEMENTS' => 'APP',
92 'ARRONDISSEMENT' => 'ARR',
94 'ASSOCIATION' => 'ASSOC',
95 'ASSURANCE' => 'ASSUR',
97 'BARAQUEMENT' => 'BRQ',
101 'BATAILLON' => 'BTN',
102 'BATAILLONS' => 'BTN',
104 'BATIMENTS' => 'BAT',
106 'BOITE POSTALE' => 'BP',
110 'CASE POSTALE' => 'CP',
112 'CITADELLE' => 'CTD',
117 'COMMANDANT' => 'CDT',
118 'COMMERCIAL' => 'CIAL',
121 'COMMUNAUX' => 'COM',
122 'COMPAGNIE' => 'CIE',
123 'COMPAGNON' => 'COMP',
124 'COMPAGNONS' => 'COMP',
125 'COOPERATIVE' => 'COOP',
126 'COURSE SPECIALE' => 'CS',
128 'DELEGATION' => 'DELEG',
129 'DEPARTEMENTAL' => 'DEP',
130 'DEPARTEMENTAUX' => 'DEP',
131 'DIRECTEUR' => 'DIR',
132 'DIRECTECTION' => 'DIR',
136 'ECONOMIQUE' => 'ECO',
137 'ECRIVAIN' => 'ECRIV',
138 'ECRIVAINS' => 'ECRIV',
139 'ENSEIGNEMENT' => 'ENST',
143 'ENTREPRISE' => 'ENTR',
146 'ETABLISSEMENT' => 'ETS',
148 'ETAT MAJOR' => 'EM',
152 'FORESTIER' => 'FOR',
156 'GENDARMERIE' => 'GEND',
158 'GOUVERNEMENTAL' => 'GOUV',
159 'GOUVERNEUR' => 'GOU',
171 'HOSPITALIER' => 'HOSP',
173 'INFANTERIE' => 'INFANT',
174 'INFERIEUR' => 'INF',
175 'INFERIEUR' => 'INF',
176 'INGENIEUR' => 'ING',
177 'INSPECTEUR' => 'INSP',
178 'INSTITUT' => 'INST',
179 'INTERNATIONAL' => 'INTERN',
180 'INTERNATIONALE' => 'INTERN',
181 'LABORATOIRE' => 'LABO',
182 'LIEUTENANT' => 'LT',
183 'LIEUTENANT DE VAISSEAU' => 'LTDV',
185 'MADEMOISELLE' => 'MLLE',
193 'MESDAMES' => 'MMES',
194 'MESDEMOISELLES' => 'MLLES',
196 'MILITAIRE' => 'MIL',
197 'MINISTERE' => 'MIN',
198 'MONSEIGNEUR' => 'MGR',
200 'MUNICIPAL' => 'MUN',
203 'NOTRE DAME' => 'ND',
206 'NOUVELLE' => 'NOUV',
207 'OBSERVATOIRE' => 'OBS',
215 'PREFECTURE' => 'PREF',
216 'PRESIDENT' => 'PDT',
217 'PROFESSEUR' => 'PR',
218 'PROFESSIONNEL' => 'PROF',
219 'PROFESSIONNELE' => 'PROF',
220 'PROLONGE' => 'PROL',
221 'PROLONGEE' => 'PROL',
222 'PROPRIETE' => 'PROP',
229 'REGIONALE' => 'REG',
230 'REPUBLIQUE' => 'REP',
231 'RESTAURANT' => 'REST',
236 'SANATORIUM' => 'SANA',
240 'SOUS COUVERT' => 'SC',
241 'SOUS-PREFET' => 'SPREF',
242 'SUPERIEUR' => 'SUP',
243 'SUPERIEURE' => 'SUP',
244 'SYNDICAT' => 'SYND',
245 'TECHNICIEN' => 'TECH',
246 'TECHNICIENNE' => 'TECH',
247 'TECHNICIQUE' => 'TECH',
249 'TRI SERVICE ARRIVEE' => 'TSA',
251 'UNIVERSITAIRE' => 'UNVT',
252 'UNIVERSITE' => 'UNIV',
253 'VELODROME' => 'VELOD',
256 'VIEILLES' => 'VIEL',
259 private static $entrepriseAbbreviations = array(
260 'COOPERATIVE D\'UTILISATION DE MATERIEL AGRICOLE EN COMMUN' => 'CUMA',
261 'ETABLISSEMENT PUBLIC A CARACTERE INDUSTRIEL ET COMMERCIAL' => 'EPIC',
262 'ETABLISSEMENT PUBLIC ADMINISTRATIF' => 'EPA',
263 'GROUPEMENT AGRICOLE D\'EXPLOITATION EN COMMUN' => 'GAEC',
264 'GROUPEMENT D\'INTERET ECONOMIQUE' => 'GIE',
265 'GROUPEMENT D\'INTERET PUBLIC' => 'GIP',
266 'GROUPEMENT EUROPEEN D\'INTERET ECONOMIQUE' => 'GEIE',
267 'OFFICE PUBLIC D\'HABITATION A LOYER MODERE' => 'OPHLM',
268 'SOCIETE A RESPONSABILITE LIMITEE' => 'SARL',
269 'SOCIETE ANONYME' => 'SA',
270 'SOCIETE CIVILE DE PLACEMENT COLLECTIF IMMOBILIER' => 'SCPI',
271 'SOCIETE CIVILE PROFESSIONNELLE' => 'SCP',
272 'SOCIETE COOPERATIVE OUVRIERE DE PRODUCTION ET DE CREDIT' => 'SCOP',
273 'SOCIETE D\'AMENAGEMENT FONCIER ET D\'EQUIPEMENT RURAL' => 'SAFER',
274 'SOCIETE D\'ECONOMIE MIXTE' => 'SEM',
275 'SOCIETE D\'INTERET COLLECTIF AGRICOLE' => 'SICA',
276 'SOCIETE D\'INVESTISSEMENT A CAPITAL VARIABLE' => 'SICAV',
277 'SOCIETE EN NOM COLLECTIF' => 'SNC',
278 'SOCIETE IMMOBILIERE POUR LE COMMERCE ET L\'INDUSTRIE' => 'SICOMI',
279 'SOCIETE MIXTE D\'INTERET AGRICOLE' => 'SMIA',
280 'SYNDICAT INTERCOMMUNAL A VOCATION MULTIPLE' => 'SIVOM',
281 'SYNDICAT INTERCOMMUNAL A VOCATION UNIQUE' => 'SIVU'
284 // Primary key fields: the quadruplet ($pid, $jobid, $type, $id) defines a unique address.
287 public $type = Address
::LINK_PROFILE
;
291 public $accuracy = 0;
293 public $postalText = '';
294 public $postalCode = null
;
295 public $localityId = null
;
296 public $subAdministrativeAreaId = null
;
297 public $administrativeAreaId = null
;
298 public $localityName = null
;
299 public $subAdministrativeAreaName = null
;
300 public $administrativeAreaName = null
;
301 public $countryId = null
;
302 public $latitude = null
;
303 public $longitude = null
;
304 public $north = null
;
305 public $south = null
;
308 public $geocodedText = null
;
309 public $geocodeChosen = null
;
311 // Database's field required for both 'home' and 'job' addresses.
314 // Database's fields required for 'home' addresses.
315 public $flags = null
; // 'current', 'temporary', 'secondary', 'mail', 'cedex', 'deliveryIssue'
316 public $comment = null
;
317 public $current = null
;
318 public $temporary = null
;
319 public $secondary = null
;
321 public $deliveryIssue = null
;
323 // Remaining fields that do not belong to profile_addresses.
324 public $phones = array();
325 public $error = false
;
329 public function __construct(array $data = array())
331 if (count($data) > 0) {
332 foreach ($data as $key => $val) {
337 if (!is_null($this->flags
)) {
338 $this->flags
= new PlFlagSet($this->flags
);
340 static $flags = array('current', 'temporary', 'secondary', 'mail', 'deliveryIssue');
342 $this->flags
= new PlFlagSet();
343 foreach ($flags as $flag) {
344 if (!is_null($this->$flag) && ($this->$flag == 1 ||
$this->$flag == 'on')) {
345 $this->flags
->addFlag($flag, 1);
348 $this->flags
->addFlag('cedex', (strpos(strtoupper(preg_replace(array("/[0-9,\"'#~:;_\- ]/", "/\r\n/"),
349 array('', "\n"), $this->text
)), 'CEDEX')) !== false
);
354 public function setId($id)
359 public function phones()
361 return $this->phones
;
364 public function addPhone(Phone
&$phone)
366 if ($phone->linkType() == Phone
::LINK_ADDRESS
&& $phone->pid() == $this->pid
) {
367 $this->phones
[$phone->uniqueId()] = $phone;
371 public function hasFlag($flag)
373 return ($this->flags
!= null
&& $this->flags
->hasFlag($flag));
376 /** Auxilary function for formatting postal addresses.
377 * If the needle is found in the haystack, it notifies the substitution's
378 * success, modifies the length accordingly and returns either the matching
379 * substitution or the needle.
381 private function substitute($needle, $haystack, &$length, &$success)
383 if (array_key_exists($needle, $haystack)) {
385 $length -= (strlen($needle) - strlen($haystack[$needle]));
386 return $haystack[$needle];
391 /** Checks if the line corresponds to a French street line.
392 * A line is considered a French street line if it starts by between 1 and 4 numbers.
394 private function isStreetFR($line)
396 return preg_match('/^\d{1,4}\D/', $line);
399 /** Retrieves a French street number and slit the rest of the line into an array.
400 * @param $words: array containing the rest of the line (a word per cell).
401 * @param $line: line to consider.
402 * Returns the street number.
404 private function getStreetNumberFR(&$line)
406 // First we define numbers and separators.
407 $numberReq = '(\d{1,4})\s*(BIS|TER|QUATER|[A-Z])?';
408 $separatorReq = '\s*(?:\\|-|&|A|ET)\s*';
410 // Then we retrieve the number(s) and the reste of the line.
411 preg_match('/^' . $numberReq . '(?:' . $separatorReq . $numberReq . ')?\s*(.*)$', $matches);
412 $number = $matches[1];
415 // If there is a precision on the address, we concatenate it to the number.
416 if ($matches[2] != '') {
417 $number .= $matches[2]{1};
418 } elseif ($matches[4] != '') {
419 $number .= $matches[4]{1};
425 /** Checks if the line corresponds to a French locality line.
426 * A line is considered a French locality line if it starts by exactly a
427 * postal code of exactly 5 numbers.
429 private function isLocalityFR($line)
431 return preg_match('/^\d{5}\D/', $line);
434 /** Retrieves a French postal code and slit the rest of the line into an array.
435 * @param $words: array containing the rest of the line (a word per cell).
436 * @param $line: line to consider.
437 * Returns the postal code, and cuts it out from the line.
439 private function getPostalCodeFR(&$line)
441 $number = substr($line, 0, 5);
442 $line = trim(substr($line, 5));
446 /** Returns the address formated for French postal use (cf AFNOR XPZ 10-011).
447 * A postal addresse containts at most 6 lines of at most 38 characters each:
448 * - addressee's identification ("MONSIEUR JEAN DURAND", "DURAND SA"…),
449 * - delivery point identification ("CHEZ TOTO APPARTEMENT 2", "SERVICE ACHAT"…),
450 * - building localisation complement ("ENTREE A BATIMENT DES JONQUILLES", "ZONE INDUSTRIELLE OUEST"…),
451 * - N° and street name ("25 RUE DES FLEURS", "LES VIGNES"…),
452 * - delivery service, street localisation complement ("BP 40122", "BP 40112 AREYRES"…),
453 * - postal code and locality or cedex code and cedex ("33500 LIBOURNE", "33506 LIBOURNE CEDEX"…).
454 * Punctuation must be removed, all leters must be uppercased.
455 * Both locality and street name must not take more than 32 characters.
457 * @param $arrayText: array containing the address to be formated, one
458 * address line per array line.
459 * @param $count: array size.
461 private function formatPostalAddressFR($arrayText, $count)
463 // First removes country if any.
464 if ($arrayText[$count - 1] == 'FRANCE') {
465 unset($arrayText[$count - 1]);
469 // All the lines must have less than 38 characters but street and
470 // locality lines whose limit is 32 characters.
471 foreach ($arrayText as $lineNumber => $line) {
472 if ($isStreetLine = $this->isStreetFR($line)) {
473 $formattedLine = $this->getStreetNumberFR($line) . ' ';
475 } elseif ($this->isLocalityFR($line)) {
476 $formattedLine = $this->getPostalCodeFR($line) . ' ';
483 $words = explode(' ', $line);
484 $count = count($words);
485 $length = $count - 1;
486 foreach ($words as $word) {
487 $length +
= strlen($word);
490 // Checks is length is ok. Otherwise, we try to shorten words and
491 // update the length of the current line accordingly.
492 for ($i = 0; $i < $count && $length > $limit; ++
$i) {
495 $sub = $this->substitute($words[$i], Address
::$streetAbbreviations, $length, $success);
497 // Entreprises' substitution are only suitable for the first two lines.
498 if ($lineNumber <= 2 && !$success) {
499 $sub = $this->substitute($words[$i], Address
::$entrepriseAbbreviations, $length, $success);
502 $sub = $this->substitute($words[$i], Address
::$otherAbbreviations, $length, $success);
505 $formattedLine .= $sub . ' ';
507 for (; $i < $count; ++
$i) {
508 $formattedLine .= $words[$i] . ' ';
510 $arrayText[$lineNumber] = trim($formattedLine);
513 return implode("\n", $arrayText);
516 // Formats postal addresses.
517 // First erases punctuation, accents… Then uppercase the address and finally
518 // calls the country's dedicated formatting function.
519 public function formatPostalAddress()
521 // Performs rough formatting.
522 $text = mb_strtoupper(replace_accent($this->text
));
523 $text = str_replace(array(',', ';', '.', ':', '!', '?', '"', '«', '»'), '', $text);
524 $text = preg_replace('/( |\t)+/', ' ', $text);
525 $arrayText = explode("\n", $text);
526 $arrayText = array_map('trim', $arrayText);
528 // Search for country.
529 $countries = DirEnum
::getOptions(DirEnum
::COUNTRIES
);
530 $countries = array_map('replace_accent', $countries);
531 $countries = array_map('strtoupper', $countries);
532 $count = count($arrayText);
533 if (in_array(strtoupper($address->country
), Address
::$formattings)) {
534 $text = call_user_func(array($this, 'formatPostalAddress' . Address
::$formattings[strtoupper($address->country
)]), $arrayText, $count);
535 } elseif (array_key_exists($arrayText[$count - 1], Address
::$formattings)) {
536 $text = call_user_func(array($this, 'formatPostalAddress' . Address
::$formattings[$arrayText[$count - 1]]), $arrayText, $count);
537 } elseif (!in_array($arrayText[$count - 1], $countries)) {
538 $text = $this->formatPostalAddressFR($arrayText, $count);
540 $text = implode("\n", $arrayText);
543 $this->postalText
= $text;
546 public function format(array $format = array())
548 if (empty($format)) {
549 $format['requireGeocoding'] = false
;
550 $format['stripGeocoding'] = false
;
551 $format['postalText'] = false
;
553 foreach (array('requireGeocoding', 'stripGeocoding', 'postalText') as $type) {
554 $format[$type] = (isset($format[$type])) ?
$format[$type] : false
;
557 $this->text
= trim($this->text
);
558 if ($this->removed
== 1) {
563 if ($format['requireGeocoding'] ||
$this->changed
== 1) {
564 $gmapsGeocoder = new GMapsGeocoder();
565 $gmapsGeocoder->getGeocodedAddress($this);
567 $this->error
= !empty($this->geocodedText
);
569 if ($format['stripGeocoding'] ||
($this->type
== self
::LINK_COMPANY
&& $this->error
) ||
$this->geocodeChosen
=== '0') {
570 $gmapsGeocoder = new GMapsGeocoder();
571 $gmapsGeocoder->stripGeocodingFromAddress($this);
572 if ($this->geocodeChosen
=== '0') {
573 $mailer = new PlMailer('profile/geocoding.mail.tpl');
574 $mailer->assign('text', $this->text
);
575 $mailer->assign('geoloc', $this->geocodedText
);
579 if ($this->countryId
== '') {
580 $this->countryId
= null
;
582 $this->geocodeChosen
= null
;
583 $this->phones
= Phone
::formatFormArray($this->phones
, $this->error
);
584 if ($format['postalText']) {
585 $this->formatPostalAddress();
587 return !$this->error
;
590 public function toFormArray()
593 'accuracy' => $this->accuracy
,
594 'text' => $this->text
,
595 'postalText' => $this->postalText
,
596 'postalCode' => $this->postalCode
,
597 'localityId' => $this->localityId
,
598 'subAdministrativeAreaId' => $this->subAdministrativeAreaId
,
599 'administrativeAreaId' => $this->administrativeAreaId
,
600 'countryId' => $this->countryId
,
601 'localityName' => $this->localityName
,
602 'subAdministrativeAreaName' => $this->subAdministrativeAreaName
,
603 'administrativeAreaName' => $this->administrativeAreaName
,
604 'latitude' => $this->latitude
,
605 'longitude' => $this->longitude
,
606 'north' => $this->north
,
607 'south' => $this->south
,
608 'east' => $this->east
,
609 'west' => $this->west
,
610 'error' => $this->error
,
611 'changed' => $this->changed
,
612 'removed' => $this->removed
,
614 if (!is_null($this->geocodedText
)) {
615 $address['geocodedText'] = $this->geocodedText
;
616 $address['geocodeChosen'] = $this->geocodeChosen
;
619 if ($this->type
== self
::LINK_PROFILE ||
$this->type
== self
::LINK_JOB
) {
620 $address['pub'] = $this->pub
;
622 if ($this->type
== self
::LINK_PROFILE
) {
623 static $flags = array('current', 'temporary', 'secondary', 'mail', 'cedex', 'deliveryIssue');
625 foreach ($flags as $flag) {
626 $address[$flag] = $this->flags
->hasFlag($flag);
628 $address['comment'] = $this->comment
;
629 $address['phones'] = Phone
::formatFormArray($this->phones
);
635 private function toString()
637 $address = 'Adresse : ' . $this->text
;
638 if ($this->type
== self
::LINK_PROFILE ||
$this->type
== self
::LINK_JOB
) {
639 $address .= ', affichage : ' . $this->pub
;
641 if ($this->type
== self
::LINK_PROFILE
) {
642 static $flags = array(
643 'current' => 'actuelle',
644 'temporary' => 'temporaire',
645 'secondary' => 'secondaire',
646 'mail' => 'conctactable par courier',
647 'deliveryIssue' => 'n\'habite pas à l\'adresse indiquée',
648 'cedex' => 'type cédex',
651 $address .= ', commentaire : ' . $this->comment
;
652 foreach ($flags as $flag => $flagName) {
653 if ($this->flags
->hasFlag($flag)) {
654 $address .= ', ' . $flagName;
657 if ($phones = Phone
::formArrayToString($this->phones
)) {
658 $address .= ', ' . $phones;
664 private function isEmpty()
666 return (!$this->text ||
$this->text
== '');
669 public function save()
671 static $areas = array('administrativeArea', 'subAdministrativeArea', 'locality');
673 $this->format(array('postalText' => true
));
674 if (!$this->isEmpty()) {
675 foreach ($areas as $area) {
676 Geocoder
::getAreaId($this, $area);
679 XDB
::execute('INSERT INTO profile_addresses (pid, jobid, type, id, flags, accuracy,
680 text, postalText, postalCode, localityId,
681 subAdministrativeAreaId, administrativeAreaId,
682 countryId, latitude, longitude, pub, comment,
683 north, south, east, west)
684 VALUES ({?}, {?}, {?}, {?}, {?}, {?}, {?}, {?}, {?}, {?}, {?},
685 {?}, {?}, {?}, {?}, {?}, {?}, {?}, {?}, {?}, {?})',
686 $this->pid
, $this->jobid
, $this->type
, $this->id
, $this->flags
, $this->accuracy
,
687 $this->text
, $this->postalText
, $this->postalCode
, $this->localityId
,
688 $this->subAdministrativeAreaId
, $this->administrativeAreaId
,
689 $this->countryId
, $this->latitude
, $this->longitude
,
690 $this->pub
, $this->comment
,
691 $this->north
, $this->south
, $this->east
, $this->west
);
693 if ($this->type
== self
::LINK_PROFILE
) {
694 Phone
::savePhones($this->phones
, $this->pid
, Phone
::LINK_ADDRESS
, $this->id
);
699 public function delete()
701 XDB
::execute('DELETE FROM profile_addresses
702 WHERE pid = {?} AND jobid = {?} AND type = {?} AND id = {?}',
703 $this->pid
, $this->jobid
, $this->type
, $this->id
);
706 static public function deleteAddresses($pid, $type, $jobid = null
)
709 if (!is_null($pid)) {
710 $where = XDB
::format(' AND pid = {?}', $pid);
712 if (!is_null($jobid)) {
713 $where = XDB
::format(' AND jobid = {?}', $jobid);
715 XDB
::execute('DELETE FROM profile_addresses
716 WHERE type = {?}' . $where,
718 if ($type == self
::LINK_PROFILE
) {
719 Phone
::deletePhones($pid, Phone
::LINK_ADDRESS
);
723 /** Saves addresses into the database.
724 * @param $data: an array of form formatted addresses.
725 * @param $pid, $type, $linkid: pid, type and id concerned by the update.
727 static public function saveFromArray(array $data, $pid, $type = self
::LINK_PROFILE
, $linkid = null
)
729 foreach ($data as $id => $value) {
730 if (!is_null($linkid)) {
731 $value['id'] = $linkid;
735 if (!is_null($pid)) {
736 $value['pid'] = $pid;
738 if (!is_null($type)) {
739 $value['type'] = $type;
741 $address = new Address($value);
746 static private function formArrayWalk(array $data, $function, &$success = true
, $requiresEmptyAddress = false
)
748 $addresses = array();
749 foreach ($data as $item) {
750 $address = new Address($item);
751 $success = ($address->format() && $success);
752 if (!$address->isEmpty()) {
753 $addresses[] = call_user_func(array($address, $function));
756 if (count($address) == 0 && $requiresEmptyAddress) {
757 $address = new Address();
758 $addresses[] = call_user_func(array($address, $function));
763 // Formats an array of form addresses into an array of form formatted addresses.
764 static public function formatFormArray(array $data, &$success = true
)
766 // Only a single address can be the profile's current address and she must have one.
768 foreach ($data as $key => &$address) {
769 if (isset($address['current']) && $address['current']) {
771 $address['current'] = false
;
777 if (!$hasCurrent && count($value) > 0) {
778 foreach ($value as &$address) {
779 $address['current'] = true
;
784 return self
::formArrayWalk($data, 'toFormArray', $success, true
);
787 static public function formArrayToString(array $data)
789 return implode(' ; ', self
::formArrayWalk($data, 'toString'));
792 static public function iterate(array $pids = array(), array $types = array(),
793 array $jobids = array(), array $pubs = array())
795 return new AddressIterator($pids, $types, $jobids, $pubs);
799 /** Iterator over a set of Phones
801 * @param $pid, $type, $jobid, $pub
803 * The iterator contains the phones that correspond to the value stored in the
804 * parameters' arrays.
806 class AddressIterator
implements PlIterator
810 public function __construct(array $pids, array $types, array $jobids, array $pubs)
813 if (count($pids) != 0) {
814 $where[] = XDB
::format('(pa.pid IN {?})', $pids);
816 if (count($types) != 0) {
817 $where[] = XDB
::format('(pa.type IN {?})', $types);
819 if (count($jobids) != 0) {
820 $where[] = XDB
::format('(pa.jobid IN {?})', $jobids);
822 if (count($pubs) != 0) {
823 $where[] = XDB
::format('(pa.pub IN {?})', $pubs);
825 $sql = 'SELECT pa.pid, pa.jobid, pa.type, pa.id, pa.flags,
826 pa.accuracy, pa.text, pa.postalText, pa.postalCode,
827 pa.localityId, pa.subAdministrativeAreaId,
828 pa.administrativeAreaId, pa.countryId,
829 pa.latitude, pa.longitude, pa.north, pa.south, pa.east, pa.west,
831 gl.name AS locality, gs.name AS subAdministrativeArea,
832 ga.name AS administrativeArea, gc.countryFR AS country
833 FROM profile_addresses AS pa
834 LEFT JOIN geoloc_localities AS gl ON (gl.id = pa.localityId)
835 LEFT JOIN geoloc_administrativeareas AS ga ON (ga.id = pa.administrativeAreaId)
836 LEFT JOIN geoloc_subadministrativeareas AS gs ON (gs.id = pa.subAdministrativeAreaId)
837 LEFT JOIN geoloc_countries AS gc ON (gc.iso_3166_1_a2 = pa.countryId)
838 ' . ((count($where) > 0) ?
'WHERE ' . implode(' AND ', $where) : '') . '
839 ORDER BY pa.pid, pa.jobid, pa.id';
840 $this->dbiter
= XDB
::iterator($sql);
843 public function next()
845 if (is_null($this->dbiter
)) {
848 $data = $this->dbiter
->next();
849 if (is_null($data)) {
852 // Adds phones to addresses.
853 $it = Phone
::iterate(array($data['pid']), array(Phone
::LINK_ADDRESS
), array($data['id']));
854 while ($phone = $it->next()) {
855 $data['phones'][$phone->id()] = $phone->toFormArray();
857 return new Address($data);
860 public function total()
862 return $this->dbiter
->total();
865 public function first()
867 return $this->dbiter
->first();
870 public function last()
872 return $this->dbiter
->last();
875 public function value()
877 return $this->dbiter
;
881 // vim:set et sw=4 sts=4 sws=4 foldmethod=marker enc=utf-8: