1<?php
2/**
3 * Copyright 2008-2017 Horde LLC (http://www.horde.org/)
4 *
5 * @author   Chuck Hagenbuch <chuck@horde.org>
6 * @author   Michael Rubinsky <mrubinsk@horde.org>
7 * @license  http://www.horde.org/licenses/bsd BSD
8 * @category Horde
9 * @package  Horde_Content
10 */
11
12/**
13 * @author   Chuck Hagenbuch <chuck@horde.org>
14 * @author   Michael Rubinsky <mrubinsk@horde.org>
15 * @license  http://www.horde.org/licenses/bsd BSD
16 * @category Horde
17 * @package  Horde_Content
18 */
19class Content_Objects_Manager
20{
21    /**
22     * Database adapter
23     *
24     * @var Horde_Db_Adapter
25     */
26    protected $_db;
27
28    /**
29     * Tables
30     *
31     * @TODO: this should probably be populated by the responsible manager...
32     * @var array
33     */
34    protected $_tables = array(
35        'objects' => 'rampage_objects',
36    );
37
38    /**
39     * Type manager
40     *
41     * @var Content_Types_Manager
42     */
43    protected $_typeManager;
44
45    /**
46     * Constructor
47     *
48     * @param Horde_Db_Adapter $db                The db adapter
49     * @param Content_Types_Manager $typeManager  A content type manager
50     *
51     * @return Content_Objects_Manager
52     */
53    public function __construct(Horde_Db_Adapter $db, Content_Types_Manager $typeManager)
54    {
55        $this->_db = $db;
56        $this->_typeManager = $typeManager;
57    }
58
59    /**
60     * Check for object existence without causing the objects to be created.
61     * Helps save queries for things like tags when we already know the object
62     * doesn't yet exist in rampage tables.
63     *
64     * @param mixed string|array $objects  Either an object identifier or an
65     *                                     array of them.
66     * @param mixed $type                  A type identifier. Either a string
67     *                                     type name or the integer type_id.
68     *
69     * @return mixed  Either a hash of object_id => object_names or false if
70     *                the object(s) do not exist.
71     * @throws InvalidArgumentException, Content_Exception
72     */
73    public function exists($objects, $type)
74    {
75        $type = current($this->_typeManager->ensureTypes($type));
76        if (!is_array($objects)) {
77            $objects = array($objects);
78        }
79        if (!count($objects)) {
80            return array();
81        }
82        // Ensure we take the object as a string indentifier.
83        foreach ($objects as &$object) {
84            $object = strval($object);
85        }
86        $params = $objects;
87        $params[] = $type;
88
89        try {
90            $ids = $this->_db->selectAssoc(
91                'SELECT object_id, object_name FROM ' . $this->_t('objects')
92                    . ' WHERE object_name IN ('
93                    . str_repeat('?,', count($objects) - 1) . '?)'
94                    . ' AND type_id = ?', $params);
95            if ($ids) {
96                return $ids;
97            }
98        } catch (Horde_Db_Exception $e) {
99            throw new Content_Exception($e);
100        }
101
102        return false;
103    }
104
105    /**
106     * Remove the object.
107     * NOTE: This does not ensure any references to this object were removed.
108     * E.g., does not remove any tags etc... That is client code's
109     * responsibility.
110     *
111     * @param  array $objects  An array of object identifiers to delete.
112     * @param  string $type    The type of the objects. All objects must be of
113     *                         the same type.
114     *
115     * @throws Content_Exception
116     */
117    public function delete(array $objects, $type)
118    {
119        $type = current($this->_typeManager->ensureTypes($type));
120
121        // Ensure we take the object as a string indentifier.
122        foreach ($objects as &$object) {
123            $object = strval($object);
124        }
125        $params = $objects;
126        $params[] = $type;
127
128        try {
129            $this->_db->delete(
130                'DELETE FROM ' . $this->_t('objects') . ' WHERE object_name IN ('
131                    . str_repeat('?,', count($objects) - 1) . '?)'
132                    . ' AND type_id = ?',
133                $params
134            );
135        } catch (Horde_Db_Exception $e) {
136            throw new Content_Exception($e);
137        }
138    }
139
140    /**
141     * Ensure that an array of objects exist in storage. Create any that don't,
142     * return object_ids for all. All objects in the $objects array must be
143     * of the same content type.
144     *
145     * @param mixed $objects  An array of objects (or single obejct value).
146     *                        Values typed as an integer are assumed to already
147     *                        be an object_id.
148     * @param mixed $type     Either a string type_name or integer type_id
149     *
150     * @return array  An array of object_ids.
151     */
152    public function ensureObjects($objects, $type)
153    {
154        if (!is_array($objects)) {
155            $objects = array($objects);
156        }
157
158        $objectIds = array();
159        $objectName = array();
160
161        $type = current($this->_typeManager->ensureTypes($type));
162
163        // Anything already typed as an integer is assumed to be an object id.
164        foreach ($objects as $objectIndex => $object) {
165            if (is_int($object)) {
166                $objectIds[$objectIndex] = $object;
167            } else {
168                $objectName[$object] = $objectIndex;
169            }
170        }
171
172        // Get the ids for any objects that already exist.
173        try {
174            if (count($objectName)) {
175                $rows = $this->_db->selectAll(
176                    'SELECT object_id, object_name FROM ' . $this->_t('objects')
177                         . ' WHERE object_name IN ('
178                         . implode(',', array_map(array($this->_db, 'quoteString'), array_keys($objectName)))
179                         . ') AND type_id = ' . $type);
180                foreach ($rows as $row) {
181                    $objectIndex = $objectName[$row['object_name']];
182                    unset($objectName[$row['object_name']]);
183                    $objectIds[$objectIndex] = $row['object_id'];
184                }
185            }
186
187            // Create any objects that didn't already exist
188            foreach ($objectName as $object => $objectIndex) {
189                $objectIds[$objectIndex] = $this->_db->insert('INSERT INTO '
190                    . $this->_t('objects') . ' (object_name, type_id) VALUES ('
191                    . $this->_db->quoteString($object) . ', ' . $type . ')');
192            }
193        } catch (Horde_Db_Exception $e) {
194            throw new Content_Exception($e);
195        }
196
197        return $objectIds;
198    }
199
200    /**
201     * Shortcut for getting a table name.
202     *
203     * @param string $tableType
204     *
205     * @return string  Configured table name.
206     */
207    protected function _t($tableType)
208    {
209        return $this->_db->quoteTableName($this->_tables[$tableType]);
210    }
211}
212