|
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 |
|
20 namespace Doctrine\ORM; |
|
21 |
|
22 use Doctrine\DBAL\LockMode; |
|
23 use Doctrine\Common\Persistence\ObjectRepository; |
|
24 |
|
25 /** |
|
26 * An EntityRepository serves as a repository for entities with generic as well as |
|
27 * business specific methods for retrieving entities. |
|
28 * |
|
29 * This class is designed for inheritance and users can subclass this class to |
|
30 * write their own repositories with business-specific methods to locate entities. |
|
31 * |
|
32 * @since 2.0 |
|
33 * @author Benjamin Eberlei <kontakt@beberlei.de> |
|
34 * @author Guilherme Blanco <guilhermeblanco@hotmail.com> |
|
35 * @author Jonathan Wage <jonwage@gmail.com> |
|
36 * @author Roman Borschel <roman@code-factory.org> |
|
37 */ |
|
38 class EntityRepository implements ObjectRepository |
|
39 { |
|
40 /** |
|
41 * @var string |
|
42 */ |
|
43 protected $_entityName; |
|
44 |
|
45 /** |
|
46 * @var EntityManager |
|
47 */ |
|
48 protected $_em; |
|
49 |
|
50 /** |
|
51 * @var Doctrine\ORM\Mapping\ClassMetadata |
|
52 */ |
|
53 protected $_class; |
|
54 |
|
55 /** |
|
56 * Initializes a new <tt>EntityRepository</tt>. |
|
57 * |
|
58 * @param EntityManager $em The EntityManager to use. |
|
59 * @param ClassMetadata $classMetadata The class descriptor. |
|
60 */ |
|
61 public function __construct($em, Mapping\ClassMetadata $class) |
|
62 { |
|
63 $this->_entityName = $class->name; |
|
64 $this->_em = $em; |
|
65 $this->_class = $class; |
|
66 } |
|
67 |
|
68 /** |
|
69 * Create a new QueryBuilder instance that is prepopulated for this entity name |
|
70 * |
|
71 * @param string $alias |
|
72 * @return QueryBuilder $qb |
|
73 */ |
|
74 public function createQueryBuilder($alias) |
|
75 { |
|
76 return $this->_em->createQueryBuilder() |
|
77 ->select($alias) |
|
78 ->from($this->_entityName, $alias); |
|
79 } |
|
80 |
|
81 /** |
|
82 * Create a new Query instance based on a predefined metadata named query. |
|
83 * |
|
84 * @param string $queryName |
|
85 * @return Query |
|
86 */ |
|
87 public function createNamedQuery($queryName) |
|
88 { |
|
89 return $this->_em->createQuery($this->_class->getNamedQuery($queryName)); |
|
90 } |
|
91 |
|
92 /** |
|
93 * Clears the repository, causing all managed entities to become detached. |
|
94 */ |
|
95 public function clear() |
|
96 { |
|
97 $this->_em->clear($this->_class->rootEntityName); |
|
98 } |
|
99 |
|
100 /** |
|
101 * Finds an entity by its primary key / identifier. |
|
102 * |
|
103 * @param $id The identifier. |
|
104 * @param int $lockMode |
|
105 * @param int $lockVersion |
|
106 * @return object The entity. |
|
107 */ |
|
108 public function find($id, $lockMode = LockMode::NONE, $lockVersion = null) |
|
109 { |
|
110 // Check identity map first |
|
111 if ($entity = $this->_em->getUnitOfWork()->tryGetById($id, $this->_class->rootEntityName)) { |
|
112 if (!($entity instanceof $this->_class->name)) { |
|
113 return null; |
|
114 } |
|
115 |
|
116 if ($lockMode != LockMode::NONE) { |
|
117 $this->_em->lock($entity, $lockMode, $lockVersion); |
|
118 } |
|
119 |
|
120 return $entity; // Hit! |
|
121 } |
|
122 |
|
123 if ( ! is_array($id) || count($id) <= 1) { |
|
124 // @todo FIXME: Not correct. Relies on specific order. |
|
125 $value = is_array($id) ? array_values($id) : array($id); |
|
126 $id = array_combine($this->_class->identifier, $value); |
|
127 } |
|
128 |
|
129 if ($lockMode == LockMode::NONE) { |
|
130 return $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName)->load($id); |
|
131 } else if ($lockMode == LockMode::OPTIMISTIC) { |
|
132 if (!$this->_class->isVersioned) { |
|
133 throw OptimisticLockException::notVersioned($this->_entityName); |
|
134 } |
|
135 $entity = $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName)->load($id); |
|
136 |
|
137 $this->_em->getUnitOfWork()->lock($entity, $lockMode, $lockVersion); |
|
138 |
|
139 return $entity; |
|
140 } else { |
|
141 if (!$this->_em->getConnection()->isTransactionActive()) { |
|
142 throw TransactionRequiredException::transactionRequired(); |
|
143 } |
|
144 |
|
145 return $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName)->load($id, null, null, array(), $lockMode); |
|
146 } |
|
147 } |
|
148 |
|
149 /** |
|
150 * Finds all entities in the repository. |
|
151 * |
|
152 * @return array The entities. |
|
153 */ |
|
154 public function findAll() |
|
155 { |
|
156 return $this->findBy(array()); |
|
157 } |
|
158 |
|
159 /** |
|
160 * Finds entities by a set of criteria. |
|
161 * |
|
162 * @param array $criteria |
|
163 * @param array|null $orderBy |
|
164 * @param int|null $limit |
|
165 * @param int|null $offset |
|
166 * @return array The objects. |
|
167 */ |
|
168 public function findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null) |
|
169 { |
|
170 return $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName)->loadAll($criteria, $orderBy, $limit, $offset); |
|
171 } |
|
172 |
|
173 /** |
|
174 * Finds a single entity by a set of criteria. |
|
175 * |
|
176 * @param array $criteria |
|
177 * @return object |
|
178 */ |
|
179 public function findOneBy(array $criteria) |
|
180 { |
|
181 return $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName)->load($criteria); |
|
182 } |
|
183 |
|
184 /** |
|
185 * Adds support for magic finders. |
|
186 * |
|
187 * @return array|object The found entity/entities. |
|
188 * @throws BadMethodCallException If the method called is an invalid find* method |
|
189 * or no find* method at all and therefore an invalid |
|
190 * method call. |
|
191 */ |
|
192 public function __call($method, $arguments) |
|
193 { |
|
194 if (substr($method, 0, 6) == 'findBy') { |
|
195 $by = substr($method, 6, strlen($method)); |
|
196 $method = 'findBy'; |
|
197 } else if (substr($method, 0, 9) == 'findOneBy') { |
|
198 $by = substr($method, 9, strlen($method)); |
|
199 $method = 'findOneBy'; |
|
200 } else { |
|
201 throw new \BadMethodCallException( |
|
202 "Undefined method '$method'. The method name must start with ". |
|
203 "either findBy or findOneBy!" |
|
204 ); |
|
205 } |
|
206 |
|
207 if ( !isset($arguments[0])) { |
|
208 // we dont even want to allow null at this point, because we cannot (yet) transform it into IS NULL. |
|
209 throw ORMException::findByRequiresParameter($method.$by); |
|
210 } |
|
211 |
|
212 $fieldName = lcfirst(\Doctrine\Common\Util\Inflector::classify($by)); |
|
213 |
|
214 if ($this->_class->hasField($fieldName) || $this->_class->hasAssociation($fieldName)) { |
|
215 return $this->$method(array($fieldName => $arguments[0])); |
|
216 } else { |
|
217 throw ORMException::invalidFindByCall($this->_entityName, $fieldName, $method.$by); |
|
218 } |
|
219 } |
|
220 |
|
221 /** |
|
222 * @return string |
|
223 */ |
|
224 protected function getEntityName() |
|
225 { |
|
226 return $this->_entityName; |
|
227 } |
|
228 |
|
229 /** |
|
230 * @return EntityManager |
|
231 */ |
|
232 protected function getEntityManager() |
|
233 { |
|
234 return $this->_em; |
|
235 } |
|
236 |
|
237 /** |
|
238 * @return Mapping\ClassMetadata |
|
239 */ |
|
240 protected function getClassMetadata() |
|
241 { |
|
242 return $this->_class; |
|
243 } |
|
244 } |