Implement duplicate searching, for now just in the SQL driver.
authorJan Schneider <jan@horde.org>
Fri, 2 Jul 2010 13:56:17 +0000 (15:56 +0200)
committerJan Schneider <jan@horde.org>
Mon, 5 Jul 2010 10:34:44 +0000 (12:34 +0200)
turba/lib/Driver.php
turba/lib/Driver/Share.php
turba/lib/Driver/Sql.php

index 2c3ae69..f737d1c 100644 (file)
@@ -527,12 +527,31 @@ class Turba_Driver
     }
 
     /**
+     * Searches the current address book for duplicate entries.
+     *
+     * Duplicates are determined by comparing email and name or last name and
+     * first name values.
+     *
+     * @return array  A hash with the following format:
+     *                <code>
+     *                array('name' => array('John Doe' => Turba_List, ...), ...)
+     *                </code>
+     * @throws Turba_Exception
+     */
+    public function searchDuplicates()
+    {
+        return array();
+    }
+
+    /**
      * Takes an array of object hashes and returns a Turba_List
      * containing the correct Turba_Objects
      *
-     * @param array $objects      An array of object hashes (keyed to backend).
-     * @param string $sort_order  Desired sort order to pass to
-     *                            Turba_List::sort()
+     * @param array $objects  An array of object hashes (keyed to backend).
+     * @param array $order    Array of hashes describing sort fields.  Each
+     *                        hash has the following fields:
+     *                        - field:     String sort field
+     *                        - ascending: Boolean indicating sort direction
      *
      * @return Turba_List containing requested Turba_Objects
      */
index 61c9ab4..9ac35a0 100644 (file)
@@ -104,6 +104,23 @@ class Turba_Driver_Share extends Turba_Driver
     }
 
     /**
+     * Searches the current address book for duplicate entries.
+     *
+     * Duplicates are determined by comparing email and name or last name and
+     * first name values.
+     *
+     * @return array  A hash with the following format:
+     *                <code>
+     *                array('name' => array('John Doe' => Turba_List, ...), ...)
+     *                </code>
+     * @throws Turba_Exception
+     */
+    public function searchDuplicates()
+    {
+        return $this->_driver->searchDuplicates();
+    }
+
+    /**
      * Reads the given data from the address book and returns the
      * results.
      *
index a78fc44..cefaac8 100644 (file)
@@ -174,7 +174,7 @@ class Turba_Driver_Sql extends Turba_Driver
         while ($row = $result->fetchRow()) {
             if (is_a($row, 'PEAR_Error')) {
                 Horde::logMessage($row, 'ERR');
-                return $result;
+                return $row;
             }
 
             $row = $this->_convertFromDriver($row);
@@ -191,6 +191,149 @@ class Turba_Driver_Sql extends Turba_Driver
     }
 
     /**
+     * Prepares field lists for searchDuplicates().
+     *
+     * @param array $array  A list of field names.
+     *
+     * @return array  A prepared list of field names.
+     */
+    protected function _buildFields($array)
+    {
+        foreach ($array as &$entry) {
+            if (is_array($entry)) {
+                $entry = implode(',', $this->_buildFields($entry));
+            } else {
+                $entry = 'a1.' . $entry;
+            }
+        }
+        return $array;
+    }
+
+    /**
+     * Builds the WHERE conditions for searchDuplicates().
+     *
+     * @param array $array  A list of field names.
+     *
+     * @return array  A list of WHERE conditions.
+     */
+    protected function _buildWhere($array)
+    {
+        foreach ($array as &$entry) {
+            if (is_array($entry)) {
+                $entry = reset($entry);
+            }
+            $entry = 'a1.' . $entry . ' IS NOT NULL AND a1.' . $entry . ' <> \'\'';
+        }
+        return $array;
+    }
+
+    /**
+     * Builds the JOIN conditions for searchDuplicates().
+     *
+     * @param array $array  A list of field names.
+     *
+     * @return array  A list of JOIN conditions.
+     */
+    protected function _buildJoin($array)
+    {
+        foreach ($array as &$entry) {
+            if (is_array($entry)) {
+                $entry = implode(' AND ', $this->_buildJoin($entry));
+            } else {
+                $entry = 'a1.' . $entry . ' = a2.' . $entry;
+            }
+        }
+        return $array;
+    }
+
+    /**
+     * Searches the current address book for duplicate entries.
+     *
+     * Duplicates are determined by comparing email and name or last name and
+     * first name values.
+     *
+     * @return array  A hash with the following format:
+     *                <code>
+     *                array('name' => array('John Doe' => Turba_List, ...), ...)
+     *                </code>
+     * @throws Turba_Exception
+     */
+    public function searchDuplicates()
+    {
+        $owner = $this->getContactOwner();
+        $fields = array();
+        if (is_array($this->map['name'])) {
+            if (in_array('lastname', $this->map['name']['fields']) &&
+                isset($this->map['lastname'])) {
+                $field = array($this->map['lastname']);
+                if (in_array('firstname', $this->map['name']['fields']) &&
+                    isset($this->map['firstname'])) {
+                    $field[] = $this->map['firstname'];
+                }
+                $fields[] = $field;
+            }
+        } else {
+            $fields[] = $this->map['name'];
+        }
+        if (isset($this->map['email'])) {
+            $fields[] = $this->map['email'];
+        }
+
+        $order = $this->_buildFields($fields);
+        $joins = $this->_buildJoin($fields);
+        $where = $this->_buildWhere($fields);
+
+        $duplicates = array();
+        for ($i = 0; $i < count($joins); $i++) {
+            /* Build up the full query. */
+            $query = sprintf('SELECT DISTINCT a1.%s FROM %s a1 JOIN %s a2 ON %s AND a1.%s <> a2.%s WHERE a1.%s = ? AND a2.%s = ? AND %s ORDER BY %s',
+                             $this->map['__key'],
+                             $this->_params['table'],
+                             $this->_params['table'],
+                             $joins[$i],
+                             $this->map['__key'],
+                             $this->map['__key'],
+                             $this->map['__owner'],
+                             $this->map['__owner'],
+                             $where[$i],
+                             $order[$i]);
+
+            /* Log the query at a DEBUG log level. */
+            Horde::logMessage('SQL query by Turba_Driver_sql::searchDuplicates(): ' . $query, 'DEBUG');
+
+            /* Run query. */
+            $ids = $this->_db->getCol($query, 0, array($owner, $owner));
+            if (is_a($ids, 'PEAR_Error')) {
+                Horde::logMessage($ids, 'ERR');
+                throw new Turba_Exception($ids);
+            }
+            if ($i == 0) {
+                $field = 'name';
+            } else {
+                $field = array_search($fields[$i], $this->map);
+            }
+            $contacts = array();
+            foreach ($ids as $id) {
+                $contact = $this->getObject($id);
+                $value = $contact->getValue($field);
+                /* HACK! */
+                if ($field == 'email') {
+                    $value = Horde_String::lower($value);
+                }
+                if (!isset($contacts[$value])) {
+                    $contacts[$value] = new Turba_List();
+                }
+                $contacts[$value]->insert($contact);
+            }
+            if ($contacts) {
+                $duplicates[$field] = $contacts;
+            }
+        }
+
+        return $duplicates;
+    }
+
+    /**
      * Reads the given data from the SQL database and returns the
      * results.
      *