source: pro-violet-viettel/sourcecode/application/libraries/Doctrine/ORM/Internal/Hydration/ArrayHydrator.php @ 356

Last change on this file since 356 was 345, checked in by quyenla, 11 years ago

collaborator page

File size: 10.7 KB
Line 
1<?php
2/*
3 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
4 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
5 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
6 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
7 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
8 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
9 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
10 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
11 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
12 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
13 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
14 *
15 * This software consists of voluntary contributions made by many individuals
16 * and is licensed under the LGPL. For more information, see
17 * <http://www.doctrine-project.org>.
18 */
19
20namespace Doctrine\ORM\Internal\Hydration;
21
22use PDO, Doctrine\DBAL\Connection, Doctrine\ORM\Mapping\ClassMetadata;
23
24/**
25 * The ArrayHydrator produces a nested array "graph" that is often (not always)
26 * interchangeable with the corresponding object graph for read-only access.
27 *
28 * @since  2.0
29 * @author Roman Borschel <roman@code-factory.org>
30 * @author Guilherme Blanco <guilhermeblanoc@hotmail.com>
31 */
32class ArrayHydrator extends AbstractHydrator
33{
34    private $_ce = array();
35    private $_rootAliases = array();
36    private $_isSimpleQuery = false;
37    private $_identifierMap = array();
38    private $_resultPointers = array();
39    private $_idTemplate = array();
40    private $_resultCounter = 0;
41
42    /**
43     * {@inheritdoc}
44     */
45    protected function prepare()
46    {
47        $this->_isSimpleQuery  = count($this->_rsm->aliasMap) <= 1;
48        $this->_identifierMap  = array();
49        $this->_resultPointers = array();
50        $this->_idTemplate     = array();
51        $this->_resultCounter  = 0;
52
53        foreach ($this->_rsm->aliasMap as $dqlAlias => $className) {
54            $this->_identifierMap[$dqlAlias]  = array();
55            $this->_resultPointers[$dqlAlias] = array();
56            $this->_idTemplate[$dqlAlias]     = '';
57        }
58    }
59
60    /**
61     * {@inheritdoc}
62     */
63    protected function hydrateAllData()
64    {
65        $result = array();
66        $cache  = array();
67
68        while ($data = $this->_stmt->fetch(PDO::FETCH_ASSOC)) {
69            $this->hydrateRowData($data, $cache, $result);
70        }
71
72        return $result;
73    }
74
75    /**
76     * {@inheritdoc}
77     */
78    protected function hydrateRowData(array $row, array &$cache, array &$result)
79    {
80        // 1) Initialize
81        $id = $this->_idTemplate; // initialize the id-memory
82        $nonemptyComponents = array();
83        $rowData = $this->gatherRowData($row, $cache, $id, $nonemptyComponents);
84
85        // Extract scalar values. They're appended at the end.
86        if (isset($rowData['scalars'])) {
87            $scalars = $rowData['scalars'];
88
89            unset($rowData['scalars']);
90
91            if (empty($rowData)) {
92                ++$this->_resultCounter;
93            }
94        }
95
96        // 2) Now hydrate the data found in the current row.
97        foreach ($rowData as $dqlAlias => $data) {
98            $index = false;
99
100            if (isset($this->_rsm->parentAliasMap[$dqlAlias])) {
101                // It's a joined result
102
103                $parent = $this->_rsm->parentAliasMap[$dqlAlias];
104                $path   = $parent . '.' . $dqlAlias;
105
106                // missing parent data, skipping as RIGHT JOIN hydration is not supported.
107                if ( ! isset($nonemptyComponents[$parent]) ) {
108                    continue;
109                }
110
111                // Get a reference to the right element in the result tree.
112                // This element will get the associated element attached.
113                if ($this->_rsm->isMixed && isset($this->_rootAliases[$parent])) {
114                        $first = reset($this->_resultPointers);
115                    // TODO: Exception if $key === null ?
116                    $baseElement =& $this->_resultPointers[$parent][key($first)];
117                } else if (isset($this->_resultPointers[$parent])) {
118                    $baseElement =& $this->_resultPointers[$parent];
119                } else {
120                    unset($this->_resultPointers[$dqlAlias]); // Ticket #1228
121                    continue;
122                }
123
124                $relationAlias = $this->_rsm->relationMap[$dqlAlias];
125                $relation = $this->getClassMetadata($this->_rsm->aliasMap[$parent])->associationMappings[$relationAlias];
126
127                // Check the type of the relation (many or single-valued)
128                if ( ! ($relation['type'] & ClassMetadata::TO_ONE)) {
129                    $oneToOne = false;
130
131                    if (isset($nonemptyComponents[$dqlAlias])) {
132                        if ( ! isset($baseElement[$relationAlias])) {
133                            $baseElement[$relationAlias] = array();
134                        }
135
136                        $indexExists  = isset($this->_identifierMap[$path][$id[$parent]][$id[$dqlAlias]]);
137                        $index        = $indexExists ? $this->_identifierMap[$path][$id[$parent]][$id[$dqlAlias]] : false;
138                        $indexIsValid = $index !== false ? isset($baseElement[$relationAlias][$index]) : false;
139
140                        if ( ! $indexExists || ! $indexIsValid) {
141                            $element = $data;
142                            if (isset($this->_rsm->indexByMap[$dqlAlias])) {
143                                $baseElement[$relationAlias][$row[$this->_rsm->indexByMap[$dqlAlias]]] = $element;
144                            } else {
145                                $baseElement[$relationAlias][] = $element;
146                            }
147
148                            end($baseElement[$relationAlias]);
149
150                            $this->_identifierMap[$path][$id[$parent]][$id[$dqlAlias]] = key($baseElement[$relationAlias]);
151                        }
152                    } else if ( ! isset($baseElement[$relationAlias])) {
153                        $baseElement[$relationAlias] = array();
154                    }
155                } else {
156                    $oneToOne = true;
157
158                    if ( ! isset($nonemptyComponents[$dqlAlias]) && ! isset($baseElement[$relationAlias])) {
159                        $baseElement[$relationAlias] = null;
160                    } else if ( ! isset($baseElement[$relationAlias])) {
161                        $baseElement[$relationAlias] = $data;
162                    }
163                }
164
165                $coll =& $baseElement[$relationAlias];
166
167                if ($coll !== null) {
168                    $this->updateResultPointer($coll, $index, $dqlAlias, $oneToOne);
169                }
170
171            } else {
172                // It's a root result element
173
174                $this->_rootAliases[$dqlAlias] = true; // Mark as root
175                $entityKey = $this->_rsm->entityMappings[$dqlAlias] ?: 0;
176
177                // if this row has a NULL value for the root result id then make it a null result.
178                if ( ! isset($nonemptyComponents[$dqlAlias]) ) {
179                    if ($this->_rsm->isMixed) {
180                        $result[] = array($entityKey => null);
181                    } else {
182                        $result[] = null;
183                    }
184                    $resultKey = $this->_resultCounter;
185                    ++$this->_resultCounter;
186                    continue;
187                }
188
189                // Check for an existing element
190                if ($this->_isSimpleQuery || ! isset($this->_identifierMap[$dqlAlias][$id[$dqlAlias]])) {
191                    $element = $rowData[$dqlAlias];
192                    if ($this->_rsm->isMixed) {
193                        $element = array($entityKey => $element);
194                    }
195
196                    if (isset($this->_rsm->indexByMap[$dqlAlias])) {
197                        $resultKey = $row[$this->_rsm->indexByMap[$dqlAlias]];
198                        $result[$resultKey] = $element;
199                    } else {
200                        $resultKey = $this->_resultCounter;
201                        $result[] = $element;
202                        ++$this->_resultCounter;
203                    }
204
205                    $this->_identifierMap[$dqlAlias][$id[$dqlAlias]] = $resultKey;
206                } else {
207                    $index = $this->_identifierMap[$dqlAlias][$id[$dqlAlias]];
208                    $resultKey = $index;
209                    /*if ($this->_rsm->isMixed) {
210                        $result[] =& $result[$index];
211                        ++$this->_resultCounter;
212                    }*/
213                }
214                $this->updateResultPointer($result, $index, $dqlAlias, false);
215            }
216        }
217
218        // Append scalar values to mixed result sets
219        if (isset($scalars)) {
220            if ( ! isset($resultKey) ) {
221                // this only ever happens when no object is fetched (scalar result only)
222                if (isset($this->_rsm->indexByMap['scalars'])) {
223                    $resultKey = $row[$this->_rsm->indexByMap['scalars']];
224                } else {
225                    $resultKey = $this->_resultCounter - 1;
226                }
227            }
228
229            foreach ($scalars as $name => $value) {
230                $result[$resultKey][$name] = $value;
231            }
232        }
233    }
234
235    /**
236     * Updates the result pointer for an Entity. The result pointers point to the
237     * last seen instance of each Entity type. This is used for graph construction.
238     *
239     * @param array $coll  The element.
240     * @param boolean|integer $index  Index of the element in the collection.
241     * @param string $dqlAlias
242     * @param boolean $oneToOne  Whether it is a single-valued association or not.
243     */
244    private function updateResultPointer(array &$coll, $index, $dqlAlias, $oneToOne)
245    {
246        if ($coll === null) {
247            unset($this->_resultPointers[$dqlAlias]); // Ticket #1228
248
249            return;
250        }
251
252        if ($index !== false) {
253            $this->_resultPointers[$dqlAlias] =& $coll[$index];
254
255            return;
256        }
257
258        if ( ! $coll) {
259            return;
260        }
261
262        if ($oneToOne) {
263            $this->_resultPointers[$dqlAlias] =& $coll;
264
265            return;
266        }
267
268        end($coll);
269        $this->_resultPointers[$dqlAlias] =& $coll[key($coll)];
270
271        return;
272    }
273
274    /**
275     * Retrieve ClassMetadata associated to entity class name.
276     *
277     * @param string $className
278     *
279     * @return \Doctrine\ORM\Mapping\ClassMetadata
280     */
281    private function getClassMetadata($className)
282    {
283        if ( ! isset($this->_ce[$className])) {
284            $this->_ce[$className] = $this->_em->getClassMetadata($className);
285        }
286
287        return $this->_ce[$className];
288    }
289}
Note: See TracBrowser for help on using the repository browser.