Completed
Pull Request — master (#1448)
by Andreas
21:52
created

Builder::hydrate()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 6
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 3
nc 1
nop 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
 * @author alcaeus <[email protected]>
34
 */
35
class Builder extends BaseBuilder
36
{
37
    /**
38
     * The DocumentManager instance for this query
39
     *
40
     * @var DocumentManager
41
     */
42
    private $dm;
43
44
    /**
45
     * The ClassMetadata instance.
46
     *
47
     * @var \Doctrine\ODM\MongoDB\Mapping\ClassMetadata
48
     */
49
    private $class;
50
51
    /**
52
     * @var string
53
     */
54
    private $hydrationClass;
55
56
    /**
57
     * Create a new aggregation builder.
58
     *
59
     * @param DocumentManager $dm
60
     * @param string $documentName
61
     */
62
    public function __construct(DocumentManager $dm, $documentName)
63
    {
64
        $this->dm = $dm;
65
        $this->class = $this->dm->getClassMetadata($documentName);
66
67
        parent::__construct($this->dm->getDocumentCollection($documentName));
68
    }
69
70
    /**
71
     * {@inheritdoc}
72
     */
73
    public function execute($options = array())
74
    {
75
        // Force cursor to be used
76
        $options = array_merge($options, array('cursor' => true));
77
78
        $cursor = parent::execute($options);
79
80
        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...
81
    }
82
83
    /**
84
     * @param string $className
85
     *
86
     * @return self
87
     */
88
    public function hydrate($className)
89
    {
90
        $this->hydrationClass = $className;
91
92
        return $this;
93
    }
94
95
    /**
96
     * @return QueryExpr
97
     */
98
    public function matchExpr()
99
    {
100
        $expr = new QueryExpr($this->dm);
101
        $expr->setClassMetadata($this->class);
102
103
        return $expr;
104
    }
105
106
    /**
107
     * @return Expr
108
     */
109
    public function expr()
110
    {
111
        return new Expr($this->dm, $this->class);
112
    }
113
114
    /**
115
     * @return Stage\Match
116
     */
117
    public function match()
118
    {
119
        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
     * {@inheritdoc}
133
     */
134
    public function sort($fieldName, $order = null)
135
    {
136
        $fields = is_array($fieldName) ? $fieldName : [$fieldName => $order];
137
        return parent::sort($this->getDocumentPersister()->prepareSortOrProjection($fields));
138
    }
139
140
    /**
141
     * {@inheritdoc}
142
     */
143
    public function unwind($fieldName)
144
    {
145
        $fieldName = $this->getDocumentPersister()->prepareFieldName($fieldName);
146
        return parent::unwind($fieldName);
147
    }
148
149
    /**
150
     * Returns the assembled aggregation pipeline
151
     *
152
     * For pipelines where the first stage is a $geoNear stage, it will apply
153
     * the document filters and discriminator queries to the query portion of
154
     * the geoNear operation. For all other pipelines, it prepends a $match stage
155
     * containing the required query.
156
     *
157
     * @return array
158
     */
159
    public function getPipeline()
160
    {
161
        $pipeline = parent::getPipeline();
162
163
        if ($this->getStage(0) instanceof GeoNear) {
164
            $pipeline[0]['$geoNear']['query'] = $this->applyFilters($pipeline[0]['$geoNear']['query']);
165
        } else {
166
            $matchStage = $this->applyFilters([]);
167
            if ($matchStage !== []) {
168
                array_unshift($pipeline, ['$match' => $matchStage]);
169
            }
170
        }
171
172
        return $pipeline;
173
    }
174
175
    /**
176
     * Applies filters and discriminator queries to the pipeline
177
     *
178
     * @param array $query
179
     * @return array
180
     */
181
    private function applyFilters(array $query)
182
    {
183
        $documentPersister = $this->dm->getUnitOfWork()->getDocumentPersister($this->class->name);
184
185
        $query = $documentPersister->addDiscriminatorToPreparedQuery($query);
186
        $query = $documentPersister->addFilterToPreparedQuery($query);
187
188
        return $query;
189
    }
190
191
    /**
192
     * @param BaseCommandCursor $cursor
193
     *
194
     * @return CommandCursor
195
     */
196
    protected function prepareCursor(BaseCommandCursor $cursor)
197
    {
198
        $class = null;
199
        if ($this->hydrationClass) {
200
            $class = $this->dm->getClassMetadata($this->hydrationClass);
201
        }
202
203
        return new CommandCursor($cursor, $this->dm->getUnitOfWork(), $class);
204
    }
205
206
    /**
207
     * @return \Doctrine\ODM\MongoDB\Persisters\DocumentPersister
208
     */
209
    private function getDocumentPersister()
210
    {
211
        return $this->dm->getUnitOfWork()->getDocumentPersister($this->class->name);
212
    }
213
}
214