465
558
// add it to our results array
466
$results[$addressId] = $strAddr ;
559
$addresses[$addressId] = $strAddr ;
566
* Method to return a flattened address query object, based on the standard being applied.
567
* @return Doctrine Query A doctrine query object.
568
* @deprecated This function was never used/implemented.
570
protected function _getFlatAddresses()
572
// start a basic query object with just the address
573
$q = agDoctrineQuery::create()
575
->from('agAddress a') ;
577
// had to be a little creative here using subqueries in the select because doctrine's rather
578
// fussy, but the workaround suits the need
579
// basically, we just loop through 'all' of the elements in our current standard and build a
580
// pivoted (aka crosstab) variant of the address with a column for each of those elements
581
foreach ($this->_addressAllowedElements as $elemId => $elem)
583
// don't ask me why, but doctrine freaks if you try to move the ON clause to a new line
584
$selectStatement = '(SELECT av%1$02d.id
585
FROM agAddressMjAgAddressValue amav%1$02d
586
INNER JOIN amav%1$02d.agAddressValue av%1$02d ON amav%1$02d.address_value_id = av%1$02d.id
587
WHERE amav%1$02d.address_id = a.id
588
AND av%1$02d.address_element_id = %1$02d) AS %s' ;
589
$selectStatement = sprintf($selectStatement, $elemId, $elemId) ;
590
$q->addSelect($selectStatement);
597
* Method to update the address hash values of existing addresses.
599
* @param array $addressIds A single dimension array of addressIds.
600
* @param Doctrine_Connection $conn An optional doctrine connection object.
601
* @deprecated This should not normally be necessary as address hashes should be generated
602
* at address creation.
604
public function updateAddressHashes($addressIds = NULL, $conn = NULL)
606
// reflect our addressIds
607
$addressIds = $this->getRecordIds($addressIds) ;
609
// pick up a default conneciton if none is passed
610
if (is_null($conn)) { $conn = Doctrine_Manager::connection() ; }
612
// use our componentsId getter to get all of the relevant components for these ids
613
$addressComponents = $this->getAddressComponentsById($addressIds) ;
615
// here we set up our collection of address records, selecting only those with the addressIds
616
// we're affecting. Note: INDEXBY is very important if we intend to access this collection via
617
// array access as we do later.
618
$q = agDoctrineQuery::create($conn)
620
->from('agAddress a INDEXBY a.id')
621
->whereIn('a.id', $addressIds) ;
622
$addressCollection = $q->execute() ;
624
foreach ($addressComponents as $addressId => $components)
626
// calculate the component hash
627
$addrHash = $this->hashAddress($components) ;
629
// update the address hash value of this addressId by array access
630
$addressCollection[$addressId]['address_hash'] = $addrHash ;
633
// start our transaction
634
$conn->beginTransaction() ;
637
$addressCollection->save() ;
642
// if that didn't pan out so well, execute a rollback and log our woes
645
$message = ('One of the addresses in your addressId collection could not be updated.
646
No changes were applied.') ;
647
sfContext::getInstance()->getLogger()->err($message) ;
648
throw new sfException($message, $e) ;
653
* Method to take an address component array and return a json encoded, md5sum'ed address hash.
654
* @param array $addressComponentArray An associative array of address components keyed by
655
* elementId with the string value.
656
* @return string(128) A 128-bit md5sum string.
658
protected function hashAddress($addressComponentArray)
660
// first off, we don't trust the sorting of the address components so we do our own
661
ksort($addressComponentArray) ;
663
// we json encode the return to
664
return md5(json_encode($addressComponentArray)) ;
668
* A quick helper method to take in an array address hashes and return an array of address ids.
669
* @param array $addressHashes A monodimensional array of md5sum, json_encoded address hashes.
670
* @return array An associative array, keyed by address hash, with a value of address_id.
672
public function getAddressIdsByHash($addressHashes)
674
$q = agDoctrineQuery::create()
675
->select('a.address_hash')
677
->from('agAddress a')
678
->whereIn('a.address_hash',$addressHashes) ;
680
return $q->execute(array(), agDoctrineQuery::HYDRATE_KEY_VALUE_PAIR) ;
684
* Method to take in address components and return address ids, inserting new addresses OR
685
* address components as necessary.
687
* NOTE: This method does not fail fast. Addresses for which address id's could not be returned,
688
* either by failed search or failed insert, are returned by index as part of the results set.
690
* @param array $addresses This multi-dimensional array of address data is keyed by an arbitrary
691
* index. The values of each index are: an array of address components, keyed by element id, and the
692
* default address standard of this address.
696
* [0] => array( [$elementId] => $valueString, ...),
697
* [1] => $addressStanardId
702
* @param Doctrine_Connection $conn A doctrine connection object.
703
* @return array A two dimensional array. The first array element ([0]), returns an array of
704
* address indexes and the newly inserted addressIds. The second array element [1], returns all
705
* address indexes that could not be inserted.
708
* [0] => array( [$addressIndex] => [$addressId], ... )
709
* [1] => array( $addressIndex, ... )
713
public function setAddresses($addresses, Doctrine_Connection $conn = NULL)
715
// declare our results array
718
// declare the flipped array we use for the first pass search
719
$searchArray = array() ;
721
// loop through the addresses, hash the components, and build the hash-keyed search array
722
foreach($addresses as $index => $addressComponents)
724
$hash = $this->hashAddress($addressComponents[0]) ;
725
$addrHashes[$index] = $hash ;
728
// return any found hashes
729
$dbHashes = $this->getAddressIdsByHash(array_unique(array_values($addrHashes))) ;
731
// loop through the generated hashes and build a couple of arrays
732
foreach ($addrHashes as $addrIndex => $addrHash)
734
// if we found an address id already, our life is much easier
735
if (array_key_exists($addrHash, $dbHashes))
737
// for each of the addresses with that ID, build our results set and
738
// unset the value from the stuff left to be processed (we're going to use that later!)
739
$results[$addrIndex] = $dbHashes[$addrHash] ;
740
unset($addresses[$addrIndex]) ;
744
// if that didn't work out for us we move the hash to the addresses array for pass-through
745
$addresses[$addrIndex][2] = $addrHash ;
748
// either way, we've already processed this address hash, so we can release it
749
unset($addrHashes[$addrIndex]) ;
752
// just 'cause this is going to be a very memory-hungry method, we'll unset the hashes too
755
// now that we have all of the 'existing' addresses, let's build the new ones
756
$newAddresses = $this->setNewAddresses($addresses) ;
757
$successes = array_shift($newAddresses) ;
759
// we don't need this anymore!
760
unset($newAddresses) ;
762
foreach ($successes as $index => $addrId)
764
// add our successes to the final results set
765
$results[$index] = $addrId ;
767
// release the address from our initial input array
768
unset($addresses[$index]) ;
770
// release it from the successes array while we're at it
771
unset($successes[$index]) ;
774
// and finally we return our results, both the successes and the failures
775
return array($results, array_keys($addresses)) ;
779
* A big honkin' method to create a new address and, if also necessary, the address elements that
780
* don't yet exist to support the address.
782
* NOTE: This method does not fail fast. Failed address inserts are returned by index as part of
785
* @param array $addresses This multi-dimensional array of address data is keyed by an arbitrary
786
* index. The values of each index are: an array of address components, keyed by element id, the
787
* default address standard of this address, and the address hash of the components.
791
* [0] => array( [$elementId] => $valueString, ...),
792
* [1] => $addressStanardId,
793
* [2] => $addressHash
798
* @param Doctrine_Connection $conn A doctrine connection object.
799
* @return array A two dimensional array. The first array element ([0]), returns an array of
800
* address indexes and the newly inserted addressIds. The second array element [1], returns all
801
* address indexes that could not be inserted.
804
* [0] => array( [$addressIndex] => [$addressId], ... )
805
* [1] => array( $addressIndex, ... )
809
protected function setNewAddresses($addresses, Doctrine_Connection $conn = NULL)
811
// we'll use this like a cache and check against it with each successive execution
812
$valuesCache = array() ;
814
// declare our results array
817
// pick up the default connection if one is not passed
818
if (is_null($conn)) { $conn = Doctrine_Manager::connection() ; }
820
// loop through our addresses and the components
821
foreach ($addresses as $index => $components)
823
// we do this so we only have to call rollback / unset once, plus it's nice to have a bool to
827
// if for whatever reason we're not passed a standard, pick up the default
828
if (! isset($components[1])) { $components[1] = $this->_returnStandardId ; }
831
// similarly, we want to wrap this whole sucker in a transaction
832
$conn->beginTransaction() ;
834
// build a results cache so we commit entire addresses at once, not just individual elements
835
$resultsCache = array() ;
837
foreach($components[0] as $elementId => $value)
839
// if we've already picked up this address value, GREAT! just load it from our
840
// cache and keep going!
841
if (isset($valuesCache[$elementId][$value]))
843
$resultsCache[$elementId] = $valuesCache[$elementId][$value] ;
847
// since we didn't find it in cache, we'll try to grab it from the db
848
$valueId = $this->getAddressValueId($elementId, $value) ;
850
// unfortunately, if we didn't get value we've got to add it!
854
$addrValue = new agAddressValue();
855
$addrValue['address_element_id'] = $elementId ;
856
$addrValue['value'] = $value ;
860
$addrValue->save($conn) ;
861
$valueId = $addrValue->getId() ;
863
// and since that went right, add it to our results arrays
864
$valuesCache[$elementId][$value] = $valueId ;
865
$resultsCache[$elementId] = $valueId ;
869
// if we run into a problem, set this once rollback will roll it all back at the end
878
// now we attempt to insert the new address_id with all of our value bits, again only useful
879
// if we've not already had an error
882
// attempt to insert the actual address
883
$newAddr = new agAddress() ;
884
$newAddr['address_standard_id'] = $components[1] ;
885
$newAddr['address_hash'] = $components[2] ;
890
$newAddr->save($conn) ;
891
$addrId = $newAddr->getId() ;
895
// if we run into a problem, set this once rollback will roll it all back at the end
900
// the final step!!! inserting into agAddressMjAgAddressValue
901
foreach ($resultsCache as $rElem => $rValueId)
903
// if we at any point pick up an error, don't bother
904
if ($err) { break ; }
906
$newAmav = new agAddressMjAgAddressValue() ;
907
$newAmav['address_id'] = $addrId ;
908
$newAmav['address_value_id'] = $rValueId ;
913
$newAmav->save($conn) ;
917
// if we run into a problem, set this once rollback will roll it all back at the end
922
// if there's been an error, at any point, we rollback any transactions for this address
929
// most excellent! no erors at all, so we commit... finally!
932
// commit our results to our final results array
933
$results[$index] = $addrId ;
935
// release the value on our input array
936
unset($addresses[$index]) ;
940
// Whew!! Now that that's over, let's just return our two results
941
return array($results, array_keys($addresses)) ;
945
* Simple method to retrieve an address value / element.
947
* @param integer $elementId The element id being queried.
948
* @param string $value String value being searched for.
949
* @param Doctrine_Connection $conn A doctrine connection object.
950
* @return integer|array This is a little funny behaviour or HYDRATE_SINGLE_SCALAR's part. If no
951
* value is returned from the query it actually returns an empty array instead of a NULL (which
952
* would seem more appropriate). Use empty() to check if there's an actual value.
954
public function getAddressValueId($elementId, $value, $conn = NULL)
956
$q = agDoctrineQuery::create()
958
->from('agAddressValue av')
959
->where('av.address_element_id = ?', $elementId)
960
->andWhere('av.value = ?', $value) ;
962
if (! is_null($conn)) { $q->setConnection($conn) ; }
964
return $q->execute(array(), Doctrine_Core::HYDRATE_SINGLE_SCALAR);