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 // Primary key fields: the quadruplet ($pid, $jobid, $type, $id) defines a unique address.
55 public $type = Address
::LINK_PROFILE
;
61 public $postalText = '';
62 public $postalCode = null
;
63 public $localityId = null
;
64 public $subAdministrativeAreaId = null
;
65 public $administrativeAreaId = null
;
66 public $localityName = null
;
67 public $subAdministrativeAreaName = null
;
68 public $administrativeAreaName = null
;
69 public $countryId = null
;
70 public $latitude = null
;
71 public $longitude = null
;
76 public $geocodedText = null
;
77 public $geocodeChosen = null
;
79 // Database's field required for both 'home' and 'job' addresses.
82 // Database's fields required for 'home' addresses.
83 public $flags = null
; // 'current', 'temporary', 'secondary', 'mail', 'cedex', 'deliveryIssue'
84 public $comment = null
;
85 public $current = null
;
86 public $temporary = null
;
87 public $secondary = null
;
89 public $deliveryIssue = null
;
91 // Remaining fields that do not belong to profile_addresses.
92 public $phones = array();
93 public $error = false
;
97 public function __construct(array $data = array())
99 if (count($data) > 0) {
100 foreach ($data as $key => $val) {
105 if (!is_null($this->flags
)) {
106 $this->flags
= new PlFlagSet($this->flags
);
108 static $flags = array('current', 'temporary', 'secondary', 'mail', 'deliveryIssue');
110 $this->flags
= new PlFlagSet();
111 foreach ($flags as $flag) {
112 if (!is_null($this->$flag) && ($this->$flag == 1 ||
$this->$flag == 'on')) {
113 $this->flags
->addFlag($flag, 1);
116 $this->flags
->addFlag('cedex', (strpos(strtoupper(preg_replace(array("/[0-9,\"'#~:;_\- ]/", "/\r\n/"),
117 array('', "\n"), $this->text
)), 'CEDEX')) !== false
);
122 public function setId($id)
127 public function phones()
129 return $this->phones
;
132 public function addPhone(Phone
&$phone)
134 if ($phone->linkType() == Phone
::LINK_ADDRESS
&& $phone->pid() == $this->pid
) {
135 $this->phones
[$phone->uniqueId()] = $phone;
139 public function hasFlag($flag)
141 return ($this->flags
!= null
&& $this->flags
->hasFlag($flag));
144 // Returns the address formated for postal use.
145 // The main rules are (cf AFNOR XPZ 10-011):
146 // -everything in upper case;
147 // -if there are more then than 38 characters in a line, split it;
148 // -if there are more then than 32 characters in the description of the "street", use abbreviations.
149 public function formatPostalAddress() {
150 static $abbreviations = array(
154 'BOULEVARD' => 'BVD',
160 $text = strtoupper($text);
161 $arrayText = explode("\n", $text);
164 foreach ($arrayText as $i => $line) {
165 $postalText .= (($i == 0) ?
'' : "\n");
166 if (($length = strlen($line)) > 32) {
167 $words = explode(' ', $line);
169 foreach ($words as $word) {
170 if (isset($abbreviations[$word])) {
171 $word = $abbreviations[$word];
173 if ($count +
($wordLength = strlen($word)) <= 38) {
174 $postalText .= (($count == 0) ?
'' : ' ') . $word;
175 $count +
= (($count == 0) ?
0 : 1) +
$wordLength;
177 $postalText .= "\n" . $word;
178 $count = strlen($word);
182 $postalText .= $line;
185 $this->postalText
= $postalText;
188 public function format(array $format = array())
190 if (empty($format)) {
191 $format['requireGeocoding'] = false
;
192 $format['stripGeocoding'] = false
;
193 $format['postalText'] = false
;
195 $this->text
= trim($this->text
);
196 if ($this->removed
== 1) {
201 if ($format['requireGeocoding'] ||
$this->changed
== 1) {
202 $gmapsGeocoder = new GMapsGeocoder();
203 $gmapsGeocoder->getGeocodedAddress($this);
205 $this->error
= !empty($this->geocodedText
);
207 if ($format['stripGeocoding'] ||
($this->type
== self
::LINK_COMPANY
&& $this->error
) ||
$this->geocodeChosen
=== '0') {
208 $gmapsGeocoder = new GMapsGeocoder();
209 $gmapsGeocoder->stripGeocodingFromAddress($this);
210 if ($this->geocodeChosen
=== '0') {
211 $mailer = new PlMailer('profile/geocoding.mail.tpl');
212 $mailer->assign('text', $this->text
);
213 $mailer->assign('geoloc', $this->geocodedText
);
217 if ($format['postalText']) {
218 $this->formatPostalAddress();
220 if ($this->countryId
== '') {
221 $this->countryId
= null
;
223 $this->geocodeChosen
= null
;
224 $this->phones
= Phone
::formatFormArray($this->phones
, $this->error
);
225 return !$this->error
;
228 public function toFormArray()
231 'accuracy' => $this->accuracy
,
232 'text' => $this->text
,
233 'postalText' => $this->postalText
,
234 'postalCode' => $this->postalCode
,
235 'localityId' => $this->localityId
,
236 'subAdministrativeAreaId' => $this->subAdministrativeAreaId
,
237 'administrativeAreaId' => $this->administrativeAreaId
,
238 'countryId' => $this->countryId
,
239 'localityName' => $this->localityName
,
240 'subAdministrativeAreaName' => $this->subAdministrativeAreaName
,
241 'administrativeAreaName' => $this->administrativeAreaName
,
242 'latitude' => $this->latitude
,
243 'longitude' => $this->longitude
,
244 'north' => $this->north
,
245 'south' => $this->south
,
246 'east' => $this->east
,
247 'west' => $this->west
,
248 'error' => $this->error
,
249 'changed' => $this->changed
,
250 'removed' => $this->removed
,
252 if (!is_null($this->geocodedText
)) {
253 $address['geocodedText'] = $this->geocodedText
;
254 $address['geocodeChosen'] = $this->geocodeChosen
;
257 if ($this->type
== self
::LINK_PROFILE ||
$this->type
== self
::LINK_JOB
) {
258 $address['pub'] = $this->pub
;
260 if ($this->type
== self
::LINK_PROFILE
) {
261 static $flags = array('current', 'temporary', 'secondary', 'mail', 'cedex', 'deliveryIssue');
263 foreach ($flags as $flag) {
264 $address[$flag] = $this->flags
->hasFlag($flag);
266 $address['comment'] = $this->comment
;
267 $address['phones'] = Phone
::formatFormArray($this->phones
);
273 private function toString()
275 $address = 'Adresse : ' . $this->text
;
276 if ($this->type
== self
::LINK_PROFILE ||
$this->type
== self
::LINK_JOB
) {
277 $address .= ', affichage : ' . $this->pub
;
279 if ($this->type
== self
::LINK_PROFILE
) {
280 static $flags = array(
281 'current' => 'actuelle',
282 'temporary' => 'temporaire',
283 'secondary' => 'secondaire',
284 'mail' => 'conctactable par courier',
285 'deliveryIssue' => 'n\'habite pas à l\'adresse indiquée',
286 'cedex' => 'type cédex',
289 $address .= ', commentaire : ' . $this->comment
;
290 foreach ($flags as $flag => $flagName) {
291 if ($this->flags
->hasFlag($flag)) {
292 $address .= ', ' . $flagName;
295 if ($phones = Phone
::formArrayToString($this->phones
)) {
296 $address .= ', ' . $phones;
302 private function isEmpty()
304 return (!$this->text ||
$this->text
== '');
307 public function save()
309 static $areas = array('administrativeArea', 'subAdministrativeArea', 'locality');
311 $this->format(array('postalText' => true
));
312 if (!$this->isEmpty()) {
313 foreach ($areas as $area) {
314 Geocoder
::getAreaId($this, $area);
317 XDB
::execute('INSERT INTO profile_addresses (pid, jobid, type, id, flags, accuracy,
318 text, postalText, postalCode, localityId,
319 subAdministrativeAreaId, administrativeAreaId,
320 countryId, latitude, longitude, pub, comment,
321 north, south, east, west)
322 VALUES ({?}, {?}, {?}, {?}, {?}, {?}, {?}, {?}, {?}, {?}, {?},
323 {?}, {?}, {?}, {?}, {?}, {?}, {?}, {?}, {?}, {?})',
324 $this->pid
, $this->jobid
, $this->type
, $this->id
, $this->flags
, $this->accuracy
,
325 $this->text
, $this->postalText
, $this->postalCode
, $this->localityId
,
326 $this->subAdministrativeAreaId
, $this->administrativeAreaId
,
327 $this->countryId
, $this->latitude
, $this->longitude
,
328 $this->pub
, $this->comment
,
329 $this->north
, $this->south
, $this->east
, $this->west
);
331 if ($this->type
== self
::LINK_PROFILE
) {
332 Phone
::savePhones($this->phones
, $this->pid
, Phone
::LINK_ADDRESS
, $this->id
);
337 public function delete()
339 XDB
::execute('DELETE FROM profile_addresses
340 WHERE pid = {?} AND jobid = {?} AND type = {?} AND id = {?}',
341 $this->pid
, $this->jobid
, $this->type
, $this->id
);
344 static public function deleteAddresses($pid, $type, $jobid = null
)
347 if (!is_null($pid)) {
348 $where = XDB
::format(' AND pid = {?}', $pid);
350 if (!is_null($jobid)) {
351 $where = XDB
::format(' AND jobid = {?}', $jobid);
353 XDB
::execute('DELETE FROM profile_addresses
354 WHERE type = {?}' . $where,
356 if ($type == self
::LINK_PROFILE
) {
357 Phone
::deletePhones($pid, Phone
::LINK_ADDRESS
);
361 /** Saves addresses into the database.
362 * @param $data: an array of form formatted addresses.
363 * @param $pid, $type, $linkid: pid, type and id concerned by the update.
365 static public function saveFromArray(array $data, $pid, $type = self
::LINK_PROFILE
, $linkid = null
)
367 foreach ($data as $id => $value) {
368 if (!is_null($linkid)) {
369 $value['id'] = $linkid;
373 if (!is_null($pid)) {
374 $value['pid'] = $pid;
376 if (!is_null($type)) {
377 $value['type'] = $type;
379 $address = new Address($value);
384 static private function formArrayWalk(array $data, $function, &$success = true
, $requiresEmptyAddress = false
)
386 $addresses = array();
387 foreach ($data as $item) {
388 $address = new Address($item);
389 $success = ($address->format() && $success);
390 if (!$address->isEmpty()) {
391 $addresses[] = call_user_func(array($address, $function));
394 if (count($address) == 0 && $requiresEmptyAddress) {
395 $address = new Address();
396 $addresses[] = call_user_func(array($address, $function));
401 // Formats an array of form addresses into an array of form formatted addresses.
402 static public function formatFormArray(array $data, &$success = true
)
404 // Only a single address can be the profile's current address and she must have one.
406 foreach ($data as $key => &$address) {
407 if (isset($address['current']) && $address['current']) {
409 $address['current'] = false
;
415 if (!$hasCurrent && count($value) > 0) {
416 foreach ($value as &$address) {
417 $address['current'] = true
;
422 return self
::formArrayWalk($data, 'toFormArray', $success, true
);
425 static public function formArrayToString(array $data)
427 return implode(' ; ', self
::formArrayWalk($data, 'toString'));
430 static public function iterate(array $pids = array(), array $types = array(),
431 array $jobids = array(), array $pubs = array())
433 return new AddressIterator($pids, $types, $jobids, $pubs);
437 /** Iterator over a set of Phones
439 * @param $pid, $type, $jobid, $pub
441 * The iterator contains the phones that correspond to the value stored in the
442 * parameters' arrays.
444 class AddressIterator
implements PlIterator
448 public function __construct(array $pids, array $types, array $jobids, array $pubs)
451 if (count($pids) != 0) {
452 $where[] = XDB
::format('(pa.pid IN {?})', $pids);
454 if (count($types) != 0) {
455 $where[] = XDB
::format('(pa.type IN {?})', $types);
457 if (count($jobids) != 0) {
458 $where[] = XDB
::format('(pa.jobid IN {?})', $jobids);
460 if (count($pubs) != 0) {
461 $where[] = XDB
::format('(pa.pub IN {?})', $pubs);
463 $sql = 'SELECT pa.pid, pa.jobid, pa.type, pa.id, pa.flags,
464 pa.accuracy, pa.text, pa.postalText, pa.postalCode,
465 pa.localityId, pa.subAdministrativeAreaId,
466 pa.administrativeAreaId, pa.countryId,
467 pa.latitude, pa.longitude, pa.north, pa.south, pa.east, pa.west,
469 gl.name AS locality, gs.name AS subAdministrativeArea,
470 ga.name AS administrativeArea, gc.countryFR AS country
471 FROM profile_addresses AS pa
472 LEFT JOIN geoloc_localities AS gl ON (gl.id = pa.localityId)
473 LEFT JOIN geoloc_administrativeareas AS ga ON (ga.id = pa.administrativeAreaId)
474 LEFT JOIN geoloc_subadministrativeareas AS gs ON (gs.id = pa.subAdministrativeAreaId)
475 LEFT JOIN geoloc_countries AS gc ON (gc.iso_3166_1_a2 = pa.countryId)
476 ' . ((count($where) > 0) ?
'WHERE ' . implode(' AND ', $where) : '') . '
477 ORDER BY pa.pid, pa.jobid, pa.id';
478 $this->dbiter
= XDB
::iterator($sql);
481 public function next()
483 if (is_null($this->dbiter
)) {
486 $data = $this->dbiter
->next();
487 if (is_null($data)) {
490 // Adds phones to addresses.
491 $it = Phone
::iterate(array($data['pid']), array(Phone
::LINK_ADDRESS
), array($data['id']));
492 while ($phone = $it->next()) {
493 $data['phones'][$phone->id()] = $phone->toFormArray();
495 return new Address($data);
498 public function total()
500 return $this->dbiter
->total();
503 public function first()
505 return $this->dbiter
->first();
508 public function last()
510 return $this->dbiter
->last();
513 public function value()
515 return $this->dbiter
;
519 // vim:set et sw=4 sts=4 sws=4 foldmethod=marker enc=utf-8: