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 20namespace Doctrine\ORM\Query; 21 22/** 23 * A ResultSetMapping describes how a result set of an SQL query maps to a Doctrine result. 24 * 25 * IMPORTANT NOTE: 26 * The properties of this class are only public for fast internal READ access and to (drastically) 27 * reduce the size of serialized instances for more effective caching due to better (un-)serialization 28 * performance. 29 * 30 * <b>Users should use the public methods.</b> 31 * 32 * @author Roman Borschel <roman@code-factory.org> 33 * @since 2.0 34 * @todo Think about whether the number of lookup maps can be reduced. 35 */ 36class ResultSetMapping 37{ 38 /** 39 * Whether the result is mixed (contains scalar values together with field values). 40 * 41 * @ignore 42 * @var boolean 43 */ 44 public $isMixed = false; 45 46 /** 47 * Whether the result is a select statement. 48 * 49 * @ignore 50 * @var boolean 51 */ 52 public $isSelect = true; 53 54 /** 55 * Maps alias names to class names. 56 * 57 * @ignore 58 * @var array 59 */ 60 public $aliasMap = array(); 61 62 /** 63 * Maps alias names to related association field names. 64 * 65 * @ignore 66 * @var array 67 */ 68 public $relationMap = array(); 69 70 /** 71 * Maps alias names to parent alias names. 72 * 73 * @ignore 74 * @var array 75 */ 76 public $parentAliasMap = array(); 77 78 /** 79 * Maps column names in the result set to field names for each class. 80 * 81 * @ignore 82 * @var array 83 */ 84 public $fieldMappings = array(); 85 86 /** 87 * Maps column names in the result set to the alias/field name to use in the mapped result. 88 * 89 * @ignore 90 * @var array 91 */ 92 public $scalarMappings = array(); 93 94 /** 95 * Maps column names in the result set to the alias/field type to use in the mapped result. 96 * 97 * @ignore 98 * @var array 99 */ 100 public $typeMappings = array(); 101 102 /** 103 * Maps entities in the result set to the alias name to use in the mapped result. 104 * 105 * @ignore 106 * @var array 107 */ 108 public $entityMappings = array(); 109 110 /** 111 * Maps column names of meta columns (foreign keys, discriminator columns, ...) to field names. 112 * 113 * @ignore 114 * @var array 115 */ 116 public $metaMappings = array(); 117 118 /** 119 * Maps column names in the result set to the alias they belong to. 120 * 121 * @ignore 122 * @var array 123 */ 124 public $columnOwnerMap = array(); 125 126 /** 127 * List of columns in the result set that are used as discriminator columns. 128 * 129 * @ignore 130 * @var array 131 */ 132 public $discriminatorColumns = array(); 133 134 /** 135 * Maps alias names to field names that should be used for indexing. 136 * 137 * @ignore 138 * @var array 139 */ 140 public $indexByMap = array(); 141 142 /** 143 * Map from column names to class names that declare the field the column is mapped to. 144 * 145 * @ignore 146 * @var array 147 */ 148 public $declaringClasses = array(); 149 150 /** 151 * This is necessary to hydrate derivate foreign keys correctly. 152 * 153 * @var array 154 */ 155 public $isIdentifierColumn = array(); 156 157 /** 158 * Maps column names in the result set to field names for each new object expression. 159 * 160 * @var array 161 */ 162 public $newObjectMappings = array(); 163 164 /** 165 * Maps metadata parameter names to the metadata attribute. 166 * 167 * @var array 168 */ 169 public $metadataParameterMapping = array(); 170 171 /** 172 * Adds an entity result to this ResultSetMapping. 173 * 174 * @param string $class The class name of the entity. 175 * @param string $alias The alias for the class. The alias must be unique among all entity 176 * results or joined entity results within this ResultSetMapping. 177 * @param string|null $resultAlias The result alias with which the entity result should be 178 * placed in the result structure. 179 * 180 * @return ResultSetMapping This ResultSetMapping instance. 181 * 182 * @todo Rename: addRootEntity 183 */ 184 public function addEntityResult($class, $alias, $resultAlias = null) 185 { 186 $this->aliasMap[$alias] = $class; 187 $this->entityMappings[$alias] = $resultAlias; 188 189 if ($resultAlias !== null) { 190 $this->isMixed = true; 191 } 192 193 return $this; 194 } 195 196 /** 197 * Sets a discriminator column for an entity result or joined entity result. 198 * The discriminator column will be used to determine the concrete class name to 199 * instantiate. 200 * 201 * @param string $alias The alias of the entity result or joined entity result the discriminator 202 * column should be used for. 203 * @param string $discrColumn The name of the discriminator column in the SQL result set. 204 * 205 * @return ResultSetMapping This ResultSetMapping instance. 206 * 207 * @todo Rename: addDiscriminatorColumn 208 */ 209 public function setDiscriminatorColumn($alias, $discrColumn) 210 { 211 $this->discriminatorColumns[$alias] = $discrColumn; 212 $this->columnOwnerMap[$discrColumn] = $alias; 213 214 return $this; 215 } 216 217 /** 218 * Sets a field to use for indexing an entity result or joined entity result. 219 * 220 * @param string $alias The alias of an entity result or joined entity result. 221 * @param string $fieldName The name of the field to use for indexing. 222 * 223 * @return ResultSetMapping This ResultSetMapping instance. 224 */ 225 public function addIndexBy($alias, $fieldName) 226 { 227 $found = false; 228 229 foreach (array_merge($this->metaMappings, $this->fieldMappings) as $columnName => $columnFieldName) { 230 if ( ! ($columnFieldName === $fieldName && $this->columnOwnerMap[$columnName] === $alias)) continue; 231 232 $this->addIndexByColumn($alias, $columnName); 233 $found = true; 234 235 break; 236 } 237 238 /* TODO: check if this exception can be put back, for now it's gone because of assumptions made by some ORM internals 239 if ( ! $found) { 240 $message = sprintf( 241 'Cannot add index by for DQL alias %s and field %s without calling addFieldResult() for them before.', 242 $alias, 243 $fieldName 244 ); 245 246 throw new \LogicException($message); 247 } 248 */ 249 250 return $this; 251 } 252 253 /** 254 * Sets to index by a scalar result column name. 255 * 256 * @param string $resultColumnName 257 * 258 * @return ResultSetMapping This ResultSetMapping instance. 259 */ 260 public function addIndexByScalar($resultColumnName) 261 { 262 $this->indexByMap['scalars'] = $resultColumnName; 263 264 return $this; 265 } 266 267 /** 268 * Sets a column to use for indexing an entity or joined entity result by the given alias name. 269 * 270 * @param string $alias 271 * @param string $resultColumnName 272 * 273 * @return ResultSetMapping This ResultSetMapping instance. 274 */ 275 public function addIndexByColumn($alias, $resultColumnName) 276 { 277 $this->indexByMap[$alias] = $resultColumnName; 278 279 return $this; 280 } 281 282 /** 283 * Checks whether an entity result or joined entity result with a given alias has 284 * a field set for indexing. 285 * 286 * @param string $alias 287 * 288 * @return boolean 289 * 290 * @todo Rename: isIndexed($alias) 291 */ 292 public function hasIndexBy($alias) 293 { 294 return isset($this->indexByMap[$alias]); 295 } 296 297 /** 298 * Checks whether the column with the given name is mapped as a field result 299 * as part of an entity result or joined entity result. 300 * 301 * @param string $columnName The name of the column in the SQL result set. 302 * 303 * @return boolean 304 * 305 * @todo Rename: isField 306 */ 307 public function isFieldResult($columnName) 308 { 309 return isset($this->fieldMappings[$columnName]); 310 } 311 312 /** 313 * Adds a field to the result that belongs to an entity or joined entity. 314 * 315 * @param string $alias The alias of the root entity or joined entity to which the field belongs. 316 * @param string $columnName The name of the column in the SQL result set. 317 * @param string $fieldName The name of the field on the declaring class. 318 * @param string|null $declaringClass The name of the class that declares/owns the specified field. 319 * When $alias refers to a superclass in a mapped hierarchy but 320 * the field $fieldName is defined on a subclass, specify that here. 321 * If not specified, the field is assumed to belong to the class 322 * designated by $alias. 323 * 324 * @return ResultSetMapping This ResultSetMapping instance. 325 * 326 * @todo Rename: addField 327 */ 328 public function addFieldResult($alias, $columnName, $fieldName, $declaringClass = null) 329 { 330 // column name (in result set) => field name 331 $this->fieldMappings[$columnName] = $fieldName; 332 // column name => alias of owner 333 $this->columnOwnerMap[$columnName] = $alias; 334 // field name => class name of declaring class 335 $this->declaringClasses[$columnName] = $declaringClass ?: $this->aliasMap[$alias]; 336 337 if ( ! $this->isMixed && $this->scalarMappings) { 338 $this->isMixed = true; 339 } 340 341 return $this; 342 } 343 344 /** 345 * Adds a joined entity result. 346 * 347 * @param string $class The class name of the joined entity. 348 * @param string $alias The unique alias to use for the joined entity. 349 * @param string $parentAlias The alias of the entity result that is the parent of this joined result. 350 * @param string $relation The association field that connects the parent entity result 351 * with the joined entity result. 352 * 353 * @return ResultSetMapping This ResultSetMapping instance. 354 * 355 * @todo Rename: addJoinedEntity 356 */ 357 public function addJoinedEntityResult($class, $alias, $parentAlias, $relation) 358 { 359 $this->aliasMap[$alias] = $class; 360 $this->parentAliasMap[$alias] = $parentAlias; 361 $this->relationMap[$alias] = $relation; 362 363 return $this; 364 } 365 366 /** 367 * Adds a scalar result mapping. 368 * 369 * @param string $columnName The name of the column in the SQL result set. 370 * @param string $alias The result alias with which the scalar result should be placed in the result structure. 371 * @param string $type The column type 372 * 373 * @return ResultSetMapping This ResultSetMapping instance. 374 * 375 * @todo Rename: addScalar 376 */ 377 public function addScalarResult($columnName, $alias, $type = 'string') 378 { 379 $this->scalarMappings[$columnName] = $alias; 380 $this->typeMappings[$columnName] = $type; 381 382 if ( ! $this->isMixed && $this->fieldMappings) { 383 $this->isMixed = true; 384 } 385 386 return $this; 387 } 388 389 /** 390 * Adds a metadata parameter mappings. 391 * 392 * @param mixed $parameter The parameter name in the SQL result set. 393 * @param string $attribute The metadata attribute. 394 */ 395 public function addMetadataParameterMapping($parameter, $attribute) 396 { 397 $this->metadataParameterMapping[$parameter] = $attribute; 398 } 399 400 /** 401 * Checks whether a column with a given name is mapped as a scalar result. 402 * 403 * @param string $columnName The name of the column in the SQL result set. 404 * 405 * @return boolean 406 * 407 * @todo Rename: isScalar 408 */ 409 public function isScalarResult($columnName) 410 { 411 return isset($this->scalarMappings[$columnName]); 412 } 413 414 /** 415 * Gets the name of the class of an entity result or joined entity result, 416 * identified by the given unique alias. 417 * 418 * @param string $alias 419 * 420 * @return string 421 */ 422 public function getClassName($alias) 423 { 424 return $this->aliasMap[$alias]; 425 } 426 427 /** 428 * Gets the field alias for a column that is mapped as a scalar value. 429 * 430 * @param string $columnName The name of the column in the SQL result set. 431 * 432 * @return string 433 */ 434 public function getScalarAlias($columnName) 435 { 436 return $this->scalarMappings[$columnName]; 437 } 438 439 /** 440 * Gets the name of the class that owns a field mapping for the specified column. 441 * 442 * @param string $columnName 443 * 444 * @return string 445 */ 446 public function getDeclaringClass($columnName) 447 { 448 return $this->declaringClasses[$columnName]; 449 } 450 451 /** 452 * @param string $alias 453 * 454 * @return AssociationMapping 455 */ 456 public function getRelation($alias) 457 { 458 return $this->relationMap[$alias]; 459 } 460 461 /** 462 * @param string $alias 463 * 464 * @return boolean 465 */ 466 public function isRelation($alias) 467 { 468 return isset($this->relationMap[$alias]); 469 } 470 471 /** 472 * Gets the alias of the class that owns a field mapping for the specified column. 473 * 474 * @param string $columnName 475 * 476 * @return string 477 */ 478 public function getEntityAlias($columnName) 479 { 480 return $this->columnOwnerMap[$columnName]; 481 } 482 483 /** 484 * Gets the parent alias of the given alias. 485 * 486 * @param string $alias 487 * 488 * @return string 489 */ 490 public function getParentAlias($alias) 491 { 492 return $this->parentAliasMap[$alias]; 493 } 494 495 /** 496 * Checks whether the given alias has a parent alias. 497 * 498 * @param string $alias 499 * 500 * @return boolean 501 */ 502 public function hasParentAlias($alias) 503 { 504 return isset($this->parentAliasMap[$alias]); 505 } 506 507 /** 508 * Gets the field name for a column name. 509 * 510 * @param string $columnName 511 * 512 * @return string 513 */ 514 public function getFieldName($columnName) 515 { 516 return $this->fieldMappings[$columnName]; 517 } 518 519 /** 520 * @return array 521 */ 522 public function getAliasMap() 523 { 524 return $this->aliasMap; 525 } 526 527 /** 528 * Gets the number of different entities that appear in the mapped result. 529 * 530 * @return integer 531 */ 532 public function getEntityResultCount() 533 { 534 return count($this->aliasMap); 535 } 536 537 /** 538 * Checks whether this ResultSetMapping defines a mixed result. 539 * 540 * Mixed results can only occur in object and array (graph) hydration. In such a 541 * case a mixed result means that scalar values are mixed with objects/array in 542 * the result. 543 * 544 * @return boolean 545 */ 546 public function isMixedResult() 547 { 548 return $this->isMixed; 549 } 550 551 /** 552 * Adds a meta column (foreign key or discriminator column) to the result set. 553 * 554 * @param string $alias The result alias with which the meta result should be placed in the result structure. 555 * @param string $columnName The name of the column in the SQL result set. 556 * @param string $fieldName The name of the field on the declaring class. 557 * @param bool $isIdentifierColumn 558 * @param string $type The column type 559 * 560 * @return ResultSetMapping This ResultSetMapping instance. 561 */ 562 public function addMetaResult($alias, $columnName, $fieldName, $isIdentifierColumn = false, $type = null) 563 { 564 $this->metaMappings[$columnName] = $fieldName; 565 $this->columnOwnerMap[$columnName] = $alias; 566 567 if ($isIdentifierColumn) { 568 $this->isIdentifierColumn[$alias][$columnName] = true; 569 } 570 571 if ($type) { 572 $this->typeMappings[$columnName] = $type; 573 } 574 575 return $this; 576 } 577} 578 579