Completed
Pull Request — master (#1601)
by Maciej
25:11 queued 22:36
created

Builder::hydrate()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 0
loc 6
ccs 3
cts 3
cp 1
rs 9.4285
cc 1
eloc 3
nc 1
nop 1
crap 1
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 MIT license. For more information, see
17
 * <http://www.doctrine-project.org>.
18
 */
19
20
namespace Doctrine\ODM\MongoDB\Aggregation;
21
22
use Doctrine\MongoDB\Aggregation\Builder as BaseBuilder;
23
use Doctrine\MongoDB\Aggregation\Stage\GeoNear;
24
use Doctrine\MongoDB\CommandCursor as BaseCommandCursor;
25
use Doctrine\ODM\MongoDB\Aggregation\Stage\Match;
26
use Doctrine\ODM\MongoDB\CommandCursor;
27
use Doctrine\ODM\MongoDB\DocumentManager;
28
use Doctrine\ODM\MongoDB\Query\Expr as QueryExpr;
29
30
/**
31
 * Fluent interface for building aggregation pipelines.
32
 */
33
class Builder extends BaseBuilder
34
{
35
    /**
36
     * The DocumentManager instance for this query
37
     *
38
     * @var DocumentManager
39
     */
40
    private $dm;
41
42
    /**
43
     * The ClassMetadata instance.
44
     *
45
     * @var \Doctrine\ODM\MongoDB\Mapping\ClassMetadata
46
     */
47
    private $class;
48
49
    /**
50
     * @var string
51
     */
52
    private $hydrationClass;
53
54
    /**
55
     * Create a new aggregation builder.
56
     *
57
     * @param DocumentManager $dm
58
     * @param string $documentName
59
     */
60 11
    public function __construct(DocumentManager $dm, $documentName)
61
    {
62 11
        $this->dm = $dm;
63 11
        $this->class = $this->dm->getClassMetadata($documentName);
64
65 11
        parent::__construct($this->dm->getDocumentCollection($documentName));
66 11
    }
67
68
    /**
69
     * {@inheritdoc}
70
     */
71 2
    public function execute($options = [])
72
    {
73
        // Force cursor to be used
74 2
        $options = array_merge($options, ['cursor' => true]);
75
76 2
        $cursor = parent::execute($options);
77
78 2
        return $this->prepareCursor($cursor);
0 ignored issues
show
Compatibility introduced by
$cursor of type object<Doctrine\MongoDB\Iterator> is not a sub-type of object<Doctrine\MongoDB\CommandCursor>. It seems like you assume a concrete implementation of the interface Doctrine\MongoDB\Iterator to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
79
    }
80
81
    /**
82
     * Set which class to use when hydrating results as document class instances.
83
     *
84
     * @param string $className
85
     *
86
     * @return self
87
     */
88 1
    public function hydrate($className)
89
    {
90 1
        $this->hydrationClass = $className;
91
92 1
        return $this;
93
    }
94
95
    /**
96
     * @return QueryExpr
97
     */
98 3
    public function matchExpr()
99
    {
100 3
        $expr = new QueryExpr($this->dm);
101 3
        $expr->setClassMetadata($this->class);
102
103 3
        return $expr;
104
    }
105
106
    /**
107
     * @return Expr
108
     */
109 4
    public function expr()
110
    {
111 4
        return new Expr($this->dm, $this->class);
112
    }
113
114
    /**
115
     * @return Stage\Match
116
     */
117 2
    public function match()
118
    {
119 2
        return $this->addStage(new Stage\Match($this));
120
    }
121
122
    /**
123
     * @param string $from
124
     * @return Stage\Lookup
125
     */
126
    public function lookup($from)
127
    {
128
        return $this->addStage(new Stage\Lookup($this, $from, $this->dm, $this->class));
129
    }
130
131
    /**
132
     * @param string $from
133
     * @return Stage\Out
134
     */
135 4
    public function out($from)
136
    {
137 4
        return $this->addStage(new Stage\Out($this, $from, $this->dm));
138
    }
139
140
    /**
141
     * {@inheritdoc}
142
     */
143 2
    public function sort($fieldName, $order = null)
144
    {
145 2
        $fields = is_array($fieldName) ? $fieldName : [$fieldName => $order];
146 2
        return parent::sort($this->getDocumentPersister()->prepareSortOrProjection($fields));
147
    }
148
149
    /**
150
     * {@inheritdoc}
151
     */
152 2
    public function unwind($fieldName)
153
    {
154 2
        $fieldName = $this->getDocumentPersister()->prepareFieldName($fieldName);
155 2
        return parent::unwind($fieldName);
156
    }
157
158
    /**
159
     * Returns the assembled aggregation pipeline
160
     *
161
     * For pipelines where the first stage is a $geoNear stage, it will apply
162
     * the document filters and discriminator queries to the query portion of
163
     * the geoNear operation. For all other pipelines, it prepends a $match stage
164
     * containing the required query.
165
     *
166
     * @return array
167
     */
168 8
    public function getPipeline()
169
    {
170 8
        $pipeline = parent::getPipeline();
171
172 8
        if ($this->getStage(0) instanceof GeoNear) {
173 1
            $pipeline[0]['$geoNear']['query'] = $this->applyFilters($pipeline[0]['$geoNear']['query']);
174 1
        } else {
175 7
            $matchStage = $this->applyFilters([]);
176 7
            if ($matchStage !== []) {
177 1
                array_unshift($pipeline, ['$match' => $matchStage]);
178 1
            }
179
        }
180
181 8
        return $pipeline;
182
    }
183
184
    /**
185
     * Applies filters and discriminator queries to the pipeline
186
     *
187
     * @param array $query
188
     * @return array
189
     */
190 8
    private function applyFilters(array $query)
191
    {
192 8
        $documentPersister = $this->dm->getUnitOfWork()->getDocumentPersister($this->class->name);
193
194 8
        $query = $documentPersister->addDiscriminatorToPreparedQuery($query);
195 8
        $query = $documentPersister->addFilterToPreparedQuery($query);
196
197 8
        return $query;
198
    }
199
200
    /**
201
     * @param BaseCommandCursor $cursor
202
     *
203
     * @return CommandCursor
204
     */
205 2
    private function prepareCursor(BaseCommandCursor $cursor)
206
    {
207 2
        $class = null;
208 2
        if ($this->hydrationClass) {
209 1
            $class = $this->dm->getClassMetadata($this->hydrationClass);
210 1
        }
211
212 2
        return new CommandCursor($cursor, $this->dm->getUnitOfWork(), $class);
213
    }
214
215
    /**
216
     * @return \Doctrine\ODM\MongoDB\Persisters\DocumentPersister
217
     */
218 2
    private function getDocumentPersister()
219
    {
220 2
        return $this->dm->getUnitOfWork()->getDocumentPersister($this->class->name);
221
    }
222
}
223