* @since 2.0
*/
class ClassMetadataInfo implements ClassMetadata
{
/* The inheritance mapping types */
/**
* NONE means the class does not participate in an inheritance hierarchy
* and therefore does not need an inheritance mapping type.
*/
const INHERITANCE_TYPE_NONE = 1;
/**
* JOINED means the class will be persisted according to the rules of
* Class Table Inheritance.
*/
const INHERITANCE_TYPE_JOINED = 2;
/**
* SINGLE_TABLE means the class will be persisted according to the rules of
* Single Table Inheritance.
*/
const INHERITANCE_TYPE_SINGLE_TABLE = 3;
/**
* TABLE_PER_CLASS means the class will be persisted according to the rules
* of Concrete Table Inheritance.
*/
const INHERITANCE_TYPE_TABLE_PER_CLASS = 4;
/* The Id generator types. */
/**
* AUTO means the generator type will depend on what the used platform prefers.
* Offers full portability.
*/
const GENERATOR_TYPE_AUTO = 1;
/**
* SEQUENCE means a separate sequence object will be used. Platforms that do
* not have native sequence support may emulate it. Full portability is currently
* not guaranteed.
*/
const GENERATOR_TYPE_SEQUENCE = 2;
/**
* TABLE means a separate table is used for id generation.
* Offers full portability.
*/
const GENERATOR_TYPE_TABLE = 3;
/**
* IDENTITY means an identity column is used for id generation. The database
* will fill in the id column on insertion. Platforms that do not support
* native identity columns may emulate them. Full portability is currently
* not guaranteed.
*/
const GENERATOR_TYPE_IDENTITY = 4;
/**
* NONE means the class does not have a generated id. That means the class
* must have a natural, manually assigned id.
*/
const GENERATOR_TYPE_NONE = 5;
/**
* UUID means that a UUID/GUID expression is used for id generation. Full
* portability is currently not guaranteed.
*/
const GENERATOR_TYPE_UUID = 6;
/**
* CUSTOM means that customer will use own ID generator that supposedly work
*/
const GENERATOR_TYPE_CUSTOM = 7;
/**
* DEFERRED_IMPLICIT means that changes of entities are calculated at commit-time
* by doing a property-by-property comparison with the original data. This will
* be done for all entities that are in MANAGED state at commit-time.
*
* This is the default change tracking policy.
*/
const CHANGETRACKING_DEFERRED_IMPLICIT = 1;
/**
* DEFERRED_EXPLICIT means that changes of entities are calculated at commit-time
* by doing a property-by-property comparison with the original data. This will
* be done only for entities that were explicitly saved (through persist() or a cascade).
*/
const CHANGETRACKING_DEFERRED_EXPLICIT = 2;
/**
* NOTIFY means that Doctrine relies on the entities sending out notifications
* when their properties change. Such entity classes must implement
* the NotifyPropertyChanged interface.
*/
const CHANGETRACKING_NOTIFY = 3;
/**
* Specifies that an association is to be fetched when it is first accessed.
*/
const FETCH_LAZY = 2;
/**
* Specifies that an association is to be fetched when the owner of the
* association is fetched.
*/
const FETCH_EAGER = 3;
/**
* Specifies that an association is to be fetched lazy (on first access) and that
* commands such as Collection#count, Collection#slice are issued directly against
* the database if the collection is not yet initialized.
*/
const FETCH_EXTRA_LAZY = 4;
/**
* Identifies a one-to-one association.
*/
const ONE_TO_ONE = 1;
/**
* Identifies a many-to-one association.
*/
const MANY_TO_ONE = 2;
/**
* Identifies a one-to-many association.
*/
const ONE_TO_MANY = 4;
/**
* Identifies a many-to-many association.
*/
const MANY_TO_MANY = 8;
/**
* Combined bitmask for to-one (single-valued) associations.
*/
const TO_ONE = 3;
/**
* Combined bitmask for to-many (collection-valued) associations.
*/
const TO_MANY = 12;
/**
* READ-ONLY: The name of the entity class.
*/
public $name;
/**
* READ-ONLY: The namespace the entity class is contained in.
*
* @var string
* @todo Not really needed. Usage could be localized.
*/
public $namespace;
/**
* READ-ONLY: The name of the entity class that is at the root of the mapped entity inheritance
* hierarchy. If the entity is not part of a mapped inheritance hierarchy this is the same
* as {@link $entityName}.
*
* @var string
*/
public $rootEntityName;
/**
* READ-ONLY: The definition of custom generator. Only used for CUSTOM
* generator type
*
* The definition has the following structure:
*
* array(
* 'class' => 'ClassName',
* )
*
*
* @var array
* @todo Merge with tableGeneratorDefinition into generic generatorDefinition
*/
public $customGeneratorDefinition;
/**
* The name of the custom repository class used for the entity class.
* (Optional).
*
* @var string
*/
public $customRepositoryClassName;
/**
* READ-ONLY: Whether this class describes the mapping of a mapped superclass.
*
* @var boolean
*/
public $isMappedSuperclass = false;
/**
* READ-ONLY: The names of the parent classes (ancestors).
*
* @var array
*/
public $parentClasses = array();
/**
* READ-ONLY: The names of all subclasses (descendants).
*
* @var array
*/
public $subClasses = array();
/**
* READ-ONLY: The named queries allowed to be called directly from Repository.
*
* @var array
*/
public $namedQueries = array();
/**
* READ-ONLY: The named native queries allowed to be called directly from Repository.
*
* A native SQL named query definition has the following structure:
*
* array(
* 'name' => ,
* 'query' => ,
* 'resultClass' => ,
* 'resultSetMapping' =>
* )
*
*/
public $namedNativeQueries = array();
/**
* READ-ONLY: The mappings of the results of native SQL queries.
*
* A native result mapping definition has the following structure:
*
* array(
* 'name' => ,
* 'entities' => array(),
* 'columns' => array()
* )
*
*/
public $sqlResultSetMappings = array();
/**
* READ-ONLY: The field names of all fields that are part of the identifier/primary key
* of the mapped entity class.
*
* @var array
*/
public $identifier = array();
/**
* READ-ONLY: The inheritance mapping type used by the class.
*
* @var integer
*/
public $inheritanceType = self::INHERITANCE_TYPE_NONE;
/**
* READ-ONLY: The Id generator type used by the class.
*
* @var string
*/
public $generatorType = self::GENERATOR_TYPE_NONE;
/**
* READ-ONLY: The field mappings of the class.
* Keys are field names and values are mapping definitions.
*
* The mapping definition array has the following values:
*
* - fieldName (string)
* The name of the field in the Entity.
*
* - type (string)
* The type name of the mapped field. Can be one of Doctrine's mapping types
* or a custom mapping type.
*
* - columnName (string, optional)
* The column name. Optional. Defaults to the field name.
*
* - length (integer, optional)
* The database length of the column. Optional. Default value taken from
* the type.
*
* - id (boolean, optional)
* Marks the field as the primary key of the entity. Multiple fields of an
* entity can have the id attribute, forming a composite key.
*
* - nullable (boolean, optional)
* Whether the column is nullable. Defaults to FALSE.
*
* - columnDefinition (string, optional, schema-only)
* The SQL fragment that is used when generating the DDL for the column.
*
* - precision (integer, optional, schema-only)
* The precision of a decimal column. Only valid if the column type is decimal.
*
* - scale (integer, optional, schema-only)
* The scale of a decimal column. Only valid if the column type is decimal.
*
[* - 'unique'] (string, optional, schema-only)
* Whether a unique constraint should be generated for the column.
*
* @var array
*/
public $fieldMappings = array();
/**
* READ-ONLY: An array of field names. Used to look up field names from column names.
* Keys are column names and values are field names.
* This is the reverse lookup map of $_columnNames.
*
* @var array
*/
public $fieldNames = array();
/**
* READ-ONLY: A map of field names to column names. Keys are field names and values column names.
* Used to look up column names from field names.
* This is the reverse lookup map of $_fieldNames.
*
* @var array
* @todo We could get rid of this array by just using $fieldMappings[$fieldName]['columnName'].
*/
public $columnNames = array();
/**
* READ-ONLY: The discriminator value of this class.
*
* This does only apply to the JOINED and SINGLE_TABLE inheritance mapping strategies
* where a discriminator column is used.
*
* @var mixed
* @see discriminatorColumn
*/
public $discriminatorValue;
/**
* READ-ONLY: The discriminator map of all mapped classes in the hierarchy.
*
* This does only apply to the JOINED and SINGLE_TABLE inheritance mapping strategies
* where a discriminator column is used.
*
* @var mixed
* @see discriminatorColumn
*/
public $discriminatorMap = array();
/**
* READ-ONLY: The definition of the discriminator column used in JOINED and SINGLE_TABLE
* inheritance mappings.
*
* @var array
*/
public $discriminatorColumn;
/**
* READ-ONLY: The primary table definition. The definition is an array with the
* following entries:
*
* name =>
* schema =>
* indexes => array
* uniqueConstraints => array
*
* @var array
*/
public $table;
/**
* READ-ONLY: The registered lifecycle callbacks for entities of this class.
*
* @var array
*/
public $lifecycleCallbacks = array();
/**
* READ-ONLY: The association mappings of this class.
*
* The mapping definition array supports the following keys:
*
* - fieldName (string)
* The name of the field in the entity the association is mapped to.
*
* - targetEntity (string)
* The class name of the target entity. If it is fully-qualified it is used as is.
* If it is a simple, unqualified class name the namespace is assumed to be the same
* as the namespace of the source entity.
*
* - mappedBy (string, required for bidirectional associations)
* The name of the field that completes the bidirectional association on the owning side.
* This key must be specified on the inverse side of a bidirectional association.
*
* - inversedBy (string, required for bidirectional associations)
* The name of the field that completes the bidirectional association on the inverse side.
* This key must be specified on the owning side of a bidirectional association.
*
* - cascade (array, optional)
* The names of persistence operations to cascade on the association. The set of possible
* values are: "persist", "remove", "detach", "merge", "refresh", "all" (implies all others).
*
* - orderBy (array, one-to-many/many-to-many only)
* A map of field names (of the target entity) to sorting directions (ASC/DESC).
* Example: array('priority' => 'desc')
*
* - fetch (integer, optional)
* The fetching strategy to use for the association, usually defaults to FETCH_LAZY.
* Possible values are: ClassMetadata::FETCH_EAGER, ClassMetadata::FETCH_LAZY.
*
* - joinTable (array, optional, many-to-many only)
* Specification of the join table and its join columns (foreign keys).
* Only valid for many-to-many mappings. Note that one-to-many associations can be mapped
* through a join table by simply mapping the association as many-to-many with a unique
* constraint on the join table.
*
* - indexBy (string, optional, to-many only)
* Specification of a field on target-entity that is used to index the collection by.
* This field HAS to be either the primary key or a unique column. Otherwise the collection
* does not contain all the entities that are actually related.
*
* A join table definition has the following structure:
*
* array(
* 'name' => ,
* 'joinColumns' => array(),
* 'inverseJoinColumns' => array()
* )
*
*
*
* @var array
*/
public $associationMappings = array();
/**
* READ-ONLY: Flag indicating whether the identifier/primary key of the class is composite.
*
* @var boolean
*/
public $isIdentifierComposite = false;
/**
* READ-ONLY: Flag indicating wheather the identifier/primary key contains at least one foreign key association.
*
* This flag is necessary because some code blocks require special treatment of this cases.
*
* @var boolean
*/
public $containsForeignIdentifier = false;
/**
* READ-ONLY: The ID generator used for generating IDs for this class.
*
* @var \Doctrine\ORM\Id\AbstractIdGenerator
* @todo Remove!
*/
public $idGenerator;
/**
* READ-ONLY: The definition of the sequence generator of this class. Only used for the
* SEQUENCE generation strategy.
*
* The definition has the following structure:
*
* array(
* 'sequenceName' => 'name',
* 'allocationSize' => 20,
* 'initialValue' => 1
* )
*
*
* @var array
* @todo Merge with tableGeneratorDefinition into generic generatorDefinition
*/
public $sequenceGeneratorDefinition;
/**
* READ-ONLY: The definition of the table generator of this class. Only used for the
* TABLE generation strategy.
*
* @var array
* @todo Merge with tableGeneratorDefinition into generic generatorDefinition
*/
public $tableGeneratorDefinition;
/**
* READ-ONLY: The policy used for change-tracking on entities of this class.
*
* @var integer
*/
public $changeTrackingPolicy = self::CHANGETRACKING_DEFERRED_IMPLICIT;
/**
* READ-ONLY: A flag for whether or not instances of this class are to be versioned
* with optimistic locking.
*
* @var boolean $isVersioned
*/
public $isVersioned;
/**
* READ-ONLY: The name of the field which is used for versioning in optimistic locking (if any).
*
* @var mixed $versionField
*/
public $versionField;
/**
* The ReflectionClass instance of the mapped class.
*
* @var ReflectionClass
*/
public $reflClass;
/**
* Is this entity marked as "read-only"?
*
* That means it is never considered for change-tracking in the UnitOfWork. It is a very helpful performance
* optimization for entities that are immutable, either in your domain or through the relation database
* (coming from a view, or a history table for example).
*
* @var bool
*/
public $isReadOnly = false;
/**
* NamingStrategy determining the default column and table names
*
* @var \Doctrine\ORM\Mapping\NamingStrategy
*/
protected $namingStrategy;
/**
* The ReflectionProperty instances of the mapped class.
*
* @var array
*/
public $reflFields = array();
/**
* The prototype from which new instances of the mapped class are created.
*
* @var object
*/
private $_prototype;
/**
* Initializes a new ClassMetadata instance that will hold the object-relational mapping
* metadata of the class with the given name.
*
* @param string $entityName The name of the entity class the new instance is used for.
* @param NamingStrategy $namingStrategy
*/
public function __construct($entityName, NamingStrategy $namingStrategy = null)
{
$this->name = $entityName;
$this->rootEntityName = $entityName;
$this->namingStrategy = $namingStrategy ?: new DefaultNamingStrategy();
}
/**
* Gets the ReflectionPropertys of the mapped class.
*
* @return array An array of ReflectionProperty instances.
*/
public function getReflectionProperties()
{
return $this->reflFields;
}
/**
* Gets a ReflectionProperty for a specific field of the mapped class.
*
* @param string $name
* @return \ReflectionProperty
*/
public function getReflectionProperty($name)
{
return $this->reflFields[$name];
}
/**
* Gets the ReflectionProperty for the single identifier field.
*
* @return \ReflectionProperty
* @throws BadMethodCallException If the class has a composite identifier.
*/
public function getSingleIdReflectionProperty()
{
if ($this->isIdentifierComposite) {
throw new BadMethodCallException("Class " . $this->name . " has a composite identifier.");
}
return $this->reflFields[$this->identifier[0]];
}
/**
* Extracts the identifier values of an entity of this class.
*
* For composite identifiers, the identifier values are returned as an array
* with the same order as the field order in {@link identifier}.
*
* @param object $entity
* @return array
*/
public function getIdentifierValues($entity)
{
if ($this->isIdentifierComposite) {
$id = array();
foreach ($this->identifier as $idField) {
$value = $this->reflFields[$idField]->getValue($entity);
if ($value !== null) {
$id[$idField] = $value;
}
}
return $id;
}
$value = $this->reflFields[$this->identifier[0]]->getValue($entity);
if ($value !== null) {
return array($this->identifier[0] => $value);
}
return array();
}
/**
* Populates the entity identifier of an entity.
*
* @param object $entity
* @param mixed $id
* @todo Rename to assignIdentifier()
*/
public function setIdentifierValues($entity, array $id)
{
foreach ($id as $idField => $idValue) {
$this->reflFields[$idField]->setValue($entity, $idValue);
}
}
/**
* Sets the specified field to the specified value on the given entity.
*
* @param object $entity
* @param string $field
* @param mixed $value
*/
public function setFieldValue($entity, $field, $value)
{
$this->reflFields[$field]->setValue($entity, $value);
}
/**
* Gets the specified field's value off the given entity.
*
* @param object $entity
* @param string $field
*/
public function getFieldValue($entity, $field)
{
return $this->reflFields[$field]->getValue($entity);
}
/**
* Creates a string representation of this instance.
*
* @return string The string representation of this instance.
* @todo Construct meaningful string representation.
*/
public function __toString()
{
return __CLASS__ . '@' . spl_object_hash($this);
}
/**
* Determines which fields get serialized.
*
* It is only serialized what is necessary for best unserialization performance.
* That means any metadata properties that are not set or empty or simply have
* their default value are NOT serialized.
*
* Parts that are also NOT serialized because they can not be properly unserialized:
* - reflClass (ReflectionClass)
* - reflFields (ReflectionProperty array)
*
* @return array The names of all the fields that should be serialized.
*/
public function __sleep()
{
// This metadata is always serialized/cached.
$serialized = array(
'associationMappings',
'columnNames', //TODO: Not really needed. Can use fieldMappings[$fieldName]['columnName']
'fieldMappings',
'fieldNames',
'identifier',
'isIdentifierComposite', // TODO: REMOVE
'name',
'namespace', // TODO: REMOVE
'table',
'rootEntityName',
'idGenerator', //TODO: Does not really need to be serialized. Could be moved to runtime.
);
// The rest of the metadata is only serialized if necessary.
if ($this->changeTrackingPolicy != self::CHANGETRACKING_DEFERRED_IMPLICIT) {
$serialized[] = 'changeTrackingPolicy';
}
if ($this->customRepositoryClassName) {
$serialized[] = 'customRepositoryClassName';
}
if ($this->inheritanceType != self::INHERITANCE_TYPE_NONE) {
$serialized[] = 'inheritanceType';
$serialized[] = 'discriminatorColumn';
$serialized[] = 'discriminatorValue';
$serialized[] = 'discriminatorMap';
$serialized[] = 'parentClasses';
$serialized[] = 'subClasses';
}
if ($this->generatorType != self::GENERATOR_TYPE_NONE) {
$serialized[] = 'generatorType';
if ($this->generatorType == self::GENERATOR_TYPE_SEQUENCE) {
$serialized[] = 'sequenceGeneratorDefinition';
}
}
if ($this->isMappedSuperclass) {
$serialized[] = 'isMappedSuperclass';
}
if ($this->containsForeignIdentifier) {
$serialized[] = 'containsForeignIdentifier';
}
if ($this->isVersioned) {
$serialized[] = 'isVersioned';
$serialized[] = 'versionField';
}
if ($this->lifecycleCallbacks) {
$serialized[] = 'lifecycleCallbacks';
}
if ($this->namedQueries) {
$serialized[] = 'namedQueries';
}
if ($this->namedNativeQueries) {
$serialized[] = 'namedNativeQueries';
}
if ($this->sqlResultSetMappings) {
$serialized[] = 'sqlResultSetMappings';
}
if ($this->isReadOnly) {
$serialized[] = 'isReadOnly';
}
if ($this->customGeneratorDefinition) {
$serialized[] = "customGeneratorDefinition";
}
return $serialized;
}
/**
* Creates a new instance of the mapped class, without invoking the constructor.
*
* @return object
*/
public function newInstance()
{
if ($this->_prototype === null) {
$this->_prototype = unserialize(sprintf('O:%d:"%s":0:{}', strlen($this->name), $this->name));
}
return clone $this->_prototype;
}
/**
* Restores some state that can not be serialized/unserialized.
*
* @param \Doctrine\Common\Persistence\Mapping\ReflectionService $reflService
* @return void
*/
public function wakeupReflection($reflService)
{
// Restore ReflectionClass and properties
$this->reflClass = $reflService->getClass($this->name);
foreach ($this->fieldMappings as $field => $mapping) {
$this->reflFields[$field] = isset($mapping['declared'])
? $reflService->getAccessibleProperty($mapping['declared'], $field)
: $reflService->getAccessibleProperty($this->name, $field);
}
foreach ($this->associationMappings as $field => $mapping) {
$this->reflFields[$field] = isset($mapping['declared'])
? $reflService->getAccessibleProperty($mapping['declared'], $field)
: $reflService->getAccessibleProperty($this->name, $field);
}
}
/**
* Initializes a new ClassMetadata instance that will hold the object-relational mapping
* metadata of the class with the given name.
*
* @param \Doctrine\Common\Persistence\Mapping\ReflectionService $reflService The reflection service.
*/
public function initializeReflection($reflService)
{
$this->reflClass = $reflService->getClass($this->name);
$this->namespace = $reflService->getClassNamespace($this->name);
if ($this->reflClass) {
$this->name = $this->rootEntityName = $this->reflClass->getName();
}
$this->table['name'] = $this->namingStrategy->classToTableName($this->name);
}
/**
* Validate Identifier
*
* @throws MappingException
* @return void
*/
public function validateIdentifier()
{
// Verify & complete identifier mapping
if ( ! $this->identifier && ! $this->isMappedSuperclass) {
throw MappingException::identifierRequired($this->name);
}
if ($this->usesIdGenerator() && $this->isIdentifierComposite) {
throw MappingException::compositeKeyAssignedIdGeneratorRequired($this->name);
}
}
/**
* Validate association targets actually exist.
*
* @throws MappingException
* @return void
*/
public function validateAssocations()
{
foreach ($this->associationMappings as $mapping) {
if ( ! ClassLoader::classExists($mapping['targetEntity']) ) {
throw MappingException::invalidTargetEntityClass($mapping['targetEntity'], $this->name, $mapping['fieldName']);
}
}
}
/**
* Validate lifecycle callbacks
*
* @param \Doctrine\Common\Persistence\Mapping\ReflectionService $reflService
* @throws MappingException
* @return void
*/
public function validateLifecycleCallbacks($reflService)
{
foreach ($this->lifecycleCallbacks as $callbacks) {
foreach ($callbacks as $callbackFuncName) {
if ( ! $reflService->hasPublicMethod($this->name, $callbackFuncName)) {
throw MappingException::lifecycleCallbackMethodNotFound($this->name, $callbackFuncName);
}
}
}
}
/**
* {@inheritDoc}
*/
public function getReflectionClass()
{
return $this->reflClass;
}
/**
* Sets the change tracking policy used by this class.
*
* @param integer $policy
*/
public function setChangeTrackingPolicy($policy)
{
$this->changeTrackingPolicy = $policy;
}
/**
* Whether the change tracking policy of this class is "deferred explicit".
*
* @return boolean
*/
public function isChangeTrackingDeferredExplicit()
{
return $this->changeTrackingPolicy == self::CHANGETRACKING_DEFERRED_EXPLICIT;
}
/**
* Whether the change tracking policy of this class is "deferred implicit".
*
* @return boolean
*/
public function isChangeTrackingDeferredImplicit()
{
return $this->changeTrackingPolicy == self::CHANGETRACKING_DEFERRED_IMPLICIT;
}
/**
* Whether the change tracking policy of this class is "notify".
*
* @return boolean
*/
public function isChangeTrackingNotify()
{
return $this->changeTrackingPolicy == self::CHANGETRACKING_NOTIFY;
}
/**
* Checks whether a field is part of the identifier/primary key field(s).
*
* @param string $fieldName The field name
* @return boolean TRUE if the field is part of the table identifier/primary key field(s),
* FALSE otherwise.
*/
public function isIdentifier($fieldName)
{
if ( ! $this->isIdentifierComposite) {
return $fieldName === $this->identifier[0];
}
return in_array($fieldName, $this->identifier);
}
/**
* Check if the field is unique.
*
* @param string $fieldName The field name
* @return boolean TRUE if the field is unique, FALSE otherwise.
*/
public function isUniqueField($fieldName)
{
$mapping = $this->getFieldMapping($fieldName);
if ($mapping !== false) {
return isset($mapping['unique']) && $mapping['unique'] == true;
}
return false;
}
/**
* Check if the field is not null.
*
* @param string $fieldName The field name
* @return boolean TRUE if the field is not null, FALSE otherwise.
*/
public function isNullable($fieldName)
{
$mapping = $this->getFieldMapping($fieldName);
if ($mapping !== false) {
return isset($mapping['nullable']) && $mapping['nullable'] == true;
}
return false;
}
/**
* Gets a column name for a field name.
* If the column name for the field cannot be found, the given field name
* is returned.
*
* @param string $fieldName The field name.
* @return string The column name.
*/
public function getColumnName($fieldName)
{
return isset($this->columnNames[$fieldName]) ?
$this->columnNames[$fieldName] : $fieldName;
}
/**
* Gets the mapping of a (regular) field that holds some data but not a
* reference to another object.
*
* @param string $fieldName The field name.
* @throws MappingException
* @return array The field mapping.
*/
public function getFieldMapping($fieldName)
{
if ( ! isset($this->fieldMappings[$fieldName])) {
throw MappingException::mappingNotFound($this->name, $fieldName);
}
return $this->fieldMappings[$fieldName];
}
/**
* Gets the mapping of an association.
*
* @see ClassMetadataInfo::$associationMappings
* @param string $fieldName The field name that represents the association in
* the object model.
* @throws MappingException
* @return array The mapping.
*/
public function getAssociationMapping($fieldName)
{
if ( ! isset($this->associationMappings[$fieldName])) {
throw MappingException::mappingNotFound($this->name, $fieldName);
}
return $this->associationMappings[$fieldName];
}
/**
* Gets all association mappings of the class.
*
* @return array
*/
public function getAssociationMappings()
{
return $this->associationMappings;
}
/**
* Gets the field name for a column name.
* If no field name can be found the column name is returned.
*
* @param string $columnName column name
* @return string column alias
*/
public function getFieldName($columnName)
{
return isset($this->fieldNames[$columnName]) ?
$this->fieldNames[$columnName] : $columnName;
}
/**
* Gets the named query.
*
* @see ClassMetadataInfo::$namedQueries
* @throws MappingException
* @param string $queryName The query name
* @return string
*/
public function getNamedQuery($queryName)
{
if ( ! isset($this->namedQueries[$queryName])) {
throw MappingException::queryNotFound($this->name, $queryName);
}
return $this->namedQueries[$queryName]['dql'];
}
/**
* Gets all named queries of the class.
*
* @return array
*/
public function getNamedQueries()
{
return $this->namedQueries;
}
/**
* Gets the named native query.
*
* @see ClassMetadataInfo::$namedNativeQueries
* @throws MappingException
* @param string $queryName The query name
* @return array
*/
public function getNamedNativeQuery($queryName)
{
if ( ! isset($this->namedNativeQueries[$queryName])) {
throw MappingException::queryNotFound($this->name, $queryName);
}
return $this->namedNativeQueries[$queryName];
}
/**
* Gets all named native queries of the class.
*
* @return array
*/
public function getNamedNativeQueries()
{
return $this->namedNativeQueries;
}
/**
* Gets the result set mapping.
*
* @see ClassMetadataInfo::$sqlResultSetMappings
* @throws MappingException
* @param string $name The result set mapping name
* @return array
*/
public function getSqlResultSetMapping($name)
{
if ( ! isset($this->sqlResultSetMappings[$name])) {
throw MappingException::resultMappingNotFound($this->name, $name);
}
return $this->sqlResultSetMappings[$name];
}
/**
* Gets all sql result set mappings of the class.
*
* @return array
*/
public function getSqlResultSetMappings()
{
return $this->sqlResultSetMappings;
}
/**
* Validates & completes the given field mapping.
*
* @param array $mapping The field mapping to validated & complete.
* @throws MappingException
* @return array The validated and completed field mapping.
*/
protected function _validateAndCompleteFieldMapping(array &$mapping)
{
// Check mandatory fields
if ( ! isset($mapping['fieldName']) || strlen($mapping['fieldName']) == 0) {
throw MappingException::missingFieldName($this->name);
}
if ( ! isset($mapping['type'])) {
// Default to string
$mapping['type'] = 'string';
}
// Complete fieldName and columnName mapping
if ( ! isset($mapping['columnName'])) {
$mapping['columnName'] = $this->namingStrategy->propertyToColumnName($mapping['fieldName']);
}
if ($mapping['columnName'][0] === '`') {
$mapping['columnName'] = trim($mapping['columnName'], '`');
$mapping['quoted'] = true;
}
$this->columnNames[$mapping['fieldName']] = $mapping['columnName'];
if (isset($this->fieldNames[$mapping['columnName']]) || ($this->discriminatorColumn != null && $this->discriminatorColumn['name'] == $mapping['columnName'])) {
throw MappingException::duplicateColumnName($this->name, $mapping['columnName']);
}
$this->fieldNames[$mapping['columnName']] = $mapping['fieldName'];
// Complete id mapping
if (isset($mapping['id']) && $mapping['id'] === true) {
if ($this->versionField == $mapping['fieldName']) {
throw MappingException::cannotVersionIdField($this->name, $mapping['fieldName']);
}
if ( ! in_array($mapping['fieldName'], $this->identifier)) {
$this->identifier[] = $mapping['fieldName'];
}
// Check for composite key
if ( ! $this->isIdentifierComposite && count($this->identifier) > 1) {
$this->isIdentifierComposite = true;
}
}
if (Type::hasType($mapping['type']) && Type::getType($mapping['type'])->canRequireSQLConversion()) {
if (isset($mapping['id']) && $mapping['id'] === true) {
throw MappingException::sqlConversionNotAllowedForIdentifiers($this->name, $mapping['fieldName'], $mapping['type']);
}
$mapping['requireSQLConversion'] = true;
}
}
/**
* Validates & completes the basic mapping information that is common to all
* association mappings (one-to-one, many-ot-one, one-to-many, many-to-many).
*
* @param array $mapping The mapping.
* @return array The updated mapping.
* @throws MappingException If something is wrong with the mapping.
*/
protected function _validateAndCompleteAssociationMapping(array $mapping)
{
if ( ! isset($mapping['mappedBy'])) {
$mapping['mappedBy'] = null;
}
if ( ! isset($mapping['inversedBy'])) {
$mapping['inversedBy'] = null;
}
$mapping['isOwningSide'] = true; // assume owning side until we hit mappedBy
// unset optional indexBy attribute if its empty
if ( ! isset($mapping['indexBy']) || !$mapping['indexBy']) {
unset($mapping['indexBy']);
}
// If targetEntity is unqualified, assume it is in the same namespace as
// the sourceEntity.
$mapping['sourceEntity'] = $this->name;
if (isset($mapping['targetEntity'])) {
if (strlen($this->namespace) > 0 && strpos($mapping['targetEntity'], '\\') === false) {
$mapping['targetEntity'] = $this->namespace . '\\' . $mapping['targetEntity'];
}
$mapping['targetEntity'] = ltrim($mapping['targetEntity'], '\\');
}
if ( ($mapping['type'] & self::MANY_TO_ONE) > 0 &&
isset($mapping['orphanRemoval']) &&
$mapping['orphanRemoval'] == true) {
throw MappingException::illegalOrphanRemoval($this->name, $mapping['fieldName']);
}
// Complete id mapping
if (isset($mapping['id']) && $mapping['id'] === true) {
if (isset($mapping['orphanRemoval']) && $mapping['orphanRemoval'] == true) {
throw MappingException::illegalOrphanRemovalOnIdentifierAssociation($this->name, $mapping['fieldName']);
}
if ( ! in_array($mapping['fieldName'], $this->identifier)) {
if (count($mapping['joinColumns']) >= 2) {
throw MappingException::cannotMapCompositePrimaryKeyEntitiesAsForeignId(
$mapping['targetEntity'], $this->name, $mapping['fieldName']
);
}
$this->identifier[] = $mapping['fieldName'];
$this->containsForeignIdentifier = true;
}
// Check for composite key
if ( ! $this->isIdentifierComposite && count($this->identifier) > 1) {
$this->isIdentifierComposite = true;
}
}
// Mandatory attributes for both sides
// Mandatory: fieldName, targetEntity
if ( ! isset($mapping['fieldName']) || strlen($mapping['fieldName']) == 0) {
throw MappingException::missingFieldName($this->name);
}
if ( ! isset($mapping['targetEntity'])) {
throw MappingException::missingTargetEntity($mapping['fieldName']);
}
// Mandatory and optional attributes for either side
if ( ! $mapping['mappedBy']) {
if (isset($mapping['joinTable']) && $mapping['joinTable']) {
if (isset($mapping['joinTable']['name']) && $mapping['joinTable']['name'][0] === '`') {
$mapping['joinTable']['name'] = trim($mapping['joinTable']['name'], '`');
$mapping['joinTable']['quoted'] = true;
}
}
} else {
$mapping['isOwningSide'] = false;
}
if (isset($mapping['id']) && $mapping['id'] === true && $mapping['type'] & self::TO_MANY) {
throw MappingException::illegalToManyIdentifierAssoaction($this->name, $mapping['fieldName']);
}
// Fetch mode. Default fetch mode to LAZY, if not set.
if ( ! isset($mapping['fetch'])) {
$mapping['fetch'] = self::FETCH_LAZY;
}
// Cascades
$cascades = isset($mapping['cascade']) ? array_map('strtolower', $mapping['cascade']) : array();
if (in_array('all', $cascades)) {
$cascades = array('remove', 'persist', 'refresh', 'merge', 'detach');
}
if (count($cascades) !== count(array_intersect($cascades, array('remove', 'persist', 'refresh', 'merge', 'detach')))) {
throw MappingException::invalidCascadeOption(
array_diff($cascades, array_intersect($cascades, array('remove', 'persist', 'refresh', 'merge', 'detach'))),
$this->name,
$mapping['fieldName']
);
}
$mapping['cascade'] = $cascades;
$mapping['isCascadeRemove'] = in_array('remove', $cascades);
$mapping['isCascadePersist'] = in_array('persist', $cascades);
$mapping['isCascadeRefresh'] = in_array('refresh', $cascades);
$mapping['isCascadeMerge'] = in_array('merge', $cascades);
$mapping['isCascadeDetach'] = in_array('detach', $cascades);
return $mapping;
}
/**
* Validates & completes a one-to-one association mapping.
*
* @param array $mapping The mapping to validate & complete.
* @throws RuntimeException
* @throws MappingException
* @return array The validated & completed mapping.@override
*/
protected function _validateAndCompleteOneToOneMapping(array $mapping)
{
$mapping = $this->_validateAndCompleteAssociationMapping($mapping);
if (isset($mapping['joinColumns']) && $mapping['joinColumns']) {
$mapping['isOwningSide'] = true;
}
if ($mapping['isOwningSide']) {
if ( ! isset($mapping['joinColumns']) || ! $mapping['joinColumns']) {
// Apply default join column
$mapping['joinColumns'] = array(array(
'name' => $this->namingStrategy->joinColumnName($mapping['fieldName']),
'referencedColumnName' => $this->namingStrategy->referenceColumnName()
));
}
$uniqueContraintColumns = array();
foreach ($mapping['joinColumns'] as &$joinColumn) {
if ($mapping['type'] === self::ONE_TO_ONE && ! $this->isInheritanceTypeSingleTable()) {
if (count($mapping['joinColumns']) == 1) {
if ( ! isset($mapping['id']) || ! $mapping['id']) {
$joinColumn['unique'] = true;
}
} else {
$uniqueContraintColumns[] = $joinColumn['name'];
}
}
if (empty($joinColumn['name'])) {
$joinColumn['name'] = $this->namingStrategy->joinColumnName($mapping['fieldName']);
}
if (empty($joinColumn['referencedColumnName'])) {
$joinColumn['referencedColumnName'] = $this->namingStrategy->referenceColumnName();
}
if ($joinColumn['name'][0] === '`') {
$joinColumn['name'] = trim($joinColumn['name'], '`');
$joinColumn['quoted'] = true;
}
if ($joinColumn['referencedColumnName'][0] === '`') {
$joinColumn['referencedColumnName'] = trim($joinColumn['referencedColumnName'], '`');
$joinColumn['quoted'] = true;
}
$mapping['sourceToTargetKeyColumns'][$joinColumn['name']] = $joinColumn['referencedColumnName'];
$mapping['joinColumnFieldNames'][$joinColumn['name']] = isset($joinColumn['fieldName'])
? $joinColumn['fieldName'] : $joinColumn['name'];
}
if ($uniqueContraintColumns) {
if ( ! $this->table) {
throw new RuntimeException("ClassMetadataInfo::setTable() has to be called before defining a one to one relationship.");
}
$this->table['uniqueConstraints'][$mapping['fieldName']."_uniq"] = array(
'columns' => $uniqueContraintColumns
);
}
$mapping['targetToSourceKeyColumns'] = array_flip($mapping['sourceToTargetKeyColumns']);
}
$mapping['orphanRemoval'] = isset($mapping['orphanRemoval']) ? (bool) $mapping['orphanRemoval'] : false;
$mapping['isCascadeRemove'] = $mapping['orphanRemoval'] ? true : $mapping['isCascadeRemove'];
if ($mapping['orphanRemoval']) {
unset($mapping['unique']);
}
if (isset($mapping['id']) && $mapping['id'] === true && !$mapping['isOwningSide']) {
throw MappingException::illegalInverseIdentifierAssocation($this->name, $mapping['fieldName']);
}
return $mapping;
}
/**
* Validates and completes the mapping.
*
* @param array $mapping The mapping to validate and complete.
* @throws MappingException
* @throws InvalidArgumentException
* @return array The validated and completed mapping.@override
*/
protected function _validateAndCompleteOneToManyMapping(array $mapping)
{
$mapping = $this->_validateAndCompleteAssociationMapping($mapping);
// OneToMany-side MUST be inverse (must have mappedBy)
if ( ! isset($mapping['mappedBy'])) {
throw MappingException::oneToManyRequiresMappedBy($mapping['fieldName']);
}
$mapping['orphanRemoval'] = isset($mapping['orphanRemoval']) ? (bool) $mapping['orphanRemoval'] : false;
$mapping['isCascadeRemove'] = $mapping['orphanRemoval'] ? true : $mapping['isCascadeRemove'];
if (isset($mapping['orderBy'])) {
if ( ! is_array($mapping['orderBy'])) {
throw new InvalidArgumentException("'orderBy' is expected to be an array, not ".gettype($mapping['orderBy']));
}
}
return $mapping;
}
protected function _validateAndCompleteManyToManyMapping(array $mapping)
{
$mapping = $this->_validateAndCompleteAssociationMapping($mapping);
if ($mapping['isOwningSide']) {
// owning side MUST have a join table
if ( ! isset($mapping['joinTable']['name'])) {
$mapping['joinTable']['name'] = $this->namingStrategy->joinTableName($mapping['sourceEntity'], $mapping['targetEntity'], $mapping['fieldName']);
}
if ( ! isset($mapping['joinTable']['joinColumns'])) {
$mapping['joinTable']['joinColumns'] = array(array(
'name' => $this->namingStrategy->joinKeyColumnName($mapping['sourceEntity']),
'referencedColumnName' => $this->namingStrategy->referenceColumnName(),
'onDelete' => 'CASCADE'));
}
if ( ! isset($mapping['joinTable']['inverseJoinColumns'])) {
$mapping['joinTable']['inverseJoinColumns'] = array(array(
'name' => $this->namingStrategy->joinKeyColumnName($mapping['targetEntity']),
'referencedColumnName' => $this->namingStrategy->referenceColumnName(),
'onDelete' => 'CASCADE'));
}
$mapping['joinTableColumns'] = array();
foreach ($mapping['joinTable']['joinColumns'] as &$joinColumn) {
if (empty($joinColumn['name'])) {
$joinColumn['name'] = $this->namingStrategy->joinKeyColumnName($mapping['sourceEntity'], $joinColumn['referencedColumnName']);
}
if (empty($joinColumn['referencedColumnName'])) {
$joinColumn['referencedColumnName'] = $this->namingStrategy->referenceColumnName();
}
if ($joinColumn['name'][0] === '`') {
$joinColumn['name'] = trim($joinColumn['name'], '`');
$joinColumn['quoted'] = true;
}
if ($joinColumn['referencedColumnName'][0] === '`') {
$joinColumn['referencedColumnName'] = trim($joinColumn['referencedColumnName'], '`');
$joinColumn['quoted'] = true;
}
if (isset($joinColumn['onDelete']) && strtolower($joinColumn['onDelete']) == 'cascade') {
$mapping['isOnDeleteCascade'] = true;
}
$mapping['relationToSourceKeyColumns'][$joinColumn['name']] = $joinColumn['referencedColumnName'];
$mapping['joinTableColumns'][] = $joinColumn['name'];
}
foreach ($mapping['joinTable']['inverseJoinColumns'] as &$inverseJoinColumn) {
if (empty($inverseJoinColumn['name'])) {
$inverseJoinColumn['name'] = $this->namingStrategy->joinKeyColumnName($mapping['targetEntity'], $inverseJoinColumn['referencedColumnName']);
}
if (empty($inverseJoinColumn['referencedColumnName'])) {
$inverseJoinColumn['referencedColumnName'] = $this->namingStrategy->referenceColumnName();
}
if ($inverseJoinColumn['name'][0] === '`') {
$inverseJoinColumn['name'] = trim($inverseJoinColumn['name'], '`');
$inverseJoinColumn['quoted'] = true;
}
if ($inverseJoinColumn['referencedColumnName'][0] === '`') {
$inverseJoinColumn['referencedColumnName'] = trim($inverseJoinColumn['referencedColumnName'], '`');
$inverseJoinColumn['quoted'] = true;
}
if (isset($inverseJoinColumn['onDelete']) && strtolower($inverseJoinColumn['onDelete']) == 'cascade') {
$mapping['isOnDeleteCascade'] = true;
}
$mapping['relationToTargetKeyColumns'][$inverseJoinColumn['name']] = $inverseJoinColumn['referencedColumnName'];
$mapping['joinTableColumns'][] = $inverseJoinColumn['name'];
}
}
$mapping['orphanRemoval'] = isset($mapping['orphanRemoval']) ? (bool) $mapping['orphanRemoval'] : false;
if (isset($mapping['orderBy'])) {
if ( ! is_array($mapping['orderBy'])) {
throw new InvalidArgumentException("'orderBy' is expected to be an array, not ".gettype($mapping['orderBy']));
}
}
return $mapping;
}
/**
* {@inheritDoc}
*/
public function getIdentifierFieldNames()
{
return $this->identifier;
}
/**
* Gets the name of the single id field. Note that this only works on
* entity classes that have a single-field pk.
*
* @return string
* @throws MappingException If the class has a composite primary key.
*/
public function getSingleIdentifierFieldName()
{
if ($this->isIdentifierComposite) {
throw MappingException::singleIdNotAllowedOnCompositePrimaryKey($this->name);
}
return $this->identifier[0];
}
/**
* Gets the column name of the single id column. Note that this only works on
* entity classes that have a single-field pk.
*
* @return string
* @throws MappingException If the class has a composite primary key.
*/
public function getSingleIdentifierColumnName()
{
return $this->getColumnName($this->getSingleIdentifierFieldName());
}
/**
* INTERNAL:
* Sets the mapped identifier/primary key fields of this class.
* Mainly used by the ClassMetadataFactory to assign inherited identifiers.
*
* @param array $identifier
*/
public function setIdentifier(array $identifier)
{
$this->identifier = $identifier;
$this->isIdentifierComposite = (count($this->identifier) > 1);
}
/**
* Gets the mapped identifier field of this class.
*
* @return array|string $identifier
*/
public function getIdentifier()
{
return $this->identifier;
}
/**
* {@inheritDoc}
*/
public function hasField($fieldName)
{
return isset($this->fieldMappings[$fieldName]);
}
/**
* Gets an array containing all the column names.
*
* @param array $fieldNames
* @return array
*/
public function getColumnNames(array $fieldNames = null)
{
if ($fieldNames === null) {
return array_keys($this->fieldNames);
} else {
$columnNames = array();
foreach ($fieldNames as $fieldName) {
$columnNames[] = $this->getColumnName($fieldName);
}
return $columnNames;
}
}
/**
* Returns an array with all the identifier column names.
*
* @return array
*/
public function getIdentifierColumnNames()
{
$columnNames = array();
foreach ($this->identifier as $idProperty) {
if (isset($this->fieldMappings[$idProperty])) {
$columnNames[] = $this->fieldMappings[$idProperty]['columnName'];
continue;
}
// Association defined as Id field
$joinColumns = $this->associationMappings[$idProperty]['joinColumns'];
$assocColumnNames = array_map(function ($joinColumn) { return $joinColumn['name']; }, $joinColumns);
$columnNames = array_merge($columnNames, $assocColumnNames);
}
return $columnNames;
}
/**
* Sets the type of Id generator to use for the mapped class.
*/
public function setIdGeneratorType($generatorType)
{
$this->generatorType = $generatorType;
}
/**
* Checks whether the mapped class uses an Id generator.
*
* @return boolean TRUE if the mapped class uses an Id generator, FALSE otherwise.
*/
public function usesIdGenerator()
{
return $this->generatorType != self::GENERATOR_TYPE_NONE;
}
/**
* @return boolean
*/
public function isInheritanceTypeNone()
{
return $this->inheritanceType == self::INHERITANCE_TYPE_NONE;
}
/**
* Checks whether the mapped class uses the JOINED inheritance mapping strategy.
*
* @return boolean TRUE if the class participates in a JOINED inheritance mapping,
* FALSE otherwise.
*/
public function isInheritanceTypeJoined()
{
return $this->inheritanceType == self::INHERITANCE_TYPE_JOINED;
}
/**
* Checks whether the mapped class uses the SINGLE_TABLE inheritance mapping strategy.
*
* @return boolean TRUE if the class participates in a SINGLE_TABLE inheritance mapping,
* FALSE otherwise.
*/
public function isInheritanceTypeSingleTable()
{
return $this->inheritanceType == self::INHERITANCE_TYPE_SINGLE_TABLE;
}
/**
* Checks whether the mapped class uses the TABLE_PER_CLASS inheritance mapping strategy.
*
* @return boolean TRUE if the class participates in a TABLE_PER_CLASS inheritance mapping,
* FALSE otherwise.
*/
public function isInheritanceTypeTablePerClass()
{
return $this->inheritanceType == self::INHERITANCE_TYPE_TABLE_PER_CLASS;
}
/**
* Checks whether the class uses an identity column for the Id generation.
*
* @return boolean TRUE if the class uses the IDENTITY generator, FALSE otherwise.
*/
public function isIdGeneratorIdentity()
{
return $this->generatorType == self::GENERATOR_TYPE_IDENTITY;
}
/**
* Checks whether the class uses a sequence for id generation.
*
* @return boolean TRUE if the class uses the SEQUENCE generator, FALSE otherwise.
*/
public function isIdGeneratorSequence()
{
return $this->generatorType == self::GENERATOR_TYPE_SEQUENCE;
}
/**
* Checks whether the class uses a table for id generation.
*
* @return boolean TRUE if the class uses the TABLE generator, FALSE otherwise.
*/
public function isIdGeneratorTable()
{
return $this->generatorType == self::GENERATOR_TYPE_TABLE;
}
/**
* Checks whether the class has a natural identifier/pk (which means it does
* not use any Id generator.
*
* @return boolean
*/
public function isIdentifierNatural()
{
return $this->generatorType == self::GENERATOR_TYPE_NONE;
}
/**
* Checks whether the class use a UUID for id generation
*
* @return boolean
*/
public function isIdentifierUuid()
{
return $this->generatorType == self::GENERATOR_TYPE_UUID;
}
/**
* Gets the type of a field.
*
* @param string $fieldName
* @return \Doctrine\DBAL\Types\Type|string
*/
public function getTypeOfField($fieldName)
{
return isset($this->fieldMappings[$fieldName]) ?
$this->fieldMappings[$fieldName]['type'] : null;
}
/**
* Gets the type of a column.
*
* @param string $columnName
* @return \Doctrine\DBAL\Types\Type
*/
public function getTypeOfColumn($columnName)
{
return $this->getTypeOfField($this->getFieldName($columnName));
}
/**
* Gets the name of the primary table.
*
* @return string
*/
public function getTableName()
{
return $this->table['name'];
}
/**
* Gets the table name to use for temporary identifier tables of this class.
*
* @return string
*/
public function getTemporaryIdTableName()
{
// replace dots with underscores because PostgreSQL creates temporary tables in a special schema
return str_replace('.', '_', $this->getTableName() . '_id_tmp');
}
/**
* Sets the mapped subclasses of this class.
*
* @param array $subclasses The names of all mapped subclasses.
*/
public function setSubclasses(array $subclasses)
{
foreach ($subclasses as $subclass) {
if (strpos($subclass, '\\') === false && strlen($this->namespace)) {
$this->subClasses[] = $this->namespace . '\\' . $subclass;
} else {
$this->subClasses[] = $subclass;
}
}
}
/**
* Sets the parent class names.
* Assumes that the class names in the passed array are in the order:
* directParent -> directParentParent -> directParentParentParent ... -> root.
*/
public function setParentClasses(array $classNames)
{
$this->parentClasses = $classNames;
if (count($classNames) > 0) {
$this->rootEntityName = array_pop($classNames);
}
}
/**
* Sets the inheritance type used by the class and it's subclasses.
*
* @param integer $type
* @throws MappingException
* @return void
*/
public function setInheritanceType($type)
{
if ( ! $this->_isInheritanceType($type)) {
throw MappingException::invalidInheritanceType($this->name, $type);
}
$this->inheritanceType = $type;
}
/**
* Sets the association to override association mapping of property for an entity relationship.
*
* @param string $fieldName
* @param array $overrideMapping
* @throws MappingException
* @return void
*/
public function setAssociationOverride($fieldName, array $overrideMapping)
{
if ( ! isset($this->associationMappings[$fieldName])) {
throw MappingException::invalidOverrideFieldName($this->name, $fieldName);
}
$mapping = $this->associationMappings[$fieldName];
if (isset($overrideMapping['joinColumns'])) {
$mapping['joinColumns'] = $overrideMapping['joinColumns'];
}
if (isset($overrideMapping['joinTable'])) {
$mapping['joinTable'] = $overrideMapping['joinTable'];
}
$mapping['joinColumnFieldNames'] = null;
$mapping['joinTableColumns'] = null;
$mapping['sourceToTargetKeyColumns'] = null;
$mapping['relationToSourceKeyColumns'] = null;
$mapping['relationToTargetKeyColumns'] = null;
switch ($mapping['type']) {
case self::ONE_TO_ONE:
$mapping = $this->_validateAndCompleteOneToOneMapping($mapping);
break;
case self::ONE_TO_MANY:
$mapping = $this->_validateAndCompleteOneToManyMapping($mapping);
break;
case self::MANY_TO_ONE:
$mapping = $this->_validateAndCompleteOneToOneMapping($mapping);
break;
case self::MANY_TO_MANY:
$mapping = $this->_validateAndCompleteManyToManyMapping($mapping);
break;
}
$this->associationMappings[$fieldName] = $mapping;
}
/**
* Sets the override for a mapped field.
*
* @param string $fieldName
* @param array $overrideMapping
* @throws MappingException
* @param array $overrideMapping
* @return void
*/
public function setAttributeOverride($fieldName, array $overrideMapping)
{
if ( ! isset($this->fieldMappings[$fieldName])) {
throw MappingException::invalidOverrideFieldName($this->name, $fieldName);
}
$mapping = $this->fieldMappings[$fieldName];
if (isset($mapping['id'])) {
$overrideMapping['id'] = $mapping['id'];
}
if ( ! isset($overrideMapping['type']) || $overrideMapping['type'] === null) {
$overrideMapping['type'] = $mapping['type'];
}
if ( ! isset($overrideMapping['fieldName']) || $overrideMapping['fieldName'] === null) {
$overrideMapping['fieldName'] = $mapping['fieldName'];
}
if ($overrideMapping['type'] !== $mapping['type']) {
throw MappingException::invalidOverrideFieldType($this->name, $fieldName);
}
unset($this->fieldMappings[$fieldName]);
unset($this->fieldNames[$mapping['columnName']]);
unset($this->columnNames[$mapping['fieldName']]);
$this->_validateAndCompleteFieldMapping($overrideMapping);
$this->fieldMappings[$fieldName] = $overrideMapping;
}
/**
* Checks whether a mapped field is inherited from an entity superclass.
*
* @param string $fieldName
* @return bool TRUE if the field is inherited, FALSE otherwise.
*/
public function isInheritedField($fieldName)
{
return isset($this->fieldMappings[$fieldName]['inherited']);
}
/**
* Check if this entity is the root in any entity-inheritance-hierachy.
*
* @return bool
*/
public function isRootEntity()
{
return $this->name == $this->rootEntityName;
}
/**
* Checks whether a mapped association field is inherited from a superclass.
*
* @param string $fieldName
* @return boolean TRUE if the field is inherited, FALSE otherwise.
*/
public function isInheritedAssociation($fieldName)
{
return isset($this->associationMappings[$fieldName]['inherited']);
}
/**
* Sets the name of the primary table the class is mapped to.
*
* @param string $tableName The table name.
* @deprecated Use {@link setPrimaryTable}.
*/
public function setTableName($tableName)
{
$this->table['name'] = $tableName;
}
/**
* Sets the primary table definition. The provided array supports the
* following structure:
*
* name => (optional, defaults to class name)
* indexes => array of indexes (optional)
* uniqueConstraints => array of constraints (optional)
*
* If a key is omitted, the current value is kept.
*
* @param array $table The table description.
*/
public function setPrimaryTable(array $table)
{
if (isset($table['name'])) {
if ($table['name'][0] === '`') {
$table['name'] = trim($table['name'], '`');
$this->table['quoted'] = true;
}
$this->table['name'] = $table['name'];
}
if (isset($table['indexes'])) {
$this->table['indexes'] = $table['indexes'];
}
if (isset($table['uniqueConstraints'])) {
$this->table['uniqueConstraints'] = $table['uniqueConstraints'];
}
if (isset($table['options'])) {
$this->table['options'] = $table['options'];
}
}
/**
* Checks whether the given type identifies an inheritance type.
*
* @param integer $type
* @return boolean TRUE if the given type identifies an inheritance type, FALSe otherwise.
*/
private function _isInheritanceType($type)
{
return $type == self::INHERITANCE_TYPE_NONE ||
$type == self::INHERITANCE_TYPE_SINGLE_TABLE ||
$type == self::INHERITANCE_TYPE_JOINED ||
$type == self::INHERITANCE_TYPE_TABLE_PER_CLASS;
}
/**
* Adds a mapped field to the class.
*
* @param array $mapping The field mapping.
* @throws MappingException
* @return void
*/
public function mapField(array $mapping)
{
$this->_validateAndCompleteFieldMapping($mapping);
if (isset($this->fieldMappings[$mapping['fieldName']]) || isset($this->associationMappings[$mapping['fieldName']])) {
throw MappingException::duplicateFieldMapping($this->name, $mapping['fieldName']);
}
$this->fieldMappings[$mapping['fieldName']] = $mapping;
}
/**
* INTERNAL:
* Adds an association mapping without completing/validating it.
* This is mainly used to add inherited association mappings to derived classes.
*
* @param array $mapping
* @throws MappingException
* @return void
*/
public function addInheritedAssociationMapping(array $mapping/*, $owningClassName = null*/)
{
if (isset($this->associationMappings[$mapping['fieldName']])) {
throw MappingException::duplicateAssociationMapping($this->name, $mapping['fieldName']);
}
$this->associationMappings[$mapping['fieldName']] = $mapping;
}
/**
* INTERNAL:
* Adds a field mapping without completing/validating it.
* This is mainly used to add inherited field mappings to derived classes.
*
* @param array $fieldMapping
* @return void
*/
public function addInheritedFieldMapping(array $fieldMapping)
{
$this->fieldMappings[$fieldMapping['fieldName']] = $fieldMapping;
$this->columnNames[$fieldMapping['fieldName']] = $fieldMapping['columnName'];
$this->fieldNames[$fieldMapping['columnName']] = $fieldMapping['fieldName'];
}
/**
* INTERNAL:
* Adds a named query to this class.
*
* @throws MappingException
* @param array $queryMapping
*/
public function addNamedQuery(array $queryMapping)
{
if (!isset($queryMapping['name'])) {
throw MappingException::nameIsMandatoryForQueryMapping($this->name);
}
if (isset($this->namedQueries[$queryMapping['name']])) {
throw MappingException::duplicateQueryMapping($this->name, $queryMapping['name']);
}
if (!isset($queryMapping['query'])) {
throw MappingException::emptyQueryMapping($this->name, $queryMapping['name']);
}
$name = $queryMapping['name'];
$query = $queryMapping['query'];
$dql = str_replace('__CLASS__', $this->name, $query);
$this->namedQueries[$name] = array(
'name' => $name,
'query' => $query,
'dql' => $dql
);
}
/**
* INTERNAL:
* Adds a named native query to this class.
*
* @throws MappingException
* @param array $queryMapping
*/
public function addNamedNativeQuery(array $queryMapping)
{
if (!isset($queryMapping['name'])) {
throw MappingException::nameIsMandatoryForQueryMapping($this->name);
}
if (isset($this->namedNativeQueries[$queryMapping['name']])) {
throw MappingException::duplicateQueryMapping($this->name, $queryMapping['name']);
}
if (!isset($queryMapping['query'])) {
throw MappingException::emptyQueryMapping($this->name, $queryMapping['name']);
}
if (!isset($queryMapping['resultClass']) && !isset($queryMapping['resultSetMapping'])) {
throw MappingException::missingQueryMapping($this->name, $queryMapping['name']);
}
$queryMapping['isSelfClass'] = false;
if (isset($queryMapping['resultClass'])) {
if($queryMapping['resultClass'] === '__CLASS__') {
$queryMapping['isSelfClass'] = true;
$queryMapping['resultClass'] = $this->name;
} else if (strlen($this->namespace) > 0 && strpos($queryMapping['resultClass'], '\\') === false) {
$queryMapping['resultClass'] = $this->namespace . '\\' . $queryMapping['resultClass'];
}
$queryMapping['resultClass'] = ltrim($queryMapping['resultClass'], '\\');
}
$this->namedNativeQueries[$queryMapping['name']] = $queryMapping;
}
/**
* INTERNAL:
* Adds a sql result set mapping to this class.
*
* @throws MappingException
* @param array $resultMapping
*/
public function addSqlResultSetMapping(array $resultMapping)
{
if (!isset($resultMapping['name'])) {
throw MappingException::nameIsMandatoryForSqlResultSetMapping($this->name);
}
if (isset($this->sqlResultSetMappings[$resultMapping['name']])) {
throw MappingException::duplicateResultSetMapping($this->name, $resultMapping['name']);
}
if (isset($resultMapping['entities'])) {
foreach ($resultMapping['entities'] as $key => $entityResult) {
if (!isset($entityResult['entityClass'])) {
throw MappingException::missingResultSetMappingEntity($this->name, $resultMapping['name']);
}
$entityResult['isSelfClass'] = false;
if($entityResult['entityClass'] === '__CLASS__') {
$entityResult['isSelfClass'] = true;
$entityResult['entityClass'] = $this->name;
} else if (strlen($this->namespace) > 0 && strpos($entityResult['entityClass'], '\\') === false) {
$entityResult['entityClass'] = $this->namespace . '\\' . $entityResult['entityClass'];
}
$resultMapping['entities'][$key]['entityClass'] = ltrim($entityResult['entityClass'], '\\');
$resultMapping['entities'][$key]['isSelfClass'] = $entityResult['isSelfClass'];
if (isset($entityResult['fields'])) {
foreach ($entityResult['fields'] as $k => $field) {
if (!isset($field['name'])) {
throw MappingException::missingResultSetMappingFieldName($this->name, $resultMapping['name']);
}
if (!isset($field['column'])) {
$fieldName = $field['name'];
if(strpos($fieldName, '.')){
list(, $fieldName) = explode('.', $fieldName);
}
$resultMapping['entities'][$key]['fields'][$k]['column'] = $fieldName;
}
}
}
}
}
$this->sqlResultSetMappings[$resultMapping['name']] = $resultMapping;
}
/**
* Adds a one-to-one mapping.
*
* @param array $mapping The mapping.
*/
public function mapOneToOne(array $mapping)
{
$mapping['type'] = self::ONE_TO_ONE;
$mapping = $this->_validateAndCompleteOneToOneMapping($mapping);
$this->_storeAssociationMapping($mapping);
}
/**
* Adds a one-to-many mapping.
*
* @param array $mapping The mapping.
*/
public function mapOneToMany(array $mapping)
{
$mapping['type'] = self::ONE_TO_MANY;
$mapping = $this->_validateAndCompleteOneToManyMapping($mapping);
$this->_storeAssociationMapping($mapping);
}
/**
* Adds a many-to-one mapping.
*
* @param array $mapping The mapping.
*/
public function mapManyToOne(array $mapping)
{
$mapping['type'] = self::MANY_TO_ONE;
// A many-to-one mapping is essentially a one-one backreference
$mapping = $this->_validateAndCompleteOneToOneMapping($mapping);
$this->_storeAssociationMapping($mapping);
}
/**
* Adds a many-to-many mapping.
*
* @param array $mapping The mapping.
*/
public function mapManyToMany(array $mapping)
{
$mapping['type'] = self::MANY_TO_MANY;
$mapping = $this->_validateAndCompleteManyToManyMapping($mapping);
$this->_storeAssociationMapping($mapping);
}
/**
* Stores the association mapping.
*
* @param array $assocMapping
* @throws MappingException
* @return void
*/
protected function _storeAssociationMapping(array $assocMapping)
{
$sourceFieldName = $assocMapping['fieldName'];
if (isset($this->fieldMappings[$sourceFieldName]) || isset($this->associationMappings[$sourceFieldName])) {
throw MappingException::duplicateFieldMapping($this->name, $sourceFieldName);
}
$this->associationMappings[$sourceFieldName] = $assocMapping;
}
/**
* Registers a custom repository class for the entity class.
*
* @param string $repositoryClassName The class name of the custom mapper.
* @return void
*/
public function setCustomRepositoryClass($repositoryClassName)
{
if ($repositoryClassName !== null && strpos($repositoryClassName, '\\') === false
&& strlen($this->namespace) > 0) {
$repositoryClassName = $this->namespace . '\\' . $repositoryClassName;
}
$this->customRepositoryClassName = $repositoryClassName;
}
/**
* Dispatches the lifecycle event of the given entity to the registered
* lifecycle callbacks and lifecycle listeners.
*
* @param string $lifecycleEvent The lifecycle event.
* @param \Object $entity The Entity on which the event occured.
*/
public function invokeLifecycleCallbacks($lifecycleEvent, $entity)
{
foreach ($this->lifecycleCallbacks[$lifecycleEvent] as $callback) {
$entity->$callback();
}
}
/**
* Whether the class has any attached lifecycle listeners or callbacks for a lifecycle event.
*
* @param string $lifecycleEvent
* @return boolean
*/
public function hasLifecycleCallbacks($lifecycleEvent)
{
return isset($this->lifecycleCallbacks[$lifecycleEvent]);
}
/**
* Gets the registered lifecycle callbacks for an event.
*
* @param string $event
* @return array
*/
public function getLifecycleCallbacks($event)
{
return isset($this->lifecycleCallbacks[$event]) ? $this->lifecycleCallbacks[$event] : array();
}
/**
* Adds a lifecycle callback for entities of this class.
*
* @param string $callback
* @param string $event
*/
public function addLifecycleCallback($callback, $event)
{
$this->lifecycleCallbacks[$event][] = $callback;
}
/**
* Sets the lifecycle callbacks for entities of this class.
* Any previously registered callbacks are overwritten.
*
* @param array $callbacks
*/
public function setLifecycleCallbacks(array $callbacks)
{
$this->lifecycleCallbacks = $callbacks;
}
/**
* Sets the discriminator column definition.
*
* @param array $columnDef
*
* @param $columnDef
* @throws MappingException
* @return void
* @see getDiscriminatorColumn()
*/
public function setDiscriminatorColumn($columnDef)
{
if ($columnDef !== null) {
if ( ! isset($columnDef['name'])) {
throw MappingException::nameIsMandatoryForDiscriminatorColumns($this->name);
}
if (isset($this->fieldNames[$columnDef['name']])) {
throw MappingException::duplicateColumnName($this->name, $columnDef['name']);
}
if ( ! isset($columnDef['fieldName'])) {
$columnDef['fieldName'] = $columnDef['name'];
}
if ( ! isset($columnDef['type'])) {
$columnDef['type'] = "string";
}
if (in_array($columnDef['type'], array("boolean", "array", "object", "datetime", "time", "date"))) {
throw MappingException::invalidDiscriminatorColumnType($this->name, $columnDef['type']);
}
$this->discriminatorColumn = $columnDef;
}
}
/**
* Sets the discriminator values used by this class.
* Used for JOINED and SINGLE_TABLE inheritance mapping strategies.
*
* @param array $map
*/
public function setDiscriminatorMap(array $map)
{
foreach ($map as $value => $className) {
$this->addDiscriminatorMapClass($value, $className);
}
}
/**
* Add one entry of the discriminator map with a new class and corresponding name.
*
* @param string $name
* @param string $className
* @throws MappingException
* @return void
*/
public function addDiscriminatorMapClass($name, $className)
{
if (strlen($this->namespace) > 0 && strpos($className, '\\') === false) {
$className = $this->namespace . '\\' . $className;
}
$className = ltrim($className, '\\');
$this->discriminatorMap[$name] = $className;
if ($this->name == $className) {
$this->discriminatorValue = $name;
} else {
if ( ! class_exists($className)) {
throw MappingException::invalidClassInDiscriminatorMap($className, $this->name);
}
if (is_subclass_of($className, $this->name) && ! in_array($className, $this->subClasses)) {
$this->subClasses[] = $className;
}
}
}
/**
* Checks whether the class has a named query with the given query name.
*
* @param string $queryName
* @return boolean
*/
public function hasNamedQuery($queryName)
{
return isset($this->namedQueries[$queryName]);
}
/**
* Checks whether the class has a named native query with the given query name.
*
* @param string $queryName
* @return boolean
*/
public function hasNamedNativeQuery($queryName)
{
return isset($this->namedNativeQueries[$queryName]);
}
/**
* Checks whether the class has a named native query with the given query name.
*
* @param string $name
* @return boolean
*/
public function hasSqlResultSetMapping($name)
{
return isset($this->sqlResultSetMappings[$name]);
}
/**
* {@inheritDoc}
*/
public function hasAssociation($fieldName)
{
return isset($this->associationMappings[$fieldName]);
}
/**
* {@inheritDoc}
*/
public function isSingleValuedAssociation($fieldName)
{
return isset($this->associationMappings[$fieldName]) &&
($this->associationMappings[$fieldName]['type'] & self::TO_ONE);
}
/**
* {@inheritDoc}
*/
public function isCollectionValuedAssociation($fieldName)
{
return isset($this->associationMappings[$fieldName]) &&
! ($this->associationMappings[$fieldName]['type'] & self::TO_ONE);
}
/**
* Is this an association that only has a single join column?
*
* @param string $fieldName
* @return bool
*/
public function isAssociationWithSingleJoinColumn($fieldName)
{
return (
isset($this->associationMappings[$fieldName]) &&
isset($this->associationMappings[$fieldName]['joinColumns'][0]) &&
!isset($this->associationMappings[$fieldName]['joinColumns'][1])
);
}
/**
* Return the single association join column (if any).
*
* @param string $fieldName
* @throws MappingException
* @return string
*/
public function getSingleAssociationJoinColumnName($fieldName)
{
if ( ! $this->isAssociationWithSingleJoinColumn($fieldName)) {
throw MappingException::noSingleAssociationJoinColumnFound($this->name, $fieldName);
}
return $this->associationMappings[$fieldName]['joinColumns'][0]['name'];
}
/**
* Return the single association referenced join column name (if any).
*
* @param string $fieldName
* @throws MappingException
* @return string
*/
public function getSingleAssociationReferencedJoinColumnName($fieldName)
{
if ( ! $this->isAssociationWithSingleJoinColumn($fieldName)) {
throw MappingException::noSingleAssociationJoinColumnFound($this->name, $fieldName);
}
return $this->associationMappings[$fieldName]['joinColumns'][0]['referencedColumnName'];
}
/**
* Used to retrieve a fieldname for either field or association from a given column,
*
* This method is used in foreign-key as primary-key contexts.
*
* @param string $columnName
* @throws MappingException
* @return string
*/
public function getFieldForColumn($columnName)
{
if (isset($this->fieldNames[$columnName])) {
return $this->fieldNames[$columnName];
} else {
foreach ($this->associationMappings as $assocName => $mapping) {
if ($this->isAssociationWithSingleJoinColumn($assocName) &&
$this->associationMappings[$assocName]['joinColumns'][0]['name'] == $columnName) {
return $assocName;
}
}
throw MappingException::noFieldNameFoundForColumn($this->name, $columnName);
}
}
/**
* Sets the ID generator used to generate IDs for instances of this class.
*
* @param \Doctrine\ORM\Id\AbstractIdGenerator $generator
*/
public function setIdGenerator($generator)
{
$this->idGenerator = $generator;
}
/**
* Sets definition
* @param array $definition
*/
public function setCustomGeneratorDefinition(array $definition)
{
$this->customGeneratorDefinition = $definition;
}
/**
* Sets the definition of the sequence ID generator for this class.
*
* The definition must have the following structure:
*
* array(
* 'sequenceName' => 'name',
* 'allocationSize' => 20,
* 'initialValue' => 1
* 'quoted' => 1
* )
*
*
* @param array $definition
*/
public function setSequenceGeneratorDefinition(array $definition)
{
if (isset($definition['name']) && $definition['name'] == '`') {
$definition['name'] = trim($definition['name'], '`');
$definition['quoted'] = true;
}
$this->sequenceGeneratorDefinition = $definition;
}
/**
* Sets the version field mapping used for versioning. Sets the default
* value to use depending on the column type.
*
* @param array $mapping The version field mapping array
* @throws MappingException
* @return void
*/
public function setVersionMapping(array &$mapping)
{
$this->isVersioned = true;
$this->versionField = $mapping['fieldName'];
if ( ! isset($mapping['default'])) {
if (in_array($mapping['type'], array('integer', 'bigint', 'smallint'))) {
$mapping['default'] = 1;
} else if ($mapping['type'] == 'datetime') {
$mapping['default'] = 'CURRENT_TIMESTAMP';
} else {
throw MappingException::unsupportedOptimisticLockingType($this->name, $mapping['fieldName'], $mapping['type']);
}
}
}
/**
* Sets whether this class is to be versioned for optimistic locking.
*
* @param boolean $bool
*/
public function setVersioned($bool)
{
$this->isVersioned = $bool;
}
/**
* Sets the name of the field that is to be used for versioning if this class is
* versioned for optimistic locking.
*
* @param string $versionField
*/
public function setVersionField($versionField)
{
$this->versionField = $versionField;
}
/**
* Mark this class as read only, no change tracking is applied to it.
*
* @return void
*/
public function markReadOnly()
{
$this->isReadOnly = true;
}
/**
* {@inheritDoc}
*/
public function getFieldNames()
{
return array_keys($this->fieldMappings);
}
/**
* {@inheritDoc}
*/
public function getAssociationNames()
{
return array_keys($this->associationMappings);
}
/**
* {@inheritDoc}
* @throws InvalidArgumentException
*/
public function getAssociationTargetClass($assocName)
{
if ( ! isset($this->associationMappings[$assocName])) {
throw new InvalidArgumentException("Association name expected, '" . $assocName ."' is not an association.");
}
return $this->associationMappings[$assocName]['targetEntity'];
}
/**
* {@inheritDoc}
*/
public function getName()
{
return $this->name;
}
/**
* Gets the (possibly quoted) identifier column names for safe use in an SQL statement.
*
* @deprecated Deprecated since version 2.3 in favor of \Doctrine\ORM\Mapping\QuoteStrategy
*
* @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform
* @return array
*/
public function getQuotedIdentifierColumnNames($platform)
{
$quotedColumnNames = array();
foreach ($this->identifier as $idProperty) {
if (isset($this->fieldMappings[$idProperty])) {
$quotedColumnNames[] = isset($this->fieldMappings[$idProperty]['quoted'])
? $platform->quoteIdentifier($this->fieldMappings[$idProperty]['columnName'])
: $this->fieldMappings[$idProperty]['columnName'];
continue;
}
// Association defined as Id field
$joinColumns = $this->associationMappings[$idProperty]['joinColumns'];
$assocQuotedColumnNames = array_map(
function ($joinColumn) use ($platform) {
return isset($joinColumn['quoted'])
? $platform->quoteIdentifier($joinColumn['name'])
: $joinColumn['name'];
},
$joinColumns
);
$quotedColumnNames = array_merge($quotedColumnNames, $assocQuotedColumnNames);
}
return $quotedColumnNames;
}
/**
* Gets the (possibly quoted) column name of a mapped field for safe use in an SQL statement.
*
* @deprecated Deprecated since version 2.3 in favor of \Doctrine\ORM\Mapping\QuoteStrategy
*
* @param string $field
* @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform
* @return string
*/
public function getQuotedColumnName($field, $platform)
{
return isset($this->fieldMappings[$field]['quoted'])
? $platform->quoteIdentifier($this->fieldMappings[$field]['columnName'])
: $this->fieldMappings[$field]['columnName'];
}
/**
* Gets the (possibly quoted) primary table name of this class for safe use in an SQL statement.
*
* @deprecated Deprecated since version 2.3 in favor of \Doctrine\ORM\Mapping\QuoteStrategy
*
* @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform
* @return string
*/
public function getQuotedTableName($platform)
{
return isset($this->table['quoted']) ? $platform->quoteIdentifier($this->table['name']) : $this->table['name'];
}
/**
* Gets the (possibly quoted) name of the join table.
*
* @deprecated Deprecated since version 2.3 in favor of \Doctrine\ORM\Mapping\QuoteStrategy
*
* @param array $assoc
* @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform
* @return string
*/
public function getQuotedJoinTableName(array $assoc, $platform)
{
return isset($assoc['joinTable']['quoted']) ? $platform->quoteIdentifier($assoc['joinTable']['name']) : $assoc['joinTable']['name'];
}
/**
* {@inheritDoc}
*/
public function isAssociationInverseSide($fieldName)
{
return isset($this->associationMappings[$fieldName]) && ! $this->associationMappings[$fieldName]['isOwningSide'];
}
/**
* {@inheritDoc}
*/
public function getAssociationMappedByTargetField($fieldName)
{
return $this->associationMappings[$fieldName]['mappedBy'];
}
/**
* @param string $targetClass
* @return array
*/
public function getAssociationsByTargetClass($targetClass)
{
$relations = array();
foreach ($this->associationMappings as $mapping) {
if ($mapping['targetEntity'] == $targetClass) {
$relations[$mapping['fieldName']] = $mapping;
}
}
return $relations;
}
}
DoctrineORM-2.3.3/Doctrine/ORM/Mapping/Column.php 0000644 0001751 0001751 00000003322 12143374607 021526 0 ustar beberlei beberlei .
*/
namespace Doctrine\ORM\Mapping;
/**
* @Annotation
* @Target({"PROPERTY","ANNOTATION"})
*/
final class Column implements Annotation
{
/** @var string */
public $name;
/** @var mixed */
public $type = 'string';
/** @var integer */
public $length;
/** @var integer */
public $precision = 0; // The precision for a decimal (exact numeric) column (Applies only for decimal column)
/** @var integer */
public $scale = 0; // The scale for a decimal (exact numeric) column (Applies only for decimal column)
/** @var boolean */
public $unique = false;
/** @var boolean */
public $nullable = false;
/** @var array */
public $options = array();
/** @var string */
public $columnDefinition;
}
DoctrineORM-2.3.3/Doctrine/ORM/Mapping/ColumnResult.php 0000644 0001751 0001751 00000002732 12143374607 022731 0 ustar beberlei beberlei .
*/
namespace Doctrine\ORM\Mapping;
/**
* References name of a column in the SELECT clause of a SQL query.
* Scalar result types can be included in the query result by specifying this annotation in the metadata.
*
* @author Fabio B. Silva
* @since 2.3
*
* @Annotation
* @Target("ANNOTATION")
*/
final class ColumnResult implements Annotation
{
/**
* The name of a column in the SELECT clause of a SQL query
*
* @var string
*/
public $name;
}
DoctrineORM-2.3.3/Doctrine/ORM/Mapping/CustomIdGenerator.php 0000644 0001751 0001751 00000002216 12143374607 023670 0 ustar beberlei beberlei .
*/
namespace Doctrine\ORM\Mapping;
/**
* @Annotation
* @Target("PROPERTY")
*/
final class CustomIdGenerator implements Annotation
{
/** @var string */
public $class;
}
DoctrineORM-2.3.3/Doctrine/ORM/Mapping/DefaultNamingStrategy.php 0000644 0001751 0001751 00000004624 12143374607 024540 0 ustar beberlei beberlei .
*/
namespace Doctrine\ORM\Mapping;
/**
* The default NamingStrategy
*
*
* @link www.doctrine-project.org
* @since 2.3
* @author Fabio B. Silva
*/
class DefaultNamingStrategy implements NamingStrategy
{
/**
* {@inheritdoc}
*/
public function classToTableName($className)
{
if (strpos($className, '\\') !== false) {
return substr($className, strrpos($className, '\\') + 1);
}
return $className;
}
/**
* {@inheritdoc}
*/
public function propertyToColumnName($propertyName)
{
return $propertyName;
}
/**
* {@inheritdoc}
*/
public function referenceColumnName()
{
return 'id';
}
/**
* {@inheritdoc}
*/
public function joinColumnName($propertyName)
{
return $propertyName . '_' . $this->referenceColumnName();
}
/**
* {@inheritdoc}
*/
public function joinTableName($sourceEntity, $targetEntity, $propertyName = null)
{
return strtolower($this->classToTableName($sourceEntity) . '_' .
$this->classToTableName($targetEntity));
}
/**
* {@inheritdoc}
*/
public function joinKeyColumnName($entityName, $referencedColumnName = null)
{
return strtolower($this->classToTableName($entityName) . '_' .
($referencedColumnName ?: $this->referenceColumnName()));
}
}
DoctrineORM-2.3.3/Doctrine/ORM/Mapping/DefaultQuoteStrategy.php 0000644 0001751 0001751 00000011547 12143374607 024426 0 ustar beberlei beberlei .
*/
namespace Doctrine\ORM\Mapping;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\DBAL\Platforms\AbstractPlatform;
/**
* A set of rules for determining the physical column, alias and table quotes
*
* @since 2.3
* @author Fabio B. Silva
*/
class DefaultQuoteStrategy implements QuoteStrategy
{
/**
* {@inheritdoc}
*/
public function getColumnName($fieldName, ClassMetadata $class, AbstractPlatform $platform)
{
return isset($class->fieldMappings[$fieldName]['quoted'])
? $platform->quoteIdentifier($class->fieldMappings[$fieldName]['columnName'])
: $class->fieldMappings[$fieldName]['columnName'];
}
/**
* {@inheritdoc}
*/
public function getTableName(ClassMetadata $class, AbstractPlatform $platform)
{
return isset($class->table['quoted'])
? $platform->quoteIdentifier($class->table['name'])
: $class->table['name'];
}
/**
* {@inheritdoc}
*/
public function getSequenceName(array $definition, ClassMetadata $class, AbstractPlatform $platform)
{
return isset($definition['quoted'])
? $platform->quoteIdentifier($definition['sequenceName'])
: $definition['sequenceName'];
}
/**
* {@inheritdoc}
*/
public function getJoinColumnName(array $joinColumn, ClassMetadata $class, AbstractPlatform $platform)
{
return isset($joinColumn['quoted'])
? $platform->quoteIdentifier($joinColumn['name'])
: $joinColumn['name'];
}
/**
* {@inheritdoc}
*/
public function getReferencedJoinColumnName(array $joinColumn, ClassMetadata $class, AbstractPlatform $platform)
{
return isset($joinColumn['quoted'])
? $platform->quoteIdentifier($joinColumn['referencedColumnName'])
: $joinColumn['referencedColumnName'];
}
/**
* {@inheritdoc}
*/
public function getJoinTableName(array $association, ClassMetadata $class, AbstractPlatform $platform)
{
return isset($association['joinTable']['quoted'])
? $platform->quoteIdentifier($association['joinTable']['name'])
: $association['joinTable']['name'];
}
/**
* {@inheritdoc}
*/
public function getIdentifierColumnNames(ClassMetadata $class, AbstractPlatform $platform)
{
$quotedColumnNames = array();
foreach ($class->identifier as $fieldName) {
if (isset($class->fieldMappings[$fieldName])) {
$quotedColumnNames[] = $this->getColumnName($fieldName, $class, $platform);
continue;
}
// Association defined as Id field
$joinColumns = $class->associationMappings[$fieldName]['joinColumns'];
$assocQuotedColumnNames = array_map(
function ($joinColumn) use ($platform)
{
return isset($joinColumn['quoted'])
? $platform->quoteIdentifier($joinColumn['name'])
: $joinColumn['name'];
},
$joinColumns
);
$quotedColumnNames = array_merge($quotedColumnNames, $assocQuotedColumnNames);
}
return $quotedColumnNames;
}
/**
* {@inheritdoc}
*/
public function getColumnAlias($columnName, $counter, AbstractPlatform $platform, ClassMetadata $class = null)
{
// Trim the column alias to the maximum identifier length of the platform.
// If the alias is to long, characters are cut off from the beginning.
// And strip non alphanumeric characters
$columnName = $columnName . $counter;
$columnName = substr($columnName, -$platform->getMaxIdentifierLength());
$columnName = preg_replace('/[^A-Za-z0-9_]/', '', $columnName);
return $platform->getSQLResultCasing($columnName);
}
} DoctrineORM-2.3.3/Doctrine/ORM/Mapping/DiscriminatorColumn.php 0000644 0001751 0001751 00000002575 12143374607 024267 0 ustar beberlei beberlei .
*/
namespace Doctrine\ORM\Mapping;
/**
* @Annotation
* @Target("CLASS")
*/
final class DiscriminatorColumn implements Annotation
{
/** @var string */
public $name;
/** @var string */
public $type;
/** @var integer */
public $length;
/** @var mixed */
public $fieldName; // field name used in non-object hydration (array/scalar)
/** @var string */
public $columnDefinition;
}
DoctrineORM-2.3.3/Doctrine/ORM/Mapping/DiscriminatorMap.php 0000644 0001751 0001751 00000002221 12143374607 023533 0 ustar beberlei beberlei .
*/
namespace Doctrine\ORM\Mapping;
/**
* @Annotation
* @Target("CLASS")
*/
final class DiscriminatorMap implements Annotation
{
/** @var array */
public $value;
}
DoctrineORM-2.3.3/Doctrine/ORM/Mapping/ElementCollection.php 0000644 0001751 0001751 00000002256 12143374607 023703 0 ustar beberlei beberlei .
*/
namespace Doctrine\ORM\Mapping;
/**
* @Annotation
* @Target("ALL")
* @todo check available targets
*/
final class ElementCollection implements Annotation
{
/** @var string */
public $tableName;
}
DoctrineORM-2.3.3/Doctrine/ORM/Mapping/Entity.php 0000644 0001751 0001751 00000002300 12143374607 021540 0 ustar beberlei beberlei .
*/
namespace Doctrine\ORM\Mapping;
/**
* @Annotation
* @Target("CLASS")
*/
final class Entity implements Annotation
{
/** @var string */
public $repositoryClass;
/** @var boolean */
public $readOnly = false;
}
DoctrineORM-2.3.3/Doctrine/ORM/Mapping/EntityResult.php 0000644 0001751 0001751 00000003771 12143374607 022754 0 ustar beberlei beberlei .
*/
namespace Doctrine\ORM\Mapping;
/**
* References an entity in the SELECT clause of a SQL query.
* If this annotation is used, the SQL statement should select all of the columns that are mapped to the entity object.
* This should include foreign key columns to related entities.
* The results obtained when insufficient data is available are undefined.
*
* @author Fabio B. Silva
* @since 2.3
*
* @Annotation
* @Target("ANNOTATION")
*/
final class EntityResult implements Annotation
{
/**
* The class of the result
*
* @var string
*/
public $entityClass;
/**
* Maps the columns specified in the SELECT list of the query to the properties or fields of the entity class.
*
* @var array<\Doctrine\ORM\Mapping\FieldResult>
*/
public $fields = array();
/**
* Specifies the column name of the column in the SELECT list that is used to determine the type of the entity instance.
*
* @var string
*/
public $discriminatorColumn;
}
DoctrineORM-2.3.3/Doctrine/ORM/Mapping/FieldResult.php 0000644 0001751 0001751 00000003022 12143374607 022510 0 ustar beberlei beberlei .
*/
namespace Doctrine\ORM\Mapping;
/**
* Is used to map the columns specified in the SELECT list of the query to the properties or fields of the entity class.
*
* @author Fabio B. Silva
* @since 2.3
*
* @Annotation
* @Target("ANNOTATION")
*/
final class FieldResult implements Annotation
{
/**
* Name of the column in the SELECT clause.
*
* @var string
*/
public $name;
/**
* Name of the persistent field or property of the class.
*
* @var string
*/
public $column;
}
DoctrineORM-2.3.3/Doctrine/ORM/Mapping/GeneratedValue.php 0000644 0001751 0001751 00000002230 12143374607 023161 0 ustar beberlei beberlei .
*/
namespace Doctrine\ORM\Mapping;
/**
* @Annotation
* @Target("PROPERTY")
*/
final class GeneratedValue implements Annotation
{
/** @var string */
public $strategy = 'AUTO';
}
DoctrineORM-2.3.3/Doctrine/ORM/Mapping/HasLifecycleCallbacks.php 0000644 0001751 0001751 00000002145 12143374607 024426 0 ustar beberlei beberlei .
*/
namespace Doctrine\ORM\Mapping;
/**
* @Annotation
* @Target("CLASS")
*/
final class HasLifecycleCallbacks implements Annotation
{
}
DoctrineORM-2.3.3/Doctrine/ORM/Mapping/Id.php 0000644 0001751 0001751 00000002125 12143374607 020625 0 ustar beberlei beberlei .
*/
namespace Doctrine\ORM\Mapping;
/**
* @Annotation
* @Target("PROPERTY")
*/
final class Id implements Annotation
{
}
DoctrineORM-2.3.3/Doctrine/ORM/Mapping/Index.php 0000644 0001751 0001751 00000002266 12143374607 021346 0 ustar beberlei beberlei .
*/
namespace Doctrine\ORM\Mapping;
/**
* @Annotation
* @Target("ANNOTATION")
*/
final class Index implements Annotation
{
/** @var string */
public $name;
/** @var array */
public $columns;
}
DoctrineORM-2.3.3/Doctrine/ORM/Mapping/InheritanceType.php 0000644 0001751 0001751 00000002211 12143374607 023360 0 ustar beberlei beberlei .
*/
namespace Doctrine\ORM\Mapping;
/**
* @Annotation
* @Target("CLASS")
*/
final class InheritanceType implements Annotation
{
/** @var string */
public $value;
}
DoctrineORM-2.3.3/Doctrine/ORM/Mapping/JoinColumn.php 0000644 0001751 0001751 00000003007 12143374607 022346 0 ustar beberlei beberlei .
*/
namespace Doctrine\ORM\Mapping;
/**
* @Annotation
* @Target({"PROPERTY","ANNOTATION"})
*/
final class JoinColumn implements Annotation
{
/** @var string */
public $name;
/** @var string */
public $referencedColumnName = 'id';
/** @var boolean */
public $unique = false;
/** @var boolean */
public $nullable = true;
/** @var mixed */
public $onDelete;
/** @var string */
public $columnDefinition;
/** @var string */
public $fieldName; // field name used in non-object hydration (array/scalar)
}
DoctrineORM-2.3.3/Doctrine/ORM/Mapping/JoinColumns.php 0000644 0001751 0001751 00000002251 12143374607 022531 0 ustar beberlei beberlei .
*/
namespace Doctrine\ORM\Mapping;
/**
* @Annotation
* @Target("PROPERTY")
*/
final class JoinColumns implements Annotation
{
/** @var array<\Doctrine\ORM\Mapping\JoinColumn> */
public $value;
}
DoctrineORM-2.3.3/Doctrine/ORM/Mapping/JoinTable.php 0000644 0001751 0001751 00000002574 12143374607 022150 0 ustar beberlei beberlei .
*/
namespace Doctrine\ORM\Mapping;
/**
* @Annotation
* @Target({"PROPERTY","ANNOTATION"})
*/
final class JoinTable implements Annotation
{
/** @var string */
public $name;
/** @var string */
public $schema;
/** @var array<\Doctrine\ORM\Mapping\JoinColumn> */
public $joinColumns = array();
/** @var array<\Doctrine\ORM\Mapping\JoinColumn> */
public $inverseJoinColumns = array();
}
DoctrineORM-2.3.3/Doctrine/ORM/Mapping/ManyToMany.php 0000644 0001751 0001751 00000002667 12143374607 022340 0 ustar beberlei beberlei .
*/
namespace Doctrine\ORM\Mapping;
/**
* @Annotation
* @Target("PROPERTY")
*/
final class ManyToMany implements Annotation
{
/** @var string */
public $targetEntity;
/** @var string */
public $mappedBy;
/** @var string */
public $inversedBy;
/** @var array */
public $cascade;
/** @var string */
public $fetch = 'LAZY';
/** @var boolean */
public $orphanRemoval = false;
/** @var string */
public $indexBy;
}
DoctrineORM-2.3.3/Doctrine/ORM/Mapping/ManyToOne.php 0000644 0001751 0001751 00000002442 12143374607 022144 0 ustar beberlei beberlei .
*/
namespace Doctrine\ORM\Mapping;
/**
* @Annotation
* @Target("PROPERTY")
*/
final class ManyToOne implements Annotation
{
/** @var string */
public $targetEntity;
/** @var array */
public $cascade;
/** @var string */
public $fetch = 'LAZY';
/** @var string */
public $inversedBy;
}
DoctrineORM-2.3.3/Doctrine/ORM/Mapping/MappedSuperclass.php 0000644 0001751 0001751 00000002224 12143374607 023544 0 ustar beberlei beberlei .
*/
namespace Doctrine\ORM\Mapping;
/**
* @Annotation
* @Target("CLASS")
*/
final class MappedSuperclass implements Annotation
{
/** @var string */
public $repositoryClass;
}
DoctrineORM-2.3.3/Doctrine/ORM/Mapping/MappingException.php 0000644 0001751 0001751 00000041660 12143374607 023552 0 ustar beberlei beberlei .
*/
namespace Doctrine\ORM\Mapping;
/**
* A MappingException indicates that something is wrong with the mapping setup.
*
* @since 2.0
*/
class MappingException extends \Doctrine\ORM\ORMException
{
public static function pathRequired()
{
return new self("Specifying the paths to your entities is required ".
"in the AnnotationDriver to retrieve all class names.");
}
public static function identifierRequired($entityName)
{
if (false !== ($parent = get_parent_class($entityName))) {
return new self(sprintf(
'No identifier/primary key specified for Entity "%s" sub class of "%s". Every Entity must have an identifier/primary key.',
$entityName, $parent
));
}
return new self(sprintf(
'No identifier/primary key specified for Entity "%s". Every Entity must have an identifier/primary key.',
$entityName
));
}
public static function invalidInheritanceType($entityName, $type)
{
return new self("The inheritance type '$type' specified for '$entityName' does not exist.");
}
public static function generatorNotAllowedWithCompositeId()
{
return new self("Id generators can't be used with a composite id.");
}
public static function missingFieldName($entity)
{
return new self("The field or association mapping misses the 'fieldName' attribute in entity '$entity'.");
}
public static function missingTargetEntity($fieldName)
{
return new self("The association mapping '$fieldName' misses the 'targetEntity' attribute.");
}
public static function missingSourceEntity($fieldName)
{
return new self("The association mapping '$fieldName' misses the 'sourceEntity' attribute.");
}
public static function mappingFileNotFound($entityName, $fileName)
{
return new self("No mapping file found named '$fileName' for class '$entityName'.");
}
/**
* Exception for invalid property name override.
*
* @param string $className The entity's name
* @param string $fieldName
*/
public static function invalidOverrideFieldName($className, $fieldName)
{
return new self("Invalid field override named '$fieldName' for class '$className'.");
}
/**
* Exception for invalid property type override.
*
* @param string $className The entity's name
* @param string $fieldName
*/
public static function invalidOverrideFieldType($className, $fieldName)
{
return new self("The column type of attribute '$fieldName' on class '$className' could not be changed.");
}
public static function mappingNotFound($className, $fieldName)
{
return new self("No mapping found for field '$fieldName' on class '$className'.");
}
public static function queryNotFound($className, $queryName)
{
return new self("No query found named '$queryName' on class '$className'.");
}
public static function resultMappingNotFound($className, $resultName)
{
return new self("No result set mapping found named '$resultName' on class '$className'.");
}
public static function emptyQueryMapping($entity, $queryName)
{
return new self('Query named "'.$queryName.'" in "'.$entity.'" could not be empty.');
}
public static function nameIsMandatoryForQueryMapping($className)
{
return new self("Query name on entity class '$className' is not defined.");
}
public static function missingQueryMapping($entity, $queryName)
{
return new self('Query named "'.$queryName.'" in "'.$entity.' requires a result class or result set mapping.');
}
public static function missingResultSetMappingEntity($entity, $resultName)
{
return new self('Result set mapping named "'.$resultName.'" in "'.$entity.' requires a entity class name.');
}
public static function missingResultSetMappingFieldName($entity, $resultName)
{
return new self('Result set mapping named "'.$resultName.'" in "'.$entity.' requires a field name.');
}
public static function nameIsMandatoryForSqlResultSetMapping($className)
{
return new self("Result set mapping name on entity class '$className' is not defined.");
}
public static function oneToManyRequiresMappedBy($fieldName)
{
return new self("OneToMany mapping on field '$fieldName' requires the 'mappedBy' attribute.");
}
public static function joinTableRequired($fieldName)
{
return new self("The mapping of field '$fieldName' requires an the 'joinTable' attribute.");
}
/**
* Called if a required option was not found but is required
*
* @param string $field which field cannot be processed?
* @param string $expectedOption which option is required
* @param string $hint Can optionally be used to supply a tip for common mistakes,
* e.g. "Did you think of the plural s?"
* @return MappingException
*/
static function missingRequiredOption($field, $expectedOption, $hint = '')
{
$message = "The mapping of field '{$field}' is invalid: The option '{$expectedOption}' is required.";
if ( ! empty($hint)) {
$message .= ' (Hint: ' . $hint . ')';
}
return new self($message);
}
/**
* Generic exception for invalid mappings.
*
* @param string $fieldName
*/
public static function invalidMapping($fieldName)
{
return new self("The mapping of field '$fieldName' is invalid.");
}
/**
* Exception for reflection exceptions - adds the entity name,
* because there might be long classnames that will be shortened
* within the stacktrace
*
* @param string $entity The entity's name
* @param \ReflectionException $previousException
*/
public static function reflectionFailure($entity, \ReflectionException $previousException)
{
return new self('An error occurred in ' . $entity, 0, $previousException);
}
public static function joinColumnMustPointToMappedField($className, $joinColumn)
{
return new self('The column ' . $joinColumn . ' must be mapped to a field in class '
. $className . ' since it is referenced by a join column of another class.');
}
public static function classIsNotAValidEntityOrMappedSuperClass($className)
{
if (false !== ($parent = get_parent_class($className))) {
return new self(sprintf(
'Class "%s" sub class of "%s" is not a valid entity or mapped super class.',
$className, $parent
));
}
return new self(sprintf(
'Class "%s" is not a valid entity or mapped super class.',
$className
));
}
public static function propertyTypeIsRequired($className, $propertyName)
{
return new self("The attribute 'type' is required for the column description of property ".$className."::\$".$propertyName.".");
}
public static function tableIdGeneratorNotImplemented($className)
{
return new self("TableIdGenerator is not yet implemented for use with class ".$className);
}
/**
* @param string $entity The entity's name
* @param string $fieldName The name of the field that was already declared
*/
public static function duplicateFieldMapping($entity, $fieldName)
{
return new self('Property "'.$fieldName.'" in "'.$entity.'" was already declared, but it must be declared only once');
}
public static function duplicateAssociationMapping($entity, $fieldName)
{
return new self('Property "'.$fieldName.'" in "'.$entity.'" was already declared, but it must be declared only once');
}
public static function duplicateQueryMapping($entity, $queryName)
{
return new self('Query named "'.$queryName.'" in "'.$entity.'" was already declared, but it must be declared only once');
}
public static function duplicateResultSetMapping($entity, $resultName)
{
return new self('Result set mapping named "'.$resultName.'" in "'.$entity.'" was already declared, but it must be declared only once');
}
public static function singleIdNotAllowedOnCompositePrimaryKey($entity)
{
return new self('Single id is not allowed on composite primary key in entity '.$entity);
}
public static function unsupportedOptimisticLockingType($entity, $fieldName, $unsupportedType)
{
return new self('Locking type "'.$unsupportedType.'" (specified in "'.$entity.'", field "'.$fieldName.'") '
.'is not supported by Doctrine.'
);
}
public static function fileMappingDriversRequireConfiguredDirectoryPath($path = null)
{
if ( ! empty($path)) {
$path = '[' . $path . ']';
}
return new self(
'File mapping drivers must have a valid directory path, ' .
'however the given path ' . $path . ' seems to be incorrect!'
);
}
/**
* Throws an exception that indicates that a class used in a discriminator map does not exist.
* An example would be an outdated (maybe renamed) classname.
*
* @param string $className The class that could not be found
* @param string $owningClass The class that declares the discriminator map.
* @return self
*/
public static function invalidClassInDiscriminatorMap($className, $owningClass)
{
return new self(
"Entity class '$className' used in the discriminator map of class '$owningClass' ".
"does not exist."
);
}
public static function duplicateDiscriminatorEntry($className, array $entries, array $map)
{
return new self(
"The entries " . implode(', ', $entries) . " in discriminator map of class '" . $className . "' is duplicated. " .
"If the discriminator map is automatically generated you have to convert it to an explicit discriminator map now. " .
"The entries of the current map are: @DiscriminatorMap({" . implode(', ', array_map(
function($a, $b) { return "'$a': '$b'"; }, array_keys($map), array_values($map)
)) . "})"
);
}
public static function missingDiscriminatorMap($className)
{
return new self("Entity class '$className' is using inheritance but no discriminator map was defined.");
}
public static function missingDiscriminatorColumn($className)
{
return new self("Entity class '$className' is using inheritance but no discriminator column was defined.");
}
public static function invalidDiscriminatorColumnType($className, $type)
{
return new self("Discriminator column type on entity class '$className' is not allowed to be '$type'. 'string' or 'integer' type variables are suggested!");
}
public static function nameIsMandatoryForDiscriminatorColumns($className)
{
return new self("Discriminator column name on entity class '$className' is not defined.");
}
public static function cannotVersionIdField($className, $fieldName)
{
return new self("Setting Id field '$fieldName' as versionale in entity class '$className' is not supported.");
}
public static function sqlConversionNotAllowedForIdentifiers($className, $fieldName, $type)
{
return new self("It is not possible to set id field '$fieldName' to type '$type' in entity class '$className'. The type '$type' requires conversion SQL which is not allowed for identifiers.");
}
/**
* @param string $className
* @param string $columnName
* @return self
*/
public static function duplicateColumnName($className, $columnName)
{
return new self("Duplicate definition of column '".$columnName."' on entity '".$className."' in a field or discriminator column mapping.");
}
public static function illegalToManyAssocationOnMappedSuperclass($className, $field)
{
return new self("It is illegal to put an inverse side one-to-many or many-to-many association on mapped superclass '".$className."#".$field."'.");
}
/**
* @param string $className
* @param string $targetEntity
* @param string $targetField
* @return self
*/
public static function cannotMapCompositePrimaryKeyEntitiesAsForeignId($className, $targetEntity, $targetField)
{
return new self("It is not possible to map entity '".$className."' with a composite primary key ".
"as part of the primary key of another entity '".$targetEntity."#".$targetField."'.");
}
public static function noSingleAssociationJoinColumnFound($className, $field)
{
return new self("'$className#$field' is not an association with a single join column.");
}
public static function noFieldNameFoundForColumn($className, $column)
{
return new self("Cannot find a field on '$className' that is mapped to column '$column'. Either the ".
"field does not exist or an association exists but it has multiple join columns.");
}
public static function illegalOrphanRemovalOnIdentifierAssociation($className, $field)
{
return new self("The orphan removal option is not allowed on an association that is ".
"part of the identifier in '$className#$field'.");
}
public static function illegalOrphanRemoval($className, $field)
{
return new self("Orphan removal is only allowed on one-to-one and one-to-many ".
"associations, but " . $className."#" .$field . " is not.");
}
public static function illegalInverseIdentifierAssocation($className, $field)
{
return new self("An inverse association is not allowed to be identifier in '$className#$field'.");
}
public static function illegalToManyIdentifierAssoaction($className, $field)
{
return new self("Many-to-many or one-to-many associations are not allowed to be identifier in '$className#$field'.");
}
public static function noInheritanceOnMappedSuperClass($className)
{
return new self("Its not supported to define inheritance information on a mapped superclass '" . $className . "'.");
}
public static function mappedClassNotPartOfDiscriminatorMap($className, $rootClassName)
{
return new self(
"Entity '" . $className . "' has to be part of the discriminator map of '" . $rootClassName . "' " .
"to be properly mapped in the inheritance hierachy. Alternatively you can make '".$className."' an abstract class " .
"to avoid this exception from occuring."
);
}
public static function lifecycleCallbackMethodNotFound($className, $methodName)
{
return new self("Entity '" . $className . "' has no method '" . $methodName . "' to be registered as lifecycle callback.");
}
public static function invalidFetchMode($className, $annotation)
{
return new self("Entity '" . $className . "' has a mapping with invalid fetch mode '" . $annotation . "'");
}
public static function compositeKeyAssignedIdGeneratorRequired($className)
{
return new self("Entity '". $className . "' has a composite identifier but uses an ID generator other than manually assigning (Identity, Sequence). This is not supported.");
}
public static function invalidTargetEntityClass($targetEntity, $sourceEntity, $associationName)
{
return new self("The target-entity " . $targetEntity . " cannot be found in '" . $sourceEntity."#".$associationName."'.");
}
public static function invalidCascadeOption(array $cascades, $className, $propertyName)
{
$cascades = implode(", ", array_map(function ($e) { return "'" . $e . "'"; }, $cascades));
return new self(sprintf(
"You have specified invalid cascade options for %s::$%s: %s; available options: 'remove', 'persist', 'refresh', 'merge', and 'detach'",
$className,
$propertyName,
$cascades
));
}
}
DoctrineORM-2.3.3/Doctrine/ORM/Mapping/NamedNativeQueries.php 0000644 0001751 0001751 00000002742 12143374607 024027 0 ustar beberlei beberlei .
*/
namespace Doctrine\ORM\Mapping;
/**
* Is used to specify an array of native SQL named queries.
* The NamedNativeQueries annotation can be applied to an entity or mapped superclass.
*
* @author Fabio B. Silva
* @since 2.3
*
* @Annotation
* @Target("CLASS")
*/
final class NamedNativeQueries implements Annotation
{
/**
* One or more NamedNativeQuery annotations.
*
* @var array<\Doctrine\ORM\Mapping\NamedNativeQuery>
*/
public $value = array();
}
DoctrineORM-2.3.3/Doctrine/ORM/Mapping/NamedNativeQuery.php 0000644 0001751 0001751 00000003446 12143374607 023521 0 ustar beberlei beberlei .
*/
namespace Doctrine\ORM\Mapping;
/**
* Is used to specify a native SQL named query.
* The NamedNativeQuery annotation can be applied to an entity or mapped superclass.
*
* @author Fabio B. Silva
* @since 2.3
*
* @Annotation
* @Target("ANNOTATION")
*/
final class NamedNativeQuery implements Annotation
{
/**
* The name used to refer to the query with the EntityManager methods that create query objects.
*
* @var string
*/
public $name;
/**
* The SQL query string.
*
* @var string
*/
public $query;
/**
* The class of the result.
*
* @var string
*/
public $resultClass;
/**
* The name of a SqlResultSetMapping, as defined in metadata.
*
* @var string
*/
public $resultSetMapping;
}
DoctrineORM-2.3.3/Doctrine/ORM/Mapping/NamedQueries.php 0000644 0001751 0001751 00000002247 12143374607 022660 0 ustar beberlei beberlei .
*/
namespace Doctrine\ORM\Mapping;
/**
* @Annotation
* @Target("CLASS")
*/
final class NamedQueries implements Annotation
{
/** @var array<\Doctrine\ORM\Mapping\NamedQuery> */
public $value;
}
DoctrineORM-2.3.3/Doctrine/ORM/Mapping/NamedQuery.php 0000644 0001751 0001751 00000002262 12143374607 022345 0 ustar beberlei beberlei .
*/
namespace Doctrine\ORM\Mapping;
/**
* @Annotation
* @Target("ANNOTATION")
*/
final class NamedQuery implements Annotation
{
/** @var string */
public $name;
/** @var string */
public $query;
}
DoctrineORM-2.3.3/Doctrine/ORM/Mapping/NamingStrategy.php 0000644 0001751 0001751 00000005111 12143374607 023223 0 ustar beberlei beberlei .
*/
namespace Doctrine\ORM\Mapping;
/**
* A set of rules for determining the physical column and table names
*
*
* @link www.doctrine-project.org
* @since 2.3
* @author Fabio B. Silva
*/
interface NamingStrategy
{
/**
* Return a table name for an entity class
*
* @param string $className The fully-qualified class name
* @return string A table name
*/
function classToTableName($className);
/**
* Return a column name for a property
*
* @param string $propertyName A property
* @return string A column name
*/
function propertyToColumnName($propertyName);
/**
* Return the default reference column name
*
* @return string A column name
*/
function referenceColumnName();
/**
* Return a join column name for a property
*
* @param string $propertyName A property
* @return string A join column name
*/
function joinColumnName($propertyName);
/**
* Return a join table name
*
* @param string $sourceEntity The source entity
* @param string $targetEntity The target entity
* @param string $propertyName A property
* @return string A join table name
*/
function joinTableName($sourceEntity, $targetEntity, $propertyName = null);
/**
* Return the foreign key column name for the given parameters
*
* @param string $entityName A entity
* @param string $referencedColumnName A property
* @return string A join column name
*/
function joinKeyColumnName($entityName, $referencedColumnName = null);
}
DoctrineORM-2.3.3/Doctrine/ORM/Mapping/OneToMany.php 0000644 0001751 0001751 00000002607 12143374607 022147 0 ustar beberlei beberlei .
*/
namespace Doctrine\ORM\Mapping;
/**
* @Annotation
* @Target("PROPERTY")
*/
final class OneToMany implements Annotation
{
/** @var string */
public $mappedBy;
/** @var string */
public $targetEntity;
/** @var array */
public $cascade;
/** @var string */
public $fetch = 'LAZY';
/** @var boolean */
public $orphanRemoval = false;
/** @var string */
public $indexBy;
}
DoctrineORM-2.3.3/Doctrine/ORM/Mapping/OneToOne.php 0000644 0001751 0001751 00000002611 12143374607 021757 0 ustar beberlei beberlei .
*/
namespace Doctrine\ORM\Mapping;
/**
* @Annotation
* @Target("PROPERTY")
*/
final class OneToOne implements Annotation
{
/** @var string */
public $targetEntity;
/** @var string */
public $mappedBy;
/** @var string */
public $inversedBy;
/** @var array */
public $cascade;
/** @var string */
public $fetch = 'LAZY';
/** @var boolean */
public $orphanRemoval = false;
}
DoctrineORM-2.3.3/Doctrine/ORM/Mapping/OrderBy.php 0000644 0001751 0001751 00000002213 12143374607 021635 0 ustar beberlei beberlei .
*/
namespace Doctrine\ORM\Mapping;
/**
* @Annotation
* @Target("PROPERTY")
*/
final class OrderBy implements Annotation
{
/** @var array */
public $value;
}
DoctrineORM-2.3.3/Doctrine/ORM/Mapping/PostLoad.php 0000644 0001751 0001751 00000002131 12143374607 022013 0 ustar beberlei beberlei .
*/
namespace Doctrine\ORM\Mapping;
/**
* @Annotation
* @Target("METHOD")
*/
final class PostLoad implements Annotation
{
}
DoctrineORM-2.3.3/Doctrine/ORM/Mapping/PostPersist.php 0000644 0001751 0001751 00000002134 12143374607 022570 0 ustar beberlei beberlei .
*/
namespace Doctrine\ORM\Mapping;
/**
* @Annotation
* @Target("METHOD")
*/
final class PostPersist implements Annotation
{
}
DoctrineORM-2.3.3/Doctrine/ORM/Mapping/PostRemove.php 0000644 0001751 0001751 00000002133 12143374607 022373 0 ustar beberlei beberlei .
*/
namespace Doctrine\ORM\Mapping;
/**
* @Annotation
* @Target("METHOD")
*/
final class PostRemove implements Annotation
{
}
DoctrineORM-2.3.3/Doctrine/ORM/Mapping/PostUpdate.php 0000644 0001751 0001751 00000002133 12143374607 022360 0 ustar beberlei beberlei .
*/
namespace Doctrine\ORM\Mapping;
/**
* @Annotation
* @Target("METHOD")
*/
final class PostUpdate implements Annotation
{
}
DoctrineORM-2.3.3/Doctrine/ORM/Mapping/PreFlush.php 0000644 0001751 0001751 00000002131 12143374607 022016 0 ustar beberlei beberlei .
*/
namespace Doctrine\ORM\Mapping;
/**
* @Annotation
* @Target("METHOD")
*/
final class PreFlush implements Annotation
{
}
DoctrineORM-2.3.3/Doctrine/ORM/Mapping/PrePersist.php 0000644 0001751 0001751 00000002133 12143374607 022370 0 ustar beberlei beberlei .
*/
namespace Doctrine\ORM\Mapping;
/**
* @Annotation
* @Target("METHOD")
*/
final class PrePersist implements Annotation
{
}
DoctrineORM-2.3.3/Doctrine/ORM/Mapping/PreRemove.php 0000644 0001751 0001751 00000002132 12143374607 022173 0 ustar beberlei beberlei .
*/
namespace Doctrine\ORM\Mapping;
/**
* @Annotation
* @Target("METHOD")
*/
final class PreRemove implements Annotation
{
}
DoctrineORM-2.3.3/Doctrine/ORM/Mapping/PreUpdate.php 0000644 0001751 0001751 00000002132 12143374607 022160 0 ustar beberlei beberlei .
*/
namespace Doctrine\ORM\Mapping;
/**
* @Annotation
* @Target("METHOD")
*/
final class PreUpdate implements Annotation
{
}
DoctrineORM-2.3.3/Doctrine/ORM/Mapping/QuoteStrategy.php 0000644 0001751 0001751 00000007414 12143374607 023117 0 ustar beberlei beberlei .
*/
namespace Doctrine\ORM\Mapping;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\DBAL\Platforms\AbstractPlatform;
/**
* A set of rules for determining the column, alias and table quotes
*
* @since 2.3
* @author Fabio B. Silva
*/
interface QuoteStrategy
{
/**
* Gets the (possibly quoted) column name for safe use in an SQL statement.
*
* @param string $fieldName
* @param ClassMetadata $class
* @param AbstractPlatform $platform
* @return string
*/
function getColumnName($fieldName, ClassMetadata $class, AbstractPlatform $platform);
/**
* Gets the (possibly quoted) primary table name for safe use in an SQL statement.
*
* @param ClassMetadata $class
* @param AbstractPlatform $platform
* @return string
*/
function getTableName(ClassMetadata $class, AbstractPlatform $platform);
/**
* Gets the (possibly quoted) sequence name for safe use in an SQL statement.
*
* @param array $definition
* @param ClassMetadata $class
* @param AbstractPlatform $platform
* @return string
*/
function getSequenceName(array $definition, ClassMetadata $class, AbstractPlatform $platform);
/**
* Gets the (possibly quoted) name of the join table.
*
* @param array $association
* @param ClassMetadata $class
* @param AbstractPlatform $platform
* @return string
*/
function getJoinTableName(array $association, ClassMetadata $class, AbstractPlatform $platform);
/**
* Gets the (possibly quoted) join column name.
*
* @param array $joinColumn
* @param ClassMetadata $class
* @param AbstractPlatform $platform
* @return string
*/
function getJoinColumnName(array $joinColumn, ClassMetadata $class, AbstractPlatform $platform);
/**
* Gets the (possibly quoted) join column name.
*
* @param array $joinColumn
* @param ClassMetadata $class
* @param AbstractPlatform $platform
* @return string
*/
function getReferencedJoinColumnName(array $joinColumn, ClassMetadata $class, AbstractPlatform $platform);
/**
* Gets the (possibly quoted) identifier column names for safe use in an SQL statement.
*
* @param ClassMetadata $class
* @param AbstractPlatform $platform
* @return array
*/
function getIdentifierColumnNames(ClassMetadata $class, AbstractPlatform $platform);
/**
* Gets the column alias.
*
* @param string $columnName
* @param integer $counter
* @param AbstractPlatform $platform
* @param ClassMetadata $class
* @return string
*/
function getColumnAlias($columnName, $counter, AbstractPlatform $platform, ClassMetadata $class = null);
} DoctrineORM-2.3.3/Doctrine/ORM/Mapping/SequenceGenerator.php 0000644 0001751 0001751 00000002403 12143374607 023707 0 ustar beberlei beberlei .
*/
namespace Doctrine\ORM\Mapping;
/**
* @Annotation
* @Target("PROPERTY")
*/
final class SequenceGenerator implements Annotation
{
/** @var string */
public $sequenceName;
/** @var integer */
public $allocationSize = 1;
/** @var integer */
public $initialValue = 1;
}
DoctrineORM-2.3.3/Doctrine/ORM/Mapping/SqlResultSetMapping.php 0000644 0001751 0001751 00000003553 12143374607 024225 0 ustar beberlei beberlei .
*/
namespace Doctrine\ORM\Mapping;
/**
* The SqlResultSetMapping annotation is used to specify the mapping of the result of a native SQL query.
* The SqlResultSetMapping annotation can be applied to an entity or mapped superclass.
*
* @author Fabio B. Silva
* @since 2.3
*
* @Annotation
* @Target("ANNOTATION")
*/
final class SqlResultSetMapping implements Annotation
{
/**
* The name given to the result set mapping, and used to refer to it in the methods of the Query API.
*
* @var string
*/
public $name;
/**
* Specifies the result set mapping to entities.
*
* @var array<\Doctrine\ORM\Mapping\EntityResult>
*/
public $entities = array();
/**
* Specifies the result set mapping to scalar values.
*
* @var array<\Doctrine\ORM\Mapping\ColumnResult>
*/
public $columns = array();
}
DoctrineORM-2.3.3/Doctrine/ORM/Mapping/SqlResultSetMappings.php 0000644 0001751 0001751 00000002734 12143374607 024410 0 ustar beberlei beberlei .
*/
namespace Doctrine\ORM\Mapping;
/**
* Is used to specify an array of mappings.
* The SqlResultSetMappings annotation can be applied to an entity or mapped superclass.
*
* @author Fabio B. Silva
* @since 2.3
*
* @Annotation
* @Target("CLASS")
*/
final class SqlResultSetMappings implements Annotation
{
/**
* One or more SqlResultSetMapping annotations.
*
* @var array<\Doctrine\ORM\Mapping\SqlResultSetMapping>
*/
public $value = array();
}
DoctrineORM-2.3.3/Doctrine/ORM/Mapping/Table.php 0000644 0001751 0001751 00000002603 12143374607 021321 0 ustar beberlei beberlei .
*/
namespace Doctrine\ORM\Mapping;
/**
* @Annotation
* @Target("CLASS")
*/
final class Table implements Annotation
{
/** @var string */
public $name;
/** @var string */
public $schema;
/** @var array<\Doctrine\ORM\Mapping\Index> */
public $indexes;
/** @var array<\Doctrine\ORM\Mapping\UniqueConstraint> */
public $uniqueConstraints;
/** @var array */
public $options = array();
}
DoctrineORM-2.3.3/Doctrine/ORM/Mapping/UnderscoreNamingStrategy.php 0000644 0001751 0001751 00000006740 12143374607 025266 0 ustar beberlei beberlei .
*/
namespace Doctrine\ORM\Mapping;
/**
* Naming strategy implementing the underscore naming convention.
* Converts 'MyEntity' to 'my_entity' or 'MY_ENTITY'.
*
*
* @link www.doctrine-project.org
* @since 2.3
* @author Fabio B. Silva
*/
class UnderscoreNamingStrategy implements NamingStrategy
{
/**
* @var integer
*/
private $case;
/**
* Underscore naming strategy construct
*
* @param integer $case CASE_LOWER | CASE_UPPER
*/
public function __construct($case = CASE_LOWER)
{
$this->case = $case;
}
/**
* @return integer
*/
public function getCase()
{
return $this->case;
}
/**
* Sets string case CASE_LOWER | CASE_UPPER
* Alphabetic characters converted to lowercase or uppercase
*
* @param integer $case
*/
public function setCase($case)
{
$this->case = $case;
}
/**
* {@inheritdoc}
*/
public function classToTableName($className)
{
if (strpos($className, '\\') !== false) {
$className = substr($className, strrpos($className, '\\') + 1);
}
return $this->underscore($className);
}
/**
* {@inheritdoc}
*/
public function propertyToColumnName($propertyName)
{
return $this->underscore($propertyName);
}
/**
* {@inheritdoc}
*/
public function referenceColumnName()
{
return $this->case === CASE_UPPER ? 'ID' : 'id';
}
/**
* {@inheritdoc}
*/
public function joinColumnName($propertyName)
{
return $this->underscore($propertyName) . '_' . $this->referenceColumnName();
}
/**
* {@inheritdoc}
*/
public function joinTableName($sourceEntity, $targetEntity, $propertyName = null)
{
return $this->classToTableName($sourceEntity) . '_' . $this->classToTableName($targetEntity);
}
/**
* {@inheritdoc}
*/
public function joinKeyColumnName($entityName, $referencedColumnName = null)
{
return $this->classToTableName($entityName) . '_' .
($referencedColumnName ?: $this->referenceColumnName());
}
/**
* @param string $string
* @return string
*/
private function underscore($string)
{
$string = preg_replace('/(?<=[a-z])([A-Z])/', '_$1', $string);
if ($this->case === CASE_UPPER) {
return strtoupper($string);
}
return strtolower($string);
}
}
DoctrineORM-2.3.3/Doctrine/ORM/Mapping/UniqueConstraint.php 0000644 0001751 0001751 00000002301 12143374607 023600 0 ustar beberlei beberlei .
*/
namespace Doctrine\ORM\Mapping;
/**
* @Annotation
* @Target("ANNOTATION")
*/
final class UniqueConstraint implements Annotation
{
/** @var string */
public $name;
/** @var array */
public $columns;
}
DoctrineORM-2.3.3/Doctrine/ORM/Mapping/Version.php 0000644 0001751 0001751 00000002132 12143374607 021714 0 ustar beberlei beberlei .
*/
namespace Doctrine\ORM\Mapping;
/**
* @Annotation
* @Target("PROPERTY")
*/
final class Version implements Annotation
{
}
DoctrineORM-2.3.3/Doctrine/ORM/Mapping/Builder/AssociationBuilder.php 0000644 0001751 0001751 00000010405 12143374607 025442 0 ustar beberlei beberlei .
*/
namespace Doctrine\ORM\Mapping\Builder;
use Doctrine\ORM\Mapping\ClassMetadata;
class AssociationBuilder
{
/**
* @var ClassMetadataBuilder
*/
protected $builder;
/**
* @var array
*/
protected $mapping;
/**
* @var array
*/
protected $joinColumns;
/**
*
* @var int
*/
protected $type;
/**
* @param ClassMetadataBuilder $builder
* @param array $mapping
*/
public function __construct(ClassMetadataBuilder $builder, array $mapping, $type)
{
$this->builder = $builder;
$this->mapping = $mapping;
$this->type = $type;
}
public function mappedBy($fieldName)
{
$this->mapping['mappedBy'] = $fieldName;
return $this;
}
public function inversedBy($fieldName)
{
$this->mapping['inversedBy'] = $fieldName;
return $this;
}
public function cascadeAll()
{
$this->mapping['cascade'] = array("ALL");
return $this;
}
public function cascadePersist()
{
$this->mapping['cascade'][] = "persist";
return $this;
}
public function cascadeRemove()
{
$this->mapping['cascade'][] = "remove";
return $this;
}
public function cascadeMerge()
{
$this->mapping['cascade'][] = "merge";
return $this;
}
public function cascadeDetach()
{
$this->mapping['cascade'][] = "detach";
return $this;
}
public function cascadeRefresh()
{
$this->mapping['cascade'][] = "refresh";
return $this;
}
public function fetchExtraLazy()
{
$this->mapping['fetch'] = ClassMetadata::FETCH_EXTRA_LAZY;
return $this;
}
public function fetchEager()
{
$this->mapping['fetch'] = ClassMetadata::FETCH_EAGER;
return $this;
}
public function fetchLazy()
{
$this->mapping['fetch'] = ClassMetadata::FETCH_LAZY;
return $this;
}
/**
* Add Join Columns
*
* @param string $columnName
* @param string $referencedColumnName
* @param bool $nullable
* @param bool $unique
* @param string $onDelete
* @param string $columnDef
*/
public function addJoinColumn($columnName, $referencedColumnName, $nullable = true, $unique = false, $onDelete = null, $columnDef = null)
{
$this->joinColumns[] = array(
'name' => $columnName,
'referencedColumnName' => $referencedColumnName,
'nullable' => $nullable,
'unique' => $unique,
'onDelete' => $onDelete,
'columnDefinition' => $columnDef,
);
return $this;
}
/**
* @return ClassMetadataBuilder
*/
public function build()
{
$mapping = $this->mapping;
if ($this->joinColumns) {
$mapping['joinColumns'] = $this->joinColumns;
}
$cm = $this->builder->getClassMetadata();
if ($this->type == ClassMetadata::MANY_TO_ONE) {
$cm->mapManyToOne($mapping);
} else if ($this->type == ClassMetadata::ONE_TO_ONE) {
$cm->mapOneToOne($mapping);
} else {
throw new \InvalidArgumentException("Type should be a ToOne Assocation here");
}
return $this->builder;
}
}
DoctrineORM-2.3.3/Doctrine/ORM/Mapping/Builder/ClassMetadataBuilder.php 0000644 0001751 0001751 00000026610 12143374607 025701 0 ustar beberlei beberlei .
*/
namespace Doctrine\ORM\Mapping\Builder;
use Doctrine\ORM\Mapping\ClassMetadata,
Doctrine\ORM\Mapping\ClassMetadataInfo;
/**
* Builder Object for ClassMetadata
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.com
* @since 2.2
* @author Benjamin Eberlei
* @author Guilherme Blanco
*/
class ClassMetadataBuilder
{
/**
* @var \Doctrine\ORM\Mapping\ClassMetadataInfo
*/
private $cm;
/**
* @param \Doctrine\ORM\Mapping\ClassMetadataInfo $cm
*/
public function __construct(ClassMetadataInfo $cm)
{
$this->cm = $cm;
}
/**
* @return ClassMetadata
*/
public function getClassMetadata()
{
return $this->cm;
}
/**
* Mark the class as mapped superclass.
*
* @return ClassMetadataBuilder
*/
public function setMappedSuperClass()
{
$this->cm->isMappedSuperclass = true;
return $this;
}
/**
* Set custom Repository class name
*
* @param string $repositoryClassName
* @return ClassMetadataBuilder
*/
public function setCustomRepositoryClass($repositoryClassName)
{
$this->cm->setCustomRepositoryClass($repositoryClassName);
return $this;
}
/**
* Mark class read only
*
* @return ClassMetadataBuilder
*/
public function setReadOnly()
{
$this->cm->markReadOnly();
return $this;
}
/**
* Set the table name
*
* @param string $name
* @return ClassMetadataBuilder
*/
public function setTable($name)
{
$this->cm->setPrimaryTable(array('name' => $name));
return $this;
}
/**
* Add Index
*
* @param array $columns
* @param string $name
* @return ClassMetadataBuilder
*/
public function addIndex(array $columns, $name)
{
if (!isset($this->cm->table['indexes'])) {
$this->cm->table['indexes'] = array();
}
$this->cm->table['indexes'][$name] = array('columns' => $columns);
return $this;
}
/**
* Add Unique Constraint
*
* @param array $columns
* @param string $name
* @return ClassMetadataBuilder
*/
public function addUniqueConstraint(array $columns, $name)
{
if ( ! isset($this->cm->table['uniqueConstraints'])) {
$this->cm->table['uniqueConstraints'] = array();
}
$this->cm->table['uniqueConstraints'][$name] = array('columns' => $columns);
return $this;
}
/**
* Add named query
*
* @param string $name
* @param string $dqlQuery
* @return ClassMetadataBuilder
*/
public function addNamedQuery($name, $dqlQuery)
{
$this->cm->addNamedQuery(array(
'name' => $name,
'query' => $dqlQuery,
));
return $this;
}
/**
* Set class as root of a joined table inheritance hierachy.
*
* @return ClassMetadataBuilder
*/
public function setJoinedTableInheritance()
{
$this->cm->setInheritanceType(ClassMetadata::INHERITANCE_TYPE_JOINED);
return $this;
}
/**
* Set class as root of a single table inheritance hierachy.
*
* @return ClassMetadataBuilder
*/
public function setSingleTableInheritance()
{
$this->cm->setInheritanceType(ClassMetadata::INHERITANCE_TYPE_SINGLE_TABLE);
return $this;
}
/**
* Set the discriminator column details.
*
* @param string $name
* @param string $type
*/
public function setDiscriminatorColumn($name, $type = 'string', $length = 255)
{
$this->cm->setDiscriminatorColumn(array(
'name' => $name,
'type' => $type,
'length' => $length,
));
return $this;
}
/**
* Add a subclass to this inheritance hierachy.
*
* @param string $name
* @param string $class
* @return ClassMetadataBuilder
*/
public function addDiscriminatorMapClass($name, $class)
{
$this->cm->addDiscriminatorMapClass($name, $class);
return $this;
}
/**
* Set deferred explicit change tracking policy.
*
* @return ClassMetadataBuilder
*/
public function setChangeTrackingPolicyDeferredExplicit()
{
$this->cm->setChangeTrackingPolicy(ClassMetadata::CHANGETRACKING_DEFERRED_EXPLICIT);
return $this;
}
/**
* Set notify change tracking policy.
*
* @return ClassMetadataBuilder
*/
public function setChangeTrackingPolicyNotify()
{
$this->cm->setChangeTrackingPolicy(ClassMetadata::CHANGETRACKING_NOTIFY);
return $this;
}
/**
* Add lifecycle event
*
* @param string $methodName
* @param string $event
* @return ClassMetadataBuilder
*/
public function addLifecycleEvent($methodName, $event)
{
$this->cm->addLifecycleCallback($methodName, $event);
return $this;
}
/**
* Add Field
*
* @param string $name
* @param string $type
* @param array $mapping
*/
public function addField($name, $type, array $mapping = array())
{
$mapping['fieldName'] = $name;
$mapping['type'] = $type;
$this->cm->mapField($mapping);
return $this;
}
/**
* Create a field builder.
*
* @param string $name
* @param string $type
* @return FieldBuilder
*/
public function createField($name, $type)
{
return new FieldBuilder(
$this,
array(
'fieldName' => $name,
'type' => $type
)
);
}
/**
* Add a simple many to one association, optionally with the inversed by field.
*
* @param string $name
* @param string $targetEntity
* @param string|null $inversedBy
* @return ClassMetadataBuilder
*/
public function addManyToOne($name, $targetEntity, $inversedBy = null)
{
$builder = $this->createManyToOne($name, $targetEntity);
if ($inversedBy) {
$builder->inversedBy($inversedBy);
}
return $builder->build();
}
/**
* Create a ManyToOne Assocation Builder.
*
* Note: This method does not add the association, you have to call build() on the AssociationBuilder.
*
* @param string $name
* @param string $targetEntity
* @return AssociationBuilder
*/
public function createManyToOne($name, $targetEntity)
{
return new AssociationBuilder(
$this,
array(
'fieldName' => $name,
'targetEntity' => $targetEntity
),
ClassMetadata::MANY_TO_ONE
);
}
/**
* Create OneToOne Assocation Builder
*
* @param string $name
* @param string $targetEntity
* @return AssociationBuilder
*/
public function createOneToOne($name, $targetEntity)
{
return new AssociationBuilder(
$this,
array(
'fieldName' => $name,
'targetEntity' => $targetEntity
),
ClassMetadata::ONE_TO_ONE
);
}
/**
* Add simple inverse one-to-one assocation.
*
* @param string $name
* @param string $targetEntity
* @param string $mappedBy
* @return ClassMetadataBuilder
*/
public function addInverseOneToOne($name, $targetEntity, $mappedBy)
{
$builder = $this->createOneToOne($name, $targetEntity);
$builder->mappedBy($mappedBy);
return $builder->build();
}
/**
* Add simple owning one-to-one assocation.
*
* @param string $name
* @param string $targetEntity
* @param string $inversedBy
* @return ClassMetadataBuilder
*/
public function addOwningOneToOne($name, $targetEntity, $inversedBy = null)
{
$builder = $this->createOneToOne($name, $targetEntity);
if ($inversedBy) {
$builder->inversedBy($inversedBy);
}
return $builder->build();
}
/**
* Create ManyToMany Assocation Builder
*
* @param string $name
* @param string $targetEntity
* @return ManyToManyAssociationBuilder
*/
public function createManyToMany($name, $targetEntity)
{
return new ManyToManyAssociationBuilder(
$this,
array(
'fieldName' => $name,
'targetEntity' => $targetEntity
),
ClassMetadata::MANY_TO_MANY
);
}
/**
* Add a simple owning many to many assocation.
*
* @param string $name
* @param string $targetEntity
* @param string|null $inversedBy
* @return ClassMetadataBuilder
*/
public function addOwningManyToMany($name, $targetEntity, $inversedBy = null)
{
$builder = $this->createManyToMany($name, $targetEntity);
if ($inversedBy) {
$builder->inversedBy($inversedBy);
}
return $builder->build();
}
/**
* Add a simple inverse many to many assocation.
*
* @param string $name
* @param string $targetEntity
* @param string $mappedBy
* @return ClassMetadataBuilder
*/
public function addInverseManyToMany($name, $targetEntity, $mappedBy)
{
$builder = $this->createManyToMany($name, $targetEntity);
$builder->mappedBy($mappedBy);
return $builder->build();
}
/**
* Create a one to many assocation builder
*
* @param string $name
* @param string $targetEntity
* @return OneToManyAssociationBuilder
*/
public function createOneToMany($name, $targetEntity)
{
return new OneToManyAssociationBuilder(
$this,
array(
'fieldName' => $name,
'targetEntity' => $targetEntity
),
ClassMetadata::ONE_TO_MANY
);
}
/**
* Add simple OneToMany assocation.
*
* @param string $name
* @param string $targetEntity
* @param string $mappedBy
* @return ClassMetadataBuilder
*/
public function addOneToMany($name, $targetEntity, $mappedBy)
{
$builder = $this->createOneToMany($name, $targetEntity);
$builder->mappedBy($mappedBy);
return $builder->build();
}
}
DoctrineORM-2.3.3/Doctrine/ORM/Mapping/Builder/FieldBuilder.php 0000644 0001751 0001751 00000012041 12143374607 024207 0 ustar beberlei beberlei .
*/
namespace Doctrine\ORM\Mapping\Builder;
/**
* Field Builder
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.com
* @since 2.2
* @author Benjamin Eberlei
*/
class FieldBuilder
{
/**
* @var ClassMetadataBuilder
*/
private $builder;
/**
* @var array
*/
private $mapping;
/**
* @var bool
*/
private $version;
/**
* @var string
*/
private $generatedValue;
/**
* @var array
*/
private $sequenceDef;
/**
*
* @param ClassMetadataBuilder $builder
* @param array $mapping
*/
public function __construct(ClassMetadataBuilder $builder, array $mapping)
{
$this->builder = $builder;
$this->mapping = $mapping;
}
/**
* Set length.
*
* @param int $length
* @return FieldBuilder
*/
public function length($length)
{
$this->mapping['length'] = $length;
return $this;
}
/**
* Set nullable
*
* @param bool
* @return FieldBuilder
*/
public function nullable($flag = true)
{
$this->mapping['nullable'] = (bool)$flag;
return $this;
}
/**
* Set Unique
*
* @param bool
* @return FieldBuilder
*/
public function unique($flag = true)
{
$this->mapping['unique'] = (bool)$flag;
return $this;
}
/**
* Set column name
*
* @param string $name
* @return FieldBuilder
*/
public function columnName($name)
{
$this->mapping['columnName'] = $name;
return $this;
}
/**
* Set Precision
*
* @param int $p
* @return FieldBuilder
*/
public function precision($p)
{
$this->mapping['precision'] = $p;
return $this;
}
/**
* Set scale.
*
* @param int $s
* @return FieldBuilder
*/
public function scale($s)
{
$this->mapping['scale'] = $s;
return $this;
}
/**
* Set field as primary key.
*
* @return FieldBuilder
*/
public function isPrimaryKey()
{
$this->mapping['id'] = true;
return $this;
}
/**
* @param int $strategy
* @return FieldBuilder
*/
public function generatedValue($strategy = 'AUTO')
{
$this->generatedValue = $strategy;
return $this;
}
/**
* Set field versioned
*
* @return FieldBuilder
*/
public function isVersionField()
{
$this->version = true;
return $this;
}
/**
* Set Sequence Generator
*
* @param string $sequenceName
* @param int $allocationSize
* @param int $initialValue
* @return FieldBuilder
*/
public function setSequenceGenerator($sequenceName, $allocationSize = 1, $initialValue = 1)
{
$this->sequenceDef = array(
'sequenceName' => $sequenceName,
'allocationSize' => $allocationSize,
'initialValue' => $initialValue,
);
return $this;
}
/**
* Set column definition.
*
* @param string $def
* @return FieldBuilder
*/
public function columnDefinition($def)
{
$this->mapping['columnDefinition'] = $def;
return $this;
}
/**
* Finalize this field and attach it to the ClassMetadata.
*
* Without this call a FieldBuilder has no effect on the ClassMetadata.
*
* @return ClassMetadataBuilder
*/
public function build()
{
$cm = $this->builder->getClassMetadata();
if ($this->generatedValue) {
$cm->setIdGeneratorType(constant('Doctrine\ORM\Mapping\ClassMetadata::GENERATOR_TYPE_' . $this->generatedValue));
}
if ($this->version) {
$cm->setVersionMapping($this->mapping);
}
$cm->mapField($this->mapping);
if ($this->sequenceDef) {
$cm->setSequenceGeneratorDefinition($this->sequenceDef);
}
return $this->builder;
}
}
DoctrineORM-2.3.3/Doctrine/ORM/Mapping/Builder/ManyToManyAssociationBuilder.php 0000644 0001751 0001751 00000005543 12143374607 027426 0 ustar beberlei beberlei .
*/
namespace Doctrine\ORM\Mapping\Builder;
/**
* ManyToMany Association Builder
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.com
* @since 2.0
* @author Benjamin Eberlei
*/
class ManyToManyAssociationBuilder extends OneToManyAssociationBuilder
{
private $joinTableName;
private $inverseJoinColumns = array();
public function setJoinTable($name)
{
$this->joinTableName = $name;
return $this;
}
/**
* Add Inverse Join Columns
*
* @param string $columnName
* @param string $referencedColumnName
* @param bool $nullable
* @param bool $unique
* @param string $onDelete
* @param string $columnDef
*/
public function addInverseJoinColumn($columnName, $referencedColumnName, $nullable = true, $unique = false, $onDelete = null, $columnDef = null)
{
$this->inverseJoinColumns[] = array(
'name' => $columnName,
'referencedColumnName' => $referencedColumnName,
'nullable' => $nullable,
'unique' => $unique,
'onDelete' => $onDelete,
'columnDefinition' => $columnDef,
);
return $this;
}
/**
* @return ClassMetadataBuilder
*/
public function build()
{
$mapping = $this->mapping;
$mapping['joinTable'] = array();
if ($this->joinColumns) {
$mapping['joinTable']['joinColumns'] = $this->joinColumns;
}
if ($this->inverseJoinColumns) {
$mapping['joinTable']['inverseJoinColumns'] = $this->inverseJoinColumns;
}
if ($this->joinTableName) {
$mapping['joinTable']['name'] = $this->joinTableName;
}
$cm = $this->builder->getClassMetadata();
$cm->mapManyToMany($mapping);
return $this->builder;
}
}
DoctrineORM-2.3.3/Doctrine/ORM/Mapping/Builder/OneToManyAssociationBuilder.php 0000644 0001751 0001751 00000003753 12143374607 027244 0 ustar beberlei beberlei .
*/
namespace Doctrine\ORM\Mapping\Builder;
/**
* OneToMany Association Builder
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.com
* @since 2.0
* @author Benjamin Eberlei
*/
class OneToManyAssociationBuilder extends AssociationBuilder
{
/**
* @param array $fieldNames
* @return OneToManyAssociationBuilder
*/
public function setOrderBy(array $fieldNames)
{
$this->mapping['orderBy'] = $fieldNames;
return $this;
}
public function setIndexBy($fieldName)
{
$this->mapping['indexBy'] = $fieldName;
return $this;
}
/**
* @return ClassMetadataBuilder
*/
public function build()
{
$mapping = $this->mapping;
if ($this->joinColumns) {
$mapping['joinColumns'] = $this->joinColumns;
}
$cm = $this->builder->getClassMetadata();
$cm->mapOneToMany($mapping);
return $this->builder;
}
}
DoctrineORM-2.3.3/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php 0000644 0001751 0001751 00000061563 12143374607 025025 0 ustar beberlei beberlei .
*/
namespace Doctrine\ORM\Mapping\Driver;
use Doctrine\Common\Annotations\AnnotationReader,
Doctrine\ORM\Mapping\MappingException,
Doctrine\ORM\Mapping\JoinColumn,
Doctrine\ORM\Mapping\Column,
Doctrine\Common\Persistence\Mapping\ClassMetadata,
Doctrine\Common\Persistence\Mapping\Driver\AnnotationDriver as AbstractAnnotationDriver;
/**
* The AnnotationDriver reads the mapping metadata from docblock annotations.
*
* @since 2.0
* @author Benjamin Eberlei
* @author Guilherme Blanco
* @author Jonathan H. Wage
* @author Roman Borschel
*/
class AnnotationDriver extends AbstractAnnotationDriver
{
/**
* {@inheritDoc}
*/
protected $entityAnnotationClasses = array(
'Doctrine\ORM\Mapping\Entity' => 1,
'Doctrine\ORM\Mapping\MappedSuperclass' => 2,
);
/**
* {@inheritDoc}
*/
public function loadMetadataForClass($className, ClassMetadata $metadata)
{
/* @var $metadata \Doctrine\ORM\Mapping\ClassMetadataInfo */
$class = $metadata->getReflectionClass();
if ( ! $class) {
// this happens when running annotation driver in combination with
// static reflection services. This is not the nicest fix
$class = new \ReflectionClass($metadata->name);
}
$classAnnotations = $this->reader->getClassAnnotations($class);
if ($classAnnotations) {
foreach ($classAnnotations as $key => $annot) {
if ( ! is_numeric($key)) {
continue;
}
$classAnnotations[get_class($annot)] = $annot;
}
}
// Evaluate Entity annotation
if (isset($classAnnotations['Doctrine\ORM\Mapping\Entity'])) {
$entityAnnot = $classAnnotations['Doctrine\ORM\Mapping\Entity'];
if ($entityAnnot->repositoryClass !== null) {
$metadata->setCustomRepositoryClass($entityAnnot->repositoryClass);
}
if ($entityAnnot->readOnly) {
$metadata->markReadOnly();
}
} else if (isset($classAnnotations['Doctrine\ORM\Mapping\MappedSuperclass'])) {
$mappedSuperclassAnnot = $classAnnotations['Doctrine\ORM\Mapping\MappedSuperclass'];
$metadata->setCustomRepositoryClass($mappedSuperclassAnnot->repositoryClass);
$metadata->isMappedSuperclass = true;
} else {
throw MappingException::classIsNotAValidEntityOrMappedSuperClass($className);
}
// Evaluate Table annotation
if (isset($classAnnotations['Doctrine\ORM\Mapping\Table'])) {
$tableAnnot = $classAnnotations['Doctrine\ORM\Mapping\Table'];
$primaryTable = array(
'name' => $tableAnnot->name,
'schema' => $tableAnnot->schema
);
if ($tableAnnot->indexes !== null) {
foreach ($tableAnnot->indexes as $indexAnnot) {
$index = array('columns' => $indexAnnot->columns);
if ( ! empty($indexAnnot->name)) {
$primaryTable['indexes'][$indexAnnot->name] = $index;
} else {
$primaryTable['indexes'][] = $index;
}
}
}
if ($tableAnnot->uniqueConstraints !== null) {
foreach ($tableAnnot->uniqueConstraints as $uniqueConstraintAnnot) {
$uniqueConstraint = array('columns' => $uniqueConstraintAnnot->columns);
if ( ! empty($uniqueConstraintAnnot->name)) {
$primaryTable['uniqueConstraints'][$uniqueConstraintAnnot->name] = $uniqueConstraint;
} else {
$primaryTable['uniqueConstraints'][] = $uniqueConstraint;
}
}
}
if ($tableAnnot->options !== null) {
$primaryTable['options'] = $tableAnnot->options;
}
$metadata->setPrimaryTable($primaryTable);
}
// Evaluate NamedNativeQueries annotation
if (isset($classAnnotations['Doctrine\ORM\Mapping\NamedNativeQueries'])) {
$namedNativeQueriesAnnot = $classAnnotations['Doctrine\ORM\Mapping\NamedNativeQueries'];
foreach ($namedNativeQueriesAnnot->value as $namedNativeQuery) {
$metadata->addNamedNativeQuery(array(
'name' => $namedNativeQuery->name,
'query' => $namedNativeQuery->query,
'resultClass' => $namedNativeQuery->resultClass,
'resultSetMapping' => $namedNativeQuery->resultSetMapping,
));
}
}
// Evaluate SqlResultSetMappings annotation
if (isset($classAnnotations['Doctrine\ORM\Mapping\SqlResultSetMappings'])) {
$sqlResultSetMappingsAnnot = $classAnnotations['Doctrine\ORM\Mapping\SqlResultSetMappings'];
foreach ($sqlResultSetMappingsAnnot->value as $resultSetMapping) {
$entities = array();
$columns = array();
foreach ($resultSetMapping->entities as $entityResultAnnot) {
$entityResult = array(
'fields' => array(),
'entityClass' => $entityResultAnnot->entityClass,
'discriminatorColumn' => $entityResultAnnot->discriminatorColumn,
);
foreach ($entityResultAnnot->fields as $fieldResultAnnot) {
$entityResult['fields'][] = array(
'name' => $fieldResultAnnot->name,
'column' => $fieldResultAnnot->column
);
}
$entities[] = $entityResult;
}
foreach ($resultSetMapping->columns as $columnResultAnnot) {
$columns[] = array(
'name' => $columnResultAnnot->name,
);
}
$metadata->addSqlResultSetMapping(array(
'name' => $resultSetMapping->name,
'entities' => $entities,
'columns' => $columns
));
}
}
// Evaluate NamedQueries annotation
if (isset($classAnnotations['Doctrine\ORM\Mapping\NamedQueries'])) {
$namedQueriesAnnot = $classAnnotations['Doctrine\ORM\Mapping\NamedQueries'];
if ( ! is_array($namedQueriesAnnot->value)) {
throw new \UnexpectedValueException("@NamedQueries should contain an array of @NamedQuery annotations.");
}
foreach ($namedQueriesAnnot->value as $namedQuery) {
if ( ! ($namedQuery instanceof \Doctrine\ORM\Mapping\NamedQuery)) {
throw new \UnexpectedValueException("@NamedQueries should contain an array of @NamedQuery annotations.");
}
$metadata->addNamedQuery(array(
'name' => $namedQuery->name,
'query' => $namedQuery->query
));
}
}
// Evaluate InheritanceType annotation
if (isset($classAnnotations['Doctrine\ORM\Mapping\InheritanceType'])) {
$inheritanceTypeAnnot = $classAnnotations['Doctrine\ORM\Mapping\InheritanceType'];
$metadata->setInheritanceType(constant('Doctrine\ORM\Mapping\ClassMetadata::INHERITANCE_TYPE_' . $inheritanceTypeAnnot->value));
if ($metadata->inheritanceType != \Doctrine\ORM\Mapping\ClassMetadata::INHERITANCE_TYPE_NONE) {
// Evaluate DiscriminatorColumn annotation
if (isset($classAnnotations['Doctrine\ORM\Mapping\DiscriminatorColumn'])) {
$discrColumnAnnot = $classAnnotations['Doctrine\ORM\Mapping\DiscriminatorColumn'];
$metadata->setDiscriminatorColumn(array(
'name' => $discrColumnAnnot->name,
'type' => $discrColumnAnnot->type,
'length' => $discrColumnAnnot->length,
'columnDefinition' => $discrColumnAnnot->columnDefinition
));
} else {
$metadata->setDiscriminatorColumn(array('name' => 'dtype', 'type' => 'string', 'length' => 255));
}
// Evaluate DiscriminatorMap annotation
if (isset($classAnnotations['Doctrine\ORM\Mapping\DiscriminatorMap'])) {
$discrMapAnnot = $classAnnotations['Doctrine\ORM\Mapping\DiscriminatorMap'];
$metadata->setDiscriminatorMap($discrMapAnnot->value);
}
}
}
// Evaluate DoctrineChangeTrackingPolicy annotation
if (isset($classAnnotations['Doctrine\ORM\Mapping\ChangeTrackingPolicy'])) {
$changeTrackingAnnot = $classAnnotations['Doctrine\ORM\Mapping\ChangeTrackingPolicy'];
$metadata->setChangeTrackingPolicy(constant('Doctrine\ORM\Mapping\ClassMetadata::CHANGETRACKING_' . $changeTrackingAnnot->value));
}
// Evaluate annotations on properties/fields
/* @var $property \ReflectionProperty */
foreach ($class->getProperties() as $property) {
if ($metadata->isMappedSuperclass && ! $property->isPrivate()
||
$metadata->isInheritedField($property->name)
||
$metadata->isInheritedAssociation($property->name)) {
continue;
}
$mapping = array();
$mapping['fieldName'] = $property->getName();
// Check for JoinColummn/JoinColumns annotations
$joinColumns = array();
if ($joinColumnAnnot = $this->reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\JoinColumn')) {
$joinColumns[] = $this->joinColumnToArray($joinColumnAnnot);
} else if ($joinColumnsAnnot = $this->reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\JoinColumns')) {
foreach ($joinColumnsAnnot->value as $joinColumn) {
$joinColumns[] = $this->joinColumnToArray($joinColumn);
}
}
// Field can only be annotated with one of:
// @Column, @OneToOne, @OneToMany, @ManyToOne, @ManyToMany
if ($columnAnnot = $this->reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\Column')) {
if ($columnAnnot->type == null) {
throw MappingException::propertyTypeIsRequired($className, $property->getName());
}
$mapping = $this->columnToArray($property->getName(), $columnAnnot);
if ($idAnnot = $this->reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\Id')) {
$mapping['id'] = true;
}
if ($generatedValueAnnot = $this->reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\GeneratedValue')) {
$metadata->setIdGeneratorType(constant('Doctrine\ORM\Mapping\ClassMetadata::GENERATOR_TYPE_' . $generatedValueAnnot->strategy));
}
if ($this->reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\Version')) {
$metadata->setVersionMapping($mapping);
}
$metadata->mapField($mapping);
// Check for SequenceGenerator/TableGenerator definition
if ($seqGeneratorAnnot = $this->reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\SequenceGenerator')) {
$metadata->setSequenceGeneratorDefinition(array(
'sequenceName' => $seqGeneratorAnnot->sequenceName,
'allocationSize' => $seqGeneratorAnnot->allocationSize,
'initialValue' => $seqGeneratorAnnot->initialValue
));
} else if ($this->reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\TableGenerator')) {
throw MappingException::tableIdGeneratorNotImplemented($className);
} else if ($customGeneratorAnnot = $this->reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\CustomIdGenerator')) {
$metadata->setCustomGeneratorDefinition(array(
'class' => $customGeneratorAnnot->class
));
}
} else if ($oneToOneAnnot = $this->reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\OneToOne')) {
if ($idAnnot = $this->reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\Id')) {
$mapping['id'] = true;
}
$mapping['targetEntity'] = $oneToOneAnnot->targetEntity;
$mapping['joinColumns'] = $joinColumns;
$mapping['mappedBy'] = $oneToOneAnnot->mappedBy;
$mapping['inversedBy'] = $oneToOneAnnot->inversedBy;
$mapping['cascade'] = $oneToOneAnnot->cascade;
$mapping['orphanRemoval'] = $oneToOneAnnot->orphanRemoval;
$mapping['fetch'] = $this->getFetchMode($className, $oneToOneAnnot->fetch);
$metadata->mapOneToOne($mapping);
} else if ($oneToManyAnnot = $this->reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\OneToMany')) {
$mapping['mappedBy'] = $oneToManyAnnot->mappedBy;
$mapping['targetEntity'] = $oneToManyAnnot->targetEntity;
$mapping['cascade'] = $oneToManyAnnot->cascade;
$mapping['indexBy'] = $oneToManyAnnot->indexBy;
$mapping['orphanRemoval'] = $oneToManyAnnot->orphanRemoval;
$mapping['fetch'] = $this->getFetchMode($className, $oneToManyAnnot->fetch);
if ($orderByAnnot = $this->reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\OrderBy')) {
$mapping['orderBy'] = $orderByAnnot->value;
}
$metadata->mapOneToMany($mapping);
} else if ($manyToOneAnnot = $this->reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\ManyToOne')) {
if ($idAnnot = $this->reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\Id')) {
$mapping['id'] = true;
}
$mapping['joinColumns'] = $joinColumns;
$mapping['cascade'] = $manyToOneAnnot->cascade;
$mapping['inversedBy'] = $manyToOneAnnot->inversedBy;
$mapping['targetEntity'] = $manyToOneAnnot->targetEntity;
$mapping['fetch'] = $this->getFetchMode($className, $manyToOneAnnot->fetch);
$metadata->mapManyToOne($mapping);
} else if ($manyToManyAnnot = $this->reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\ManyToMany')) {
$joinTable = array();
if ($joinTableAnnot = $this->reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\JoinTable')) {
$joinTable = array(
'name' => $joinTableAnnot->name,
'schema' => $joinTableAnnot->schema
);
foreach ($joinTableAnnot->joinColumns as $joinColumn) {
$joinTable['joinColumns'][] = $this->joinColumnToArray($joinColumn);
}
foreach ($joinTableAnnot->inverseJoinColumns as $joinColumn) {
$joinTable['inverseJoinColumns'][] = $this->joinColumnToArray($joinColumn);
}
}
$mapping['joinTable'] = $joinTable;
$mapping['targetEntity'] = $manyToManyAnnot->targetEntity;
$mapping['mappedBy'] = $manyToManyAnnot->mappedBy;
$mapping['inversedBy'] = $manyToManyAnnot->inversedBy;
$mapping['cascade'] = $manyToManyAnnot->cascade;
$mapping['indexBy'] = $manyToManyAnnot->indexBy;
$mapping['orphanRemoval'] = $manyToManyAnnot->orphanRemoval;
$mapping['fetch'] = $this->getFetchMode($className, $manyToManyAnnot->fetch);
if ($orderByAnnot = $this->reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\OrderBy')) {
$mapping['orderBy'] = $orderByAnnot->value;
}
$metadata->mapManyToMany($mapping);
}
}
// Evaluate AssociationOverrides annotation
if (isset($classAnnotations['Doctrine\ORM\Mapping\AssociationOverrides'])) {
$associationOverridesAnnot = $classAnnotations['Doctrine\ORM\Mapping\AssociationOverrides'];
foreach ($associationOverridesAnnot->value as $associationOverride) {
$override = array();
$fieldName = $associationOverride->name;
// Check for JoinColummn/JoinColumns annotations
if ($associationOverride->joinColumns) {
$joinColumns = array();
foreach ($associationOverride->joinColumns as $joinColumn) {
$joinColumns[] = $this->joinColumnToArray($joinColumn);
}
$override['joinColumns'] = $joinColumns;
}
// Check for JoinTable annotations
if ($associationOverride->joinTable) {
$joinTable = null;
$joinTableAnnot = $associationOverride->joinTable;
$joinTable = array(
'name' => $joinTableAnnot->name,
'schema' => $joinTableAnnot->schema
);
foreach ($joinTableAnnot->joinColumns as $joinColumn) {
$joinTable['joinColumns'][] = $this->joinColumnToArray($joinColumn);
}
foreach ($joinTableAnnot->inverseJoinColumns as $joinColumn) {
$joinTable['inverseJoinColumns'][] = $this->joinColumnToArray($joinColumn);
}
$override['joinTable'] = $joinTable;
}
$metadata->setAssociationOverride($fieldName, $override);
}
}
// Evaluate AttributeOverrides annotation
if (isset($classAnnotations['Doctrine\ORM\Mapping\AttributeOverrides'])) {
$attributeOverridesAnnot = $classAnnotations['Doctrine\ORM\Mapping\AttributeOverrides'];
foreach ($attributeOverridesAnnot->value as $attributeOverrideAnnot) {
$attributeOverride = $this->columnToArray($attributeOverrideAnnot->name, $attributeOverrideAnnot->column);
$metadata->setAttributeOverride($attributeOverrideAnnot->name, $attributeOverride);
}
}
// Evaluate @HasLifecycleCallbacks annotation
if (isset($classAnnotations['Doctrine\ORM\Mapping\HasLifecycleCallbacks'])) {
/* @var $method \ReflectionMethod */
foreach ($class->getMethods() as $method) {
// filter for the declaring class only, callbacks from parents will already be registered.
if ($method->isPublic() && $method->getDeclaringClass()->getName() == $class->name) {
$annotations = $this->reader->getMethodAnnotations($method);
if ($annotations) {
foreach ($annotations as $key => $annot) {
if ( ! is_numeric($key)) {
continue;
}
$annotations[get_class($annot)] = $annot;
}
}
if (isset($annotations['Doctrine\ORM\Mapping\PrePersist'])) {
$metadata->addLifecycleCallback($method->getName(), \Doctrine\ORM\Events::prePersist);
}
if (isset($annotations['Doctrine\ORM\Mapping\PostPersist'])) {
$metadata->addLifecycleCallback($method->getName(), \Doctrine\ORM\Events::postPersist);
}
if (isset($annotations['Doctrine\ORM\Mapping\PreUpdate'])) {
$metadata->addLifecycleCallback($method->getName(), \Doctrine\ORM\Events::preUpdate);
}
if (isset($annotations['Doctrine\ORM\Mapping\PostUpdate'])) {
$metadata->addLifecycleCallback($method->getName(), \Doctrine\ORM\Events::postUpdate);
}
if (isset($annotations['Doctrine\ORM\Mapping\PreRemove'])) {
$metadata->addLifecycleCallback($method->getName(), \Doctrine\ORM\Events::preRemove);
}
if (isset($annotations['Doctrine\ORM\Mapping\PostRemove'])) {
$metadata->addLifecycleCallback($method->getName(), \Doctrine\ORM\Events::postRemove);
}
if (isset($annotations['Doctrine\ORM\Mapping\PostLoad'])) {
$metadata->addLifecycleCallback($method->getName(), \Doctrine\ORM\Events::postLoad);
}
if (isset($annotations['Doctrine\ORM\Mapping\PreFlush'])) {
$metadata->addLifecycleCallback($method->getName(), \Doctrine\ORM\Events::preFlush);
}
}
}
}
}
/**
* Attempts to resolve the fetch mode.
*
* @param string $className The class name
* @param string $fetchMode The fetch mode
* @return integer The fetch mode as defined in ClassMetadata
* @throws MappingException If the fetch mode is not valid
*/
private function getFetchMode($className, $fetchMode)
{
if( ! defined('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . $fetchMode)) {
throw MappingException::invalidFetchMode($className, $fetchMode);
}
return constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . $fetchMode);
}
/**
* Parse the given JoinColumn as array
*
* @param JoinColumn $joinColumn
* @return array
*/
private function joinColumnToArray(JoinColumn $joinColumn)
{
return array(
'name' => $joinColumn->name,
'unique' => $joinColumn->unique,
'nullable' => $joinColumn->nullable,
'onDelete' => $joinColumn->onDelete,
'columnDefinition' => $joinColumn->columnDefinition,
'referencedColumnName' => $joinColumn->referencedColumnName,
);
}
/**
* Parse the given Column as array
*
* @param string $fieldName
* @param Column $column
* @return array
*/
private function columnToArray($fieldName, Column $column)
{
$mapping = array(
'fieldName' => $fieldName,
'type' => $column->type,
'scale' => $column->scale,
'length' => $column->length,
'unique' => $column->unique,
'nullable' => $column->nullable,
'precision' => $column->precision
);
if ($column->options) {
$mapping['options'] = $column->options;
}
if (isset($column->name)) {
$mapping['columnName'] = $column->name;
}
if (isset($column->columnDefinition)) {
$mapping['columnDefinition'] = $column->columnDefinition;
}
return $mapping;
}
/**
* Factory method for the Annotation Driver
*
* @param array|string $paths
* @param AnnotationReader $reader
* @return AnnotationDriver
*/
static public function create($paths = array(), AnnotationReader $reader = null)
{
if ($reader == null) {
$reader = new AnnotationReader();
}
return new self($reader, $paths);
}
}
DoctrineORM-2.3.3/Doctrine/ORM/Mapping/Driver/DatabaseDriver.php 0000644 0001751 0001751 00000035153 12143374607 024413 0 ustar beberlei beberlei .
*/
namespace Doctrine\ORM\Mapping\Driver;
use Doctrine\DBAL\Schema\AbstractSchemaManager,
Doctrine\DBAL\Schema\SchemaException,
Doctrine\Common\Persistence\Mapping\Driver\MappingDriver,
Doctrine\Common\Persistence\Mapping\ClassMetadata,
Doctrine\ORM\Mapping\ClassMetadataInfo,
Doctrine\Common\Util\Inflector,
Doctrine\ORM\Mapping\MappingException;
/**
* The DatabaseDriver reverse engineers the mapping metadata from a database.
*
*
* @link www.doctrine-project.org
* @since 2.0
* @author Guilherme Blanco
* @author Jonathan Wage
* @author Benjamin Eberlei
*/
class DatabaseDriver implements MappingDriver
{
/**
* @var AbstractSchemaManager
*/
private $_sm;
/**
* @var array
*/
private $tables = null;
private $classToTableNames = array();
/**
* @var array
*/
private $manyToManyTables = array();
/**
* @var array
*/
private $classNamesForTables = array();
/**
* @var array
*/
private $fieldNamesForColumns = array();
/**
* The namespace for the generated entities.
*
* @var string
*/
private $namespace;
/**
*
* @param AbstractSchemaManager $schemaManager
*/
public function __construct(AbstractSchemaManager $schemaManager)
{
$this->_sm = $schemaManager;
}
/**
* Set tables manually instead of relying on the reverse engeneering capabilities of SchemaManager.
*
* @param array $entityTables
* @param array $manyToManyTables
* @return void
*/
public function setTables($entityTables, $manyToManyTables)
{
$this->tables = $this->manyToManyTables = $this->classToTableNames = array();
foreach ($entityTables as $table) {
$className = $this->getClassNameForTable($table->getName());
$this->classToTableNames[$className] = $table->getName();
$this->tables[$table->getName()] = $table;
}
foreach ($manyToManyTables as $table) {
$this->manyToManyTables[$table->getName()] = $table;
}
}
private function reverseEngineerMappingFromDatabase()
{
if ($this->tables !== null) {
return;
}
$tables = array();
foreach ($this->_sm->listTableNames() as $tableName) {
$tables[$tableName] = $this->_sm->listTableDetails($tableName);
}
$this->tables = $this->manyToManyTables = $this->classToTableNames = array();
foreach ($tables as $tableName => $table) {
/* @var $table \Doctrine\DBAL\Schema\Table */
if ($this->_sm->getDatabasePlatform()->supportsForeignKeyConstraints()) {
$foreignKeys = $table->getForeignKeys();
} else {
$foreignKeys = array();
}
$allForeignKeyColumns = array();
foreach ($foreignKeys as $foreignKey) {
$allForeignKeyColumns = array_merge($allForeignKeyColumns, $foreignKey->getLocalColumns());
}
if ( ! $table->hasPrimaryKey()) {
throw new MappingException(
"Table " . $table->getName() . " has no primary key. Doctrine does not ".
"support reverse engineering from tables that don't have a primary key."
);
}
$pkColumns = $table->getPrimaryKey()->getColumns();
sort($pkColumns);
sort($allForeignKeyColumns);
if ($pkColumns == $allForeignKeyColumns && count($foreignKeys) == 2) {
$this->manyToManyTables[$tableName] = $table;
} else {
// lower-casing is necessary because of Oracle Uppercase Tablenames,
// assumption is lower-case + underscore separated.
$className = $this->getClassNameForTable($tableName);
$this->tables[$tableName] = $table;
$this->classToTableNames[$className] = $tableName;
}
}
}
/**
* {@inheritDoc}
*/
public function loadMetadataForClass($className, ClassMetadata $metadata)
{
$this->reverseEngineerMappingFromDatabase();
if (!isset($this->classToTableNames[$className])) {
throw new \InvalidArgumentException("Unknown class " . $className);
}
$tableName = $this->classToTableNames[$className];
$metadata->name = $className;
$metadata->table['name'] = $tableName;
$columns = $this->tables[$tableName]->getColumns();
$indexes = $this->tables[$tableName]->getIndexes();
try {
$primaryKeyColumns = $this->tables[$tableName]->getPrimaryKey()->getColumns();
} catch(SchemaException $e) {
$primaryKeyColumns = array();
}
if ($this->_sm->getDatabasePlatform()->supportsForeignKeyConstraints()) {
$foreignKeys = $this->tables[$tableName]->getForeignKeys();
} else {
$foreignKeys = array();
}
$allForeignKeyColumns = array();
foreach ($foreignKeys as $foreignKey) {
$allForeignKeyColumns = array_merge($allForeignKeyColumns, $foreignKey->getLocalColumns());
}
$ids = array();
$fieldMappings = array();
foreach ($columns as $column) {
$fieldMapping = array();
if (in_array($column->getName(), $allForeignKeyColumns)) {
continue;
} else if ($primaryKeyColumns && in_array($column->getName(), $primaryKeyColumns)) {
$fieldMapping['id'] = true;
}
$fieldMapping['fieldName'] = $this->getFieldNameForColumn($tableName, $column->getName(), false);
$fieldMapping['columnName'] = $column->getName();
$fieldMapping['type'] = strtolower((string) $column->getType());
if ($column->getType() instanceof \Doctrine\DBAL\Types\StringType) {
$fieldMapping['length'] = $column->getLength();
$fieldMapping['fixed'] = $column->getFixed();
} else if ($column->getType() instanceof \Doctrine\DBAL\Types\IntegerType) {
$fieldMapping['unsigned'] = $column->getUnsigned();
}
$fieldMapping['nullable'] = $column->getNotNull() ? false : true;
if (isset($fieldMapping['id'])) {
$ids[] = $fieldMapping;
} else {
$fieldMappings[] = $fieldMapping;
}
}
if ($ids) {
if (count($ids) == 1) {
$metadata->setIdGeneratorType(ClassMetadataInfo::GENERATOR_TYPE_AUTO);
}
foreach ($ids as $id) {
$metadata->mapField($id);
}
}
foreach ($fieldMappings as $fieldMapping) {
$metadata->mapField($fieldMapping);
}
foreach ($this->manyToManyTables as $manyTable) {
foreach ($manyTable->getForeignKeys() as $foreignKey) {
// foreign key maps to the table of the current entity, many to many association probably exists
if (strtolower($tableName) == strtolower($foreignKey->getForeignTableName())) {
$myFk = $foreignKey;
$otherFk = null;
foreach ($manyTable->getForeignKeys() as $foreignKey) {
if ($foreignKey != $myFk) {
$otherFk = $foreignKey;
break;
}
}
if (!$otherFk) {
// the definition of this many to many table does not contain
// enough foreign key information to continue reverse engeneering.
continue;
}
$localColumn = current($myFk->getColumns());
$associationMapping = array();
$associationMapping['fieldName'] = $this->getFieldNameForColumn($manyTable->getName(), current($otherFk->getColumns()), true);
$associationMapping['targetEntity'] = $this->getClassNameForTable($otherFk->getForeignTableName());
if (current($manyTable->getColumns())->getName() == $localColumn) {
$associationMapping['inversedBy'] = $this->getFieldNameForColumn($manyTable->getName(), current($myFk->getColumns()), true);
$associationMapping['joinTable'] = array(
'name' => strtolower($manyTable->getName()),
'joinColumns' => array(),
'inverseJoinColumns' => array(),
);
$fkCols = $myFk->getForeignColumns();
$cols = $myFk->getColumns();
for ($i = 0; $i < count($cols); $i++) {
$associationMapping['joinTable']['joinColumns'][] = array(
'name' => $cols[$i],
'referencedColumnName' => $fkCols[$i],
);
}
$fkCols = $otherFk->getForeignColumns();
$cols = $otherFk->getColumns();
for ($i = 0; $i < count($cols); $i++) {
$associationMapping['joinTable']['inverseJoinColumns'][] = array(
'name' => $cols[$i],
'referencedColumnName' => $fkCols[$i],
);
}
} else {
$associationMapping['mappedBy'] = $this->getFieldNameForColumn($manyTable->getName(), current($myFk->getColumns()), true);
}
$metadata->mapManyToMany($associationMapping);
break;
}
}
}
foreach ($foreignKeys as $foreignKey) {
$foreignTable = $foreignKey->getForeignTableName();
$cols = $foreignKey->getColumns();
$fkCols = $foreignKey->getForeignColumns();
$localColumn = current($cols);
$associationMapping = array();
$associationMapping['fieldName'] = $this->getFieldNameForColumn($tableName, $localColumn, true);
$associationMapping['targetEntity'] = $this->getClassNameForTable($foreignTable);
if (isset($metadata->fieldMappings[$associationMapping['fieldName']])) {
$associationMapping['fieldName'] = $associationMapping['fieldName'] . "2";
}
if ($primaryKeyColumns && in_array($localColumn, $primaryKeyColumns)) {
$associationMapping['id'] = true;
}
for ($i = 0; $i < count($cols); $i++) {
$associationMapping['joinColumns'][] = array(
'name' => $cols[$i],
'referencedColumnName' => $fkCols[$i],
);
}
//Here we need to check if $cols are the same as $primaryKeyColums
if (!array_diff($cols,$primaryKeyColumns)) {
$metadata->mapOneToOne($associationMapping);
} else {
$metadata->mapManyToOne($associationMapping);
}
}
}
/**
* {@inheritDoc}
*/
public function isTransient($className)
{
return true;
}
/**
* {@inheritDoc}
*/
public function getAllClassNames()
{
$this->reverseEngineerMappingFromDatabase();
return array_keys($this->classToTableNames);
}
/**
* Set class name for a table.
*
* @param string $tableName
* @param string $className
* @return void
*/
public function setClassNameForTable($tableName, $className)
{
$this->classNamesForTables[$tableName] = $className;
}
/**
* Set field name for a column on a specific table.
*
* @param string $tableName
* @param string $columnName
* @param string $fieldName
* @return void
*/
public function setFieldNameForColumn($tableName, $columnName, $fieldName)
{
$this->fieldNamesForColumns[$tableName][$columnName] = $fieldName;
}
/**
* Return the mapped class name for a table if it exists. Otherwise return "classified" version.
*
* @param string $tableName
* @return string
*/
private function getClassNameForTable($tableName)
{
if (isset($this->classNamesForTables[$tableName])) {
return $this->namespace . $this->classNamesForTables[$tableName];
}
return $this->namespace . Inflector::classify(strtolower($tableName));
}
/**
* Return the mapped field name for a column, if it exists. Otherwise return camelized version.
*
* @param string $tableName
* @param string $columnName
* @param boolean $fk Whether the column is a foreignkey or not.
* @return string
*/
private function getFieldNameForColumn($tableName, $columnName, $fk = false)
{
if (isset($this->fieldNamesForColumns[$tableName]) && isset($this->fieldNamesForColumns[$tableName][$columnName])) {
return $this->fieldNamesForColumns[$tableName][$columnName];
}
$columnName = strtolower($columnName);
// Replace _id if it is a foreignkey column
if ($fk) {
$columnName = str_replace('_id', '', $columnName);
}
return Inflector::camelize($columnName);
}
/**
* Set the namespace for the generated entities.
*
* @param string $namespace
* @return void
*/
public function setNamespace($namespace)
{
$this->namespace = $namespace;
}
}
DoctrineORM-2.3.3/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php 0000644 0001751 0001751 00000006061 12143374607 025514 0 ustar beberlei beberlei .
*/
require_once __DIR__.'/../Annotation.php';
require_once __DIR__.'/../Entity.php';
require_once __DIR__.'/../MappedSuperclass.php';
require_once __DIR__.'/../InheritanceType.php';
require_once __DIR__.'/../DiscriminatorColumn.php';
require_once __DIR__.'/../DiscriminatorMap.php';
require_once __DIR__.'/../Id.php';
require_once __DIR__.'/../GeneratedValue.php';
require_once __DIR__.'/../Version.php';
require_once __DIR__.'/../JoinColumn.php';
require_once __DIR__.'/../JoinColumns.php';
require_once __DIR__.'/../Column.php';
require_once __DIR__.'/../OneToOne.php';
require_once __DIR__.'/../OneToMany.php';
require_once __DIR__.'/../ManyToOne.php';
require_once __DIR__.'/../ManyToMany.php';
require_once __DIR__.'/../ElementCollection.php';
require_once __DIR__.'/../Table.php';
require_once __DIR__.'/../UniqueConstraint.php';
require_once __DIR__.'/../Index.php';
require_once __DIR__.'/../JoinTable.php';
require_once __DIR__.'/../SequenceGenerator.php';
require_once __DIR__.'/../CustomIdGenerator.php';
require_once __DIR__.'/../ChangeTrackingPolicy.php';
require_once __DIR__.'/../OrderBy.php';
require_once __DIR__.'/../NamedQueries.php';
require_once __DIR__.'/../NamedQuery.php';
require_once __DIR__.'/../HasLifecycleCallbacks.php';
require_once __DIR__.'/../PrePersist.php';
require_once __DIR__.'/../PostPersist.php';
require_once __DIR__.'/../PreUpdate.php';
require_once __DIR__.'/../PostUpdate.php';
require_once __DIR__.'/../PreRemove.php';
require_once __DIR__.'/../PostRemove.php';
require_once __DIR__.'/../PostLoad.php';
require_once __DIR__.'/../PreFlush.php';
require_once __DIR__.'/../FieldResult.php';
require_once __DIR__.'/../ColumnResult.php';
require_once __DIR__.'/../EntityResult.php';
require_once __DIR__.'/../NamedNativeQuery.php';
require_once __DIR__.'/../NamedNativeQueries.php';
require_once __DIR__.'/../SqlResultSetMapping.php';
require_once __DIR__.'/../SqlResultSetMappings.php';
require_once __DIR__.'/../AssociationOverride.php';
require_once __DIR__.'/../AssociationOverrides.php';
require_once __DIR__.'/../AttributeOverride.php';
require_once __DIR__.'/../AttributeOverrides.php';
DoctrineORM-2.3.3/Doctrine/ORM/Mapping/Driver/DriverChain.php 0000644 0001751 0001751 00000002413 12143374607 023722 0 ustar beberlei beberlei .
*/
namespace Doctrine\ORM\Mapping\Driver;
use Doctrine\Common\Persistence\Mapping\Driver\MappingDriverChain;
/**
* {@inheritDoc}
*
* @deprecated this driver will be removed. Use Doctrine\Common\Persistence\Mapping\Driver\MappingDriverChain instead
*/
class DriverChain extends MappingDriverChain
{
} DoctrineORM-2.3.3/Doctrine/ORM/Mapping/Driver/PHPDriver.php 0000644 0001751 0001751 00000002407 12143374607 023332 0 ustar beberlei beberlei .
*/
namespace Doctrine\ORM\Mapping\Driver;
use Doctrine\Common\Persistence\Mapping\Driver\PHPDriver as CommonPHPDriver;
/**
* {@inheritDoc}
*
* @deprecated this driver will be removed. Use Doctrine\Common\Persistence\Mapping\Driver\PHPDriver instead
*/
class PHPDriver extends CommonPHPDriver
{
} DoctrineORM-2.3.3/Doctrine/ORM/Mapping/Driver/SimplifiedXmlDriver.php 0000644 0001751 0001751 00000003205 12143374607 025446 0 ustar beberlei beberlei .
*/
namespace Doctrine\ORM\Mapping\Driver;
use Doctrine\Common\Persistence\Mapping\Driver\SymfonyFileLocator;
/**
* XmlDriver that additionally looks for mapping information in a global file.
*
* @author Fabien Potencier
* @author Benjamin Eberlei
* @license MIT
*/
class SimplifiedXmlDriver extends XmlDriver
{
const DEFAULT_FILE_EXTENSION = '.orm.xml';
/**
* {@inheritDoc}
*/
public function __construct($prefixes, $fileExtension = self::DEFAULT_FILE_EXTENSION)
{
$locator = new SymfonyFileLocator((array) $prefixes, $fileExtension);
parent::__construct($locator, $fileExtension);
}
}
DoctrineORM-2.3.3/Doctrine/ORM/Mapping/Driver/SimplifiedYamlDriver.php 0000644 0001751 0001751 00000003210 12143374607 025604 0 ustar beberlei beberlei .
*/
namespace Doctrine\ORM\Mapping\Driver;
use Doctrine\Common\Persistence\Mapping\Driver\SymfonyFileLocator;
/**
* YamlDriver that additionally looks for mapping information in a global file.
*
* @author Fabien Potencier
* @author Benjamin Eberlei
* @license MIT
*/
class SimplifiedYamlDriver extends YamlDriver
{
const DEFAULT_FILE_EXTENSION = '.orm.yml';
/**
* {@inheritDoc}
*/
public function __construct($prefixes, $fileExtension = self::DEFAULT_FILE_EXTENSION)
{
$locator = new SymfonyFileLocator((array) $prefixes, $fileExtension);
parent::__construct($locator, $fileExtension);
}
}
DoctrineORM-2.3.3/Doctrine/ORM/Mapping/Driver/StaticPHPDriver.php 0000644 0001751 0001751 00000002445 12143374607 024504 0 ustar beberlei beberlei .
*/
namespace Doctrine\ORM\Mapping\Driver;
use Doctrine\Common\Persistence\Mapping\Driver\StaticPHPDriver as CommonStaticPHPDriver;
/**
* {@inheritDoc}
*
* @deprecated this driver will be removed. Use Doctrine\Common\Persistence\Mapping\Driver\StaticPHPDriver instead
*/
class StaticPHPDriver extends CommonStaticPHPDriver
{
} DoctrineORM-2.3.3/Doctrine/ORM/Mapping/Driver/XmlDriver.php 0000644 0001751 0001751 00000073473 12143374607 023456 0 ustar beberlei beberlei .
*/
namespace Doctrine\ORM\Mapping\Driver;
use SimpleXMLElement,
Doctrine\Common\Persistence\Mapping\Driver\FileDriver,
Doctrine\Common\Persistence\Mapping\ClassMetadata,
Doctrine\ORM\Mapping\MappingException;
/**
* XmlDriver is a metadata driver that enables mapping through XML files.
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org
* @since 2.0
* @author Benjamin Eberlei
* @author Guilherme Blanco
* @author Jonathan H. Wage
* @author Roman Borschel
*/
class XmlDriver extends FileDriver
{
const DEFAULT_FILE_EXTENSION = '.dcm.xml';
/**
* {@inheritDoc}
*/
public function __construct($locator, $fileExtension = self::DEFAULT_FILE_EXTENSION)
{
parent::__construct($locator, $fileExtension);
}
/**
* {@inheritDoc}
*/
public function loadMetadataForClass($className, ClassMetadata $metadata)
{
/* @var $metadata \Doctrine\ORM\Mapping\ClassMetadataInfo */
/* @var $xmlRoot SimpleXMLElement */
$xmlRoot = $this->getElement($className);
if ($xmlRoot->getName() == 'entity') {
if (isset($xmlRoot['repository-class'])) {
$metadata->setCustomRepositoryClass((string)$xmlRoot['repository-class']);
}
if (isset($xmlRoot['read-only']) && $this->evaluateBoolean($xmlRoot['read-only'])) {
$metadata->markReadOnly();
}
} else if ($xmlRoot->getName() == 'mapped-superclass') {
$metadata->setCustomRepositoryClass(
isset($xmlRoot['repository-class']) ? (string)$xmlRoot['repository-class'] : null
);
$metadata->isMappedSuperclass = true;
} else {
throw MappingException::classIsNotAValidEntityOrMappedSuperClass($className);
}
// Evaluate attributes
$table = array();
if (isset($xmlRoot['table'])) {
$table['name'] = (string)$xmlRoot['table'];
}
$metadata->setPrimaryTable($table);
// Evaluate named queries
if (isset($xmlRoot->{'named-queries'})) {
foreach ($xmlRoot->{'named-queries'}->{'named-query'} as $namedQueryElement) {
$metadata->addNamedQuery(array(
'name' => (string)$namedQueryElement['name'],
'query' => (string)$namedQueryElement['query']
));
}
}
// Evaluate native named queries
if (isset($xmlRoot->{'named-native-queries'})) {
foreach ($xmlRoot->{'named-native-queries'}->{'named-native-query'} as $nativeQueryElement) {
$metadata->addNamedNativeQuery(array(
'name' => isset($nativeQueryElement['name']) ? (string)$nativeQueryElement['name'] : null,
'query' => isset($nativeQueryElement->query) ? (string)$nativeQueryElement->query : null,
'resultClass' => isset($nativeQueryElement['result-class']) ? (string)$nativeQueryElement['result-class'] : null,
'resultSetMapping' => isset($nativeQueryElement['result-set-mapping']) ? (string)$nativeQueryElement['result-set-mapping'] : null,
));
}
}
// Evaluate sql result set mapping
if (isset($xmlRoot->{'sql-result-set-mappings'})) {
foreach ($xmlRoot->{'sql-result-set-mappings'}->{'sql-result-set-mapping'} as $rsmElement) {
$entities = array();
$columns = array();
foreach ($rsmElement as $entityElement) {
//
if (isset($entityElement['entity-class'])) {
$entityResult = array(
'fields' => array(),
'entityClass' => (string)$entityElement['entity-class'],
'discriminatorColumn' => isset($entityElement['discriminator-column']) ? (string)$entityElement['discriminator-column'] : null,
);
foreach ($entityElement as $fieldElement) {
$entityResult['fields'][] = array(
'name' => isset($fieldElement['name']) ? (string)$fieldElement['name'] : null,
'column' => isset($fieldElement['column']) ? (string)$fieldElement['column'] : null,
);
}
$entities[] = $entityResult;
}
//
if (isset($entityElement['name'])) {
$columns[] = array(
'name' => (string)$entityElement['name'],
);
}
}
$metadata->addSqlResultSetMapping(array(
'name' => (string)$rsmElement['name'],
'entities' => $entities,
'columns' => $columns
));
}
}
/* not implemented specially anyway. use table = schema.table
if (isset($xmlRoot['schema'])) {
$metadata->table['schema'] = (string)$xmlRoot['schema'];
}*/
if (isset($xmlRoot['inheritance-type'])) {
$inheritanceType = (string)$xmlRoot['inheritance-type'];
$metadata->setInheritanceType(constant('Doctrine\ORM\Mapping\ClassMetadata::INHERITANCE_TYPE_' . $inheritanceType));
if ($metadata->inheritanceType != \Doctrine\ORM\Mapping\ClassMetadata::INHERITANCE_TYPE_NONE) {
// Evaluate
if (isset($xmlRoot->{'discriminator-column'})) {
$discrColumn = $xmlRoot->{'discriminator-column'};
$metadata->setDiscriminatorColumn(array(
'name' => isset($discrColumn['name']) ? (string)$discrColumn['name'] : null,
'type' => isset($discrColumn['type']) ? (string)$discrColumn['type'] : null,
'length' => isset($discrColumn['length']) ? (string)$discrColumn['length'] : null,
'columnDefinition' => isset($discrColumn['column-definition']) ? (string)$discrColumn['column-definition'] : null
));
} else {
$metadata->setDiscriminatorColumn(array('name' => 'dtype', 'type' => 'string', 'length' => 255));
}
// Evaluate
if (isset($xmlRoot->{'discriminator-map'})) {
$map = array();
foreach ($xmlRoot->{'discriminator-map'}->{'discriminator-mapping'} as $discrMapElement) {
$map[(string)$discrMapElement['value']] = (string)$discrMapElement['class'];
}
$metadata->setDiscriminatorMap($map);
}
}
}
// Evaluate
if (isset($xmlRoot['change-tracking-policy'])) {
$metadata->setChangeTrackingPolicy(constant('Doctrine\ORM\Mapping\ClassMetadata::CHANGETRACKING_'
. strtoupper((string)$xmlRoot['change-tracking-policy'])));
}
// Evaluate
if (isset($xmlRoot->indexes)) {
$metadata->table['indexes'] = array();
foreach ($xmlRoot->indexes->index as $index) {
$columns = explode(',', (string)$index['columns']);
if (isset($index['name'])) {
$metadata->table['indexes'][(string)$index['name']] = array(
'columns' => $columns
);
} else {
$metadata->table['indexes'][] = array(
'columns' => $columns
);
}
}
}
// Evaluate
if (isset($xmlRoot->{'unique-constraints'})) {
$metadata->table['uniqueConstraints'] = array();
foreach ($xmlRoot->{'unique-constraints'}->{'unique-constraint'} as $unique) {
$columns = explode(',', (string)$unique['columns']);
if (isset($unique['name'])) {
$metadata->table['uniqueConstraints'][(string)$unique['name']] = array(
'columns' => $columns
);
} else {
$metadata->table['uniqueConstraints'][] = array(
'columns' => $columns
);
}
}
}
if (isset($xmlRoot->options)) {
$metadata->table['options'] = $this->_parseOptions($xmlRoot->options->children());
}
// The mapping assignement is done in 2 times as a bug might occurs on some php/xml lib versions
// The internal SimpleXmlIterator get resetted, to this generate a duplicate field exception
$mappings = array();
// Evaluate mappings
if (isset($xmlRoot->field)) {
foreach ($xmlRoot->field as $fieldMapping) {
$mapping = $this->columnToArray($fieldMapping);
if (isset($mapping['version'])) {
$metadata->setVersionMapping($mapping);
unset($mapping['version']);
}
$metadata->mapField($mapping);
}
}
foreach ($mappings as $mapping) {
if (isset($mapping['version'])) {
$metadata->setVersionMapping($mapping);
}
$metadata->mapField($mapping);
}
// Evaluate mappings
$associationIds = array();
foreach ($xmlRoot->id as $idElement) {
if (isset($idElement['association-key']) && $this->evaluateBoolean($idElement['association-key'])) {
$associationIds[(string)$idElement['name']] = true;
continue;
}
$mapping = array(
'id' => true,
'fieldName' => (string)$idElement['name']
);
if (isset($idElement['type'])) {
$mapping['type'] = (string)$idElement['type'];
}
if (isset($idElement['length'])) {
$mapping['length'] = (string)$idElement['length'];
}
if (isset($idElement['column'])) {
$mapping['columnName'] = (string)$idElement['column'];
}
if (isset($idElement['column-definition'])) {
$mapping['columnDefinition'] = (string)$idElement['column-definition'];
}
$metadata->mapField($mapping);
if (isset($idElement->generator)) {
$strategy = isset($idElement->generator['strategy']) ?
(string)$idElement->generator['strategy'] : 'AUTO';
$metadata->setIdGeneratorType(constant('Doctrine\ORM\Mapping\ClassMetadata::GENERATOR_TYPE_'
. $strategy));
}
// Check for SequenceGenerator/TableGenerator definition
if (isset($idElement->{'sequence-generator'})) {
$seqGenerator = $idElement->{'sequence-generator'};
$metadata->setSequenceGeneratorDefinition(array(
'sequenceName' => (string)$seqGenerator['sequence-name'],
'allocationSize' => (string)$seqGenerator['allocation-size'],
'initialValue' => (string)$seqGenerator['initial-value']
));
} else if (isset($idElement->{'custom-id-generator'})) {
$customGenerator = $idElement->{'custom-id-generator'};
$metadata->setCustomGeneratorDefinition(array(
'class' => (string) $customGenerator['class']
));
} else if (isset($idElement->{'table-generator'})) {
throw MappingException::tableIdGeneratorNotImplemented($className);
}
}
// Evaluate mappings
if (isset($xmlRoot->{'one-to-one'})) {
foreach ($xmlRoot->{'one-to-one'} as $oneToOneElement) {
$mapping = array(
'fieldName' => (string)$oneToOneElement['field'],
'targetEntity' => (string)$oneToOneElement['target-entity']
);
if (isset($associationIds[$mapping['fieldName']])) {
$mapping['id'] = true;
}
if (isset($oneToOneElement['fetch'])) {
$mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . (string)$oneToOneElement['fetch']);
}
if (isset($oneToOneElement['mapped-by'])) {
$mapping['mappedBy'] = (string)$oneToOneElement['mapped-by'];
} else {
if (isset($oneToOneElement['inversed-by'])) {
$mapping['inversedBy'] = (string)$oneToOneElement['inversed-by'];
}
$joinColumns = array();
if (isset($oneToOneElement->{'join-column'})) {
$joinColumns[] = $this->joinColumnToArray($oneToOneElement->{'join-column'});
} else if (isset($oneToOneElement->{'join-columns'})) {
foreach ($oneToOneElement->{'join-columns'}->{'join-column'} as $joinColumnElement) {
$joinColumns[] = $this->joinColumnToArray($joinColumnElement);
}
}
$mapping['joinColumns'] = $joinColumns;
}
if (isset($oneToOneElement->cascade)) {
$mapping['cascade'] = $this->_getCascadeMappings($oneToOneElement->cascade);
}
if (isset($oneToOneElement['orphan-removal'])) {
$mapping['orphanRemoval'] = $this->evaluateBoolean($oneToOneElement['orphan-removal']);
}
$metadata->mapOneToOne($mapping);
}
}
// Evaluate mappings
if (isset($xmlRoot->{'one-to-many'})) {
foreach ($xmlRoot->{'one-to-many'} as $oneToManyElement) {
$mapping = array(
'fieldName' => (string)$oneToManyElement['field'],
'targetEntity' => (string)$oneToManyElement['target-entity'],
'mappedBy' => (string)$oneToManyElement['mapped-by']
);
if (isset($oneToManyElement['fetch'])) {
$mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . (string)$oneToManyElement['fetch']);
}
if (isset($oneToManyElement->cascade)) {
$mapping['cascade'] = $this->_getCascadeMappings($oneToManyElement->cascade);
}
if (isset($oneToManyElement['orphan-removal'])) {
$mapping['orphanRemoval'] = $this->evaluateBoolean($oneToManyElement['orphan-removal']);
}
if (isset($oneToManyElement->{'order-by'})) {
$orderBy = array();
foreach ($oneToManyElement->{'order-by'}->{'order-by-field'} as $orderByField) {
$orderBy[(string)$orderByField['name']] = (string)$orderByField['direction'];
}
$mapping['orderBy'] = $orderBy;
}
if (isset($oneToManyElement['index-by'])) {
$mapping['indexBy'] = (string)$oneToManyElement['index-by'];
} else if (isset($oneToManyElement->{'index-by'})) {
throw new \InvalidArgumentException(" is not a valid tag");
}
$metadata->mapOneToMany($mapping);
}
}
// Evaluate mappings
if (isset($xmlRoot->{'many-to-one'})) {
foreach ($xmlRoot->{'many-to-one'} as $manyToOneElement) {
$mapping = array(
'fieldName' => (string)$manyToOneElement['field'],
'targetEntity' => (string)$manyToOneElement['target-entity']
);
if (isset($associationIds[$mapping['fieldName']])) {
$mapping['id'] = true;
}
if (isset($manyToOneElement['fetch'])) {
$mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . (string)$manyToOneElement['fetch']);
}
if (isset($manyToOneElement['inversed-by'])) {
$mapping['inversedBy'] = (string)$manyToOneElement['inversed-by'];
}
$joinColumns = array();
if (isset($manyToOneElement->{'join-column'})) {
$joinColumns[] = $this->joinColumnToArray($manyToOneElement->{'join-column'});
} else if (isset($manyToOneElement->{'join-columns'})) {
foreach ($manyToOneElement->{'join-columns'}->{'join-column'} as $joinColumnElement) {
$joinColumns[] = $this->joinColumnToArray($joinColumnElement);
}
}
$mapping['joinColumns'] = $joinColumns;
if (isset($manyToOneElement->cascade)) {
$mapping['cascade'] = $this->_getCascadeMappings($manyToOneElement->cascade);
}
$metadata->mapManyToOne($mapping);
}
}
// Evaluate mappings
if (isset($xmlRoot->{'many-to-many'})) {
foreach ($xmlRoot->{'many-to-many'} as $manyToManyElement) {
$mapping = array(
'fieldName' => (string)$manyToManyElement['field'],
'targetEntity' => (string)$manyToManyElement['target-entity']
);
if (isset($manyToManyElement['fetch'])) {
$mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . (string)$manyToManyElement['fetch']);
}
if (isset($manyToManyElement['orphan-removal'])) {
$mapping['orphanRemoval'] = $this->evaluateBoolean($manyToManyElement['orphan-removal']);
}
if (isset($manyToManyElement['mapped-by'])) {
$mapping['mappedBy'] = (string)$manyToManyElement['mapped-by'];
} else if (isset($manyToManyElement->{'join-table'})) {
if (isset($manyToManyElement['inversed-by'])) {
$mapping['inversedBy'] = (string)$manyToManyElement['inversed-by'];
}
$joinTableElement = $manyToManyElement->{'join-table'};
$joinTable = array(
'name' => (string)$joinTableElement['name']
);
if (isset($joinTableElement['schema'])) {
$joinTable['schema'] = (string)$joinTableElement['schema'];
}
foreach ($joinTableElement->{'join-columns'}->{'join-column'} as $joinColumnElement) {
$joinTable['joinColumns'][] = $this->joinColumnToArray($joinColumnElement);
}
foreach ($joinTableElement->{'inverse-join-columns'}->{'join-column'} as $joinColumnElement) {
$joinTable['inverseJoinColumns'][] = $this->joinColumnToArray($joinColumnElement);
}
$mapping['joinTable'] = $joinTable;
}
if (isset($manyToManyElement->cascade)) {
$mapping['cascade'] = $this->_getCascadeMappings($manyToManyElement->cascade);
}
if (isset($manyToManyElement->{'order-by'})) {
$orderBy = array();
foreach ($manyToManyElement->{'order-by'}->{'order-by-field'} as $orderByField) {
$orderBy[(string)$orderByField['name']] = (string)$orderByField['direction'];
}
$mapping['orderBy'] = $orderBy;
}
if (isset($manyToManyElement['index-by'])) {
$mapping['indexBy'] = (string)$manyToManyElement['index-by'];
} else if (isset($manyToManyElement->{'index-by'})) {
throw new \InvalidArgumentException(" is not a valid tag");
}
$metadata->mapManyToMany($mapping);
}
}
// Evaluate association-overrides
if (isset($xmlRoot->{'attribute-overrides'})) {
foreach ($xmlRoot->{'attribute-overrides'}->{'attribute-override'} as $overrideElement) {
$fieldName = (string) $overrideElement['name'];
foreach ($overrideElement->field as $field) {
$mapping = $this->columnToArray($field);
$mapping['fieldName'] = $fieldName;
$metadata->setAttributeOverride($fieldName, $mapping);
}
}
}
// Evaluate association-overrides
if (isset($xmlRoot->{'association-overrides'})) {
foreach ($xmlRoot->{'association-overrides'}->{'association-override'} as $overrideElement) {
$fieldName = (string) $overrideElement['name'];
$override = array();
// Check for join-columns
if (isset($overrideElement->{'join-columns'})) {
$joinColumns = array();
foreach ($overrideElement->{'join-columns'}->{'join-column'} as $joinColumnElement) {
$joinColumns[] = $this->joinColumnToArray($joinColumnElement);
}
$override['joinColumns'] = $joinColumns;
}
// Check for join-table
if ($overrideElement->{'join-table'}) {
$joinTable = null;
$joinTableElement = $overrideElement->{'join-table'};
$joinTable = array(
'name' => (string) $joinTableElement['name'],
'schema' => (string) $joinTableElement['schema']
);
if (isset($joinTableElement->{'join-columns'})) {
foreach ($joinTableElement->{'join-columns'}->{'join-column'} as $joinColumnElement) {
$joinTable['joinColumns'][] = $this->joinColumnToArray($joinColumnElement);
}
}
if (isset($joinTableElement->{'inverse-join-columns'})) {
foreach ($joinTableElement->{'inverse-join-columns'}->{'join-column'} as $joinColumnElement) {
$joinTable['inverseJoinColumns'][] = $this->joinColumnToArray($joinColumnElement);
}
}
$override['joinTable'] = $joinTable;
}
$metadata->setAssociationOverride($fieldName, $override);
}
}
// Evaluate
if (isset($xmlRoot->{'lifecycle-callbacks'})) {
foreach ($xmlRoot->{'lifecycle-callbacks'}->{'lifecycle-callback'} as $lifecycleCallback) {
$metadata->addLifecycleCallback((string)$lifecycleCallback['method'], constant('Doctrine\ORM\Events::' . (string)$lifecycleCallback['type']));
}
}
}
/**
* Parses (nested) option elements.
*
* @param SimpleXMLElement $options the XML element.
* @return array The options array.
*/
private function _parseOptions(SimpleXMLElement $options)
{
$array = array();
/* @var $option SimpleXMLElement */
foreach ($options as $option) {
if ($option->count()) {
$value = $this->_parseOptions($option->children());
} else {
$value = (string) $option;
}
$attr = $option->attributes();
if (isset($attr->name)) {
$array[(string) $attr->name] = $value;
} else {
$array[] = $value;
}
}
return $array;
}
/**
* Constructs a joinColumn mapping array based on the information
* found in the given SimpleXMLElement.
*
* @param SimpleXMLElement $joinColumnElement the XML element.
* @return array The mapping array.
*/
private function joinColumnToArray(SimpleXMLElement $joinColumnElement)
{
$joinColumn = array(
'name' => (string)$joinColumnElement['name'],
'referencedColumnName' => (string)$joinColumnElement['referenced-column-name']
);
if (isset($joinColumnElement['unique'])) {
$joinColumn['unique'] = $this->evaluateBoolean($joinColumnElement['unique']);
}
if (isset($joinColumnElement['nullable'])) {
$joinColumn['nullable'] = $this->evaluateBoolean($joinColumnElement['nullable']);
}
if (isset($joinColumnElement['on-delete'])) {
$joinColumn['onDelete'] = (string)$joinColumnElement['on-delete'];
}
if (isset($joinColumnElement['column-definition'])) {
$joinColumn['columnDefinition'] = (string)$joinColumnElement['column-definition'];
}
return $joinColumn;
}
/**
* Parse the given field as array
*
* @param SimpleXMLElement $fieldMapping
* @return array
*/
private function columnToArray(SimpleXMLElement $fieldMapping)
{
$mapping = array(
'fieldName' => (string) $fieldMapping['name'],
);
if (isset($fieldMapping['type'])) {
$mapping['type'] = (string) $fieldMapping['type'];
}
if (isset($fieldMapping['column'])) {
$mapping['columnName'] = (string) $fieldMapping['column'];
}
if (isset($fieldMapping['length'])) {
$mapping['length'] = (int) $fieldMapping['length'];
}
if (isset($fieldMapping['precision'])) {
$mapping['precision'] = (int) $fieldMapping['precision'];
}
if (isset($fieldMapping['scale'])) {
$mapping['scale'] = (int) $fieldMapping['scale'];
}
if (isset($fieldMapping['unique'])) {
$mapping['unique'] = $this->evaluateBoolean($fieldMapping['unique']);
}
if (isset($fieldMapping['nullable'])) {
$mapping['nullable'] = $this->evaluateBoolean($fieldMapping['nullable']);
}
if (isset($fieldMapping['version']) && $fieldMapping['version']) {
$mapping['version'] = $this->evaluateBoolean($fieldMapping['version']);
}
if (isset($fieldMapping['column-definition'])) {
$mapping['columnDefinition'] = (string) $fieldMapping['column-definition'];
}
if (isset($fieldMapping->options)) {
$mapping['options'] = $this->_parseOptions($fieldMapping->options->children());
}
return $mapping;
}
/**
* Gathers a list of cascade options found in the given cascade element.
*
* @param SimpleXMLElement $cascadeElement the cascade element.
* @return array The list of cascade options.
*/
private function _getCascadeMappings($cascadeElement)
{
$cascades = array();
/* @var $action SimpleXmlElement */
foreach ($cascadeElement->children() as $action) {
// According to the JPA specifications, XML uses "cascade-persist"
// instead of "persist". Here, both variations
// are supported because both YAML and Annotation use "persist"
// and we want to make sure that this driver doesn't need to know
// anything about the supported cascading actions
$cascades[] = str_replace('cascade-', '', $action->getName());
}
return $cascades;
}
/**
* {@inheritDoc}
*/
protected function loadMappingFile($file)
{
$result = array();
$xmlElement = simplexml_load_file($file);
if (isset($xmlElement->entity)) {
foreach ($xmlElement->entity as $entityElement) {
$entityName = (string)$entityElement['name'];
$result[$entityName] = $entityElement;
}
} else if (isset($xmlElement->{'mapped-superclass'})) {
foreach ($xmlElement->{'mapped-superclass'} as $mappedSuperClass) {
$className = (string)$mappedSuperClass['name'];
$result[$className] = $mappedSuperClass;
}
}
return $result;
}
protected function evaluateBoolean($element)
{
$flag = (string)$element;
return ($flag === true || $flag == "true" || $flag == "1");
}
}
DoctrineORM-2.3.3/Doctrine/ORM/Mapping/Driver/YamlDriver.php 0000644 0001751 0001751 00000065371 12143374607 023616 0 ustar beberlei beberlei .
*/
namespace Doctrine\ORM\Mapping\Driver;
use Doctrine\Common\Persistence\Mapping\ClassMetadata,
Doctrine\Common\Persistence\Mapping\Driver\FileDriver,
Doctrine\ORM\Mapping\MappingException,
Symfony\Component\Yaml\Yaml;
/**
* The YamlDriver reads the mapping metadata from yaml schema files.
*
* @since 2.0
* @author Benjamin Eberlei
* @author Guilherme Blanco
* @author Jonathan H. Wage
* @author Roman Borschel
*/
class YamlDriver extends FileDriver
{
const DEFAULT_FILE_EXTENSION = '.dcm.yml';
/**
* {@inheritDoc}
*/
public function __construct($locator, $fileExtension = self::DEFAULT_FILE_EXTENSION)
{
parent::__construct($locator, $fileExtension);
}
/**
* {@inheritDoc}
*/
public function loadMetadataForClass($className, ClassMetadata $metadata)
{
/* @var $metadata \Doctrine\ORM\Mapping\ClassMetadataInfo */
$element = $this->getElement($className);
if ($element['type'] == 'entity') {
if (isset($element['repositoryClass'])) {
$metadata->setCustomRepositoryClass($element['repositoryClass']);
}
if (isset($element['readOnly']) && $element['readOnly'] == true) {
$metadata->markReadOnly();
}
} else if ($element['type'] == 'mappedSuperclass') {
$metadata->setCustomRepositoryClass(
isset($element['repositoryClass']) ? $element['repositoryClass'] : null
);
$metadata->isMappedSuperclass = true;
} else {
throw MappingException::classIsNotAValidEntityOrMappedSuperClass($className);
}
// Evaluate root level properties
$table = array();
if (isset($element['table'])) {
$table['name'] = $element['table'];
}
$metadata->setPrimaryTable($table);
// Evaluate named queries
if (isset($element['namedQueries'])) {
foreach ($element['namedQueries'] as $name => $queryMapping) {
if (is_string($queryMapping)) {
$queryMapping = array('query' => $queryMapping);
}
if ( ! isset($queryMapping['name'])) {
$queryMapping['name'] = $name;
}
$metadata->addNamedQuery($queryMapping);
}
}
// Evaluate named native queries
if (isset($element['namedNativeQueries'])) {
foreach ($element['namedNativeQueries'] as $name => $mappingElement) {
if (!isset($mappingElement['name'])) {
$mappingElement['name'] = $name;
}
$metadata->addNamedNativeQuery(array(
'name' => $mappingElement['name'],
'query' => isset($mappingElement['query']) ? $mappingElement['query'] : null,
'resultClass' => isset($mappingElement['resultClass']) ? $mappingElement['resultClass'] : null,
'resultSetMapping' => isset($mappingElement['resultSetMapping']) ? $mappingElement['resultSetMapping'] : null,
));
}
}
// Evaluate sql result set mappings
if (isset($element['sqlResultSetMappings'])) {
foreach ($element['sqlResultSetMappings'] as $name => $resultSetMapping) {
if (!isset($resultSetMapping['name'])) {
$resultSetMapping['name'] = $name;
}
$entities = array();
$columns = array();
if (isset($resultSetMapping['entityResult'])) {
foreach ($resultSetMapping['entityResult'] as $entityResultElement) {
$entityResult = array(
'fields' => array(),
'entityClass' => isset($entityResultElement['entityClass']) ? $entityResultElement['entityClass'] : null,
'discriminatorColumn' => isset($entityResultElement['discriminatorColumn']) ? $entityResultElement['discriminatorColumn'] : null,
);
if (isset($entityResultElement['fieldResult'])) {
foreach ($entityResultElement['fieldResult'] as $fieldResultElement) {
$entityResult['fields'][] = array(
'name' => isset($fieldResultElement['name']) ? $fieldResultElement['name'] : null,
'column' => isset($fieldResultElement['column']) ? $fieldResultElement['column'] : null,
);
}
}
$entities[] = $entityResult;
}
}
if (isset($resultSetMapping['columnResult'])) {
foreach ($resultSetMapping['columnResult'] as $columnResultAnnot) {
$columns[] = array(
'name' => isset($columnResultAnnot['name']) ? $columnResultAnnot['name'] : null,
);
}
}
$metadata->addSqlResultSetMapping(array(
'name' => $resultSetMapping['name'],
'entities' => $entities,
'columns' => $columns
));
}
}
/* not implemented specially anyway. use table = schema.table
if (isset($element['schema'])) {
$metadata->table['schema'] = $element['schema'];
}*/
if (isset($element['inheritanceType'])) {
$metadata->setInheritanceType(constant('Doctrine\ORM\Mapping\ClassMetadata::INHERITANCE_TYPE_' . strtoupper($element['inheritanceType'])));
if ($metadata->inheritanceType != \Doctrine\ORM\Mapping\ClassMetadata::INHERITANCE_TYPE_NONE) {
// Evaluate discriminatorColumn
if (isset($element['discriminatorColumn'])) {
$discrColumn = $element['discriminatorColumn'];
$metadata->setDiscriminatorColumn(array(
'name' => isset($discrColumn['name']) ? (string)$discrColumn['name'] : null,
'type' => isset($discrColumn['type']) ? (string)$discrColumn['type'] : null,
'length' => isset($discrColumn['length']) ? (string)$discrColumn['length'] : null,
'columnDefinition' => isset($discrColumn['columnDefinition']) ? (string)$discrColumn['columnDefinition'] : null
));
} else {
$metadata->setDiscriminatorColumn(array('name' => 'dtype', 'type' => 'string', 'length' => 255));
}
// Evaluate discriminatorMap
if (isset($element['discriminatorMap'])) {
$metadata->setDiscriminatorMap($element['discriminatorMap']);
}
}
}
// Evaluate changeTrackingPolicy
if (isset($element['changeTrackingPolicy'])) {
$metadata->setChangeTrackingPolicy(constant('Doctrine\ORM\Mapping\ClassMetadata::CHANGETRACKING_'
. strtoupper($element['changeTrackingPolicy'])));
}
// Evaluate indexes
if (isset($element['indexes'])) {
foreach ($element['indexes'] as $name => $index) {
if ( ! isset($index['name'])) {
$index['name'] = $name;
}
if (is_string($index['columns'])) {
$columns = explode(',', $index['columns']);
$columns = array_map('trim', $columns);
} else {
$columns = $index['columns'];
}
$metadata->table['indexes'][$index['name']] = array(
'columns' => $columns
);
}
}
// Evaluate uniqueConstraints
if (isset($element['uniqueConstraints'])) {
foreach ($element['uniqueConstraints'] as $name => $unique) {
if ( ! isset($unique['name'])) {
$unique['name'] = $name;
}
if (is_string($unique['columns'])) {
$columns = explode(',', $unique['columns']);
$columns = array_map('trim', $columns);
} else {
$columns = $unique['columns'];
}
$metadata->table['uniqueConstraints'][$unique['name']] = array(
'columns' => $columns
);
}
}
if (isset($element['options'])) {
$metadata->table['options'] = $element['options'];
}
$associationIds = array();
if (isset($element['id'])) {
// Evaluate identifier settings
foreach ($element['id'] as $name => $idElement) {
if (isset($idElement['associationKey']) && $idElement['associationKey'] == true) {
$associationIds[$name] = true;
continue;
}
$mapping = array(
'id' => true,
'fieldName' => $name
);
if (isset($idElement['type'])) {
$mapping['type'] = $idElement['type'];
}
if (isset($idElement['column'])) {
$mapping['columnName'] = $idElement['column'];
}
if (isset($idElement['length'])) {
$mapping['length'] = $idElement['length'];
}
if (isset($idElement['columnDefinition'])) {
$mapping['columnDefinition'] = $idElement['columnDefinition'];
}
$metadata->mapField($mapping);
if (isset($idElement['generator'])) {
$metadata->setIdGeneratorType(constant('Doctrine\ORM\Mapping\ClassMetadata::GENERATOR_TYPE_'
. strtoupper($idElement['generator']['strategy'])));
}
// Check for SequenceGenerator/TableGenerator definition
if (isset($idElement['sequenceGenerator'])) {
$metadata->setSequenceGeneratorDefinition($idElement['sequenceGenerator']);
} else if (isset($idElement['customIdGenerator'])) {
$customGenerator = $idElement['customIdGenerator'];
$metadata->setCustomGeneratorDefinition(array(
'class' => (string) $customGenerator['class']
));
} else if (isset($idElement['tableGenerator'])) {
throw MappingException::tableIdGeneratorNotImplemented($className);
}
}
}
// Evaluate fields
if (isset($element['fields'])) {
foreach ($element['fields'] as $name => $fieldMapping) {
$mapping = $this->columnToArray($name, $fieldMapping);
if (isset($fieldMapping['id'])) {
$mapping['id'] = true;
if (isset($fieldMapping['generator']['strategy'])) {
$metadata->setIdGeneratorType(constant('Doctrine\ORM\Mapping\ClassMetadata::GENERATOR_TYPE_'
. strtoupper($fieldMapping['generator']['strategy'])));
}
}
if (isset($mapping['version'])) {
$metadata->setVersionMapping($mapping);
unset($mapping['version']);
}
$metadata->mapField($mapping);
}
}
// Evaluate oneToOne relationships
if (isset($element['oneToOne'])) {
foreach ($element['oneToOne'] as $name => $oneToOneElement) {
$mapping = array(
'fieldName' => $name,
'targetEntity' => $oneToOneElement['targetEntity']
);
if (isset($associationIds[$mapping['fieldName']])) {
$mapping['id'] = true;
}
if (isset($oneToOneElement['fetch'])) {
$mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . $oneToOneElement['fetch']);
}
if (isset($oneToOneElement['mappedBy'])) {
$mapping['mappedBy'] = $oneToOneElement['mappedBy'];
} else {
if (isset($oneToOneElement['inversedBy'])) {
$mapping['inversedBy'] = $oneToOneElement['inversedBy'];
}
$joinColumns = array();
if (isset($oneToOneElement['joinColumn'])) {
$joinColumns[] = $this->joinColumnToArray($oneToOneElement['joinColumn']);
} else if (isset($oneToOneElement['joinColumns'])) {
foreach ($oneToOneElement['joinColumns'] as $joinColumnName => $joinColumnElement) {
if ( ! isset($joinColumnElement['name'])) {
$joinColumnElement['name'] = $joinColumnName;
}
$joinColumns[] = $this->joinColumnToArray($joinColumnElement);
}
}
$mapping['joinColumns'] = $joinColumns;
}
if (isset($oneToOneElement['cascade'])) {
$mapping['cascade'] = $oneToOneElement['cascade'];
}
if (isset($oneToOneElement['orphanRemoval'])) {
$mapping['orphanRemoval'] = (bool)$oneToOneElement['orphanRemoval'];
}
$metadata->mapOneToOne($mapping);
}
}
// Evaluate oneToMany relationships
if (isset($element['oneToMany'])) {
foreach ($element['oneToMany'] as $name => $oneToManyElement) {
$mapping = array(
'fieldName' => $name,
'targetEntity' => $oneToManyElement['targetEntity'],
'mappedBy' => $oneToManyElement['mappedBy']
);
if (isset($oneToManyElement['fetch'])) {
$mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . $oneToManyElement['fetch']);
}
if (isset($oneToManyElement['cascade'])) {
$mapping['cascade'] = $oneToManyElement['cascade'];
}
if (isset($oneToManyElement['orphanRemoval'])) {
$mapping['orphanRemoval'] = (bool)$oneToManyElement['orphanRemoval'];
}
if (isset($oneToManyElement['orderBy'])) {
$mapping['orderBy'] = $oneToManyElement['orderBy'];
}
if (isset($oneToManyElement['indexBy'])) {
$mapping['indexBy'] = $oneToManyElement['indexBy'];
}
$metadata->mapOneToMany($mapping);
}
}
// Evaluate manyToOne relationships
if (isset($element['manyToOne'])) {
foreach ($element['manyToOne'] as $name => $manyToOneElement) {
$mapping = array(
'fieldName' => $name,
'targetEntity' => $manyToOneElement['targetEntity']
);
if (isset($associationIds[$mapping['fieldName']])) {
$mapping['id'] = true;
}
if (isset($manyToOneElement['fetch'])) {
$mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . $manyToOneElement['fetch']);
}
if (isset($manyToOneElement['inversedBy'])) {
$mapping['inversedBy'] = $manyToOneElement['inversedBy'];
}
$joinColumns = array();
if (isset($manyToOneElement['joinColumn'])) {
$joinColumns[] = $this->joinColumnToArray($manyToOneElement['joinColumn']);
} else if (isset($manyToOneElement['joinColumns'])) {
foreach ($manyToOneElement['joinColumns'] as $joinColumnName => $joinColumnElement) {
if ( ! isset($joinColumnElement['name'])) {
$joinColumnElement['name'] = $joinColumnName;
}
$joinColumns[] = $this->joinColumnToArray($joinColumnElement);
}
}
$mapping['joinColumns'] = $joinColumns;
if (isset($manyToOneElement['cascade'])) {
$mapping['cascade'] = $manyToOneElement['cascade'];
}
$metadata->mapManyToOne($mapping);
}
}
// Evaluate manyToMany relationships
if (isset($element['manyToMany'])) {
foreach ($element['manyToMany'] as $name => $manyToManyElement) {
$mapping = array(
'fieldName' => $name,
'targetEntity' => $manyToManyElement['targetEntity']
);
if (isset($manyToManyElement['fetch'])) {
$mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . $manyToManyElement['fetch']);
}
if (isset($manyToManyElement['mappedBy'])) {
$mapping['mappedBy'] = $manyToManyElement['mappedBy'];
} else if (isset($manyToManyElement['joinTable'])) {
$joinTableElement = $manyToManyElement['joinTable'];
$joinTable = array(
'name' => $joinTableElement['name']
);
if (isset($joinTableElement['schema'])) {
$joinTable['schema'] = $joinTableElement['schema'];
}
foreach ($joinTableElement['joinColumns'] as $joinColumnName => $joinColumnElement) {
if ( ! isset($joinColumnElement['name'])) {
$joinColumnElement['name'] = $joinColumnName;
}
$joinTable['joinColumns'][] = $this->joinColumnToArray($joinColumnElement);
}
foreach ($joinTableElement['inverseJoinColumns'] as $joinColumnName => $joinColumnElement) {
if ( ! isset($joinColumnElement['name'])) {
$joinColumnElement['name'] = $joinColumnName;
}
$joinTable['inverseJoinColumns'][] = $this->joinColumnToArray($joinColumnElement);
}
$mapping['joinTable'] = $joinTable;
}
if (isset($manyToManyElement['inversedBy'])) {
$mapping['inversedBy'] = $manyToManyElement['inversedBy'];
}
if (isset($manyToManyElement['cascade'])) {
$mapping['cascade'] = $manyToManyElement['cascade'];
}
if (isset($manyToManyElement['orderBy'])) {
$mapping['orderBy'] = $manyToManyElement['orderBy'];
}
if (isset($manyToManyElement['indexBy'])) {
$mapping['indexBy'] = $manyToManyElement['indexBy'];
}
if (isset($manyToManyElement['orphanRemoval'])) {
$mapping['orphanRemoval'] = (bool)$manyToManyElement['orphanRemoval'];
}
$metadata->mapManyToMany($mapping);
}
}
// Evaluate associationOverride
if (isset($element['associationOverride']) && is_array($element['associationOverride'])) {
foreach ($element['associationOverride'] as $fieldName => $associationOverrideElement) {
$override = array();
// Check for joinColumn
if (isset($associationOverrideElement['joinColumn'])) {
$joinColumns = array();
foreach ($associationOverrideElement['joinColumn'] as $name => $joinColumnElement) {
if ( ! isset($joinColumnElement['name'])) {
$joinColumnElement['name'] = $name;
}
$joinColumns[] = $this->joinColumnToArray($joinColumnElement);
}
$override['joinColumns'] = $joinColumns;
}
// Check for joinTable
if (isset($associationOverrideElement['joinTable'])) {
$joinTableElement = $associationOverrideElement['joinTable'];
$joinTable = array(
'name' => $joinTableElement['name']
);
if (isset($joinTableElement['schema'])) {
$joinTable['schema'] = $joinTableElement['schema'];
}
foreach ($joinTableElement['joinColumns'] as $name => $joinColumnElement) {
if ( ! isset($joinColumnElement['name'])) {
$joinColumnElement['name'] = $name;
}
$joinTable['joinColumns'][] = $this->joinColumnToArray($joinColumnElement);
}
foreach ($joinTableElement['inverseJoinColumns'] as $name => $joinColumnElement) {
if ( ! isset($joinColumnElement['name'])) {
$joinColumnElement['name'] = $name;
}
$joinTable['inverseJoinColumns'][] = $this->joinColumnToArray($joinColumnElement);
}
$override['joinTable'] = $joinTable;
}
$metadata->setAssociationOverride($fieldName, $override);
}
}
// Evaluate associationOverride
if (isset($element['attributeOverride']) && is_array($element['attributeOverride'])) {
foreach ($element['attributeOverride'] as $fieldName => $attributeOverrideElement) {
$mapping = $this->columnToArray($fieldName, $attributeOverrideElement);
$metadata->setAttributeOverride($fieldName, $mapping);
}
}
// Evaluate lifeCycleCallbacks
if (isset($element['lifecycleCallbacks'])) {
foreach ($element['lifecycleCallbacks'] as $type => $methods) {
foreach ($methods as $method) {
$metadata->addLifecycleCallback($method, constant('Doctrine\ORM\Events::' . $type));
}
}
}
}
/**
* Constructs a joinColumn mapping array based on the information
* found in the given join column element.
*
* @param array $joinColumnElement The array join column element
* @return array The mapping array.
*/
private function joinColumnToArray($joinColumnElement)
{
$joinColumn = array();
if (isset($joinColumnElement['referencedColumnName'])) {
$joinColumn['referencedColumnName'] = (string) $joinColumnElement['referencedColumnName'];
}
if (isset($joinColumnElement['name'])) {
$joinColumn['name'] = (string) $joinColumnElement['name'];
}
if (isset($joinColumnElement['fieldName'])) {
$joinColumn['fieldName'] = (string) $joinColumnElement['fieldName'];
}
if (isset($joinColumnElement['unique'])) {
$joinColumn['unique'] = (bool) $joinColumnElement['unique'];
}
if (isset($joinColumnElement['nullable'])) {
$joinColumn['nullable'] = (bool) $joinColumnElement['nullable'];
}
if (isset($joinColumnElement['onDelete'])) {
$joinColumn['onDelete'] = $joinColumnElement['onDelete'];
}
if (isset($joinColumnElement['columnDefinition'])) {
$joinColumn['columnDefinition'] = $joinColumnElement['columnDefinition'];
}
return $joinColumn;
}
/**
* Parse the given column as array
*
* @param string $fieldName
* @param array $column
* @return array
*/
private function columnToArray($fieldName, $column)
{
$mapping = array(
'fieldName' => $fieldName
);
if (isset($column['type'])) {
$params = explode('(', $column['type']);
$column['type'] = $params[0];
$mapping['type'] = $column['type'];
if (isset($params[1])) {
$column['length'] = (integer) substr($params[1], 0, strlen($params[1]) - 1);
}
}
if (isset($column['column'])) {
$mapping['columnName'] = $column['column'];
}
if (isset($column['length'])) {
$mapping['length'] = $column['length'];
}
if (isset($column['precision'])) {
$mapping['precision'] = $column['precision'];
}
if (isset($column['scale'])) {
$mapping['scale'] = $column['scale'];
}
if (isset($column['unique'])) {
$mapping['unique'] = (bool)$column['unique'];
}
if (isset($column['options'])) {
$mapping['options'] = $column['options'];
}
if (isset($column['nullable'])) {
$mapping['nullable'] = $column['nullable'];
}
if (isset($column['version']) && $column['version']) {
$mapping['version'] = $column['version'];
}
if (isset($column['columnDefinition'])) {
$mapping['columnDefinition'] = $column['columnDefinition'];
}
return $mapping;
}
/**
* {@inheritDoc}
*/
protected function loadMappingFile($file)
{
return Yaml::parse($file);
}
}
DoctrineORM-2.3.3/Doctrine/ORM/Persisters/AbstractCollectionPersister.php 0000644 0001751 0001751 00000015732 12143374607 026531 0 ustar beberlei beberlei .
*/
namespace Doctrine\ORM\Persisters;
use Doctrine\ORM\EntityManager,
Doctrine\ORM\PersistentCollection;
/**
* Base class for all collection persisters.
*
* @since 2.0
* @author Roman Borschel
*/
abstract class AbstractCollectionPersister
{
/**
* @var EntityManager
*/
protected $_em;
/**
* @var \Doctrine\DBAL\Connection
*/
protected $_conn;
/**
* @var \Doctrine\ORM\UnitOfWork
*/
protected $_uow;
/**
* The database platform.
*
* @var \Doctrine\DBAL\Platforms\AbstractPlatform
*/
protected $platform;
/**
* The quote strategy.
*
* @var \Doctrine\ORM\Mapping\QuoteStrategy
*/
protected $quoteStrategy;
/**
* Initializes a new instance of a class derived from AbstractCollectionPersister.
*
* @param \Doctrine\ORM\EntityManager $em
*/
public function __construct(EntityManager $em)
{
$this->_em = $em;
$this->_uow = $em->getUnitOfWork();
$this->_conn = $em->getConnection();
$this->platform = $this->_conn->getDatabasePlatform();
$this->quoteStrategy = $em->getConfiguration()->getQuoteStrategy();
}
/**
* Deletes the persistent state represented by the given collection.
*
* @param PersistentCollection $coll
*/
public function delete(PersistentCollection $coll)
{
$mapping = $coll->getMapping();
if ( ! $mapping['isOwningSide']) {
return; // ignore inverse side
}
$sql = $this->_getDeleteSQL($coll);
$this->_conn->executeUpdate($sql, $this->_getDeleteSQLParameters($coll));
}
/**
* Gets the SQL statement for deleting the given collection.
*
* @param PersistentCollection $coll
*/
abstract protected function _getDeleteSQL(PersistentCollection $coll);
/**
* Gets the SQL parameters for the corresponding SQL statement to delete
* the given collection.
*
* @param PersistentCollection $coll
*/
abstract protected function _getDeleteSQLParameters(PersistentCollection $coll);
/**
* Updates the given collection, synchronizing it's state with the database
* by inserting, updating and deleting individual elements.
*
* @param PersistentCollection $coll
*/
public function update(PersistentCollection $coll)
{
$mapping = $coll->getMapping();
if ( ! $mapping['isOwningSide']) {
return; // ignore inverse side
}
$this->deleteRows($coll);
//$this->updateRows($coll);
$this->insertRows($coll);
}
public function deleteRows(PersistentCollection $coll)
{
$deleteDiff = $coll->getDeleteDiff();
$sql = $this->_getDeleteRowSQL($coll);
foreach ($deleteDiff as $element) {
$this->_conn->executeUpdate($sql, $this->_getDeleteRowSQLParameters($coll, $element));
}
}
//public function updateRows(PersistentCollection $coll)
//{}
public function insertRows(PersistentCollection $coll)
{
$insertDiff = $coll->getInsertDiff();
$sql = $this->_getInsertRowSQL($coll);
foreach ($insertDiff as $element) {
$this->_conn->executeUpdate($sql, $this->_getInsertRowSQLParameters($coll, $element));
}
}
public function count(PersistentCollection $coll)
{
throw new \BadMethodCallException("Counting the size of this persistent collection is not supported by this CollectionPersister.");
}
public function slice(PersistentCollection $coll, $offset, $length = null)
{
throw new \BadMethodCallException("Slicing elements is not supported by this CollectionPersister.");
}
public function contains(PersistentCollection $coll, $element)
{
throw new \BadMethodCallException("Checking for existance of an element is not supported by this CollectionPersister.");
}
public function containsKey(PersistentCollection $coll, $key)
{
throw new \BadMethodCallException("Checking for existance of a key is not supported by this CollectionPersister.");
}
public function removeElement(PersistentCollection $coll, $element)
{
throw new \BadMethodCallException("Removing an element is not supported by this CollectionPersister.");
}
public function removeKey(PersistentCollection $coll, $key)
{
throw new \BadMethodCallException("Removing a key is not supported by this CollectionPersister.");
}
public function get(PersistentCollection $coll, $index)
{
throw new \BadMethodCallException("Selecting a collection by index is not supported by this CollectionPersister.");
}
/**
* Gets the SQL statement used for deleting a row from the collection.
*
* @param PersistentCollection $coll
*/
abstract protected function _getDeleteRowSQL(PersistentCollection $coll);
/**
* Gets the SQL parameters for the corresponding SQL statement to delete the given
* element from the given collection.
*
* @param PersistentCollection $coll
* @param mixed $element
*/
abstract protected function _getDeleteRowSQLParameters(PersistentCollection $coll, $element);
/**
* Gets the SQL statement used for updating a row in the collection.
*
* @param PersistentCollection $coll
*/
abstract protected function _getUpdateRowSQL(PersistentCollection $coll);
/**
* Gets the SQL statement used for inserting a row in the collection.
*
* @param PersistentCollection $coll
*/
abstract protected function _getInsertRowSQL(PersistentCollection $coll);
/**
* Gets the SQL parameters for the corresponding SQL statement to insert the given
* element of the given collection into the database.
*
* @param PersistentCollection $coll
* @param mixed $element
*/
abstract protected function _getInsertRowSQLParameters(PersistentCollection $coll, $element);
}
DoctrineORM-2.3.3/Doctrine/ORM/Persisters/AbstractEntityInheritancePersister.php 0000644 0001751 0001751 00000006300 12143374607 030053 0 ustar beberlei beberlei .
*/
namespace Doctrine\ORM\Persisters;
use Doctrine\ORM\Mapping\ClassMetadata,
Doctrine\DBAL\Types\Type;
/**
* Base class for entity persisters that implement a certain inheritance mapping strategy.
* All these persisters are assumed to use a discriminator column to discriminate entity
* types in the hierarchy.
*
* @author Roman Borschel
* @author Benjamin Eberlei
* @since 2.0
*/
abstract class AbstractEntityInheritancePersister extends BasicEntityPersister
{
/**
* {@inheritdoc}
*/
protected function _prepareInsertData($entity)
{
$data = parent::_prepareInsertData($entity);
// Populate the discriminator column
$discColumn = $this->_class->discriminatorColumn;
$this->_columnTypes[$discColumn['name']] = $discColumn['type'];
$data[$this->_getDiscriminatorColumnTableName()][$discColumn['name']] = $this->_class->discriminatorValue;
return $data;
}
/**
* Gets the name of the table that contains the discriminator column.
*
* @return string The table name.
*/
abstract protected function _getDiscriminatorColumnTableName();
/**
* {@inheritdoc}
*/
protected function _getSelectColumnSQL($field, ClassMetadata $class, $alias = 'r')
{
$columnName = $class->columnNames[$field];
$sql = $this->_getSQLTableAlias($class->name, $alias == 'r' ? '' : $alias) . '.' . $this->quoteStrategy->getColumnName($field, $class, $this->_platform);
$columnAlias = $this->getSQLColumnAlias($columnName);
$this->_rsm->addFieldResult($alias, $columnAlias, $field, $class->name);
if (isset($class->fieldMappings[$field]['requireSQLConversion'])) {
$type = Type::getType($class->getTypeOfField($field));
$sql = $type->convertToPHPValueSQL($sql, $this->_platform);
}
return $sql . ' AS ' . $columnAlias;
}
protected function getSelectJoinColumnSQL($tableAlias, $joinColumnName, $className)
{
$columnAlias = $this->getSQLColumnAlias($joinColumnName);
$this->_rsm->addMetaResult('r', $columnAlias, $joinColumnName);
return $tableAlias . '.' . $joinColumnName . ' AS ' . $columnAlias;
}
}
DoctrineORM-2.3.3/Doctrine/ORM/Persisters/BasicEntityPersister.php 0000644 0001751 0001751 00000210702 12143374607 025162 0 ustar beberlei beberlei .
*/
namespace Doctrine\ORM\Persisters;
use PDO;
use Doctrine\DBAL\LockMode;
use Doctrine\DBAL\Types\Type;
use Doctrine\DBAL\Connection;
use Doctrine\ORM\ORMException;
use Doctrine\ORM\OptimisticLockException;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\UnitOfWork;
use Doctrine\ORM\Query;
use Doctrine\ORM\PersistentCollection;
use Doctrine\ORM\Mapping\MappingException;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\Events;
use Doctrine\ORM\Event\LifecycleEventArgs;
use Doctrine\Common\Util\ClassUtils;
use Doctrine\Common\Collections\Criteria;
use Doctrine\Common\Collections\Expr\Comparison;
/**
* A BasicEntityPersiter maps an entity to a single table in a relational database.
*
* A persister is always responsible for a single entity type.
*
* EntityPersisters are used during a UnitOfWork to apply any changes to the persistent
* state of entities onto a relational database when the UnitOfWork is committed,
* as well as for basic querying of entities and their associations (not DQL).
*
* The persisting operations that are invoked during a commit of a UnitOfWork to
* persist the persistent entity state are:
*
* - {@link addInsert} : To schedule an entity for insertion.
* - {@link executeInserts} : To execute all scheduled insertions.
* - {@link update} : To update the persistent state of an entity.
* - {@link delete} : To delete the persistent state of an entity.
*
* As can be seen from the above list, insertions are batched and executed all at once
* for increased efficiency.
*
* The querying operations invoked during a UnitOfWork, either through direct find
* requests or lazy-loading, are the following:
*
* - {@link load} : Loads (the state of) a single, managed entity.
* - {@link loadAll} : Loads multiple, managed entities.
* - {@link loadOneToOneEntity} : Loads a one/many-to-one entity association (lazy-loading).
* - {@link loadOneToManyCollection} : Loads a one-to-many entity association (lazy-loading).
* - {@link loadManyToManyCollection} : Loads a many-to-many entity association (lazy-loading).
*
* The BasicEntityPersister implementation provides the default behavior for
* persisting and querying entities that are mapped to a single database table.
*
* Subclasses can be created to provide custom persisting and querying strategies,
* i.e. spanning multiple tables.
*
* @author Roman Borschel
* @author Giorgio Sironi
* @author Benjamin Eberlei
* @author Alexander
* @since 2.0
*/
class BasicEntityPersister
{
/**
* @var array
*/
static private $comparisonMap = array(
Comparison::EQ => '= %s',
Comparison::IS => 'IS %s',
Comparison::NEQ => '!= %s',
Comparison::GT => '> %s',
Comparison::GTE => '>= %s',
Comparison::LT => '< %s',
Comparison::LTE => '<= %s',
Comparison::IN => 'IN (%s)',
Comparison::NIN => 'NOT IN (%s)',
);
/**
* Metadata object that describes the mapping of the mapped entity class.
*
* @var \Doctrine\ORM\Mapping\ClassMetadata
*/
protected $_class;
/**
* The underlying DBAL Connection of the used EntityManager.
*
* @var \Doctrine\DBAL\Connection $conn
*/
protected $_conn;
/**
* The database platform.
*
* @var \Doctrine\DBAL\Platforms\AbstractPlatform
*/
protected $_platform;
/**
* The EntityManager instance.
*
* @var \Doctrine\ORM\EntityManager
*/
protected $_em;
/**
* Queued inserts.
*
* @var array
*/
protected $_queuedInserts = array();
/**
* ResultSetMapping that is used for all queries. Is generated lazily once per request.
*
* TODO: Evaluate Caching in combination with the other cached SQL snippets.
*
* @var Query\ResultSetMapping
*/
protected $_rsm;
/**
* The map of column names to DBAL mapping types of all prepared columns used
* when INSERTing or UPDATEing an entity.
*
* @var array
* @see _prepareInsertData($entity)
* @see _prepareUpdateData($entity)
*/
protected $_columnTypes = array();
/**
* The map of quoted column names.
*
* @var array
* @see _prepareInsertData($entity)
* @see _prepareUpdateData($entity)
*/
protected $quotedColumns = array();
/**
* The INSERT SQL statement used for entities handled by this persister.
* This SQL is only generated once per request, if at all.
*
* @var string
*/
private $_insertSql;
/**
* The SELECT column list SQL fragment used for querying entities by this persister.
* This SQL fragment is only generated once per request, if at all.
*
* @var string
*/
protected $_selectColumnListSql;
/**
* The JOIN SQL fragement used to eagerly load all many-to-one and one-to-one
* associations configured as FETCH_EAGER, aswell as all inverse one-to-one associations.
*
* @var string
*/
protected $_selectJoinSql;
/**
* Counter for creating unique SQL table and column aliases.
*
* @var integer
*/
protected $_sqlAliasCounter = 0;
/**
* Map from class names (FQCN) to the corresponding generated SQL table aliases.
*
* @var array
*/
protected $_sqlTableAliases = array();
/**
* The quote strategy.
*
* @var \Doctrine\ORM\Mapping\QuoteStrategy
*/
protected $quoteStrategy;
/**
* Initializes a new BasicEntityPersister that uses the given EntityManager
* and persists instances of the class described by the given ClassMetadata descriptor.
*
* @param \Doctrine\ORM\EntityManager $em
* @param \Doctrine\ORM\Mapping\ClassMetadata $class
*/
public function __construct(EntityManager $em, ClassMetadata $class)
{
$this->_em = $em;
$this->_class = $class;
$this->_conn = $em->getConnection();
$this->_platform = $this->_conn->getDatabasePlatform();
$this->quoteStrategy = $em->getConfiguration()->getQuoteStrategy();
}
/**
* @return \Doctrine\ORM\Mapping\ClassMetadata
*/
public function getClassMetadata()
{
return $this->_class;
}
/**
* Adds an entity to the queued insertions.
* The entity remains queued until {@link executeInserts} is invoked.
*
* @param object $entity The entity to queue for insertion.
*/
public function addInsert($entity)
{
$this->_queuedInserts[spl_object_hash($entity)] = $entity;
}
/**
* Executes all queued entity insertions and returns any generated post-insert
* identifiers that were created as a result of the insertions.
*
* If no inserts are queued, invoking this method is a NOOP.
*
* @return array An array of any generated post-insert IDs. This will be an empty array
* if the entity class does not use the IDENTITY generation strategy.
*/
public function executeInserts()
{
if ( ! $this->_queuedInserts) {
return;
}
$postInsertIds = array();
$idGen = $this->_class->idGenerator;
$isPostInsertId = $idGen->isPostInsertGenerator();
$stmt = $this->_conn->prepare($this->_getInsertSQL());
$tableName = $this->_class->getTableName();
foreach ($this->_queuedInserts as $entity) {
$insertData = $this->_prepareInsertData($entity);
if (isset($insertData[$tableName])) {
$paramIndex = 1;
foreach ($insertData[$tableName] as $column => $value) {
$stmt->bindValue($paramIndex++, $value, $this->_columnTypes[$column]);
}
}
$stmt->execute();
if ($isPostInsertId) {
$id = $idGen->generate($this->_em, $entity);
$postInsertIds[$id] = $entity;
} else {
$id = $this->_class->getIdentifierValues($entity);
}
if ($this->_class->isVersioned) {
$this->assignDefaultVersionValue($entity, $id);
}
}
$stmt->closeCursor();
$this->_queuedInserts = array();
return $postInsertIds;
}
/**
* Retrieves the default version value which was created
* by the preceding INSERT statement and assigns it back in to the
* entities version field.
*
* @param object $entity
* @param mixed $id
*/
protected function assignDefaultVersionValue($entity, $id)
{
$value = $this->fetchVersionValue($this->_class, $id);
$this->_class->setFieldValue($entity, $this->_class->versionField, $value);
}
/**
* Fetch the current version value of a versioned entity.
*
* @param \Doctrine\ORM\Mapping\ClassMetadata $versionedClass
* @param mixed $id
* @return mixed
*/
protected function fetchVersionValue($versionedClass, $id)
{
$versionField = $versionedClass->versionField;
$identifier = $this->quoteStrategy->getIdentifierColumnNames($versionedClass, $this->_platform);
$versionFieldColumnName = $this->quoteStrategy->getColumnName($versionField, $versionedClass, $this->_platform);
//FIXME: Order with composite keys might not be correct
$sql = 'SELECT ' . $versionFieldColumnName
. ' FROM ' . $this->quoteStrategy->getTableName($versionedClass, $this->_platform)
. ' WHERE ' . implode(' = ? AND ', $identifier) . ' = ?';
$value = $this->_conn->fetchColumn($sql, array_values((array)$id));
return Type::getType($versionedClass->fieldMappings[$versionField]['type'])->convertToPHPValue($value, $this->_platform);
}
/**
* Updates a managed entity. The entity is updated according to its current changeset
* in the running UnitOfWork. If there is no changeset, nothing is updated.
*
* The data to update is retrieved through {@link _prepareUpdateData}.
* Subclasses that override this method are supposed to obtain the update data
* in the same way, through {@link _prepareUpdateData}.
*
* Subclasses are also supposed to take care of versioning when overriding this method,
* if necessary. The {@link _updateTable} method can be used to apply the data retrieved
* from {@_prepareUpdateData} on the target tables, thereby optionally applying versioning.
*
* @param object $entity The entity to update.
*/
public function update($entity)
{
$updateData = $this->_prepareUpdateData($entity);
$tableName = $this->_class->getTableName();
if (isset($updateData[$tableName]) && $updateData[$tableName]) {
$this->_updateTable(
$entity, $this->quoteStrategy->getTableName($this->_class, $this->_platform),
$updateData[$tableName], $this->_class->isVersioned
);
if ($this->_class->isVersioned) {
$id = $this->_em->getUnitOfWork()->getEntityIdentifier($entity);
$this->assignDefaultVersionValue($entity, $id);
}
}
}
/**
* Performs an UPDATE statement for an entity on a specific table.
* The UPDATE can optionally be versioned, which requires the entity to have a version field.
*
* @param object $entity The entity object being updated.
* @param string $quotedTableName The quoted name of the table to apply the UPDATE on.
* @param array $updateData The map of columns to update (column => value).
* @param boolean $versioned Whether the UPDATE should be versioned.
*/
protected final function _updateTable($entity, $quotedTableName, array $updateData, $versioned = false)
{
$set = $params = $types = array();
foreach ($updateData as $columnName => $value) {
$column = $columnName;
$placeholder = '?';
if (isset($this->_class->fieldNames[$columnName])) {
$column = $this->quoteStrategy->getColumnName($this->_class->fieldNames[$columnName], $this->_class, $this->_platform);
if (isset($this->_class->fieldMappings[$this->_class->fieldNames[$columnName]]['requireSQLConversion'])) {
$type = Type::getType($this->_columnTypes[$columnName]);
$placeholder = $type->convertToDatabaseValueSQL('?', $this->_platform);
}
} else if (isset($this->quotedColumns[$columnName])) {
$column = $this->quotedColumns[$columnName];
}
$set[] = $column . ' = ' . $placeholder;
$params[] = $value;
$types[] = $this->_columnTypes[$columnName];
}
$where = array();
$id = $this->_em->getUnitOfWork()->getEntityIdentifier($entity);
foreach ($this->_class->identifier as $idField) {
if (isset($this->_class->associationMappings[$idField])) {
$targetMapping = $this->_em->getClassMetadata($this->_class->associationMappings[$idField]['targetEntity']);
$where[] = $this->_class->associationMappings[$idField]['joinColumns'][0]['name'];
$params[] = $id[$idField];
switch (true) {
case (isset($targetMapping->fieldMappings[$targetMapping->identifier[0]])):
$types[] = $targetMapping->fieldMappings[$targetMapping->identifier[0]]['type'];
break;
case (isset($targetMapping->associationMappings[$targetMapping->identifier[0]])):
$types[] = $targetMapping->associationMappings[$targetMapping->identifier[0]]['type'];
break;
default:
throw ORMException::unrecognizedField($targetMapping->identifier[0]);
}
} else {
$where[] = $this->quoteStrategy->getColumnName($idField, $this->_class, $this->_platform);
$params[] = $id[$idField];
$types[] = $this->_class->fieldMappings[$idField]['type'];
}
}
if ($versioned) {
$versionField = $this->_class->versionField;
$versionFieldType = $this->_class->fieldMappings[$versionField]['type'];
$versionColumn = $this->quoteStrategy->getColumnName($versionField, $this->_class, $this->_platform);
if ($versionFieldType == Type::INTEGER) {
$set[] = $versionColumn . ' = ' . $versionColumn . ' + 1';
} else if ($versionFieldType == Type::DATETIME) {
$set[] = $versionColumn . ' = CURRENT_TIMESTAMP';
}
$where[] = $versionColumn;
$params[] = $this->_class->reflFields[$versionField]->getValue($entity);
$types[] = $this->_class->fieldMappings[$versionField]['type'];
}
$sql = 'UPDATE ' . $quotedTableName
. ' SET ' . implode(', ', $set)
. ' WHERE ' . implode(' = ? AND ', $where) . ' = ?';
$result = $this->_conn->executeUpdate($sql, $params, $types);
if ($versioned && ! $result) {
throw OptimisticLockException::lockFailed($entity);
}
}
/**
* @todo Add check for platform if it supports foreign keys/cascading.
* @param array $identifier
* @return void
*/
protected function deleteJoinTableRecords($identifier)
{
foreach ($this->_class->associationMappings as $mapping) {
if ($mapping['type'] == ClassMetadata::MANY_TO_MANY) {
// @Todo this only covers scenarios with no inheritance or of the same level. Is there something
// like self-referential relationship between different levels of an inheritance hierachy? I hope not!
$selfReferential = ($mapping['targetEntity'] == $mapping['sourceEntity']);
$otherKeys = array();
$keys = array();
if ( ! $mapping['isOwningSide']) {
$relatedClass = $this->_em->getClassMetadata($mapping['targetEntity']);
$mapping = $relatedClass->associationMappings[$mapping['mappedBy']];
foreach ($mapping['joinTable']['inverseJoinColumns'] as $joinColumn) {
$keys[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $relatedClass, $this->_platform);
}
if ($selfReferential) {
foreach ($mapping['joinTable']['joinColumns'] as $joinColumn) {
$otherKeys[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $relatedClass, $this->_platform);
}
}
} else {
foreach ($mapping['joinTable']['joinColumns'] as $joinColumn) {
$keys[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $this->_class, $this->_platform);
}
if ($selfReferential) {
foreach ($mapping['joinTable']['inverseJoinColumns'] as $joinColumn) {
$otherKeys[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $this->_class, $this->_platform);
}
}
}
if ( ! isset($mapping['isOnDeleteCascade'])) {
$joinTableName = $this->quoteStrategy->getJoinTableName($mapping, $this->_class, $this->_platform);
$this->_conn->delete($joinTableName, array_combine($keys, $identifier));
if ($selfReferential) {
$this->_conn->delete($joinTableName, array_combine($otherKeys, $identifier));
}
}
}
}
}
/**
* Deletes a managed entity.
*
* The entity to delete must be managed and have a persistent identifier.
* The deletion happens instantaneously.
*
* Subclasses may override this method to customize the semantics of entity deletion.
*
* @param object $entity The entity to delete.
*/
public function delete($entity)
{
$identifier = $this->_em->getUnitOfWork()->getEntityIdentifier($entity);
$this->deleteJoinTableRecords($identifier);
$id = array_combine($this->quoteStrategy->getIdentifierColumnNames($this->_class, $this->_platform), $identifier);
$this->_conn->delete($this->quoteStrategy->getTableName($this->_class, $this->_platform), $id);
}
/**
* Prepares the changeset of an entity for database insertion (UPDATE).
*
* The changeset is obtained from the currently running UnitOfWork.
*
* During this preparation the array that is passed as the second parameter is filled with
* => pairs, grouped by table name.
*
* Example:
*
* array(
* 'foo_table' => array('column1' => 'value1', 'column2' => 'value2', ...),
* 'bar_table' => array('columnX' => 'valueX', 'columnY' => 'valueY', ...),
* ...
* )
*
*
* @param object $entity The entity for which to prepare the data.
* @return array The prepared data.
*/
protected function _prepareUpdateData($entity)
{
$result = array();
$uow = $this->_em->getUnitOfWork();
if (($versioned = $this->_class->isVersioned) != false) {
$versionField = $this->_class->versionField;
}
foreach ($uow->getEntityChangeSet($entity) as $field => $change) {
if ($versioned && $versionField == $field) {
continue;
}
$newVal = $change[1];
if (isset($this->_class->associationMappings[$field])) {
$assoc = $this->_class->associationMappings[$field];
// Only owning side of x-1 associations can have a FK column.
if ( ! $assoc['isOwningSide'] || ! ($assoc['type'] & ClassMetadata::TO_ONE)) {
continue;
}
if ($newVal !== null) {
$oid = spl_object_hash($newVal);
if (isset($this->_queuedInserts[$oid]) || $uow->isScheduledForInsert($newVal)) {
// The associated entity $newVal is not yet persisted, so we must
// set $newVal = null, in order to insert a null value and schedule an
// extra update on the UnitOfWork.
$uow->scheduleExtraUpdate($entity, array(
$field => array(null, $newVal)
));
$newVal = null;
}
}
if ($newVal !== null) {
$newValId = $uow->getEntityIdentifier($newVal);
}
$targetClass = $this->_em->getClassMetadata($assoc['targetEntity']);
$owningTable = $this->getOwningTable($field);
foreach ($assoc['joinColumns'] as $joinColumn) {
$sourceColumn = $joinColumn['name'];
$targetColumn = $joinColumn['referencedColumnName'];
$quotedColumn = $this->quoteStrategy->getJoinColumnName($joinColumn, $this->_class, $this->_platform);
$this->quotedColumns[$sourceColumn] = $quotedColumn;
if ($newVal === null) {
$result[$owningTable][$sourceColumn] = null;
} else if ($targetClass->containsForeignIdentifier) {
$result[$owningTable][$sourceColumn] = $newValId[$targetClass->getFieldForColumn($targetColumn)];
} else {
$result[$owningTable][$sourceColumn] = $newValId[$targetClass->fieldNames[$targetColumn]];
}
$this->_columnTypes[$sourceColumn] = $targetClass->getTypeOfColumn($targetColumn);
}
} else {
$columnName = $this->_class->columnNames[$field];
$this->_columnTypes[$columnName] = $this->_class->fieldMappings[$field]['type'];
$result[$this->getOwningTable($field)][$columnName] = $newVal;
}
}
return $result;
}
/**
* Prepares the data changeset of a managed entity for database insertion (initial INSERT).
* The changeset of the entity is obtained from the currently running UnitOfWork.
*
* The default insert data preparation is the same as for updates.
*
* @param object $entity The entity for which to prepare the data.
* @return array The prepared data for the tables to update.
* @see _prepareUpdateData
*/
protected function _prepareInsertData($entity)
{
return $this->_prepareUpdateData($entity);
}
/**
* Gets the name of the table that owns the column the given field is mapped to.
*
* The default implementation in BasicEntityPersister always returns the name
* of the table the entity type of this persister is mapped to, since an entity
* is always persisted to a single table with a BasicEntityPersister.
*
* @param string $fieldName The field name.
* @return string The table name.
*/
public function getOwningTable($fieldName)
{
return $this->_class->getTableName();
}
/**
* Loads an entity by a list of field criteria.
*
* @param array $criteria The criteria by which to load the entity.
* @param object $entity The entity to load the data into. If not specified,
* a new entity is created.
* @param $assoc The association that connects the entity to load to another entity, if any.
* @param array $hints Hints for entity creation.
* @param int $lockMode
* @param int $limit Limit number of results
* @param array $orderBy Criteria to order by
* @return object The loaded and managed entity instance or NULL if the entity can not be found.
* @todo Check identity map? loadById method? Try to guess whether $criteria is the id?
*/
public function load(array $criteria, $entity = null, $assoc = null, array $hints = array(), $lockMode = 0, $limit = null, array $orderBy = null)
{
$sql = $this->_getSelectEntitiesSQL($criteria, $assoc, $lockMode, $limit, null, $orderBy);
list($params, $types) = $this->expandParameters($criteria);
$stmt = $this->_conn->executeQuery($sql, $params, $types);
if ($entity !== null) {
$hints[Query::HINT_REFRESH] = true;
$hints[Query::HINT_REFRESH_ENTITY] = $entity;
}
$hydrator = $this->_em->newHydrator($this->_selectJoinSql ? Query::HYDRATE_OBJECT : Query::HYDRATE_SIMPLEOBJECT);
$entities = $hydrator->hydrateAll($stmt, $this->_rsm, $hints);
return $entities ? $entities[0] : null;
}
/**
* Loads an entity of this persister's mapped class as part of a single-valued
* association from another entity.
*
* @param array $assoc The association to load.
* @param object $sourceEntity The entity that owns the association (not necessarily the "owning side").
* @param array $identifier The identifier of the entity to load. Must be provided if
* the association to load represents the owning side, otherwise
* the identifier is derived from the $sourceEntity.
* @return object The loaded and managed entity instance or NULL if the entity can not be found.
*/
public function loadOneToOneEntity(array $assoc, $sourceEntity, array $identifier = array())
{
if (($foundEntity = $this->_em->getUnitOfWork()->tryGetById($identifier, $assoc['targetEntity'])) != false) {
return $foundEntity;
}
$targetClass = $this->_em->getClassMetadata($assoc['targetEntity']);
if ($assoc['isOwningSide']) {
$isInverseSingleValued = $assoc['inversedBy'] && ! $targetClass->isCollectionValuedAssociation($assoc['inversedBy']);
// Mark inverse side as fetched in the hints, otherwise the UoW would
// try to load it in a separate query (remember: to-one inverse sides can not be lazy).
$hints = array();
if ($isInverseSingleValued) {
$hints['fetched']["r"][$assoc['inversedBy']] = true;
}
/* cascade read-only status
if ($this->_em->getUnitOfWork()->isReadOnly($sourceEntity)) {
$hints[Query::HINT_READ_ONLY] = true;
}
*/
$targetEntity = $this->load($identifier, null, $assoc, $hints);
// Complete bidirectional association, if necessary
if ($targetEntity !== null && $isInverseSingleValued) {
$targetClass->reflFields[$assoc['inversedBy']]->setValue($targetEntity, $sourceEntity);
}
} else {
$sourceClass = $this->_em->getClassMetadata($assoc['sourceEntity']);
$owningAssoc = $targetClass->getAssociationMapping($assoc['mappedBy']);
// TRICKY: since the association is specular source and target are flipped
foreach ($owningAssoc['targetToSourceKeyColumns'] as $sourceKeyColumn => $targetKeyColumn) {
if ( ! isset($sourceClass->fieldNames[$sourceKeyColumn])) {
throw MappingException::joinColumnMustPointToMappedField(
$sourceClass->name, $sourceKeyColumn
);
}
// unset the old value and set the new sql aliased value here. By definition
// unset($identifier[$targetKeyColumn] works here with how UnitOfWork::createEntity() calls this method.
$identifier[$this->_getSQLTableAlias($targetClass->name) . "." . $targetKeyColumn] =
$sourceClass->reflFields[$sourceClass->fieldNames[$sourceKeyColumn]]->getValue($sourceEntity);
unset($identifier[$targetKeyColumn]);
}
$targetEntity = $this->load($identifier, null, $assoc);
if ($targetEntity !== null) {
$targetClass->setFieldValue($targetEntity, $assoc['mappedBy'], $sourceEntity);
}
}
return $targetEntity;
}
/**
* Refreshes a managed entity.
*
* @param array $id The identifier of the entity as an associative array from
* column or field names to values.
* @param object $entity The entity to refresh.
*/
public function refresh(array $id, $entity, $lockMode = 0)
{
$sql = $this->_getSelectEntitiesSQL($id, null, $lockMode);
list($params, $types) = $this->expandParameters($id);
$stmt = $this->_conn->executeQuery($sql, $params, $types);
$hydrator = $this->_em->newHydrator(Query::HYDRATE_OBJECT);
$hydrator->hydrateAll($stmt, $this->_rsm, array(Query::HINT_REFRESH => true));
}
/**
* Load Entities matching the given Criteria object
*
* @param \Doctrine\Common\Collections\Criteria $criteria
*
* @return array
*/
public function loadCriteria(Criteria $criteria)
{
$orderBy = $criteria->getOrderings();
$limit = $criteria->getMaxResults();
$offset = $criteria->getFirstResult();
$sql = $this->_getSelectEntitiesSQL($criteria, null, 0, $limit, $offset, $orderBy);
list($params, $types) = $this->expandCriteriaParameters($criteria);
$stmt = $this->_conn->executeQuery($sql, $params, $types);
$hydrator = $this->_em->newHydrator(($this->_selectJoinSql) ? Query::HYDRATE_OBJECT : Query::HYDRATE_SIMPLEOBJECT);
return $hydrator->hydrateAll($stmt, $this->_rsm, array('deferEagerLoads' => true));
}
/**
* Expand Criteria Parameters by walking the expressions and grabbing all
* parameters and types from it.
*
* @param \Doctrine\Common\Collections\Criteria $criteria
*
* @return array(array(), array())
*/
private function expandCriteriaParameters(Criteria $criteria)
{
$expression = $criteria->getWhereExpression();
if ($expression === null) {
return array(array(), array());
}
$valueVisitor = new SqlValueVisitor();
$valueVisitor->dispatch($expression);
list($values, $types) = $valueVisitor->getParamsAndTypes();
$sqlValues = array();
foreach ($values as $value) {
$sqlValues[] = $this->getValue($value);
}
$sqlTypes = array();
foreach ($types as $type) {
list($field, $value) = $type;
$sqlTypes[] = $this->getType($field, $value);
}
return array($sqlValues, $sqlTypes);
}
/**
* Loads a list of entities by a list of field criteria.
*
* @param array $criteria
* @param array $orderBy
* @param int $limit
* @param int $offset
* @return array
*/
public function loadAll(array $criteria = array(), array $orderBy = null, $limit = null, $offset = null)
{
$sql = $this->_getSelectEntitiesSQL($criteria, null, 0, $limit, $offset, $orderBy);
list($params, $types) = $this->expandParameters($criteria);
$stmt = $this->_conn->executeQuery($sql, $params, $types);
$hydrator = $this->_em->newHydrator(($this->_selectJoinSql) ? Query::HYDRATE_OBJECT : Query::HYDRATE_SIMPLEOBJECT);
return $hydrator->hydrateAll($stmt, $this->_rsm, array('deferEagerLoads' => true));
}
/**
* Get (sliced or full) elements of the given collection.
*
* @param array $assoc
* @param object $sourceEntity
* @param int|null $offset
* @param int|null $limit
* @return array
*/
public function getManyToManyCollection(array $assoc, $sourceEntity, $offset = null, $limit = null)
{
$stmt = $this->getManyToManyStatement($assoc, $sourceEntity, $offset, $limit);
return $this->loadArrayFromStatement($assoc, $stmt);
}
/**
* Load an array of entities from a given dbal statement.
*
* @param array $assoc
* @param \Doctrine\DBAL\Statement $stmt
*
* @return array
*/
private function loadArrayFromStatement($assoc, $stmt)
{
$hints = array('deferEagerLoads' => true);
if (isset($assoc['indexBy'])) {
$rsm = clone ($this->_rsm); // this is necessary because the "default rsm" should be changed.
$rsm->addIndexBy('r', $assoc['indexBy']);
} else {
$rsm = $this->_rsm;
}
$hydrator = $this->_em->newHydrator(Query::HYDRATE_OBJECT);
return $hydrator->hydrateAll($stmt, $rsm, $hints);
}
/**
* Hydrate a collection from a given dbal statement.
*
* @param array $assoc
* @param \Doctrine\DBAL\Statement $stmt
* @param PersistentCollection $coll
*
* @return array
*/
private function loadCollectionFromStatement($assoc, $stmt, $coll)
{
$hints = array('deferEagerLoads' => true, 'collection' => $coll);
if (isset($assoc['indexBy'])) {
$rsm = clone ($this->_rsm); // this is necessary because the "default rsm" should be changed.
$rsm->addIndexBy('r', $assoc['indexBy']);
} else {
$rsm = $this->_rsm;
}
$hydrator = $this->_em->newHydrator(Query::HYDRATE_OBJECT);
return $hydrator->hydrateAll($stmt, $rsm, $hints);
}
/**
* Loads a collection of entities of a many-to-many association.
*
* @param ManyToManyMapping $assoc The association mapping of the association being loaded.
* @param object $sourceEntity The entity that owns the collection.
* @param PersistentCollection $coll The collection to fill.
* @param int|null $offset
* @param int|null $limit
* @return array
*/
public function loadManyToManyCollection(array $assoc, $sourceEntity, PersistentCollection $coll)
{
$stmt = $this->getManyToManyStatement($assoc, $sourceEntity);
return $this->loadCollectionFromStatement($assoc, $stmt, $coll);
}
private function getManyToManyStatement(array $assoc, $sourceEntity, $offset = null, $limit = null)
{
$criteria = array();
$sourceClass = $this->_em->getClassMetadata($assoc['sourceEntity']);
if ($assoc['isOwningSide']) {
$quotedJoinTable = $this->quoteStrategy->getJoinTableName($assoc, $sourceClass, $this->_platform);
foreach ($assoc['joinTable']['joinColumns'] as $joinColumn) {
$relationKeyColumn = $joinColumn['name'];
$sourceKeyColumn = $joinColumn['referencedColumnName'];
$quotedKeyColumn = $this->quoteStrategy->getJoinColumnName($joinColumn, $sourceClass, $this->_platform);
if ($sourceClass->containsForeignIdentifier) {
$field = $sourceClass->getFieldForColumn($sourceKeyColumn);
$value = $sourceClass->reflFields[$field]->getValue($sourceEntity);
if (isset($sourceClass->associationMappings[$field])) {
$value = $this->_em->getUnitOfWork()->getEntityIdentifier($value);
$value = $value[$this->_em->getClassMetadata($sourceClass->associationMappings[$field]['targetEntity'])->identifier[0]];
}
$criteria[$quotedJoinTable . "." . $quotedKeyColumn] = $value;
} else if (isset($sourceClass->fieldNames[$sourceKeyColumn])) {
$criteria[$quotedJoinTable . "." . $quotedKeyColumn] = $sourceClass->reflFields[$sourceClass->fieldNames[$sourceKeyColumn]]->getValue($sourceEntity);
} else {
throw MappingException::joinColumnMustPointToMappedField(
$sourceClass->name, $sourceKeyColumn
);
}
}
} else {
$owningAssoc = $this->_em->getClassMetadata($assoc['targetEntity'])->associationMappings[$assoc['mappedBy']];
$quotedJoinTable = $this->quoteStrategy->getJoinTableName($owningAssoc, $sourceClass, $this->_platform);
// TRICKY: since the association is inverted source and target are flipped
foreach ($owningAssoc['joinTable']['inverseJoinColumns'] as $joinColumn) {
$relationKeyColumn = $joinColumn['name'];
$sourceKeyColumn = $joinColumn['referencedColumnName'];
$quotedKeyColumn = $this->quoteStrategy->getJoinColumnName($joinColumn, $sourceClass, $this->_platform);
if ($sourceClass->containsForeignIdentifier) {
$field = $sourceClass->getFieldForColumn($sourceKeyColumn);
$value = $sourceClass->reflFields[$field]->getValue($sourceEntity);
if (isset($sourceClass->associationMappings[$field])) {
$value = $this->_em->getUnitOfWork()->getEntityIdentifier($value);
$value = $value[$this->_em->getClassMetadata($sourceClass->associationMappings[$field]['targetEntity'])->identifier[0]];
}
$criteria[$quotedJoinTable . "." . $quotedKeyColumn] = $value;
} else if (isset($sourceClass->fieldNames[$sourceKeyColumn])) {
$criteria[$quotedJoinTable . "." . $quotedKeyColumn] = $sourceClass->reflFields[$sourceClass->fieldNames[$sourceKeyColumn]]->getValue($sourceEntity);
} else {
throw MappingException::joinColumnMustPointToMappedField(
$sourceClass->name, $sourceKeyColumn
);
}
}
}
$sql = $this->_getSelectEntitiesSQL($criteria, $assoc, 0, $limit, $offset);
list($params, $types) = $this->expandParameters($criteria);
return $this->_conn->executeQuery($sql, $params, $types);
}
/**
* Gets the SELECT SQL to select one or more entities by a set of field criteria.
*
* @param array|\Doctrine\Common\Collections\Criteria $criteria
* @param AssociationMapping $assoc
* @param string $orderBy
* @param int $lockMode
* @param int $limit
* @param int $offset
* @param array $orderBy
* @return string
* @todo Refactor: _getSelectSQL(...)
*/
protected function _getSelectEntitiesSQL($criteria, $assoc = null, $lockMode = 0, $limit = null, $offset = null, array $orderBy = null)
{
$joinSql = ($assoc != null && $assoc['type'] == ClassMetadata::MANY_TO_MANY) ? $this->_getSelectManyToManyJoinSQL($assoc) : '';
$conditionSql = ($criteria instanceof Criteria)
? $this->_getSelectConditionCriteriaSQL($criteria)
: $this->_getSelectConditionSQL($criteria, $assoc);
$orderBy = ($assoc !== null && isset($assoc['orderBy'])) ? $assoc['orderBy'] : $orderBy;
$orderBySql = $orderBy ? $this->_getOrderBySQL($orderBy, $this->_getSQLTableAlias($this->_class->name)) : '';
$lockSql = '';
if ($lockMode == LockMode::PESSIMISTIC_READ) {
$lockSql = ' ' . $this->_platform->getReadLockSql();
} else if ($lockMode == LockMode::PESSIMISTIC_WRITE) {
$lockSql = ' ' . $this->_platform->getWriteLockSql();
}
$alias = $this->_getSQLTableAlias($this->_class->name);
if ($filterSql = $this->generateFilterConditionSQL($this->_class, $alias)) {
if ($conditionSql) {
$conditionSql .= ' AND ';
}
$conditionSql .= $filterSql;
}
return $this->_platform->modifyLimitQuery('SELECT ' . $this->_getSelectColumnListSQL()
. $this->_platform->appendLockHint(' FROM ' . $this->quoteStrategy->getTableName($this->_class, $this->_platform) . ' '
. $alias, $lockMode)
. $this->_selectJoinSql . $joinSql
. ($conditionSql ? ' WHERE ' . $conditionSql : '')
. $orderBySql, $limit, $offset)
. $lockSql;
}
/**
* Gets the ORDER BY SQL snippet for ordered collections.
*
* @param array $orderBy
* @param string $baseTableAlias
* @return string
*/
protected final function _getOrderBySQL(array $orderBy, $baseTableAlias)
{
$orderBySql = '';
foreach ($orderBy as $fieldName => $orientation) {
if ( ! isset($this->_class->fieldMappings[$fieldName])) {
throw ORMException::unrecognizedField($fieldName);
}
$orientation = strtoupper(trim($orientation));
if ($orientation != 'ASC' && $orientation != 'DESC') {
throw ORMException::invalidOrientation($this->_class->name, $fieldName);
}
$tableAlias = isset($this->_class->fieldMappings[$fieldName]['inherited']) ?
$this->_getSQLTableAlias($this->_class->fieldMappings[$fieldName]['inherited'])
: $baseTableAlias;
$columnName = $this->quoteStrategy->getColumnName($fieldName, $this->_class, $this->_platform);
$orderBySql .= $orderBySql ? ', ' : ' ORDER BY ';
$orderBySql .= $tableAlias . '.' . $columnName . ' ' . $orientation;
}
return $orderBySql;
}
/**
* Gets the SQL fragment with the list of columns to select when querying for
* an entity in this persister.
*
* Subclasses should override this method to alter or change the select column
* list SQL fragment. Note that in the implementation of BasicEntityPersister
* the resulting SQL fragment is generated only once and cached in {@link _selectColumnListSql}.
* Subclasses may or may not do the same.
*
* @return string The SQL fragment.
* @todo Rename: _getSelectColumnsSQL()
*/
protected function _getSelectColumnListSQL()
{
if ($this->_selectColumnListSql !== null) {
return $this->_selectColumnListSql;
}
$columnList = '';
$this->_rsm = new Query\ResultSetMapping();
$this->_rsm->addEntityResult($this->_class->name, 'r'); // r for root
// Add regular columns to select list
foreach ($this->_class->fieldNames as $field) {
if ($columnList) $columnList .= ', ';
$columnList .= $this->_getSelectColumnSQL($field, $this->_class);
}
$this->_selectJoinSql = '';
$eagerAliasCounter = 0;
foreach ($this->_class->associationMappings as $assocField => $assoc) {
$assocColumnSQL = $this->_getSelectColumnAssociationSQL($assocField, $assoc, $this->_class);
if ($assocColumnSQL) {
if ($columnList) $columnList .= ', ';
$columnList .= $assocColumnSQL;
}
if ($assoc['type'] & ClassMetadata::TO_ONE && ($assoc['fetch'] == ClassMetadata::FETCH_EAGER || !$assoc['isOwningSide'])) {
$eagerEntity = $this->_em->getClassMetadata($assoc['targetEntity']);
if ($eagerEntity->inheritanceType != ClassMetadata::INHERITANCE_TYPE_NONE) {
continue; // now this is why you shouldn't use inheritance
}
$assocAlias = 'e' . ($eagerAliasCounter++);
$this->_rsm->addJoinedEntityResult($assoc['targetEntity'], $assocAlias, 'r', $assocField);
foreach ($eagerEntity->fieldNames as $field) {
if ($columnList) $columnList .= ', ';
$columnList .= $this->_getSelectColumnSQL($field, $eagerEntity, $assocAlias);
}
foreach ($eagerEntity->associationMappings as $assoc2Field => $assoc2) {
$assoc2ColumnSQL = $this->_getSelectColumnAssociationSQL($assoc2Field, $assoc2, $eagerEntity, $assocAlias);
if ($assoc2ColumnSQL) {
if ($columnList) $columnList .= ', ';
$columnList .= $assoc2ColumnSQL;
}
}
$first = true;
if ($assoc['isOwningSide']) {
$this->_selectJoinSql .= ' ' . $this->getJoinSQLForJoinColumns($assoc['joinColumns']);
$this->_selectJoinSql .= ' ' . $this->quoteStrategy->getTableName($eagerEntity, $this->_platform) . ' ' . $this->_getSQLTableAlias($eagerEntity->name, $assocAlias) .' ON ';
$tableAlias = $this->_getSQLTableAlias($assoc['targetEntity'], $assocAlias);
foreach ($assoc['joinColumns'] as $joinColumn) {
$sourceCol = $this->quoteStrategy->getJoinColumnName($joinColumn, $this->_class, $this->_platform);
$targetCol = $this->quoteStrategy->getReferencedJoinColumnName($joinColumn, $this->_class, $this->_platform);
if ( ! $first) {
$this->_selectJoinSql .= ' AND ';
}
$this->_selectJoinSql .= $this->_getSQLTableAlias($assoc['sourceEntity']) . '.' . $sourceCol . ' = '
. $tableAlias . '.' . $targetCol;
$first = false;
}
// Add filter SQL
if ($filterSql = $this->generateFilterConditionSQL($eagerEntity, $tableAlias)) {
$this->_selectJoinSql .= ' AND ' . $filterSql;
}
} else {
$eagerEntity = $this->_em->getClassMetadata($assoc['targetEntity']);
$owningAssoc = $eagerEntity->getAssociationMapping($assoc['mappedBy']);
$this->_selectJoinSql .= ' LEFT JOIN';
$this->_selectJoinSql .= ' ' . $this->quoteStrategy->getTableName($eagerEntity, $this->_platform) . ' '
. $this->_getSQLTableAlias($eagerEntity->name, $assocAlias) . ' ON ';
foreach ($owningAssoc['sourceToTargetKeyColumns'] as $sourceCol => $targetCol) {
if ( ! $first) {
$this->_selectJoinSql .= ' AND ';
}
$this->_selectJoinSql .= $this->_getSQLTableAlias($owningAssoc['sourceEntity'], $assocAlias) . '.' . $sourceCol . ' = '
. $this->_getSQLTableAlias($owningAssoc['targetEntity']) . '.' . $targetCol;
$first = false;
}
}
}
}
$this->_selectColumnListSql = $columnList;
return $this->_selectColumnListSql;
}
/**
* Gets the SQL join fragment used when selecting entities from an association.
*
* @param string $field
* @param array $assoc
* @param ClassMetadata $class
* @param string $alias
*
* @return string
*/
protected function _getSelectColumnAssociationSQL($field, $assoc, ClassMetadata $class, $alias = 'r')
{
$columnList = array();
if ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE) {
foreach ($assoc['joinColumns'] as $joinColumn) {
$quotedColumn = $this->quoteStrategy->getJoinColumnName($joinColumn, $this->_class, $this->_platform);
$resultColumnName = $this->getSQLColumnAlias($joinColumn['name']);
$columnList[] = $this->_getSQLTableAlias($class->name, ($alias == 'r' ? '' : $alias) )
. '.' . $quotedColumn . ' AS ' . $resultColumnName;
$this->_rsm->addMetaResult($alias, $resultColumnName, $quotedColumn, isset($assoc['id']) && $assoc['id'] === true);
}
}
return implode(', ', $columnList);
}
/**
* Gets the SQL join fragment used when selecting entities from a
* many-to-many association.
*
* @param ManyToManyMapping $manyToMany
* @return string
*/
protected function _getSelectManyToManyJoinSQL(array $manyToMany)
{
$conditions = array();
$association = $manyToMany;
$sourceTableAlias = $this->_getSQLTableAlias($this->_class->name);
if ( ! $manyToMany['isOwningSide']) {
$targetEntity = $this->_em->getClassMetadata($manyToMany['targetEntity']);
$association = $targetEntity->associationMappings[$manyToMany['mappedBy']];
}
$joinTableName = $this->quoteStrategy->getJoinTableName($association, $this->_class, $this->_platform);
$joinColumns = ($manyToMany['isOwningSide'])
? $association['joinTable']['inverseJoinColumns']
: $association['joinTable']['joinColumns'];
foreach ($joinColumns as $joinColumn) {
$quotedSourceColumn = $this->quoteStrategy->getJoinColumnName($joinColumn, $this->_class, $this->_platform);
$quotedTargetColumn = $this->quoteStrategy->getReferencedJoinColumnName($joinColumn, $this->_class, $this->_platform);
$conditions[] = $sourceTableAlias . '.' . $quotedTargetColumn . ' = ' . $joinTableName . '.' . $quotedSourceColumn;
}
return ' INNER JOIN ' . $joinTableName . ' ON ' . implode(' AND ', $conditions);
}
/**
* Gets the INSERT SQL used by the persister to persist a new entity.
*
* @return string
*/
protected function _getInsertSQL()
{
if ($this->_insertSql === null) {
$insertSql = '';
$columns = $this->_getInsertColumnList();
if (empty($columns)) {
$insertSql = $this->_platform->getEmptyIdentityInsertSQL(
$this->quoteStrategy->getTableName($this->_class, $this->_platform),
$this->quoteStrategy->getColumnName($this->_class->identifier[0], $this->_class, $this->_platform)
);
} else {
$columns = array_unique($columns);
$values = array();
foreach ($columns as $column) {
$placeholder = '?';
if (isset($this->_class->fieldNames[$column]) &&
isset($this->_columnTypes[$this->_class->fieldNames[$column]]) &&
isset($this->_class->fieldMappings[$this->_class->fieldNames[$column]]['requireSQLConversion'])) {
$type = Type::getType($this->_columnTypes[$this->_class->fieldNames[$column]]);
$placeholder = $type->convertToDatabaseValueSQL('?', $this->_platform);
}
$values[] = $placeholder;
}
$insertSql = 'INSERT INTO ' . $this->quoteStrategy->getTableName($this->_class, $this->_platform)
. ' (' . implode(', ', $columns) . ') VALUES (' . implode(', ', $values) . ')';
}
$this->_insertSql = $insertSql;
}
return $this->_insertSql;
}
/**
* Gets the list of columns to put in the INSERT SQL statement.
*
* Subclasses should override this method to alter or change the list of
* columns placed in the INSERT statements used by the persister.
*
* @return array The list of columns.
*/
protected function _getInsertColumnList()
{
$columns = array();
foreach ($this->_class->reflFields as $name => $field) {
if ($this->_class->isVersioned && $this->_class->versionField == $name) {
continue;
}
if (isset($this->_class->associationMappings[$name])) {
$assoc = $this->_class->associationMappings[$name];
if ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE) {
foreach ($assoc['joinColumns'] as $joinColumn) {
$columns[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $this->_class, $this->_platform);
}
}
} else if ($this->_class->generatorType != ClassMetadata::GENERATOR_TYPE_IDENTITY || $this->_class->identifier[0] != $name) {
$columns[] = $this->quoteStrategy->getColumnName($name, $this->_class, $this->_platform);
$this->_columnTypes[$name] = $this->_class->fieldMappings[$name]['type'];
}
}
return $columns;
}
/**
* Gets the SQL snippet of a qualified column name for the given field name.
*
* @param string $field The field name.
* @param ClassMetadata $class The class that declares this field. The table this class is
* mapped to must own the column for the given field.
* @param string $alias
*/
protected function _getSelectColumnSQL($field, ClassMetadata $class, $alias = 'r')
{
$sql = $this->_getSQLTableAlias($class->name, $alias == 'r' ? '' : $alias)
. '.' . $this->quoteStrategy->getColumnName($field, $class, $this->_platform);
$columnAlias = $this->getSQLColumnAlias($class->columnNames[$field]);
$this->_rsm->addFieldResult($alias, $columnAlias, $field);
if (isset($class->fieldMappings[$field]['requireSQLConversion'])) {
$type = Type::getType($class->getTypeOfField($field));
$sql = $type->convertToPHPValueSQL($sql, $this->_platform);
}
return $sql . ' AS ' . $columnAlias;
}
/**
* Gets the SQL table alias for the given class name.
*
* @param string $className
* @return string The SQL table alias.
* @todo Reconsider. Binding table aliases to class names is not such a good idea.
*/
protected function _getSQLTableAlias($className, $assocName = '')
{
if ($assocName) {
$className .= '#' . $assocName;
}
if (isset($this->_sqlTableAliases[$className])) {
return $this->_sqlTableAliases[$className];
}
$tableAlias = 't' . $this->_sqlAliasCounter++;
$this->_sqlTableAliases[$className] = $tableAlias;
return $tableAlias;
}
/**
* Lock all rows of this entity matching the given criteria with the specified pessimistic lock mode
*
* @param array $criteria
* @param int $lockMode
* @return void
*/
public function lock(array $criteria, $lockMode)
{
$conditionSql = $this->_getSelectConditionSQL($criteria);
if ($lockMode == LockMode::PESSIMISTIC_READ) {
$lockSql = $this->_platform->getReadLockSql();
} else if ($lockMode == LockMode::PESSIMISTIC_WRITE) {
$lockSql = $this->_platform->getWriteLockSql();
}
$sql = 'SELECT 1 '
. $this->_platform->appendLockHint($this->getLockTablesSql(), $lockMode)
. ($conditionSql ? ' WHERE ' . $conditionSql : '') . ' ' . $lockSql;
list($params, $types) = $this->expandParameters($criteria);
$this->_conn->executeQuery($sql, $params, $types);
}
/**
* Get the FROM and optionally JOIN conditions to lock the entity managed by this persister.
*
* @return string
*/
protected function getLockTablesSql()
{
return 'FROM ' . $this->quoteStrategy->getTableName($this->_class, $this->_platform) . ' '
. $this->_getSQLTableAlias($this->_class->name);
}
/**
* Get the Select Where Condition from a Criteria object.
*
* @param \Doctrine\Common\Collections\Criteria $criteria
* @return string
*/
protected function _getSelectConditionCriteriaSQL(Criteria $criteria)
{
$expression = $criteria->getWhereExpression();
if ($expression === null) {
return '';
}
$visitor = new SqlExpressionVisitor($this);
return $visitor->dispatch($expression);
}
/**
* Get the SQL WHERE condition for matching a field with a given value.
*
* @param string $field
* @param mixed $value
* @param array|null $assoc
* @param string $comparison
*
* @return string
*/
public function getSelectConditionStatementSQL($field, $value, $assoc = null, $comparison = null)
{
$conditionSql = $this->getSelectConditionStatementColumnSQL($field, $assoc);
$placeholder = '?';
if (isset($this->_class->fieldMappings[$field]['requireSQLConversion'])) {
$type = Type::getType($this->_class->getTypeOfField($field));
$placeholder = $type->convertToDatabaseValueSQL($placeholder, $this->_platform);
}
$conditionSql .= ($comparison === null)
? ((is_array($value)) ? ' IN (?)' : (($value === null) ? ' IS NULL' : ' = ' . $placeholder))
: ' ' . sprintf(self::$comparisonMap[$comparison], $placeholder);
return $conditionSql;
}
/**
* Build the left-hand-side of a where condition statement.
*
* @param string $field
* @param array $assoc
*
* @return string
*/
protected function getSelectConditionStatementColumnSQL($field, $assoc = null)
{
switch (true) {
case (isset($this->_class->columnNames[$field])):
$className = (isset($this->_class->fieldMappings[$field]['inherited']))
? $this->_class->fieldMappings[$field]['inherited']
: $this->_class->name;
return $this->_getSQLTableAlias($className) . '.' . $this->quoteStrategy->getColumnName($field, $this->_class, $this->_platform);
case (isset($this->_class->associationMappings[$field])):
if ( ! $this->_class->associationMappings[$field]['isOwningSide']) {
throw ORMException::invalidFindByInverseAssociation($this->_class->name, $field);
}
$className = (isset($this->_class->associationMappings[$field]['inherited']))
? $this->_class->associationMappings[$field]['inherited']
: $this->_class->name;
return $this->_getSQLTableAlias($className) . '.' . $this->_class->associationMappings[$field]['joinColumns'][0]['name'];
case ($assoc !== null && strpos($field, " ") === false && strpos($field, "(") === false):
// very careless developers could potentially open up this normally hidden api for userland attacks,
// therefore checking for spaces and function calls which are not allowed.
// found a join column condition, not really a "field"
return $field;
}
throw ORMException::unrecognizedField($field);
}
/**
* Gets the conditional SQL fragment used in the WHERE clause when selecting
* entities in this persister.
*
* Subclasses are supposed to override this method if they intend to change
* or alter the criteria by which entities are selected.
*
* @param array $criteria
* @param AssociationMapping $assoc
* @return string
*/
protected function _getSelectConditionSQL(array $criteria, $assoc = null)
{
$conditionSql = '';
foreach ($criteria as $field => $value) {
$conditionSql .= $conditionSql ? ' AND ' : '';
$conditionSql .= $this->getSelectConditionStatementSQL($field, $value, $assoc);
}
return $conditionSql;
}
/**
* Return an array with (sliced or full list) of elements in the specified collection.
*
* @param array $assoc
* @param object $sourceEntity
* @param int $offset
* @param int $limit
* @return array
*/
public function getOneToManyCollection(array $assoc, $sourceEntity, $offset = null, $limit = null)
{
$stmt = $this->getOneToManyStatement($assoc, $sourceEntity, $offset, $limit);
return $this->loadArrayFromStatement($assoc, $stmt);
}
/**
* Loads a collection of entities in a one-to-many association.
*
* @param array $assoc
* @param object $sourceEntity
* @param PersistentCollection $coll The collection to load/fill.
* @param int|null $offset
* @param int|null $limit
*/
public function loadOneToManyCollection(array $assoc, $sourceEntity, PersistentCollection $coll)
{
$stmt = $this->getOneToManyStatement($assoc, $sourceEntity);
return $this->loadCollectionFromStatement($assoc, $stmt, $coll);
}
/**
* Build criteria and execute SQL statement to fetch the one to many entities from.
*
* @param array $assoc
* @param object $sourceEntity
* @param int|null $offset
* @param int|null $limit
* @return \Doctrine\DBAL\Statement
*/
private function getOneToManyStatement(array $assoc, $sourceEntity, $offset = null, $limit = null)
{
$criteria = array();
$owningAssoc = $this->_class->associationMappings[$assoc['mappedBy']];
$sourceClass = $this->_em->getClassMetadata($assoc['sourceEntity']);
$tableAlias = $this->_getSQLTableAlias(isset($owningAssoc['inherited']) ? $owningAssoc['inherited'] : $this->_class->name);
foreach ($owningAssoc['targetToSourceKeyColumns'] as $sourceKeyColumn => $targetKeyColumn) {
if ($sourceClass->containsForeignIdentifier) {
$field = $sourceClass->getFieldForColumn($sourceKeyColumn);
$value = $sourceClass->reflFields[$field]->getValue($sourceEntity);
if (isset($sourceClass->associationMappings[$field])) {
$value = $this->_em->getUnitOfWork()->getEntityIdentifier($value);
$value = $value[$this->_em->getClassMetadata($sourceClass->associationMappings[$field]['targetEntity'])->identifier[0]];
}
$criteria[$tableAlias . "." . $targetKeyColumn] = $value;
} else {
$criteria[$tableAlias . "." . $targetKeyColumn] = $sourceClass->reflFields[$sourceClass->fieldNames[$sourceKeyColumn]]->getValue($sourceEntity);
}
}
$sql = $this->_getSelectEntitiesSQL($criteria, $assoc, 0, $limit, $offset);
list($params, $types) = $this->expandParameters($criteria);
return $this->_conn->executeQuery($sql, $params, $types);
}
/**
* Expand the parameters from the given criteria and use the correct binding types if found.
*
* @param array $criteria
* @return array
*/
private function expandParameters($criteria)
{
$params = $types = array();
foreach ($criteria as $field => $value) {
if ($value === null) {
continue; // skip null values.
}
$types[] = $this->getType($field, $value);
$params[] = $this->getValue($value);
}
return array($params, $types);
}
/**
* Infer field type to be used by parameter type casting.
*
* @param string $field
* @param mixed $value
* @return integer
*/
private function getType($field, $value)
{
switch (true) {
case (isset($this->_class->fieldMappings[$field])):
$type = $this->_class->fieldMappings[$field]['type'];
break;
case (isset($this->_class->associationMappings[$field])):
$assoc = $this->_class->associationMappings[$field];
if (count($assoc['sourceToTargetKeyColumns']) > 1) {
throw Query\QueryException::associationPathCompositeKeyNotSupported();
}
$targetClass = $this->_em->getClassMetadata($assoc['targetEntity']);
$targetColumn = $assoc['joinColumns'][0]['referencedColumnName'];
$type = null;
if (isset($targetClass->fieldNames[$targetColumn])) {
$type = $targetClass->fieldMappings[$targetClass->fieldNames[$targetColumn]]['type'];
}
break;
default:
$type = null;
}
if (is_array($value)) {
$type = Type::getType( $type )->getBindingType();
$type += Connection::ARRAY_PARAM_OFFSET;
}
return $type;
}
/**
* Retrieve parameter value
*
* @param mixed $value
* @return mixed
*/
private function getValue($value)
{
if (is_array($value)) {
$newValue = array();
foreach ($value as $itemValue) {
$newValue[] = $this->getIndividualValue($itemValue);
}
return $newValue;
}
return $this->getIndividualValue($value);
}
/**
* Retrieve an invidiual parameter value
*
* @param mixed $value
* @return mixed
*/
private function getIndividualValue($value)
{
if (is_object($value) && $this->_em->getMetadataFactory()->hasMetadataFor(ClassUtils::getClass($value))) {
if ($this->_em->getUnitOfWork()->getEntityState($value) === UnitOfWork::STATE_MANAGED) {
$idValues = $this->_em->getUnitOfWork()->getEntityIdentifier($value);
} else {
$class = $this->_em->getClassMetadata(get_class($value));
$idValues = $class->getIdentifierValues($value);
}
$key = key($idValues);
if (null !== $key){
$value = $idValues[$key];
}
}
return $value;
}
/**
* Checks whether the given managed entity exists in the database.
*
* @param object $entity
* @return boolean TRUE if the entity exists in the database, FALSE otherwise.
*/
public function exists($entity, array $extraConditions = array())
{
$criteria = $this->_class->getIdentifierValues($entity);
if ( ! $criteria) {
return false;
}
if ($extraConditions) {
$criteria = array_merge($criteria, $extraConditions);
}
$alias = $this->_getSQLTableAlias($this->_class->name);
$sql = 'SELECT 1 '
. $this->getLockTablesSql()
. ' WHERE ' . $this->_getSelectConditionSQL($criteria);
if ($filterSql = $this->generateFilterConditionSQL($this->_class, $alias)) {
$sql .= ' AND ' . $filterSql;
}
list($params) = $this->expandParameters($criteria);
return (bool) $this->_conn->fetchColumn($sql, $params);
}
/**
* Generates the appropriate join SQL for the given join column.
*
* @param array $joinColumns The join columns definition of an association.
* @return string LEFT JOIN if one of the columns is nullable, INNER JOIN otherwise.
*/
protected function getJoinSQLForJoinColumns($joinColumns)
{
// if one of the join columns is nullable, return left join
foreach ($joinColumns as $joinColumn) {
if ( ! isset($joinColumn['nullable']) || $joinColumn['nullable']) {
return 'LEFT JOIN';
}
}
return 'INNER JOIN';
}
/**
* Gets an SQL column alias for a column name.
*
* @param string $columnName
* @return string
*/
public function getSQLColumnAlias($columnName)
{
return $this->quoteStrategy->getColumnAlias($columnName, $this->_sqlAliasCounter++, $this->_platform);
}
/**
* Generates the filter SQL for a given entity and table alias.
*
* @param ClassMetadata $targetEntity Metadata of the target entity.
* @param string $targetTableAlias The table alias of the joined/selected table.
*
* @return string The SQL query part to add to a query.
*/
protected function generateFilterConditionSQL(ClassMetadata $targetEntity, $targetTableAlias)
{
$filterClauses = array();
foreach ($this->_em->getFilters()->getEnabledFilters() as $filter) {
if ('' !== $filterExpr = $filter->addFilterConstraint($targetEntity, $targetTableAlias)) {
$filterClauses[] = '(' . $filterExpr . ')';
}
}
$sql = implode(' AND ', $filterClauses);
return $sql ? "(" . $sql . ")" : ""; // Wrap again to avoid "X or Y and FilterConditionSQL"
}
}
DoctrineORM-2.3.3/Doctrine/ORM/Persisters/ElementCollectionPersister.php 0000644 0001751 0001751 00000002444 12143374607 026353 0 ustar beberlei beberlei .
*/
namespace Doctrine\ORM\Persisters;
/**
* Persister for collections of basic elements / value types.
*
* @author robo
* @todo Implementation once support for collections of basic elements (i.e. strings) is added.
*/
abstract class ElementCollectionPersister extends AbstractCollectionPersister
{
//put your code here
}
DoctrineORM-2.3.3/Doctrine/ORM/Persisters/JoinedSubclassPersister.php 0000644 0001751 0001751 00000046566 12143374607 025673 0 ustar beberlei beberlei .
*/
namespace Doctrine\ORM\Persisters;
use Doctrine\ORM\ORMException;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\Query\ResultSetMapping;
use Doctrine\DBAL\LockMode;
use Doctrine\DBAL\Types\Type;
use Doctrine\Common\Collections\Criteria;
/**
* The joined subclass persister maps a single entity instance to several tables in the
* database as it is defined by the Class Table Inheritance strategy.
*
* @author Roman Borschel
* @author Benjamin Eberlei
* @author Alexander
* @since 2.0
* @see http://martinfowler.com/eaaCatalog/classTableInheritance.html
*/
class JoinedSubclassPersister extends AbstractEntityInheritancePersister
{
/**
* Map that maps column names to the table names that own them.
* This is mainly a temporary cache, used during a single request.
*
* @var array
*/
private $_owningTableMap = array();
/**
* Map of table to quoted table names.
*
* @var array
*/
private $_quotedTableMap = array();
/**
* {@inheritdoc}
*/
protected function _getDiscriminatorColumnTableName()
{
$class = ($this->_class->name !== $this->_class->rootEntityName)
? $this->_em->getClassMetadata($this->_class->rootEntityName)
: $this->_class;
return $class->getTableName();
}
/**
* This function finds the ClassMetadata instance in an inheritance hierarchy
* that is responsible for enabling versioning.
*
* @return \Doctrine\ORM\Mapping\ClassMetadata
*/
private function _getVersionedClassMetadata()
{
if (isset($this->_class->fieldMappings[$this->_class->versionField]['inherited'])) {
$definingClassName = $this->_class->fieldMappings[$this->_class->versionField]['inherited'];
return $this->_em->getClassMetadata($definingClassName);
}
return $this->_class;
}
/**
* Gets the name of the table that owns the column the given field is mapped to.
*
* @param string $fieldName
* @return string
* @override
*/
public function getOwningTable($fieldName)
{
if (isset($this->_owningTableMap[$fieldName])) {
return $this->_owningTableMap[$fieldName];
}
if (isset($this->_class->associationMappings[$fieldName]['inherited'])) {
$cm = $this->_em->getClassMetadata($this->_class->associationMappings[$fieldName]['inherited']);
} else if (isset($this->_class->fieldMappings[$fieldName]['inherited'])) {
$cm = $this->_em->getClassMetadata($this->_class->fieldMappings[$fieldName]['inherited']);
} else {
$cm = $this->_class;
}
$tableName = $cm->getTableName();
$this->_owningTableMap[$fieldName] = $tableName;
$this->_quotedTableMap[$tableName] = $this->quoteStrategy->getTableName($cm, $this->_platform);
return $tableName;
}
/**
* {@inheritdoc}
*/
public function executeInserts()
{
if ( ! $this->_queuedInserts) {
return;
}
$postInsertIds = array();
$idGen = $this->_class->idGenerator;
$isPostInsertId = $idGen->isPostInsertGenerator();
// Prepare statement for the root table
$rootClass = ($this->_class->name !== $this->_class->rootEntityName) ? $this->_em->getClassMetadata($this->_class->rootEntityName) : $this->_class;
$rootPersister = $this->_em->getUnitOfWork()->getEntityPersister($rootClass->name);
$rootTableName = $rootClass->getTableName();
$rootTableStmt = $this->_conn->prepare($rootPersister->_getInsertSQL());
// Prepare statements for sub tables.
$subTableStmts = array();
if ($rootClass !== $this->_class) {
$subTableStmts[$this->_class->getTableName()] = $this->_conn->prepare($this->_getInsertSQL());
}
foreach ($this->_class->parentClasses as $parentClassName) {
$parentClass = $this->_em->getClassMetadata($parentClassName);
$parentTableName = $parentClass->getTableName();
if ($parentClass !== $rootClass) {
$parentPersister = $this->_em->getUnitOfWork()->getEntityPersister($parentClassName);
$subTableStmts[$parentTableName] = $this->_conn->prepare($parentPersister->_getInsertSQL());
}
}
// Execute all inserts. For each entity:
// 1) Insert on root table
// 2) Insert on sub tables
foreach ($this->_queuedInserts as $entity) {
$insertData = $this->_prepareInsertData($entity);
// Execute insert on root table
$paramIndex = 1;
foreach ($insertData[$rootTableName] as $columnName => $value) {
$rootTableStmt->bindValue($paramIndex++, $value, $this->_columnTypes[$columnName]);
}
$rootTableStmt->execute();
if ($isPostInsertId) {
$id = $idGen->generate($this->_em, $entity);
$postInsertIds[$id] = $entity;
} else {
$id = $this->_em->getUnitOfWork()->getEntityIdentifier($entity);
}
// Execute inserts on subtables.
// The order doesn't matter because all child tables link to the root table via FK.
foreach ($subTableStmts as $tableName => $stmt) {
$data = isset($insertData[$tableName]) ? $insertData[$tableName] : array();
$paramIndex = 1;
foreach ((array) $id as $idName => $idVal) {
$type = isset($this->_columnTypes[$idName]) ? $this->_columnTypes[$idName] : Type::STRING;
$stmt->bindValue($paramIndex++, $idVal, $type);
}
foreach ($data as $columnName => $value) {
$stmt->bindValue($paramIndex++, $value, $this->_columnTypes[$columnName]);
}
$stmt->execute();
}
}
$rootTableStmt->closeCursor();
foreach ($subTableStmts as $stmt) {
$stmt->closeCursor();
}
if ($this->_class->isVersioned) {
$this->assignDefaultVersionValue($entity, $id);
}
$this->_queuedInserts = array();
return $postInsertIds;
}
/**
* {@inheritdoc}
*/
public function update($entity)
{
$updateData = $this->_prepareUpdateData($entity);
if (($isVersioned = $this->_class->isVersioned) != false) {
$versionedClass = $this->_getVersionedClassMetadata();
$versionedTable = $versionedClass->getTableName();
}
if ($updateData) {
foreach ($updateData as $tableName => $data) {
$this->_updateTable(
$entity, $this->_quotedTableMap[$tableName], $data, $isVersioned && $versionedTable == $tableName
);
}
// Make sure the table with the version column is updated even if no columns on that
// table were affected.
if ($isVersioned) {
if ( ! isset($updateData[$versionedTable])) {
$this->_updateTable($entity, $this->quoteStrategy->getTableName($versionedClass, $this->_platform), array(), true);
}
$id = $this->_em->getUnitOfWork()->getEntityIdentifier($entity);
$this->assignDefaultVersionValue($entity, $id);
}
}
}
/**
* {@inheritdoc}
*/
public function delete($entity)
{
$identifier = $this->_em->getUnitOfWork()->getEntityIdentifier($entity);
$this->deleteJoinTableRecords($identifier);
$id = array_combine($this->_class->getIdentifierColumnNames(), $identifier);
// If the database platform supports FKs, just
// delete the row from the root table. Cascades do the rest.
if ($this->_platform->supportsForeignKeyConstraints()) {
$this->_conn->delete(
$this->quoteStrategy->getTableName($this->_em->getClassMetadata($this->_class->rootEntityName), $this->_platform), $id
);
} else {
// Delete from all tables individually, starting from this class' table up to the root table.
$this->_conn->delete($this->quoteStrategy->getTableName($this->_class, $this->_platform), $id);
foreach ($this->_class->parentClasses as $parentClass) {
$this->_conn->delete(
$this->quoteStrategy->getTableName($this->_em->getClassMetadata($parentClass), $this->_platform), $id
);
}
}
}
/**
* {@inheritdoc}
*/
protected function _getSelectEntitiesSQL($criteria, $assoc = null, $lockMode = 0, $limit = null, $offset = null, array $orderBy = null)
{
$idColumns = $this->_class->getIdentifierColumnNames();
$baseTableAlias = $this->_getSQLTableAlias($this->_class->name);
// Create the column list fragment only once
if ($this->_selectColumnListSql === null) {
$this->_rsm = new ResultSetMapping();
$this->_rsm->addEntityResult($this->_class->name, 'r');
// Add regular columns
$columnList = '';
foreach ($this->_class->fieldMappings as $fieldName => $mapping) {
if ($columnList != '') $columnList .= ', ';
$columnList .= $this->_getSelectColumnSQL(
$fieldName,
isset($mapping['inherited']) ? $this->_em->getClassMetadata($mapping['inherited']) : $this->_class
);
}
// Add foreign key columns
foreach ($this->_class->associationMappings as $assoc2) {
if ($assoc2['isOwningSide'] && $assoc2['type'] & ClassMetadata::TO_ONE) {
$tableAlias = isset($assoc2['inherited']) ? $this->_getSQLTableAlias($assoc2['inherited']) : $baseTableAlias;
foreach ($assoc2['targetToSourceKeyColumns'] as $srcColumn) {
if ($columnList != '') $columnList .= ', ';
$columnList .= $this->getSelectJoinColumnSQL(
$tableAlias,
$srcColumn,
isset($assoc2['inherited']) ? $assoc2['inherited'] : $this->_class->name
);
}
}
}
// Add discriminator column (DO NOT ALIAS, see AbstractEntityInheritancePersister#_processSQLResult).
$discrColumn = $this->_class->discriminatorColumn['name'];
$tableAlias = ($this->_class->rootEntityName == $this->_class->name) ? $baseTableAlias : $this->_getSQLTableAlias($this->_class->rootEntityName);
$columnList .= ', ' . $tableAlias . '.' . $discrColumn;
$resultColumnName = $this->_platform->getSQLResultCasing($discrColumn);
$this->_rsm->setDiscriminatorColumn('r', $resultColumnName);
$this->_rsm->addMetaResult('r', $resultColumnName, $discrColumn);
}
// INNER JOIN parent tables
$joinSql = '';
foreach ($this->_class->parentClasses as $parentClassName) {
$parentClass = $this->_em->getClassMetadata($parentClassName);
$tableAlias = $this->_getSQLTableAlias($parentClassName);
$joinSql .= ' INNER JOIN ' . $this->quoteStrategy->getTableName($parentClass, $this->_platform) . ' ' . $tableAlias . ' ON ';
$first = true;
foreach ($idColumns as $idColumn) {
if ($first) $first = false; else $joinSql .= ' AND ';
$joinSql .= $baseTableAlias . '.' . $idColumn . ' = ' . $tableAlias . '.' . $idColumn;
}
}
// OUTER JOIN sub tables
foreach ($this->_class->subClasses as $subClassName) {
$subClass = $this->_em->getClassMetadata($subClassName);
$tableAlias = $this->_getSQLTableAlias($subClassName);
if ($this->_selectColumnListSql === null) {
// Add subclass columns
foreach ($subClass->fieldMappings as $fieldName => $mapping) {
if (isset($mapping['inherited'])) continue;
$columnList .= ', ' . $this->_getSelectColumnSQL($fieldName, $subClass);
}
// Add join columns (foreign keys)
foreach ($subClass->associationMappings as $assoc2) {
if ($assoc2['isOwningSide'] && $assoc2['type'] & ClassMetadata::TO_ONE && ! isset($assoc2['inherited'])) {
foreach ($assoc2['targetToSourceKeyColumns'] as $srcColumn) {
if ($columnList != '') $columnList .= ', ';
$columnList .= $this->getSelectJoinColumnSQL(
$tableAlias,
$srcColumn,
isset($assoc2['inherited']) ? $assoc2['inherited'] : $subClass->name
);
}
}
}
}
// Add LEFT JOIN
$joinSql .= ' LEFT JOIN ' . $this->quoteStrategy->getTableName($subClass, $this->_platform) . ' ' . $tableAlias . ' ON ';
$first = true;
foreach ($idColumns as $idColumn) {
if ($first) $first = false; else $joinSql .= ' AND ';
$joinSql .= $baseTableAlias . '.' . $idColumn . ' = ' . $tableAlias . '.' . $idColumn;
}
}
$joinSql .= ($assoc != null && $assoc['type'] == ClassMetadata::MANY_TO_MANY) ? $this->_getSelectManyToManyJoinSQL($assoc) : '';
$conditionSql = ($criteria instanceof Criteria)
? $this->_getSelectConditionCriteriaSQL($criteria)
: $this->_getSelectConditionSQL($criteria, $assoc);
// If the current class in the root entity, add the filters
if ($filterSql = $this->generateFilterConditionSQL($this->_em->getClassMetadata($this->_class->rootEntityName), $this->_getSQLTableAlias($this->_class->rootEntityName))) {
if ($conditionSql) {
$conditionSql .= ' AND ';
}
$conditionSql .= $filterSql;
}
$orderBy = ($assoc !== null && isset($assoc['orderBy'])) ? $assoc['orderBy'] : $orderBy;
$orderBySql = $orderBy ? $this->_getOrderBySQL($orderBy, $baseTableAlias) : '';
if ($this->_selectColumnListSql === null) {
$this->_selectColumnListSql = $columnList;
}
$lockSql = '';
if ($lockMode == LockMode::PESSIMISTIC_READ) {
$lockSql = ' ' . $this->_platform->getReadLockSql();
} else if ($lockMode == LockMode::PESSIMISTIC_WRITE) {
$lockSql = ' ' . $this->_platform->getWriteLockSql();
}
return $this->_platform->modifyLimitQuery('SELECT ' . $this->_selectColumnListSql
. ' FROM ' . $this->quoteStrategy->getTableName($this->_class, $this->_platform) . ' ' . $baseTableAlias
. $joinSql
. ($conditionSql != '' ? ' WHERE ' . $conditionSql : '') . $orderBySql, $limit, $offset)
. $lockSql;
}
/**
* Get the FROM and optionally JOIN conditions to lock the entity managed by this persister.
*
* @return string
*/
public function getLockTablesSql()
{
$idColumns = $this->_class->getIdentifierColumnNames();
$baseTableAlias = $this->_getSQLTableAlias($this->_class->name);
// INNER JOIN parent tables
$joinSql = '';
foreach ($this->_class->parentClasses as $parentClassName) {
$parentClass = $this->_em->getClassMetadata($parentClassName);
$tableAlias = $this->_getSQLTableAlias($parentClassName);
$joinSql .= ' INNER JOIN ' . $this->quoteStrategy->getTableName($parentClass, $this->_platform) . ' ' . $tableAlias . ' ON ';
$first = true;
foreach ($idColumns as $idColumn) {
if ($first) $first = false; else $joinSql .= ' AND ';
$joinSql .= $baseTableAlias . '.' . $idColumn . ' = ' . $tableAlias . '.' . $idColumn;
}
}
return 'FROM ' .$this->quoteStrategy->getTableName($this->_class, $this->_platform) . ' ' . $baseTableAlias . $joinSql;
}
/* Ensure this method is never called. This persister overrides _getSelectEntitiesSQL directly. */
protected function _getSelectColumnListSQL()
{
throw new \BadMethodCallException("Illegal invocation of ".__METHOD__.".");
}
/** {@inheritdoc} */
protected function _getInsertColumnList()
{
// Identifier columns must always come first in the column list of subclasses.
$columns = $this->_class->parentClasses ? $this->_class->getIdentifierColumnNames() : array();
foreach ($this->_class->reflFields as $name => $field) {
if (isset($this->_class->fieldMappings[$name]['inherited']) && ! isset($this->_class->fieldMappings[$name]['id'])
|| isset($this->_class->associationMappings[$name]['inherited'])
|| ($this->_class->isVersioned && $this->_class->versionField == $name)) {
continue;
}
if (isset($this->_class->associationMappings[$name])) {
$assoc = $this->_class->associationMappings[$name];
if ($assoc['type'] & ClassMetadata::TO_ONE && $assoc['isOwningSide']) {
foreach ($assoc['targetToSourceKeyColumns'] as $sourceCol) {
$columns[] = $sourceCol;
}
}
} else if ($this->_class->name != $this->_class->rootEntityName ||
! $this->_class->isIdGeneratorIdentity() || $this->_class->identifier[0] != $name) {
$columns[] = $this->quoteStrategy->getColumnName($name, $this->_class, $this->_platform);
$this->_columnTypes[$name] = $this->_class->fieldMappings[$name]['type'];
}
}
// Add discriminator column if it is the topmost class.
if ($this->_class->name == $this->_class->rootEntityName) {
$columns[] = $this->_class->discriminatorColumn['name'];
}
return $columns;
}
/**
* {@inheritdoc}
*/
protected function assignDefaultVersionValue($entity, $id)
{
$value = $this->fetchVersionValue($this->_getVersionedClassMetadata(), $id);
$this->_class->setFieldValue($entity, $this->_class->versionField, $value);
}
}
DoctrineORM-2.3.3/Doctrine/ORM/Persisters/ManyToManyPersister.php 0000644 0001751 0001751 00000040474 12143374607 025007 0 ustar beberlei beberlei .
*/
namespace Doctrine\ORM\Persisters;
use Doctrine\ORM\Mapping\ClassMetadata,
Doctrine\ORM\PersistentCollection,
Doctrine\ORM\UnitOfWork;
/**
* Persister for many-to-many collections.
*
* @author Roman Borschel
* @author Guilherme Blanco
* @author Alexander
* @since 2.0
*/
class ManyToManyPersister extends AbstractCollectionPersister
{
/**
* {@inheritdoc}
*
* @override
*/
protected function _getDeleteRowSQL(PersistentCollection $coll)
{
$columns = array();
$mapping = $coll->getMapping();
$class = $this->_em->getClassMetadata(get_class($coll->getOwner()));
foreach ($mapping['joinTable']['joinColumns'] as $joinColumn) {
$columns[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $class, $this->platform);
}
foreach ($mapping['joinTable']['inverseJoinColumns'] as $joinColumn) {
$columns[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $class, $this->platform);
}
return 'DELETE FROM ' . $this->quoteStrategy->getJoinTableName($mapping, $class, $this->platform)
. ' WHERE ' . implode(' = ? AND ', $columns) . ' = ?';
}
/**
* {@inheritdoc}
*
* @override
* @internal Order of the parameters must be the same as the order of the columns in
* _getDeleteRowSql.
*/
protected function _getDeleteRowSQLParameters(PersistentCollection $coll, $element)
{
return $this->_collectJoinTableColumnParameters($coll, $element);
}
/**
* {@inheritdoc}
*
* @override
*/
protected function _getUpdateRowSQL(PersistentCollection $coll)
{}
/**
* {@inheritdoc}
*
* @override
* @internal Order of the parameters must be the same as the order of the columns in
* _getInsertRowSql.
*/
protected function _getInsertRowSQL(PersistentCollection $coll)
{
$columns = array();
$mapping = $coll->getMapping();
$class = $this->_em->getClassMetadata(get_class($coll->getOwner()));
$joinTable = $this->quoteStrategy->getJoinTableName($mapping, $class, $this->platform);
foreach ($mapping['joinTable']['joinColumns'] as $joinColumn) {
$columns[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $class, $this->platform);
}
foreach ($mapping['joinTable']['inverseJoinColumns'] as $joinColumn) {
$columns[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $class, $this->platform);
}
return 'INSERT INTO ' . $joinTable . ' (' . implode(', ', $columns) . ')'
. ' VALUES (' . implode(', ', array_fill(0, count($columns), '?')) . ')';
}
/**
* {@inheritdoc}
*
* @override
* @internal Order of the parameters must be the same as the order of the columns in
* _getInsertRowSql.
*/
protected function _getInsertRowSQLParameters(PersistentCollection $coll, $element)
{
return $this->_collectJoinTableColumnParameters($coll, $element);
}
/**
* Collects the parameters for inserting/deleting on the join table in the order
* of the join table columns as specified in ManyToManyMapping#joinTableColumns.
*
* @param $coll
* @param $element
* @return array
*/
private function _collectJoinTableColumnParameters(PersistentCollection $coll, $element)
{
$params = array();
$mapping = $coll->getMapping();
$isComposite = count($mapping['joinTableColumns']) > 2;
$identifier1 = $this->_uow->getEntityIdentifier($coll->getOwner());
$identifier2 = $this->_uow->getEntityIdentifier($element);
if ($isComposite) {
$class1 = $this->_em->getClassMetadata(get_class($coll->getOwner()));
$class2 = $coll->getTypeClass();
}
foreach ($mapping['joinTableColumns'] as $joinTableColumn) {
$isRelationToSource = isset($mapping['relationToSourceKeyColumns'][$joinTableColumn]);
if ( ! $isComposite) {
$params[] = $isRelationToSource ? array_pop($identifier1) : array_pop($identifier2);
continue;
}
if ($isRelationToSource) {
$params[] = $identifier1[$class1->getFieldForColumn($mapping['relationToSourceKeyColumns'][$joinTableColumn])];
continue;
}
$params[] = $identifier2[$class2->getFieldForColumn($mapping['relationToTargetKeyColumns'][$joinTableColumn])];
}
return $params;
}
/**
* {@inheritdoc}
*
* @override
*/
protected function _getDeleteSQL(PersistentCollection $coll)
{
$columns = array();
$mapping = $coll->getMapping();
$class = $this->_em->getClassMetadata(get_class($coll->getOwner()));
$joinTable = $this->quoteStrategy->getJoinTableName($mapping, $class, $this->platform);
foreach ($mapping['joinTable']['joinColumns'] as $joinColumn) {
$columns[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $class, $this->platform);
}
return 'DELETE FROM ' . $joinTable
. ' WHERE ' . implode(' = ? AND ', $columns) . ' = ?';
}
/**
* {@inheritdoc}
*
* @override
* @internal Order of the parameters must be the same as the order of the columns in
* _getDeleteSql.
*/
protected function _getDeleteSQLParameters(PersistentCollection $coll)
{
$identifier = $this->_uow->getEntityIdentifier($coll->getOwner());
$mapping = $coll->getMapping();
$params = array();
// Optimization for single column identifier
if (count($mapping['relationToSourceKeyColumns']) === 1) {
$params[] = array_pop($identifier);
return $params;
}
// Composite identifier
$sourceClass = $this->_em->getClassMetadata(get_class($coll->getOwner()));
foreach ($mapping['relationToSourceKeyColumns'] as $srcColumn) {
$params[] = $identifier[$sourceClass->fieldNames[$srcColumn]];
}
return $params;
}
/**
* {@inheritdoc}
*/
public function count(PersistentCollection $coll)
{
$conditions = array();
$params = array();
$mapping = $coll->getMapping();
$association = $mapping;
$class = $this->_em->getClassMetadata($mapping['sourceEntity']);
$id = $this->_em->getUnitOfWork()->getEntityIdentifier($coll->getOwner());
if ( ! $mapping['isOwningSide']) {
$targetEntity = $this->_em->getClassMetadata($mapping['targetEntity']);
$association = $targetEntity->associationMappings[$mapping['mappedBy']];
}
$joinColumns = ( ! $mapping['isOwningSide'])
? $association['joinTable']['inverseJoinColumns']
: $association['joinTable']['joinColumns'];
foreach ($joinColumns as $joinColumn) {
$columnName = $this->quoteStrategy->getJoinColumnName($joinColumn, $class, $this->platform);
$referencedName = $joinColumn['referencedColumnName'];
$conditions[] = $columnName . ' = ?';
$params[] = ($class->containsForeignIdentifier)
? $id[$class->getFieldForColumn($referencedName)]
: $id[$class->fieldNames[$referencedName]];
}
$joinTableName = $this->quoteStrategy->getJoinTableName($association, $class, $this->platform);
list($joinTargetEntitySQL, $filterSql) = $this->getFilterSql($mapping);
if ($filterSql) {
$conditions[] = $filterSql;
}
$sql = 'SELECT COUNT(*)'
. ' FROM ' . $joinTableName . ' t'
. $joinTargetEntitySQL
. ' WHERE ' . implode(' AND ', $conditions);
return $this->_conn->fetchColumn($sql, $params);
}
/**
* @param PersistentCollection $coll
* @param int $offset
* @param int $length
* @return array
*/
public function slice(PersistentCollection $coll, $offset, $length = null)
{
$mapping = $coll->getMapping();
return $this->_em->getUnitOfWork()->getEntityPersister($mapping['targetEntity'])->getManyToManyCollection($mapping, $coll->getOwner(), $offset, $length);
}
/**
* @param PersistentCollection $coll
* @param object $element
* @return boolean
*/
public function contains(PersistentCollection $coll, $element)
{
$uow = $this->_em->getUnitOfWork();
// Shortcut for new entities
$entityState = $uow->getEntityState($element, UnitOfWork::STATE_NEW);
if ($entityState === UnitOfWork::STATE_NEW) {
return false;
}
// Entity is scheduled for inclusion
if ($entityState === UnitOfWork::STATE_MANAGED && $uow->isScheduledForInsert($element)) {
return false;
}
list($quotedJoinTable, $whereClauses, $params) = $this->getJoinTableRestrictions($coll, $element, true);
$sql = 'SELECT 1 FROM ' . $quotedJoinTable . ' WHERE ' . implode(' AND ', $whereClauses);
return (bool) $this->_conn->fetchColumn($sql, $params);
}
/**
* @param PersistentCollection $coll
* @param object $element
* @return boolean
*/
public function removeElement(PersistentCollection $coll, $element)
{
$uow = $this->_em->getUnitOfWork();
// shortcut for new entities
$entityState = $uow->getEntityState($element, UnitOfWork::STATE_NEW);
if ($entityState === UnitOfWork::STATE_NEW) {
return false;
}
// If Entity is scheduled for inclusion, it is not in this collection.
// We can assure that because it would have return true before on array check
if ($entityState === UnitOfWork::STATE_MANAGED && $uow->isScheduledForInsert($element)) {
return false;
}
list($quotedJoinTable, $whereClauses, $params) = $this->getJoinTableRestrictions($coll, $element, false);
$sql = 'DELETE FROM ' . $quotedJoinTable . ' WHERE ' . implode(' AND ', $whereClauses);
return (bool) $this->_conn->executeUpdate($sql, $params);
}
/**
* @param \Doctrine\ORM\PersistentCollection $coll
* @param object $element
* @param boolean $addFilters Whether the filter SQL should be included or not.
* @return array
*/
private function getJoinTableRestrictions(PersistentCollection $coll, $element, $addFilters)
{
$uow = $this->_em->getUnitOfWork();
$mapping = $filterMapping = $coll->getMapping();
if ( ! $mapping['isOwningSide']) {
$sourceClass = $this->_em->getClassMetadata($mapping['targetEntity']);
$targetClass = $this->_em->getClassMetadata($mapping['sourceEntity']);
$sourceId = $uow->getEntityIdentifier($element);
$targetId = $uow->getEntityIdentifier($coll->getOwner());
$mapping = $sourceClass->associationMappings[$mapping['mappedBy']];
} else {
$sourceClass = $this->_em->getClassMetadata($mapping['sourceEntity']);
$targetClass = $this->_em->getClassMetadata($mapping['targetEntity']);
$sourceId = $uow->getEntityIdentifier($coll->getOwner());
$targetId = $uow->getEntityIdentifier($element);
}
$quotedJoinTable = $this->quoteStrategy->getJoinTableName($mapping, $sourceClass, $this->platform);
$whereClauses = array();
$params = array();
foreach ($mapping['joinTableColumns'] as $joinTableColumn) {
$whereClauses[] = $joinTableColumn . ' = ?';
if (isset($mapping['relationToTargetKeyColumns'][$joinTableColumn])) {
$params[] = ($targetClass->containsForeignIdentifier)
? $targetId[$targetClass->getFieldForColumn($mapping['relationToTargetKeyColumns'][$joinTableColumn])]
: $targetId[$targetClass->fieldNames[$mapping['relationToTargetKeyColumns'][$joinTableColumn]]];
continue;
}
// relationToSourceKeyColumns
$params[] = ($sourceClass->containsForeignIdentifier)
? $sourceId[$sourceClass->getFieldForColumn($mapping['relationToSourceKeyColumns'][$joinTableColumn])]
: $sourceId[$sourceClass->fieldNames[$mapping['relationToSourceKeyColumns'][$joinTableColumn]]];
}
if ($addFilters) {
list($joinTargetEntitySQL, $filterSql) = $this->getFilterSql($filterMapping);
if ($filterSql) {
$quotedJoinTable .= ' t ' . $joinTargetEntitySQL;
$whereClauses[] = $filterSql;
}
}
return array($quotedJoinTable, $whereClauses, $params);
}
/**
* Generates the filter SQL for a given mapping.
*
* This method is not used for actually grabbing the related entities
* but when the extra-lazy collection methods are called on a filtered
* association. This is why besides the many to many table we also
* have to join in the actual entities table leading to additional
* JOIN.
*
* @param array $mapping Array containing mapping information.
*
* @return string The SQL query part to add to a query.
*/
public function getFilterSql($mapping)
{
$targetClass = $this->_em->getClassMetadata($mapping['targetEntity']);
if ($mapping['isOwningSide']) {
$joinColumns = $mapping['relationToTargetKeyColumns'];
} else {
$mapping = $targetClass->associationMappings[$mapping['mappedBy']];
$joinColumns = $mapping['relationToSourceKeyColumns'];
}
$targetClass = $this->_em->getClassMetadata($targetClass->rootEntityName);
// A join is needed if there is filtering on the target entity
$joinTargetEntitySQL = '';
if ($filterSql = $this->generateFilterConditionSQL($targetClass, 'te')) {
$joinTargetEntitySQL = ' JOIN '
. $this->quoteStrategy->getTableName($targetClass, $this->platform) . ' te'
. ' ON';
$joinTargetEntitySQLClauses = array();
foreach ($joinColumns as $joinTableColumn => $targetTableColumn) {
$joinTargetEntitySQLClauses[] = ' t.' . $joinTableColumn . ' = ' . 'te.' . $targetTableColumn;
}
$joinTargetEntitySQL .= implode(' AND ', $joinTargetEntitySQLClauses);
}
return array($joinTargetEntitySQL, $filterSql);
}
/**
* Generates the filter SQL for a given entity and table alias.
*
* @param ClassMetadata $targetEntity Metadata of the target entity.
* @param string $targetTableAlias The table alias of the joined/selected table.
*
* @return string The SQL query part to add to a query.
*/
protected function generateFilterConditionSQL(ClassMetadata $targetEntity, $targetTableAlias)
{
$filterClauses = array();
foreach ($this->_em->getFilters()->getEnabledFilters() as $filter) {
if ($filterExpr = $filter->addFilterConstraint($targetEntity, $targetTableAlias)) {
$filterClauses[] = '(' . $filterExpr . ')';
}
}
$sql = implode(' AND ', $filterClauses);
return $sql ? "(" . $sql . ")" : "";
}
}
DoctrineORM-2.3.3/Doctrine/ORM/Persisters/OneToManyPersister.php 0000644 0001751 0001751 00000016525 12143374607 024624 0 ustar beberlei beberlei .
*/
namespace Doctrine\ORM\Persisters;
use Doctrine\ORM\PersistentCollection,
Doctrine\ORM\UnitOfWork;
/**
* Persister for one-to-many collections.
*
* @author Roman Borschel
* @author Guilherme Blanco
* @author Alexander
* @since 2.0
*/
class OneToManyPersister extends AbstractCollectionPersister
{
/**
* Generates the SQL UPDATE that updates a particular row's foreign
* key to null.
*
* @param PersistentCollection $coll
* @return string
* @override
*/
protected function _getDeleteRowSQL(PersistentCollection $coll)
{
$mapping = $coll->getMapping();
$class = $this->_em->getClassMetadata($mapping['targetEntity']);
return 'DELETE FROM ' . $this->quoteStrategy->getTableName($class, $this->platform)
. ' WHERE ' . implode('= ? AND ', $class->getIdentifierColumnNames()) . ' = ?';
}
/**
* {@inheritdoc}
*
*/
protected function _getDeleteRowSQLParameters(PersistentCollection $coll, $element)
{
return array_values($this->_uow->getEntityIdentifier($element));
}
protected function _getInsertRowSQL(PersistentCollection $coll)
{
return "UPDATE xxx SET foreign_key = yyy WHERE foreign_key = zzz";
}
/**
* Gets the SQL parameters for the corresponding SQL statement to insert the given
* element of the given collection into the database.
*
* @param PersistentCollection $coll
* @param mixed $element
*/
protected function _getInsertRowSQLParameters(PersistentCollection $coll, $element)
{}
/* Not used for OneToManyPersister */
protected function _getUpdateRowSQL(PersistentCollection $coll)
{
return;
}
/**
* Generates the SQL UPDATE that updates all the foreign keys to null.
*
* @param PersistentCollection $coll
*/
protected function _getDeleteSQL(PersistentCollection $coll)
{
}
/**
* Gets the SQL parameters for the corresponding SQL statement to delete
* the given collection.
*
* @param PersistentCollection $coll
*/
protected function _getDeleteSQLParameters(PersistentCollection $coll)
{}
/**
* {@inheritdoc}
*/
public function count(PersistentCollection $coll)
{
$mapping = $coll->getMapping();
$targetClass = $this->_em->getClassMetadata($mapping['targetEntity']);
$sourceClass = $this->_em->getClassMetadata($mapping['sourceEntity']);
$id = $this->_em->getUnitOfWork()->getEntityIdentifier($coll->getOwner());
$whereClauses = array();
$params = array();
foreach ($targetClass->associationMappings[$mapping['mappedBy']]['joinColumns'] as $joinColumn) {
$whereClauses[] = $joinColumn['name'] . ' = ?';
$params[] = ($targetClass->containsForeignIdentifier)
? $id[$sourceClass->getFieldForColumn($joinColumn['referencedColumnName'])]
: $id[$sourceClass->fieldNames[$joinColumn['referencedColumnName']]];
}
$filterTargetClass = $this->_em->getClassMetadata($targetClass->rootEntityName);
foreach ($this->_em->getFilters()->getEnabledFilters() as $filter) {
if ($filterExpr = $filter->addFilterConstraint($filterTargetClass, 't')) {
$whereClauses[] = '(' . $filterExpr . ')';
}
}
$sql = 'SELECT count(*)'
. ' FROM ' . $this->quoteStrategy->getTableName($targetClass, $this->platform) . ' t'
. ' WHERE ' . implode(' AND ', $whereClauses);
return $this->_conn->fetchColumn($sql, $params);
}
/**
* @param PersistentCollection $coll
* @param int $offset
* @param int $length
* @return \Doctrine\Common\Collections\ArrayCollection
*/
public function slice(PersistentCollection $coll, $offset, $length = null)
{
$mapping = $coll->getMapping();
$uow = $this->_em->getUnitOfWork();
$persister = $uow->getEntityPersister($mapping['targetEntity']);
return $persister->getOneToManyCollection($mapping, $coll->getOwner(), $offset, $length);
}
/**
* @param PersistentCollection $coll
* @param object $element
* @return boolean
*/
public function contains(PersistentCollection $coll, $element)
{
$mapping = $coll->getMapping();
$uow = $this->_em->getUnitOfWork();
// shortcut for new entities
$entityState = $uow->getEntityState($element, UnitOfWork::STATE_NEW);
if ($entityState === UnitOfWork::STATE_NEW) {
return false;
}
// Entity is scheduled for inclusion
if ($entityState === UnitOfWork::STATE_MANAGED && $uow->isScheduledForInsert($element)) {
return false;
}
$persister = $uow->getEntityPersister($mapping['targetEntity']);
// only works with single id identifier entities. Will throw an
// exception in Entity Persisters if that is not the case for the
// 'mappedBy' field.
$id = current( $uow->getEntityIdentifier($coll->getOwner()));
return $persister->exists($element, array($mapping['mappedBy'] => $id));
}
/**
* @param PersistentCollection $coll
* @param object $element
* @return boolean
*/
public function removeElement(PersistentCollection $coll, $element)
{
$uow = $this->_em->getUnitOfWork();
// shortcut for new entities
$entityState = $uow->getEntityState($element, UnitOfWork::STATE_NEW);
if ($entityState === UnitOfWork::STATE_NEW) {
return false;
}
// If Entity is scheduled for inclusion, it is not in this collection.
// We can assure that because it would have return true before on array check
if ($entityState === UnitOfWork::STATE_MANAGED && $uow->isScheduledForInsert($element)) {
return false;
}
$mapping = $coll->getMapping();
$class = $this->_em->getClassMetadata($mapping['targetEntity']);
$sql = 'DELETE FROM ' . $this->quoteStrategy->getTableName($class, $this->platform)
. ' WHERE ' . implode('= ? AND ', $class->getIdentifierColumnNames()) . ' = ?';
return (bool) $this->_conn->executeUpdate($sql, $this->_getDeleteRowSQLParameters($coll, $element));
}
}
DoctrineORM-2.3.3/Doctrine/ORM/Persisters/SingleTablePersister.php 0000644 0001751 0001751 00000013651 12143374607 025141 0 ustar beberlei beberlei .
*/
namespace Doctrine\ORM\Persisters;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\Common\Collections\Criteria;
/**
* Persister for entities that participate in a hierarchy mapped with the
* SINGLE_TABLE strategy.
*
* @author Roman Borschel
* @author Benjamin Eberlei
* @author Alexander
* @since 2.0
* @link http://martinfowler.com/eaaCatalog/singleTableInheritance.html
*/
class SingleTablePersister extends AbstractEntityInheritancePersister
{
/** {@inheritdoc} */
protected function _getDiscriminatorColumnTableName()
{
return $this->_class->getTableName();
}
/** {@inheritdoc} */
protected function _getSelectColumnListSQL()
{
if ($this->_selectColumnListSql !== null) {
return $this->_selectColumnListSql;
}
$columnList = parent::_getSelectColumnListSQL();
$rootClass = $this->_em->getClassMetadata($this->_class->rootEntityName);
$tableAlias = $this->_getSQLTableAlias($rootClass->name);
// Append discriminator column
$discrColumn = $this->_class->discriminatorColumn['name'];
$columnList .= ', ' . $tableAlias . '.' . $discrColumn;
$resultColumnName = $this->_platform->getSQLResultCasing($discrColumn);
$this->_rsm->setDiscriminatorColumn('r', $resultColumnName);
$this->_rsm->addMetaResult('r', $resultColumnName, $discrColumn);
// Append subclass columns
foreach ($this->_class->subClasses as $subClassName) {
$subClass = $this->_em->getClassMetadata($subClassName);
// Regular columns
foreach ($subClass->fieldMappings as $fieldName => $mapping) {
if ( ! isset($mapping['inherited'])) {
$columnList .= ', ' . $this->_getSelectColumnSQL($fieldName, $subClass);
}
}
// Foreign key columns
foreach ($subClass->associationMappings as $assoc) {
if ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE && ! isset($assoc['inherited'])) {
foreach ($assoc['targetToSourceKeyColumns'] as $srcColumn) {
if ($columnList != '') $columnList .= ', ';
$columnList .= $this->getSelectJoinColumnSQL(
$tableAlias,
$srcColumn,
isset($assoc['inherited']) ? $assoc['inherited'] : $this->_class->name
);
}
}
}
}
$this->_selectColumnListSql = $columnList;
return $this->_selectColumnListSql;
}
/** {@inheritdoc} */
protected function _getInsertColumnList()
{
$columns = parent::_getInsertColumnList();
// Add discriminator column to the INSERT SQL
$columns[] = $this->_class->discriminatorColumn['name'];
return $columns;
}
/** {@inheritdoc} */
protected function _getSQLTableAlias($className, $assocName = '')
{
return parent::_getSQLTableAlias($this->_class->rootEntityName, $assocName);
}
/** {@inheritdoc} */
protected function _getSelectConditionSQL(array $criteria, $assoc = null)
{
$conditionSql = parent::_getSelectConditionSQL($criteria, $assoc);
if ($conditionSql) {
$conditionSql .= ' AND ';
}
return $conditionSql . $this->_getSelectConditionDiscriminatorValueSQL();
}
/** {@inheritdoc} */
protected function _getSelectConditionCriteriaSQL(Criteria $criteria)
{
$conditionSql = parent::_getSelectConditionCriteriaSQL($criteria);
if ($conditionSql) {
$conditionSql .= ' AND ';
}
return $conditionSql . $this->_getSelectConditionDiscriminatorValueSQL();
}
protected function _getSelectConditionDiscriminatorValueSQL()
{
$values = array();
if ($this->_class->discriminatorValue !== null) { // discriminators can be 0
$values[] = $this->_conn->quote($this->_class->discriminatorValue);
}
$discrValues = array_flip($this->_class->discriminatorMap);
foreach ($this->_class->subClasses as $subclassName) {
$values[] = $this->_conn->quote($discrValues[$subclassName]);
}
return $this->_getSQLTableAlias($this->_class->name) . '.' . $this->_class->discriminatorColumn['name']
. ' IN (' . implode(', ', $values) . ')';
}
/** {@inheritdoc} */
protected function generateFilterConditionSQL(ClassMetadata $targetEntity, $targetTableAlias)
{
// Ensure that the filters are applied to the root entity of the inheritance tree
$targetEntity = $this->_em->getClassMetadata($targetEntity->rootEntityName);
// we dont care about the $targetTableAlias, in a STI there is only one table.
return parent::generateFilterConditionSQL($targetEntity, $targetTableAlias);
}
}
DoctrineORM-2.3.3/Doctrine/ORM/Persisters/SqlExpressionVisitor.php 0000644 0001751 0001751 00000006413 12143374607 025244 0 ustar beberlei beberlei .
*/
namespace Doctrine\ORM\Persisters;
use Doctrine\Common\Collections\Expr\ExpressionVisitor;
use Doctrine\Common\Collections\Expr\Comparison;
use Doctrine\Common\Collections\Expr\Value;
use Doctrine\Common\Collections\Expr\CompositeExpression;
/**
* Visit Expressions and generate SQL WHERE conditions from them.
*
* @author Benjamin Eberlei
* @since 2.3
*/
class SqlExpressionVisitor extends ExpressionVisitor
{
/**
* @var \Doctrine\ORM\Persisters\BasicEntityPersister
*/
private $persister;
/**
* @param \Doctrine\ORM\Persisters\BasicEntityPersister $persister
*/
public function __construct(BasicEntityPersister $persister)
{
$this->persister = $persister;
}
/**
* Convert a comparison expression into the target query language output
*
* @param \Doctrine\Common\Collections\Expr\Comparison $comparison
*
* @return mixed
*/
public function walkComparison(Comparison $comparison)
{
$field = $comparison->getField();
$value = $comparison->getValue()->getValue(); // shortcut for walkValue()
return $this->persister->getSelectConditionStatementSQL($field, $value, null, $comparison->getOperator());
}
/**
* Convert a composite expression into the target query language output
*
* @param \Doctrine\Common\Collections\Expr\CompositeExpression $expr
*
* @return mixed
*/
public function walkCompositeExpression(CompositeExpression $expr)
{
$expressionList = array();
foreach ($expr->getExpressionList() as $child) {
$expressionList[] = $this->dispatch($child);
}
switch($expr->getType()) {
case CompositeExpression::TYPE_AND:
return '(' . implode(' AND ', $expressionList) . ')';
case CompositeExpression::TYPE_OR:
return '(' . implode(' OR ', $expressionList) . ')';
default:
throw new \RuntimeException("Unknown composite " . $expr->getType());
}
}
/**
* Convert a value expression into the target query language part.
*
* @param \Doctrine\Common\Collections\Expr\Value $value
*
* @return mixed
*/
public function walkValue(Value $value)
{
return '?';
}
}
DoctrineORM-2.3.3/Doctrine/ORM/Persisters/SqlValueVisitor.php 0000644 0001751 0001751 00000005655 12143374607 024170 0 ustar beberlei beberlei .
*/
namespace Doctrine\ORM\Persisters;
use Doctrine\Common\Collections\Expr\ExpressionVisitor;
use Doctrine\Common\Collections\Expr\Comparison;
use Doctrine\Common\Collections\Expr\Value;
use Doctrine\Common\Collections\Expr\CompositeExpression;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\DBAL\Types\Type;
use Doctrine\DBAL\Connection;
/**
* Extract the values from a criteria/expression
*
* @author Benjamin Eberlei
*/
class SqlValueVisitor extends ExpressionVisitor
{
/**
* @var array
*/
private $values = array();
/**
* @var array
*/
private $types = array();
/**
* Convert a comparison expression into the target query language output
*
* @param \Doctrine\Common\Collections\Expr\Comparison $comparison
*
* @return mixed
*/
public function walkComparison(Comparison $comparison)
{
$value = $comparison->getValue()->getValue();
$field = $comparison->getField();
$this->values[] = $value;
$this->types[] = array($field, $value);
}
/**
* Convert a composite expression into the target query language output
*
* @param \Doctrine\Common\Collections\Expr\CompositeExpression $expr
*
* @return mixed
*/
public function walkCompositeExpression(CompositeExpression $expr)
{
foreach ($expr->getExpressionList() as $child) {
$this->dispatch($child);
}
}
/**
* Convert a value expression into the target query language part.
*
* @param \Doctrine\Common\Collections\Expr\Value $value
*
* @return mixed
*/
public function walkValue(Value $value)
{
return;
}
/**
* Return the Parameters and Types necessary for matching the last visited expression.
*
* @return array
*/
public function getParamsAndTypes()
{
return array($this->values, $this->types);
}
}
DoctrineORM-2.3.3/Doctrine/ORM/Persisters/UnionSubclassPersister.php 0000644 0001751 0001751 00000000151 12143374607 025527 0 ustar beberlei beberlei .
*/
namespace Doctrine\ORM\Proxy;
/**
* Special Autoloader for Proxy classes because them not being PSR-0 compatible.
*
* @author Benjamin Eberlei
*/
class Autoloader
{
/**
* Resolve proxy class name to a filename based on the following pattern.
*
* 1. Remove Proxy namespace from class name
* 2. Remove namespace seperators from remaining class name.
* 3. Return PHP filename from proxy-dir with the result from 2.
*
* @param string $proxyDir
* @param string $proxyNamespace
* @param string $className
* @return string
*/
static public function resolveFile($proxyDir, $proxyNamespace, $className)
{
if (0 !== strpos($className, $proxyNamespace)) {
throw ProxyException::notProxyClass($className, $proxyNamespace);
}
$className = str_replace('\\', '', substr($className, strlen($proxyNamespace) +1));
return $proxyDir . DIRECTORY_SEPARATOR . $className.'.php';
}
/**
* Register and return autoloader callback for the given proxy dir and
* namespace.
*
* @param string $proxyDir
* @param string $proxyNamespace
* @param Closure $notFoundCallback Invoked when the proxy file is not found.
* @return Closure
*/
static public function register($proxyDir, $proxyNamespace, \Closure $notFoundCallback = null)
{
$proxyNamespace = ltrim($proxyNamespace, "\\");
$autoloader = function($className) use ($proxyDir, $proxyNamespace, $notFoundCallback) {
if (0 === strpos($className, $proxyNamespace)) {
$file = Autoloader::resolveFile($proxyDir, $proxyNamespace, $className);
if ($notFoundCallback && ! file_exists($file)) {
$notFoundCallback($proxyDir, $proxyNamespace, $className);
}
require $file;
}
};
spl_autoload_register($autoloader);
return $autoloader;
}
}
DoctrineORM-2.3.3/Doctrine/ORM/Proxy/Proxy.php 0000644 0001751 0001751 00000002302 12143374607 021135 0 ustar beberlei beberlei .
*/
namespace Doctrine\ORM\Proxy;
use Doctrine\Common\Persistence\Proxy as BaseProxy;
/**
* Interface for proxy classes.
*
* @author Roman Borschel
* @since 2.0
*/
interface Proxy extends BaseProxy {}
DoctrineORM-2.3.3/Doctrine/ORM/Proxy/ProxyException.php 0000644 0001751 0001751 00000003655 12143374607 023030 0 ustar beberlei beberlei .
*/
namespace Doctrine\ORM\Proxy;
/**
* ORM Proxy Exception
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.com
* @since 1.0
* @author Benjamin Eberlei
*/
class ProxyException extends \Doctrine\ORM\ORMException {
public static function proxyDirectoryRequired() {
return new self("You must configure a proxy directory. See docs for details");
}
public static function proxyDirectoryNotWritable() {
return new self("Your proxy directory must be writable.");
}
public static function proxyNamespaceRequired() {
return new self("You must configure a proxy namespace. See docs for details");
}
public static function notProxyClass($className, $proxyNamespace)
{
return new self(sprintf(
"The class %s is not part of the proxy namespace %s",
$className, $proxyNamespace
));
}
}
DoctrineORM-2.3.3/Doctrine/ORM/Proxy/ProxyFactory.php 0000644 0001751 0001751 00000035230 12143374607 022473 0 ustar beberlei beberlei .
*/
namespace Doctrine\ORM\Proxy;
use Doctrine\ORM\EntityManager,
Doctrine\ORM\Mapping\ClassMetadata,
Doctrine\Common\Util\ClassUtils;
/**
* This factory is used to create proxy objects for entities at runtime.
*
* @author Roman Borschel
* @author Giorgio Sironi
* @since 2.0
*/
class ProxyFactory
{
/** The EntityManager this factory is bound to. */
private $_em;
/** Whether to automatically (re)generate proxy classes. */
private $_autoGenerate;
/** The namespace that contains all proxy classes. */
private $_proxyNamespace;
/** The directory that contains all proxy classes. */
private $_proxyDir;
/**
* Used to match very simple id methods that don't need
* to be proxied since the identifier is known.
*
* @var string
*/
const PATTERN_MATCH_ID_METHOD = '((public\s)?(function\s{1,}%s\s?\(\)\s{1,})\s{0,}{\s{0,}return\s{0,}\$this->%s;\s{0,}})i';
/**
* Initializes a new instance of the ProxyFactory class that is
* connected to the given EntityManager.
*
* @param EntityManager $em The EntityManager the new factory works for.
* @param string $proxyDir The directory to use for the proxy classes. It must exist.
* @param string $proxyNs The namespace to use for the proxy classes.
* @param boolean $autoGenerate Whether to automatically generate proxy classes.
*/
public function __construct(EntityManager $em, $proxyDir, $proxyNs, $autoGenerate = false)
{
if ( ! $proxyDir) {
throw ProxyException::proxyDirectoryRequired();
}
if ( ! $proxyNs) {
throw ProxyException::proxyNamespaceRequired();
}
$this->_em = $em;
$this->_proxyDir = $proxyDir;
$this->_autoGenerate = $autoGenerate;
$this->_proxyNamespace = $proxyNs;
}
/**
* Gets a reference proxy instance for the entity of the given type and identified by
* the given identifier.
*
* @param string $className
* @param mixed $identifier
* @return object
*/
public function getProxy($className, $identifier)
{
$fqn = ClassUtils::generateProxyClassName($className, $this->_proxyNamespace);
if (! class_exists($fqn, false)) {
$fileName = $this->getProxyFileName($className);
if ($this->_autoGenerate) {
$this->_generateProxyClass($this->_em->getClassMetadata($className), $fileName, self::$_proxyClassTemplate);
}
require $fileName;
}
$entityPersister = $this->_em->getUnitOfWork()->getEntityPersister($className);
return new $fqn($entityPersister, $identifier);
}
/**
* Generate the Proxy file name
*
* @param string $className
* @param string $baseDir Optional base directory for proxy file name generation.
* If not specified, the directory configured on the Configuration of the
* EntityManager will be used by this factory.
* @return string
*/
private function getProxyFileName($className, $baseDir = null)
{
$proxyDir = $baseDir ?: $this->_proxyDir;
return $proxyDir . DIRECTORY_SEPARATOR . '__CG__' . str_replace('\\', '', $className) . '.php';
}
/**
* Generates proxy classes for all given classes.
*
* @param array $classes The classes (ClassMetadata instances) for which to generate proxies.
* @param string $toDir The target directory of the proxy classes. If not specified, the
* directory configured on the Configuration of the EntityManager used
* by this factory is used.
* @return int Number of generated proxies.
*/
public function generateProxyClasses(array $classes, $toDir = null)
{
$proxyDir = $toDir ?: $this->_proxyDir;
$proxyDir = rtrim($proxyDir, DIRECTORY_SEPARATOR);
$num = 0;
foreach ($classes as $class) {
/* @var $class ClassMetadata */
if ($class->isMappedSuperclass || $class->reflClass->isAbstract()) {
continue;
}
$proxyFileName = $this->getProxyFileName($class->name, $proxyDir);
$this->_generateProxyClass($class, $proxyFileName, self::$_proxyClassTemplate);
$num++;
}
return $num;
}
/**
* Generates a proxy class file.
*
* @param ClassMetadata $class Metadata for the original class
* @param string $fileName Filename (full path) for the generated class
* @param string $file The proxy class template data
*/
private function _generateProxyClass(ClassMetadata $class, $fileName, $file)
{
$methods = $this->_generateMethods($class);
$sleepImpl = $this->_generateSleep($class);
$cloneImpl = $class->reflClass->hasMethod('__clone') ? 'parent::__clone();' : ''; // hasMethod() checks case-insensitive
$placeholders = array(
'',
'', '',
'', '', ''
);
$className = ltrim($class->name, '\\');
$proxyClassName = ClassUtils::generateProxyClassName($class->name, $this->_proxyNamespace);
$parts = explode('\\', strrev($proxyClassName), 2);
$proxyClassNamespace = strrev($parts[1]);
$proxyClassName = strrev($parts[0]);
$replacements = array(
$proxyClassNamespace,
$proxyClassName,
$className,
$methods,
$sleepImpl,
$cloneImpl
);
$file = str_replace($placeholders, $replacements, $file);
$parentDirectory = dirname($fileName);
if ( ! is_dir($parentDirectory)) {
if (false === @mkdir($parentDirectory, 0775, true)) {
throw ProxyException::proxyDirectoryNotWritable();
}
} else if ( ! is_writable($parentDirectory)) {
throw ProxyException::proxyDirectoryNotWritable();
}
$tmpFileName = $fileName . '.' . uniqid("", true);
file_put_contents($tmpFileName, $file);
rename($tmpFileName, $fileName);
}
/**
* Generates the methods of a proxy class.
*
* @param ClassMetadata $class
* @return string The code of the generated methods.
*/
private function _generateMethods(ClassMetadata $class)
{
$methods = '';
$methodNames = array();
foreach ($class->reflClass->getMethods() as $method) {
/* @var $method ReflectionMethod */
if ($method->isConstructor() || in_array(strtolower($method->getName()), array("__sleep", "__clone")) || isset($methodNames[$method->getName()])) {
continue;
}
$methodNames[$method->getName()] = true;
if ($method->isPublic() && ! $method->isFinal() && ! $method->isStatic()) {
$methods .= "\n" . ' public function ';
if ($method->returnsReference()) {
$methods .= '&';
}
$methods .= $method->getName() . '(';
$firstParam = true;
$parameterString = $argumentString = '';
foreach ($method->getParameters() as $param) {
if ($firstParam) {
$firstParam = false;
} else {
$parameterString .= ', ';
$argumentString .= ', ';
}
// We need to pick the type hint class too
if (($paramClass = $param->getClass()) !== null) {
$parameterString .= '\\' . $paramClass->getName() . ' ';
} else if ($param->isArray()) {
$parameterString .= 'array ';
}
if ($param->isPassedByReference()) {
$parameterString .= '&';
}
$parameterString .= '$' . $param->getName();
$argumentString .= '$' . $param->getName();
if ($param->isDefaultValueAvailable()) {
$parameterString .= ' = ' . var_export($param->getDefaultValue(), true);
}
}
$methods .= $parameterString . ')';
$methods .= "\n" . ' {' . "\n";
if ($this->isShortIdentifierGetter($method, $class)) {
$identifier = lcfirst(substr($method->getName(), 3));
$cast = in_array($class->fieldMappings[$identifier]['type'], array('integer', 'smallint')) ? '(int) ' : '';
$methods .= ' if ($this->__isInitialized__ === false) {' . "\n";
$methods .= ' return ' . $cast . '$this->_identifier["' . $identifier . '"];' . "\n";
$methods .= ' }' . "\n";
}
$methods .= ' $this->__load();' . "\n";
$methods .= ' return parent::' . $method->getName() . '(' . $argumentString . ');';
$methods .= "\n" . ' }' . "\n";
}
}
return $methods;
}
/**
* Check if the method is a short identifier getter.
*
* What does this mean? For proxy objects the identifier is already known,
* however accessing the getter for this identifier usually triggers the
* lazy loading, leading to a query that may not be necessary if only the
* ID is interesting for the userland code (for example in views that
* generate links to the entity, but do not display anything else).
*
* @param ReflectionMethod $method
* @param ClassMetadata $class
* @return bool
*/
private function isShortIdentifierGetter($method, ClassMetadata $class)
{
$identifier = lcfirst(substr($method->getName(), 3));
$cheapCheck = (
$method->getNumberOfParameters() == 0 &&
substr($method->getName(), 0, 3) == "get" &&
in_array($identifier, $class->identifier, true) &&
$class->hasField($identifier) &&
(($method->getEndLine() - $method->getStartLine()) <= 4)
&& in_array($class->fieldMappings[$identifier]['type'], array('integer', 'bigint', 'smallint', 'string'))
);
if ($cheapCheck) {
$code = file($method->getDeclaringClass()->getFileName());
$code = trim(implode(" ", array_slice($code, $method->getStartLine() - 1, $method->getEndLine() - $method->getStartLine() + 1)));
$pattern = sprintf(self::PATTERN_MATCH_ID_METHOD, $method->getName(), $identifier);
if (preg_match($pattern, $code)) {
return true;
}
}
return false;
}
/**
* Generates the code for the __sleep method for a proxy class.
*
* @param $class
* @return string
*/
private function _generateSleep(ClassMetadata $class)
{
$sleepImpl = '';
if ($class->reflClass->hasMethod('__sleep')) {
$sleepImpl .= "return array_merge(array('__isInitialized__'), parent::__sleep());";
} else {
$sleepImpl .= "return array('__isInitialized__', ";
$first = true;
foreach ($class->getReflectionProperties() as $name => $prop) {
if ($first) {
$first = false;
} else {
$sleepImpl .= ', ';
}
$sleepImpl .= "'" . $name . "'";
}
$sleepImpl .= ');';
}
return $sleepImpl;
}
/** Proxy class code template */
private static $_proxyClassTemplate =
';
/**
* THIS CLASS WAS GENERATED BY THE DOCTRINE ORM. DO NOT EDIT THIS FILE.
*/
class extends \ implements \Doctrine\ORM\Proxy\Proxy
{
private $_entityPersister;
private $_identifier;
public $__isInitialized__ = false;
public function __construct($entityPersister, $identifier)
{
$this->_entityPersister = $entityPersister;
$this->_identifier = $identifier;
}
/** @private */
public function __load()
{
if (!$this->__isInitialized__ && $this->_entityPersister) {
$this->__isInitialized__ = true;
if (method_exists($this, "__wakeup")) {
// call this after __isInitialized__to avoid infinite recursion
// but before loading to emulate what ClassMetadata::newInstance()
// provides.
$this->__wakeup();
}
if ($this->_entityPersister->load($this->_identifier, $this) === null) {
throw new \Doctrine\ORM\EntityNotFoundException();
}
unset($this->_entityPersister, $this->_identifier);
}
}
/** @private */
public function __isInitialized()
{
return $this->__isInitialized__;
}
public function __sleep()
{
}
public function __clone()
{
if (!$this->__isInitialized__ && $this->_entityPersister) {
$this->__isInitialized__ = true;
$class = $this->_entityPersister->getClassMetadata();
$original = $this->_entityPersister->load($this->_identifier);
if ($original === null) {
throw new \Doctrine\ORM\EntityNotFoundException();
}
foreach ($class->reflFields as $field => $reflProperty) {
$reflProperty->setValue($this, $reflProperty->getValue($original));
}
unset($this->_entityPersister, $this->_identifier);
}
}
}';
}
DoctrineORM-2.3.3/Doctrine/ORM/Query/Expr.php 0000644 0001751 0001751 00000043143 12143374607 020726 0 ustar beberlei beberlei .
*/
namespace Doctrine\ORM\Query;
/**
* This class is used to generate DQL expressions via a set of PHP static functions
*
*
* @link www.doctrine-project.org
* @since 2.0
* @author Guilherme Blanco
* @author Jonathan Wage
* @author Roman Borschel
* @author Benjamin Eberlei
* @todo Rename: ExpressionBuilder
*/
class Expr
{
/**
* Creates a conjunction of the given boolean expressions.
*
* Example:
*
* [php]
* // (u.type = ?1) AND (u.role = ?2)
* $expr->andX($expr->eq('u.type', ':1'), $expr->eq('u.role', ':2'));
*
* @param \Doctrine\ORM\Query\Expr\Comparison |
* \Doctrine\ORM\Query\Expr\Func |
* \Doctrine\ORM\Query\Expr\Orx
* $x Optional clause. Defaults = null, but requires at least one defined when converting to string.
* @return Expr\Andx
*/
public function andX($x = null)
{
return new Expr\Andx(func_get_args());
}
/**
* Creates a disjunction of the given boolean expressions.
*
* Example:
*
* [php]
* // (u.type = ?1) OR (u.role = ?2)
* $q->where($q->expr()->orX('u.type = ?1', 'u.role = ?2'));
*
* @param mixed $x Optional clause. Defaults = null, but requires
* at least one defined when converting to string.
* @return Expr\Orx
*/
public function orX($x = null)
{
return new Expr\Orx(func_get_args());
}
/**
* Creates an ASCending order expression.
*
* @param $sort
* @return Expr\OrderBy
*/
public function asc($expr)
{
return new Expr\OrderBy($expr, 'ASC');
}
/**
* Creates a DESCending order expression.
*
* @param $sort
* @return Expr\OrderBy
*/
public function desc($expr)
{
return new Expr\OrderBy($expr, 'DESC');
}
/**
* Creates an equality comparison expression with the given arguments.
*
* First argument is considered the left expression and the second is the right expression.
* When converted to string, it will generated a = . Example:
*
* [php]
* // u.id = ?1
* $expr->eq('u.id', '?1');
*
* @param mixed $x Left expression
* @param mixed $y Right expression
* @return Expr\Comparison
*/
public function eq($x, $y)
{
return new Expr\Comparison($x, Expr\Comparison::EQ, $y);
}
/**
* Creates an instance of Expr\Comparison, with the given arguments.
* First argument is considered the left expression and the second is the right expression.
* When converted to string, it will generated a <> . Example:
*
* [php]
* // u.id <> ?1
* $q->where($q->expr()->neq('u.id', '?1'));
*
* @param mixed $x Left expression
* @param mixed $y Right expression
* @return Expr\Comparison
*/
public function neq($x, $y)
{
return new Expr\Comparison($x, Expr\Comparison::NEQ, $y);
}
/**
* Creates an instance of Expr\Comparison, with the given arguments.
* First argument is considered the left expression and the second is the right expression.
* When converted to string, it will generated a < . Example:
*
* [php]
* // u.id < ?1
* $q->where($q->expr()->lt('u.id', '?1'));
*
* @param mixed $x Left expression
* @param mixed $y Right expression
* @return Expr\Comparison
*/
public function lt($x, $y)
{
return new Expr\Comparison($x, Expr\Comparison::LT, $y);
}
/**
* Creates an instance of Expr\Comparison, with the given arguments.
* First argument is considered the left expression and the second is the right expression.
* When converted to string, it will generated a <= . Example:
*
* [php]
* // u.id <= ?1
* $q->where($q->expr()->lte('u.id', '?1'));
*
* @param mixed $x Left expression
* @param mixed $y Right expression
* @return Expr\Comparison
*/
public function lte($x, $y)
{
return new Expr\Comparison($x, Expr\Comparison::LTE, $y);
}
/**
* Creates an instance of Expr\Comparison, with the given arguments.
* First argument is considered the left expression and the second is the right expression.
* When converted to string, it will generated a > . Example:
*
* [php]
* // u.id > ?1
* $q->where($q->expr()->gt('u.id', '?1'));
*
* @param mixed $x Left expression
* @param mixed $y Right expression
* @return Expr\Comparison
*/
public function gt($x, $y)
{
return new Expr\Comparison($x, Expr\Comparison::GT, $y);
}
/**
* Creates an instance of Expr\Comparison, with the given arguments.
* First argument is considered the left expression and the second is the right expression.
* When converted to string, it will generated a >= . Example:
*
* [php]
* // u.id >= ?1
* $q->where($q->expr()->gte('u.id', '?1'));
*
* @param mixed $x Left expression
* @param mixed $y Right expression
* @return Expr\Comparison
*/
public function gte($x, $y)
{
return new Expr\Comparison($x, Expr\Comparison::GTE, $y);
}
/**
* Creates an instance of AVG() function, with the given argument.
*
* @param mixed $x Argument to be used in AVG() function.
* @return Expr\Func
*/
public function avg($x)
{
return new Expr\Func('AVG', array($x));
}
/**
* Creates an instance of MAX() function, with the given argument.
*
* @param mixed $x Argument to be used in MAX() function.
* @return Expr\Func
*/
public function max($x)
{
return new Expr\Func('MAX', array($x));
}
/**
* Creates an instance of MIN() function, with the given argument.
*
* @param mixed $x Argument to be used in MIN() function.
* @return Expr\Func
*/
public function min($x)
{
return new Expr\Func('MIN', array($x));
}
/**
* Creates an instance of COUNT() function, with the given argument.
*
* @param mixed $x Argument to be used in COUNT() function.
* @return Expr\Func
*/
public function count($x)
{
return new Expr\Func('COUNT', array($x));
}
/**
* Creates an instance of COUNT(DISTINCT) function, with the given argument.
*
* @param mixed $x Argument to be used in COUNT(DISTINCT) function.
* @return string
*/
public function countDistinct($x)
{
return 'COUNT(DISTINCT ' . implode(', ', func_get_args()) . ')';
}
/**
* Creates an instance of EXISTS() function, with the given DQL Subquery.
*
* @param mixed $subquery DQL Subquery to be used in EXISTS() function.
* @return Expr\Func
*/
public function exists($subquery)
{
return new Expr\Func('EXISTS', array($subquery));
}
/**
* Creates an instance of ALL() function, with the given DQL Subquery.
*
* @param mixed $subquery DQL Subquery to be used in ALL() function.
* @return Expr\Func
*/
public function all($subquery)
{
return new Expr\Func('ALL', array($subquery));
}
/**
* Creates a SOME() function expression with the given DQL subquery.
*
* @param mixed $subquery DQL Subquery to be used in SOME() function.
* @return Expr\Func
*/
public function some($subquery)
{
return new Expr\Func('SOME', array($subquery));
}
/**
* Creates an ANY() function expression with the given DQL subquery.
*
* @param mixed $subquery DQL Subquery to be used in ANY() function.
* @return Expr\Func
*/
public function any($subquery)
{
return new Expr\Func('ANY', array($subquery));
}
/**
* Creates a negation expression of the given restriction.
*
* @param mixed $restriction Restriction to be used in NOT() function.
* @return Expr\Func
*/
public function not($restriction)
{
return new Expr\Func('NOT', array($restriction));
}
/**
* Creates an ABS() function expression with the given argument.
*
* @param mixed $x Argument to be used in ABS() function.
* @return Expr\Func
*/
public function abs($x)
{
return new Expr\Func('ABS', array($x));
}
/**
* Creates a product mathematical expression with the given arguments.
*
* First argument is considered the left expression and the second is the right expression.
* When converted to string, it will generated a * . Example:
*
* [php]
* // u.salary * u.percentAnualSalaryIncrease
* $q->expr()->prod('u.salary', 'u.percentAnualSalaryIncrease')
*
* @param mixed $x Left expression
* @param mixed $y Right expression
* @return Expr\Math
*/
public function prod($x, $y)
{
return new Expr\Math($x, '*', $y);
}
/**
* Creates a difference mathematical expression with the given arguments.
* First argument is considered the left expression and the second is the right expression.
* When converted to string, it will generated a - . Example:
*
* [php]
* // u.monthlySubscriptionCount - 1
* $q->expr()->diff('u.monthlySubscriptionCount', '1')
*
* @param mixed $x Left expression
* @param mixed $y Right expression
* @return Expr\Math
*/
public function diff($x, $y)
{
return new Expr\Math($x, '-', $y);
}
/**
* Creates a sum mathematical expression with the given arguments.
* First argument is considered the left expression and the second is the right expression.
* When converted to string, it will generated a + . Example:
*
* [php]
* // u.numChildren + 1
* $q->expr()->diff('u.numChildren', '1')
*
* @param mixed $x Left expression
* @param mixed $y Right expression
* @return Expr\Math
*/
public function sum($x, $y)
{
return new Expr\Math($x, '+', $y);
}
/**
* Creates a quotient mathematical expression with the given arguments.
* First argument is considered the left expression and the second is the right expression.
* When converted to string, it will generated a / . Example:
*
* [php]
* // u.total / u.period
* $expr->quot('u.total', 'u.period')
*
* @param mixed $x Left expression
* @param mixed $y Right expression
* @return Expr\Math
*/
public function quot($x, $y)
{
return new Expr\Math($x, '/', $y);
}
/**
* Creates a SQRT() function expression with the given argument.
*
* @param mixed $x Argument to be used in SQRT() function.
* @return Expr\Func
*/
public function sqrt($x)
{
return new Expr\Func('SQRT', array($x));
}
/**
* Creates an IN() expression with the given arguments.
*
* @param string $x Field in string format to be restricted by IN() function
* @param mixed $y Argument to be used in IN() function.
* @return Expr\Func
*/
public function in($x, $y)
{
if (is_array($y)) {
foreach ($y as &$literal) {
if ( ! ($literal instanceof Expr\Literal)) {
$literal = $this->_quoteLiteral($literal);
}
}
}
return new Expr\Func($x . ' IN', (array) $y);
}
/**
* Creates a NOT IN() expression with the given arguments.
*
* @param string $x Field in string format to be restricted by NOT IN() function
* @param mixed $y Argument to be used in NOT IN() function.
* @return Expr\Func
*/
public function notIn($x, $y)
{
if (is_array($y)) {
foreach ($y as &$literal) {
if ( ! ($literal instanceof Expr\Literal)) {
$literal = $this->_quoteLiteral($literal);
}
}
}
return new Expr\Func($x . ' NOT IN', (array) $y);
}
/**
* Creates an IS NULL expression with the given arguments.
*
* @param string $x Field in string format to be restricted by IS NULL
* @return string
*/
public function isNull($x)
{
return $x . ' IS NULL';
}
/**
* Creates an IS NOT NULL expression with the given arguments.
*
* @param string $x Field in string format to be restricted by IS NOT NULL
* @return string
*/
public function isNotNull($x)
{
return $x . ' IS NOT NULL';
}
/**
* Creates a LIKE() comparison expression with the given arguments.
*
* @param string $x Field in string format to be inspected by LIKE() comparison.
* @param mixed $y Argument to be used in LIKE() comparison.
* @return Expr\Comparison
*/
public function like($x, $y)
{
return new Expr\Comparison($x, 'LIKE', $y);
}
/**
* Creates a CONCAT() function expression with the given arguments.
*
* @param mixed $x First argument to be used in CONCAT() function.
* @param mixed $x Second argument to be used in CONCAT() function.
* @return Expr\Func
*/
public function concat($x, $y)
{
return new Expr\Func('CONCAT', array($x, $y));
}
/**
* Creates a SUBSTRING() function expression with the given arguments.
*
* @param mixed $x Argument to be used as string to be cropped by SUBSTRING() function.
* @param integer $from Initial offset to start cropping string. May accept negative values.
* @param integer $len Length of crop. May accept negative values.
* @return Expr\Func
*/
public function substring($x, $from, $len = null)
{
$args = array($x, $from);
if (null !== $len) {
$args[] = $len;
}
return new Expr\Func('SUBSTRING', $args);
}
/**
* Creates a LOWER() function expression with the given argument.
*
* @param mixed $x Argument to be used in LOWER() function.
* @return Expr\Func A LOWER function expression.
*/
public function lower($x)
{
return new Expr\Func('LOWER', array($x));
}
/**
* Creates an UPPER() function expression with the given argument.
*
* @param mixed $x Argument to be used in UPPER() function.
* @return Expr\Func An UPPER function expression.
*/
public function upper($x)
{
return new Expr\Func('UPPER', array($x));
}
/**
* Creates a LENGTH() function expression with the given argument.
*
* @param mixed $x Argument to be used as argument of LENGTH() function.
* @return Expr\Func A LENGTH function expression.
*/
public function length($x)
{
return new Expr\Func('LENGTH', array($x));
}
/**
* Creates a literal expression of the given argument.
*
* @param mixed $literal Argument to be converted to literal.
* @return Expr\Literal
*/
public function literal($literal)
{
return new Expr\Literal($this->_quoteLiteral($literal));
}
/**
* Quotes a literal value, if necessary, according to the DQL syntax.
*
* @param mixed $literal The literal value.
* @return string
*/
private function _quoteLiteral($literal)
{
if (is_numeric($literal) && !is_string($literal)) {
return (string) $literal;
} else if (is_bool($literal)) {
return $literal ? "true" : "false";
} else {
return "'" . str_replace("'", "''", $literal) . "'";
}
}
/**
* Creates an instance of BETWEEN() function, with the given argument.
*
* @param mixed $val Valued to be inspected by range values.
* @param integer $x Starting range value to be used in BETWEEN() function.
* @param integer $y End point value to be used in BETWEEN() function.
* @return Expr\Func A BETWEEN expression.
*/
public function between($val, $x, $y)
{
return $val . ' BETWEEN ' . $x . ' AND ' . $y;
}
/**
* Creates an instance of TRIM() function, with the given argument.
*
* @param mixed $x Argument to be used as argument of TRIM() function.
* @return Expr\Func a TRIM expression.
*/
public function trim($x)
{
return new Expr\Func('TRIM', $x);
}
}
DoctrineORM-2.3.3/Doctrine/ORM/Query/FilterCollection.php 0000644 0001751 0001751 00000012272 12143374607 023250 0 ustar beberlei beberlei .
*/
namespace Doctrine\ORM\Query;
use Doctrine\ORM\Configuration,
Doctrine\ORM\EntityManager;
/**
* Collection class for all the query filters.
*
* @author Alexander
*/
class FilterCollection
{
/* Filter STATES */
/**
* A filter object is in CLEAN state when it has no changed parameters.
*/
const FILTERS_STATE_CLEAN = 1;
/**
* A filter object is in DIRTY state when it has changed parameters.
*/
const FILTERS_STATE_DIRTY = 2;
/**
* The used Configuration.
*
* @var Doctrine\ORM\Configuration
*/
private $config;
/**
* The EntityManager that "owns" this FilterCollection instance.
*
* @var Doctrine\ORM\EntityManager
*/
private $em;
/**
* Instances of enabled filters.
*
* @var array
*/
private $enabledFilters = array();
/**
* @var string The filter hash from the last time the query was parsed.
*/
private $filterHash;
/**
* @var integer $state The current state of this filter
*/
private $filtersState = self::FILTERS_STATE_CLEAN;
/**
* Constructor.
*
* @param EntityManager $em
*/
public function __construct(EntityManager $em)
{
$this->em = $em;
$this->config = $em->getConfiguration();
}
/**
* Get all the enabled filters.
*
* @return array The enabled filters.
*/
public function getEnabledFilters()
{
return $this->enabledFilters;
}
/**
* Enables a filter from the collection.
*
* @param string $name Name of the filter.
*
* @throws \InvalidArgumentException If the filter does not exist.
*
* @return SQLFilter The enabled filter.
*/
public function enable($name)
{
if (null === $filterClass = $this->config->getFilterClassName($name)) {
throw new \InvalidArgumentException("Filter '" . $name . "' does not exist.");
}
if (!isset($this->enabledFilters[$name])) {
$this->enabledFilters[$name] = new $filterClass($this->em);
// Keep the enabled filters sorted for the hash
ksort($this->enabledFilters);
// Now the filter collection is dirty
$this->filtersState = self::FILTERS_STATE_DIRTY;
}
return $this->enabledFilters[$name];
}
/**
* Disables a filter.
*
* @param string $name Name of the filter.
*
* @return SQLFilter The disabled filter.
*
* @throws \InvalidArgumentException If the filter does not exist.
*/
public function disable($name)
{
// Get the filter to return it
$filter = $this->getFilter($name);
unset($this->enabledFilters[$name]);
// Now the filter collection is dirty
$this->filtersState = self::FILTERS_STATE_DIRTY;
return $filter;
}
/**
* Get an enabled filter from the collection.
*
* @param string $name Name of the filter.
*
* @return SQLFilter The filter.
*
* @throws \InvalidArgumentException If the filter is not enabled.
*/
public function getFilter($name)
{
if (!isset($this->enabledFilters[$name])) {
throw new \InvalidArgumentException("Filter '" . $name . "' is not enabled.");
}
return $this->enabledFilters[$name];
}
/**
* @return boolean True, if the filter collection is clean.
*/
public function isClean()
{
return self::FILTERS_STATE_CLEAN === $this->filtersState;
}
/**
* Generates a string of currently enabled filters to use for the cache id.
*
* @return string
*/
public function getHash()
{
// If there are only clean filters, the previous hash can be returned
if (self::FILTERS_STATE_CLEAN === $this->filtersState) {
return $this->filterHash;
}
$filterHash = '';
foreach ($this->enabledFilters as $name => $filter) {
$filterHash .= $name . $filter;
}
return $filterHash;
}
/**
* Set the filter state to dirty.
*/
public function setFiltersStateDirty()
{
$this->filtersState = self::FILTERS_STATE_DIRTY;
}
}
DoctrineORM-2.3.3/Doctrine/ORM/Query/Lexer.php 0000644 0001751 0001751 00000016007 12143374607 021066 0 ustar beberlei beberlei .
*/
namespace Doctrine\ORM\Query;
/**
* Scans a DQL query for tokens.
*
* @author Guilherme Blanco
* @author Janne Vanhala
* @author Roman Borschel
* @since 2.0
*/
class Lexer extends \Doctrine\Common\Lexer
{
// All tokens that are not valid identifiers must be < 100
const T_NONE = 1;
const T_INTEGER = 2;
const T_STRING = 3;
const T_INPUT_PARAMETER = 4;
const T_FLOAT = 5;
const T_CLOSE_PARENTHESIS = 6;
const T_OPEN_PARENTHESIS = 7;
const T_COMMA = 8;
const T_DIVIDE = 9;
const T_DOT = 10;
const T_EQUALS = 11;
const T_GREATER_THAN = 12;
const T_LOWER_THAN = 13;
const T_MINUS = 14;
const T_MULTIPLY = 15;
const T_NEGATE = 16;
const T_PLUS = 17;
const T_OPEN_CURLY_BRACE = 18;
const T_CLOSE_CURLY_BRACE = 19;
// All tokens that are also identifiers should be >= 100
const T_IDENTIFIER = 100;
const T_ALL = 101;
const T_AND = 102;
const T_ANY = 103;
const T_AS = 104;
const T_ASC = 105;
const T_AVG = 106;
const T_BETWEEN = 107;
const T_BOTH = 108;
const T_BY = 109;
const T_CASE = 110;
const T_COALESCE = 111;
const T_COUNT = 112;
const T_DELETE = 113;
const T_DESC = 114;
const T_DISTINCT = 115;
const T_ELSE = 116;
const T_EMPTY = 117;
const T_END = 118;
const T_ESCAPE = 119;
const T_EXISTS = 120;
const T_FALSE = 121;
const T_FROM = 122;
const T_GROUP = 123;
const T_HAVING = 124;
const T_HIDDEN = 125;
const T_IN = 126;
const T_INDEX = 127;
const T_INNER = 128;
const T_INSTANCE = 129;
const T_IS = 130;
const T_JOIN = 131;
const T_LEADING = 132;
const T_LEFT = 133;
const T_LIKE = 134;
const T_MAX = 135;
const T_MEMBER = 136;
const T_MIN = 137;
const T_NOT = 138;
const T_NULL = 139;
const T_NULLIF = 140;
const T_OF = 141;
const T_OR = 142;
const T_ORDER = 143;
const T_OUTER = 144;
const T_SELECT = 145;
const T_SET = 146;
const T_SOME = 147;
const T_SUM = 148;
const T_THEN = 149;
const T_TRAILING = 150;
const T_TRUE = 151;
const T_UPDATE = 152;
const T_WHEN = 153;
const T_WHERE = 154;
const T_WITH = 155;
const T_PARTIAL = 156;
/**
* Creates a new query scanner object.
*
* @param string $input a query string
*/
public function __construct($input)
{
$this->setInput($input);
}
/**
* @inheritdoc
*/
protected function getCatchablePatterns()
{
return array(
'[a-z_\\\][a-z0-9_\:\\\]*[a-z0-9_]{1}',
'(?:[0-9]+(?:[\.][0-9]+)*)(?:e[+-]?[0-9]+)?',
"'(?:[^']|'')*'",
'\?[0-9]*|:[a-z]{1}[a-z0-9_]{0,}'
);
}
/**
* @inheritdoc
*/
protected function getNonCatchablePatterns()
{
return array('\s+', '(.)');
}
/**
* @inheritdoc
*/
protected function getType(&$value)
{
$type = self::T_NONE;
switch (true) {
// Recognize numeric values
case (is_numeric($value)):
if (strpos($value, '.') !== false || stripos($value, 'e') !== false) {
return self::T_FLOAT;
}
return self::T_INTEGER;
// Recognize quoted strings
case ($value[0] === "'"):
$value = str_replace("''", "'", substr($value, 1, strlen($value) - 2));
return self::T_STRING;
// Recognize identifiers
case (ctype_alpha($value[0]) || $value[0] === '_'):
$name = 'Doctrine\ORM\Query\Lexer::T_' . strtoupper($value);
if (defined($name)) {
$type = constant($name);
if ($type > 100) {
return $type;
}
}
return self::T_IDENTIFIER;
// Recognize input parameters
case ($value[0] === '?' || $value[0] === ':'):
return self::T_INPUT_PARAMETER;
// Recognize symbols
case ($value === '.'): return self::T_DOT;
case ($value === ','): return self::T_COMMA;
case ($value === '('): return self::T_OPEN_PARENTHESIS;
case ($value === ')'): return self::T_CLOSE_PARENTHESIS;
case ($value === '='): return self::T_EQUALS;
case ($value === '>'): return self::T_GREATER_THAN;
case ($value === '<'): return self::T_LOWER_THAN;
case ($value === '+'): return self::T_PLUS;
case ($value === '-'): return self::T_MINUS;
case ($value === '*'): return self::T_MULTIPLY;
case ($value === '/'): return self::T_DIVIDE;
case ($value === '!'): return self::T_NEGATE;
case ($value === '{'): return self::T_OPEN_CURLY_BRACE;
case ($value === '}'): return self::T_CLOSE_CURLY_BRACE;
// Default
default:
// Do nothing
}
return $type;
}
}
DoctrineORM-2.3.3/Doctrine/ORM/Query/Parameter.php 0000644 0001751 0001751 00000004727 12143374607 021735 0 ustar beberlei beberlei .
*/
namespace Doctrine\ORM\Query;
/**
* Define a Query Parameter
*
* @link www.doctrine-project.org
* @since 2.3
* @author Guilherme Blanco
*/
class Parameter
{
/**
* @var string Parameter name
*/
private $name;
/**
* @var mixed Parameter value
*/
private $value;
/**
* @var mixed Parameter type
*/
private $type;
/**
* Constructor.
*
* @param string $name Parameter name
* @param mixed $value Parameter value
* @param mixed $type Parameter type
*/
public function __construct($name, $value, $type = null)
{
$this->name = trim($name, ':');
$this->setValue($value, $type);
}
/**
* Retrieve the Parameter name.
*
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* Retrieve the Parameter value.
*
* @return mixed
*/
public function getValue()
{
return $this->value;
}
/**
* Retrieve the Parameter type.
*
* @return mixed
*/
public function getType()
{
return $this->type;
}
/**
* Define the Parameter value.
*
* @param mixed $value Parameter value
* @param mixed $type Parameter type
*/
public function setValue($value, $type = null)
{
$this->value = $value;
$this->type = $type ?: ParameterTypeInferer::inferType($value);
}
}
DoctrineORM-2.3.3/Doctrine/ORM/Query/ParameterTypeInferer.php 0000644 0001751 0001751 00000004174 12143374607 024106 0 ustar beberlei beberlei .
*/
namespace Doctrine\ORM\Query;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Types\Type;
/**
* Provides an enclosed support for parameter infering.
*
*
* @link www.doctrine-project.org
* @since 2.0
* @author Benjamin Eberlei
* @author Guilherme Blanco
* @author Jonathan Wage
* @author Roman Borschel
*/
class ParameterTypeInferer
{
/**
* Infer type of a given value, returning a compatible constant:
* - Type (\Doctrine\DBAL\Types\Type::*)
* - Connection (\Doctrine\DBAL\Connection::PARAM_*)
*
* @param mixed $value Parameter value
*
* @return mixed Parameter type constant
*/
public static function inferType($value)
{
if (is_integer($value)) {
return Type::INTEGER;
}
if ($value instanceof \DateTime) {
return Type::DATETIME;
}
if (is_array($value)) {
return is_integer(current($value))
? Connection::PARAM_INT_ARRAY
: Connection::PARAM_STR_ARRAY;
}
return \PDO::PARAM_STR;
}
} DoctrineORM-2.3.3/Doctrine/ORM/Query/Parser.php 0000644 0001751 0001751 00000311634 12143374607 021247 0 ustar beberlei beberlei .
*/
namespace Doctrine\ORM\Query;
use Doctrine\ORM\Query;
use Doctrine\ORM\Mapping\ClassMetadata;
/**
* An LL(*) recursive-descent parser for the context-free grammar of the Doctrine Query Language.
* Parses a DQL query, reports any errors in it, and generates an AST.
*
* @since 2.0
* @author Guilherme Blanco
* @author Jonathan Wage
* @author Roman Borschel
* @author Janne Vanhala
*/
class Parser
{
/** READ-ONLY: Maps BUILT-IN string function names to AST class names. */
private static $_STRING_FUNCTIONS = array(
'concat' => 'Doctrine\ORM\Query\AST\Functions\ConcatFunction',
'substring' => 'Doctrine\ORM\Query\AST\Functions\SubstringFunction',
'trim' => 'Doctrine\ORM\Query\AST\Functions\TrimFunction',
'lower' => 'Doctrine\ORM\Query\AST\Functions\LowerFunction',
'upper' => 'Doctrine\ORM\Query\AST\Functions\UpperFunction',
'identity' => 'Doctrine\ORM\Query\AST\Functions\IdentityFunction',
);
/** READ-ONLY: Maps BUILT-IN numeric function names to AST class names. */
private static $_NUMERIC_FUNCTIONS = array(
'length' => 'Doctrine\ORM\Query\AST\Functions\LengthFunction',
'locate' => 'Doctrine\ORM\Query\AST\Functions\LocateFunction',
'abs' => 'Doctrine\ORM\Query\AST\Functions\AbsFunction',
'sqrt' => 'Doctrine\ORM\Query\AST\Functions\SqrtFunction',
'mod' => 'Doctrine\ORM\Query\AST\Functions\ModFunction',
'size' => 'Doctrine\ORM\Query\AST\Functions\SizeFunction',
'date_diff' => 'Doctrine\ORM\Query\AST\Functions\DateDiffFunction',
'bit_and' => 'Doctrine\ORM\Query\AST\Functions\BitAndFunction',
'bit_or' => 'Doctrine\ORM\Query\AST\Functions\BitOrFunction',
);
/** READ-ONLY: Maps BUILT-IN datetime function names to AST class names. */
private static $_DATETIME_FUNCTIONS = array(
'current_date' => 'Doctrine\ORM\Query\AST\Functions\CurrentDateFunction',
'current_time' => 'Doctrine\ORM\Query\AST\Functions\CurrentTimeFunction',
'current_timestamp' => 'Doctrine\ORM\Query\AST\Functions\CurrentTimestampFunction',
'date_add' => 'Doctrine\ORM\Query\AST\Functions\DateAddFunction',
'date_sub' => 'Doctrine\ORM\Query\AST\Functions\DateSubFunction',
);
/**
* Expressions that were encountered during parsing of identifiers and expressions
* and still need to be validated.
*/
private $_deferredIdentificationVariables = array();
private $_deferredPartialObjectExpressions = array();
private $_deferredPathExpressions = array();
private $_deferredResultVariables = array();
/**
* The lexer.
*
* @var \Doctrine\ORM\Query\Lexer
*/
private $_lexer;
/**
* The parser result.
*
* @var \Doctrine\ORM\Query\ParserResult
*/
private $_parserResult;
/**
* The EntityManager.
*
* @var EnityManager
*/
private $_em;
/**
* The Query to parse.
*
* @var Query
*/
private $_query;
/**
* Map of declared query components in the parsed query.
*
* @var array
*/
private $_queryComponents = array();
/**
* Keeps the nesting level of defined ResultVariables
*
* @var integer
*/
private $_nestingLevel = 0;
/**
* Any additional custom tree walkers that modify the AST.
*
* @var array
*/
private $_customTreeWalkers = array();
/**
* The custom last tree walker, if any, that is responsible for producing the output.
*
* @var TreeWalker
*/
private $_customOutputWalker;
/**
* @var array
*/
private $_identVariableExpressions = array();
/**
* Check if a function is internally defined. Used to prevent overwriting
* of built-in functions through user-defined functions.
*
* @param string $functionName
* @return bool
*/
static public function isInternalFunction($functionName)
{
$functionName = strtolower($functionName);
return isset(self::$_STRING_FUNCTIONS[$functionName])
|| isset(self::$_DATETIME_FUNCTIONS[$functionName])
|| isset(self::$_NUMERIC_FUNCTIONS[$functionName]);
}
/**
* Creates a new query parser object.
*
* @param Query $query The Query to parse.
*/
public function __construct(Query $query)
{
$this->_query = $query;
$this->_em = $query->getEntityManager();
$this->_lexer = new Lexer($query->getDql());
$this->_parserResult = new ParserResult();
}
/**
* Sets a custom tree walker that produces output.
* This tree walker will be run last over the AST, after any other walkers.
*
* @param string $className
*/
public function setCustomOutputTreeWalker($className)
{
$this->_customOutputWalker = $className;
}
/**
* Adds a custom tree walker for modifying the AST.
*
* @param string $className
*/
public function addCustomTreeWalker($className)
{
$this->_customTreeWalkers[] = $className;
}
/**
* Gets the lexer used by the parser.
*
* @return \Doctrine\ORM\Query\Lexer
*/
public function getLexer()
{
return $this->_lexer;
}
/**
* Gets the ParserResult that is being filled with information during parsing.
*
* @return \Doctrine\ORM\Query\ParserResult
*/
public function getParserResult()
{
return $this->_parserResult;
}
/**
* Gets the EntityManager used by the parser.
*
* @return EntityManager
*/
public function getEntityManager()
{
return $this->_em;
}
/**
* Parse and build AST for the given Query.
*
* @return \Doctrine\ORM\Query\AST\SelectStatement |
* \Doctrine\ORM\Query\AST\UpdateStatement |
* \Doctrine\ORM\Query\AST\DeleteStatement
*/
public function getAST()
{
// Parse & build AST
$AST = $this->QueryLanguage();
// Process any deferred validations of some nodes in the AST.
// This also allows post-processing of the AST for modification purposes.
$this->_processDeferredIdentificationVariables();
if ($this->_deferredPartialObjectExpressions) {
$this->_processDeferredPartialObjectExpressions();
}
if ($this->_deferredPathExpressions) {
$this->_processDeferredPathExpressions($AST);
}
if ($this->_deferredResultVariables) {
$this->_processDeferredResultVariables();
}
$this->_processRootEntityAliasSelected();
// TODO: Is there a way to remove this? It may impact the mixed hydration resultset a lot!
$this->fixIdentificationVariableOrder($AST);
return $AST;
}
/**
* Attempts to match the given token with the current lookahead token.
*
* If they match, updates the lookahead token; otherwise raises a syntax
* error.
*
* @param int token type
* @return void
* @throws QueryException If the tokens dont match.
*/
public function match($token)
{
$lookaheadType = $this->_lexer->lookahead['type'];
// short-circuit on first condition, usually types match
if ($lookaheadType !== $token && $token !== Lexer::T_IDENTIFIER && $lookaheadType <= Lexer::T_IDENTIFIER) {
$this->syntaxError($this->_lexer->getLiteral($token));
}
$this->_lexer->moveNext();
}
/**
* Free this parser enabling it to be reused
*
* @param boolean $deep Whether to clean peek and reset errors
* @param integer $position Position to reset
*/
public function free($deep = false, $position = 0)
{
// WARNING! Use this method with care. It resets the scanner!
$this->_lexer->resetPosition($position);
// Deep = true cleans peek and also any previously defined errors
if ($deep) {
$this->_lexer->resetPeek();
}
$this->_lexer->token = null;
$this->_lexer->lookahead = null;
}
/**
* Parses a query string.
*
* @return ParserResult
*/
public function parse()
{
$AST = $this->getAST();
if (($customWalkers = $this->_query->getHint(Query::HINT_CUSTOM_TREE_WALKERS)) !== false) {
$this->_customTreeWalkers = $customWalkers;
}
if (($customOutputWalker = $this->_query->getHint(Query::HINT_CUSTOM_OUTPUT_WALKER)) !== false) {
$this->_customOutputWalker = $customOutputWalker;
}
// Run any custom tree walkers over the AST
if ($this->_customTreeWalkers) {
$treeWalkerChain = new TreeWalkerChain($this->_query, $this->_parserResult, $this->_queryComponents);
foreach ($this->_customTreeWalkers as $walker) {
$treeWalkerChain->addTreeWalker($walker);
}
switch (true) {
case ($AST instanceof AST\UpdateStatement):
$treeWalkerChain->walkUpdateStatement($AST);
break;
case ($AST instanceof AST\DeleteStatement):
$treeWalkerChain->walkDeleteStatement($AST);
break;
case ($AST instanceof AST\SelectStatement):
default:
$treeWalkerChain->walkSelectStatement($AST);
}
}
$outputWalkerClass = $this->_customOutputWalker ?: __NAMESPACE__ . '\SqlWalker';
$outputWalker = new $outputWalkerClass($this->_query, $this->_parserResult, $this->_queryComponents);
// Assign an SQL executor to the parser result
$this->_parserResult->setSqlExecutor($outputWalker->getExecutor($AST));
return $this->_parserResult;
}
/**
* Fix order of identification variables.
*
* They have to appear in the select clause in the same order as the
* declarations (from ... x join ... y join ... z ...) appear in the query
* as the hydration process relies on that order for proper operation.
*
* @param AST\SelectStatement|AST\DeleteStatement|AST\UpdateStatement $AST
* @return void
*/
private function fixIdentificationVariableOrder($AST)
{
if (count($this->_identVariableExpressions) <= 1) {
return;
}
foreach ($this->_queryComponents as $dqlAlias => $qComp) {
if ( ! isset($this->_identVariableExpressions[$dqlAlias])) {
continue;
}
$expr = $this->_identVariableExpressions[$dqlAlias];
$key = array_search($expr, $AST->selectClause->selectExpressions);
unset($AST->selectClause->selectExpressions[$key]);
$AST->selectClause->selectExpressions[] = $expr;
}
}
/**
* Generates a new syntax error.
*
* @param string $expected Expected string.
* @param array $token Got token.
*
* @throws \Doctrine\ORM\Query\QueryException
*/
public function syntaxError($expected = '', $token = null)
{
if ($token === null) {
$token = $this->_lexer->lookahead;
}
$tokenPos = (isset($token['position'])) ? $token['position'] : '-1';
$message = "line 0, col {$tokenPos}: Error: ";
$message .= ($expected !== '') ? "Expected {$expected}, got " : 'Unexpected ';
$message .= ($this->_lexer->lookahead === null) ? 'end of string.' : "'{$token['value']}'";
throw QueryException::syntaxError($message, QueryException::dqlError($this->_query->getDQL()));
}
/**
* Generates a new semantical error.
*
* @param string $message Optional message.
* @param array $token Optional token.
*
* @throws \Doctrine\ORM\Query\QueryException
*/
public function semanticalError($message = '', $token = null)
{
if ($token === null) {
$token = $this->_lexer->lookahead;
}
// Minimum exposed chars ahead of token
$distance = 12;
// Find a position of a final word to display in error string
$dql = $this->_query->getDql();
$length = strlen($dql);
$pos = $token['position'] + $distance;
$pos = strpos($dql, ' ', ($length > $pos) ? $pos : $length);
$length = ($pos !== false) ? $pos - $token['position'] : $distance;
$tokenPos = (isset($token['position']) && $token['position'] > 0) ? $token['position'] : '-1';
$tokenStr = substr($dql, $token['position'], $length);
// Building informative message
$message = 'line 0, col ' . $tokenPos . " near '" . $tokenStr . "': Error: " . $message;
throw QueryException::semanticalError($message, QueryException::dqlError($this->_query->getDQL()));
}
/**
* Peek beyond the matched closing parenthesis and return the first token after that one.
*
* @param boolean $resetPeek Reset peek after finding the closing parenthesis
* @return array
*/
private function _peekBeyondClosingParenthesis($resetPeek = true)
{
$token = $this->_lexer->peek();
$numUnmatched = 1;
while ($numUnmatched > 0 && $token !== null) {
switch ($token['type']) {
case Lexer::T_OPEN_PARENTHESIS:
++$numUnmatched;
break;
case Lexer::T_CLOSE_PARENTHESIS:
--$numUnmatched;
break;
default:
// Do nothing
}
$token = $this->_lexer->peek();
}
if ($resetPeek) {
$this->_lexer->resetPeek();
}
return $token;
}
/**
* Checks if the given token indicates a mathematical operator.
*
* @return boolean TRUE if the token is a mathematical operator, FALSE otherwise.
*/
private function _isMathOperator($token)
{
return in_array($token['type'], array(Lexer::T_PLUS, Lexer::T_MINUS, Lexer::T_DIVIDE, Lexer::T_MULTIPLY));
}
/**
* Checks if the next-next (after lookahead) token starts a function.
*
* @return boolean TRUE if the next-next tokens start a function, FALSE otherwise.
*/
private function _isFunction()
{
$peek = $this->_lexer->peek();
$nextpeek = $this->_lexer->peek();
$this->_lexer->resetPeek();
// We deny the COUNT(SELECT * FROM User u) here. COUNT won't be considered a function
return ($peek['type'] === Lexer::T_OPEN_PARENTHESIS && $nextpeek['type'] !== Lexer::T_SELECT);
}
/**
* Checks whether the given token type indicates an aggregate function.
*
* @return boolean TRUE if the token type is an aggregate function, FALSE otherwise.
*/
private function _isAggregateFunction($tokenType)
{
return in_array($tokenType, array(Lexer::T_AVG, Lexer::T_MIN, Lexer::T_MAX, Lexer::T_SUM, Lexer::T_COUNT));
}
/**
* Checks whether the current lookahead token of the lexer has the type T_ALL, T_ANY or T_SOME.
*
* @return boolean
*/
private function _isNextAllAnySome()
{
return in_array($this->_lexer->lookahead['type'], array(Lexer::T_ALL, Lexer::T_ANY, Lexer::T_SOME));
}
/**
* Validates that the given IdentificationVariable is semantically correct.
* It must exist in query components list.
*
* @return void
*/
private function _processDeferredIdentificationVariables()
{
foreach ($this->_deferredIdentificationVariables as $deferredItem) {
$identVariable = $deferredItem['expression'];
// Check if IdentificationVariable exists in queryComponents
if ( ! isset($this->_queryComponents[$identVariable])) {
$this->semanticalError(
"'$identVariable' is not defined.", $deferredItem['token']
);
}
$qComp = $this->_queryComponents[$identVariable];
// Check if queryComponent points to an AbstractSchemaName or a ResultVariable
if ( ! isset($qComp['metadata'])) {
$this->semanticalError(
"'$identVariable' does not point to a Class.", $deferredItem['token']
);
}
// Validate if identification variable nesting level is lower or equal than the current one
if ($qComp['nestingLevel'] > $deferredItem['nestingLevel']) {
$this->semanticalError(
"'$identVariable' is used outside the scope of its declaration.", $deferredItem['token']
);
}
}
}
/**
* Validates that the given PartialObjectExpression is semantically correct.
* It must exist in query components list.
*
* @return void
*/
private function _processDeferredPartialObjectExpressions()
{
foreach ($this->_deferredPartialObjectExpressions as $deferredItem) {
$expr = $deferredItem['expression'];
$class = $this->_queryComponents[$expr->identificationVariable]['metadata'];
foreach ($expr->partialFieldSet as $field) {
if (isset($class->fieldMappings[$field])) {
continue;
}
$this->semanticalError(
"There is no mapped field named '$field' on class " . $class->name . ".", $deferredItem['token']
);
}
if (array_intersect($class->identifier, $expr->partialFieldSet) != $class->identifier) {
$this->semanticalError(
"The partial field selection of class " . $class->name . " must contain the identifier.",
$deferredItem['token']
);
}
}
}
/**
* Validates that the given ResultVariable is semantically correct.
* It must exist in query components list.
*
* @return void
*/
private function _processDeferredResultVariables()
{
foreach ($this->_deferredResultVariables as $deferredItem) {
$resultVariable = $deferredItem['expression'];
// Check if ResultVariable exists in queryComponents
if ( ! isset($this->_queryComponents[$resultVariable])) {
$this->semanticalError(
"'$resultVariable' is not defined.", $deferredItem['token']
);
}
$qComp = $this->_queryComponents[$resultVariable];
// Check if queryComponent points to an AbstractSchemaName or a ResultVariable
if ( ! isset($qComp['resultVariable'])) {
$this->semanticalError(
"'$resultVariable' does not point to a ResultVariable.", $deferredItem['token']
);
}
// Validate if identification variable nesting level is lower or equal than the current one
if ($qComp['nestingLevel'] > $deferredItem['nestingLevel']) {
$this->semanticalError(
"'$resultVariable' is used outside the scope of its declaration.", $deferredItem['token']
);
}
}
}
/**
* Validates that the given PathExpression is semantically correct for grammar rules:
*
* AssociationPathExpression ::= CollectionValuedPathExpression | SingleValuedAssociationPathExpression
* SingleValuedPathExpression ::= StateFieldPathExpression | SingleValuedAssociationPathExpression
* StateFieldPathExpression ::= IdentificationVariable "." StateField
* SingleValuedAssociationPathExpression ::= IdentificationVariable "." SingleValuedAssociationField
* CollectionValuedPathExpression ::= IdentificationVariable "." CollectionValuedAssociationField
*
* @param array $deferredItem
* @param mixed $AST
*/
private function _processDeferredPathExpressions($AST)
{
foreach ($this->_deferredPathExpressions as $deferredItem) {
$pathExpression = $deferredItem['expression'];
$qComp = $this->_queryComponents[$pathExpression->identificationVariable];
$class = $qComp['metadata'];
if (($field = $pathExpression->field) === null) {
$field = $pathExpression->field = $class->identifier[0];
}
// Check if field or association exists
if ( ! isset($class->associationMappings[$field]) && ! isset($class->fieldMappings[$field])) {
$this->semanticalError(
'Class ' . $class->name . ' has no field or association named ' . $field,
$deferredItem['token']
);
}
$fieldType = AST\PathExpression::TYPE_STATE_FIELD;
if (isset($class->associationMappings[$field])) {
$assoc = $class->associationMappings[$field];
$fieldType = ($assoc['type'] & ClassMetadata::TO_ONE)
? AST\PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION
: AST\PathExpression::TYPE_COLLECTION_VALUED_ASSOCIATION;
}
// Validate if PathExpression is one of the expected types
$expectedType = $pathExpression->expectedType;
if ( ! ($expectedType & $fieldType)) {
// We need to recognize which was expected type(s)
$expectedStringTypes = array();
// Validate state field type
if ($expectedType & AST\PathExpression::TYPE_STATE_FIELD) {
$expectedStringTypes[] = 'StateFieldPathExpression';
}
// Validate single valued association (*-to-one)
if ($expectedType & AST\PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION) {
$expectedStringTypes[] = 'SingleValuedAssociationField';
}
// Validate single valued association (*-to-many)
if ($expectedType & AST\PathExpression::TYPE_COLLECTION_VALUED_ASSOCIATION) {
$expectedStringTypes[] = 'CollectionValuedAssociationField';
}
// Build the error message
$semanticalError = 'Invalid PathExpression. ';
$semanticalError .= (count($expectedStringTypes) == 1)
? 'Must be a ' . $expectedStringTypes[0] . '.'
: implode(' or ', $expectedStringTypes) . ' expected.';
$this->semanticalError($semanticalError, $deferredItem['token']);
}
// We need to force the type in PathExpression
$pathExpression->type = $fieldType;
}
}
private function _processRootEntityAliasSelected()
{
if ( ! count($this->_identVariableExpressions)) {
return;
}
$foundRootEntity = false;
foreach ($this->_identVariableExpressions as $dqlAlias => $expr) {
if (isset($this->_queryComponents[$dqlAlias]) && $this->_queryComponents[$dqlAlias]['parent'] === null) {
$foundRootEntity = true;
}
}
if ( ! $foundRootEntity) {
$this->semanticalError('Cannot select entity through identification variables without choosing at least one root entity alias.');
}
}
/**
* QueryLanguage ::= SelectStatement | UpdateStatement | DeleteStatement
*
* @return \Doctrine\ORM\Query\AST\SelectStatement |
* \Doctrine\ORM\Query\AST\UpdateStatement |
* \Doctrine\ORM\Query\AST\DeleteStatement
*/
public function QueryLanguage()
{
$this->_lexer->moveNext();
switch ($this->_lexer->lookahead['type']) {
case Lexer::T_SELECT:
$statement = $this->SelectStatement();
break;
case Lexer::T_UPDATE:
$statement = $this->UpdateStatement();
break;
case Lexer::T_DELETE:
$statement = $this->DeleteStatement();
break;
default:
$this->syntaxError('SELECT, UPDATE or DELETE');
break;
}
// Check for end of string
if ($this->_lexer->lookahead !== null) {
$this->syntaxError('end of string');
}
return $statement;
}
/**
* SelectStatement ::= SelectClause FromClause [WhereClause] [GroupByClause] [HavingClause] [OrderByClause]
*
* @return \Doctrine\ORM\Query\AST\SelectStatement
*/
public function SelectStatement()
{
$selectStatement = new AST\SelectStatement($this->SelectClause(), $this->FromClause());
$selectStatement->whereClause = $this->_lexer->isNextToken(Lexer::T_WHERE) ? $this->WhereClause() : null;
$selectStatement->groupByClause = $this->_lexer->isNextToken(Lexer::T_GROUP) ? $this->GroupByClause() : null;
$selectStatement->havingClause = $this->_lexer->isNextToken(Lexer::T_HAVING) ? $this->HavingClause() : null;
$selectStatement->orderByClause = $this->_lexer->isNextToken(Lexer::T_ORDER) ? $this->OrderByClause() : null;
return $selectStatement;
}
/**
* UpdateStatement ::= UpdateClause [WhereClause]
*
* @return \Doctrine\ORM\Query\AST\UpdateStatement
*/
public function UpdateStatement()
{
$updateStatement = new AST\UpdateStatement($this->UpdateClause());
$updateStatement->whereClause = $this->_lexer->isNextToken(Lexer::T_WHERE) ? $this->WhereClause() : null;
return $updateStatement;
}
/**
* DeleteStatement ::= DeleteClause [WhereClause]
*
* @return \Doctrine\ORM\Query\AST\DeleteStatement
*/
public function DeleteStatement()
{
$deleteStatement = new AST\DeleteStatement($this->DeleteClause());
$deleteStatement->whereClause = $this->_lexer->isNextToken(Lexer::T_WHERE) ? $this->WhereClause() : null;
return $deleteStatement;
}
/**
* IdentificationVariable ::= identifier
*
* @return string
*/
public function IdentificationVariable()
{
$this->match(Lexer::T_IDENTIFIER);
$identVariable = $this->_lexer->token['value'];
$this->_deferredIdentificationVariables[] = array(
'expression' => $identVariable,
'nestingLevel' => $this->_nestingLevel,
'token' => $this->_lexer->token,
);
return $identVariable;
}
/**
* AliasIdentificationVariable = identifier
*
* @return string
*/
public function AliasIdentificationVariable()
{
$this->match(Lexer::T_IDENTIFIER);
$aliasIdentVariable = $this->_lexer->token['value'];
$exists = isset($this->_queryComponents[$aliasIdentVariable]);
if ($exists) {
$this->semanticalError("'$aliasIdentVariable' is already defined.", $this->_lexer->token);
}
return $aliasIdentVariable;
}
/**
* AbstractSchemaName ::= identifier
*
* @return string
*/
public function AbstractSchemaName()
{
$this->match(Lexer::T_IDENTIFIER);
$schemaName = ltrim($this->_lexer->token['value'], '\\');
if (strrpos($schemaName, ':') !== false) {
list($namespaceAlias, $simpleClassName) = explode(':', $schemaName);
$schemaName = $this->_em->getConfiguration()->getEntityNamespace($namespaceAlias) . '\\' . $simpleClassName;
}
$exists = class_exists($schemaName, true);
if ( ! $exists) {
$this->semanticalError("Class '$schemaName' is not defined.", $this->_lexer->token);
}
return $schemaName;
}
/**
* AliasResultVariable ::= identifier
*
* @return string
*/
public function AliasResultVariable()
{
$this->match(Lexer::T_IDENTIFIER);
$resultVariable = $this->_lexer->token['value'];
$exists = isset($this->_queryComponents[$resultVariable]);
if ($exists) {
$this->semanticalError("'$resultVariable' is already defined.", $this->_lexer->token);
}
return $resultVariable;
}
/**
* ResultVariable ::= identifier
*
* @return string
*/
public function ResultVariable()
{
$this->match(Lexer::T_IDENTIFIER);
$resultVariable = $this->_lexer->token['value'];
// Defer ResultVariable validation
$this->_deferredResultVariables[] = array(
'expression' => $resultVariable,
'nestingLevel' => $this->_nestingLevel,
'token' => $this->_lexer->token,
);
return $resultVariable;
}
/**
* JoinAssociationPathExpression ::= IdentificationVariable "." (CollectionValuedAssociationField | SingleValuedAssociationField)
*
* @return \Doctrine\ORM\Query\AST\JoinAssociationPathExpression
*/
public function JoinAssociationPathExpression()
{
$identVariable = $this->IdentificationVariable();
if ( ! isset($this->_queryComponents[$identVariable])) {
$this->semanticalError(
'Identification Variable ' . $identVariable .' used in join path expression but was not defined before.'
);
}
$this->match(Lexer::T_DOT);
$this->match(Lexer::T_IDENTIFIER);
$field = $this->_lexer->token['value'];
// Validate association field
$qComp = $this->_queryComponents[$identVariable];
$class = $qComp['metadata'];
if ( ! $class->hasAssociation($field)) {
$this->semanticalError('Class ' . $class->name . ' has no association named ' . $field);
}
return new AST\JoinAssociationPathExpression($identVariable, $field);
}
/**
* Parses an arbitrary path expression and defers semantical validation
* based on expected types.
*
* PathExpression ::= IdentificationVariable "." identifier
*
* @param integer $expectedTypes
* @return \Doctrine\ORM\Query\AST\PathExpression
*/
public function PathExpression($expectedTypes)
{
$identVariable = $this->IdentificationVariable();
$field = null;
if ($this->_lexer->isNextToken(Lexer::T_DOT)) {
$this->match(Lexer::T_DOT);
$this->match(Lexer::T_IDENTIFIER);
$field = $this->_lexer->token['value'];
}
// Creating AST node
$pathExpr = new AST\PathExpression($expectedTypes, $identVariable, $field);
// Defer PathExpression validation if requested to be defered
$this->_deferredPathExpressions[] = array(
'expression' => $pathExpr,
'nestingLevel' => $this->_nestingLevel,
'token' => $this->_lexer->token,
);
return $pathExpr;
}
/**
* AssociationPathExpression ::= CollectionValuedPathExpression | SingleValuedAssociationPathExpression
*
* @return \Doctrine\ORM\Query\AST\PathExpression
*/
public function AssociationPathExpression()
{
return $this->PathExpression(
AST\PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION |
AST\PathExpression::TYPE_COLLECTION_VALUED_ASSOCIATION
);
}
/**
* SingleValuedPathExpression ::= StateFieldPathExpression | SingleValuedAssociationPathExpression
*
* @return \Doctrine\ORM\Query\AST\PathExpression
*/
public function SingleValuedPathExpression()
{
return $this->PathExpression(
AST\PathExpression::TYPE_STATE_FIELD |
AST\PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION
);
}
/**
* StateFieldPathExpression ::= IdentificationVariable "." StateField
*
* @return \Doctrine\ORM\Query\AST\PathExpression
*/
public function StateFieldPathExpression()
{
return $this->PathExpression(AST\PathExpression::TYPE_STATE_FIELD);
}
/**
* SingleValuedAssociationPathExpression ::= IdentificationVariable "." SingleValuedAssociationField
*
* @return \Doctrine\ORM\Query\AST\PathExpression
*/
public function SingleValuedAssociationPathExpression()
{
return $this->PathExpression(AST\PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION);
}
/**
* CollectionValuedPathExpression ::= IdentificationVariable "." CollectionValuedAssociationField
*
* @return \Doctrine\ORM\Query\AST\PathExpression
*/
public function CollectionValuedPathExpression()
{
return $this->PathExpression(AST\PathExpression::TYPE_COLLECTION_VALUED_ASSOCIATION);
}
/**
* SelectClause ::= "SELECT" ["DISTINCT"] SelectExpression {"," SelectExpression}
*
* @return \Doctrine\ORM\Query\AST\SelectClause
*/
public function SelectClause()
{
$isDistinct = false;
$this->match(Lexer::T_SELECT);
// Check for DISTINCT
if ($this->_lexer->isNextToken(Lexer::T_DISTINCT)) {
$this->match(Lexer::T_DISTINCT);
$isDistinct = true;
}
// Process SelectExpressions (1..N)
$selectExpressions = array();
$selectExpressions[] = $this->SelectExpression();
while ($this->_lexer->isNextToken(Lexer::T_COMMA)) {
$this->match(Lexer::T_COMMA);
$selectExpressions[] = $this->SelectExpression();
}
return new AST\SelectClause($selectExpressions, $isDistinct);
}
/**
* SimpleSelectClause ::= "SELECT" ["DISTINCT"] SimpleSelectExpression
*
* @return \Doctrine\ORM\Query\AST\SimpleSelectClause
*/
public function SimpleSelectClause()
{
$isDistinct = false;
$this->match(Lexer::T_SELECT);
if ($this->_lexer->isNextToken(Lexer::T_DISTINCT)) {
$this->match(Lexer::T_DISTINCT);
$isDistinct = true;
}
return new AST\SimpleSelectClause($this->SimpleSelectExpression(), $isDistinct);
}
/**
* UpdateClause ::= "UPDATE" AbstractSchemaName ["AS"] AliasIdentificationVariable "SET" UpdateItem {"," UpdateItem}*
*
* @return \Doctrine\ORM\Query\AST\UpdateClause
*/
public function UpdateClause()
{
$this->match(Lexer::T_UPDATE);
$token = $this->_lexer->lookahead;
$abstractSchemaName = $this->AbstractSchemaName();
if ($this->_lexer->isNextToken(Lexer::T_AS)) {
$this->match(Lexer::T_AS);
}
$aliasIdentificationVariable = $this->AliasIdentificationVariable();
$class = $this->_em->getClassMetadata($abstractSchemaName);
// Building queryComponent
$queryComponent = array(
'metadata' => $class,
'parent' => null,
'relation' => null,
'map' => null,
'nestingLevel' => $this->_nestingLevel,
'token' => $token,
);
$this->_queryComponents[$aliasIdentificationVariable] = $queryComponent;
$this->match(Lexer::T_SET);
$updateItems = array();
$updateItems[] = $this->UpdateItem();
while ($this->_lexer->isNextToken(Lexer::T_COMMA)) {
$this->match(Lexer::T_COMMA);
$updateItems[] = $this->UpdateItem();
}
$updateClause = new AST\UpdateClause($abstractSchemaName, $updateItems);
$updateClause->aliasIdentificationVariable = $aliasIdentificationVariable;
return $updateClause;
}
/**
* DeleteClause ::= "DELETE" ["FROM"] AbstractSchemaName ["AS"] AliasIdentificationVariable
*
* @return \Doctrine\ORM\Query\AST\DeleteClause
*/
public function DeleteClause()
{
$this->match(Lexer::T_DELETE);
if ($this->_lexer->isNextToken(Lexer::T_FROM)) {
$this->match(Lexer::T_FROM);
}
$token = $this->_lexer->lookahead;
$deleteClause = new AST\DeleteClause($this->AbstractSchemaName());
if ($this->_lexer->isNextToken(Lexer::T_AS)) {
$this->match(Lexer::T_AS);
}
$aliasIdentificationVariable = $this->AliasIdentificationVariable();
$deleteClause->aliasIdentificationVariable = $aliasIdentificationVariable;
$class = $this->_em->getClassMetadata($deleteClause->abstractSchemaName);
// Building queryComponent
$queryComponent = array(
'metadata' => $class,
'parent' => null,
'relation' => null,
'map' => null,
'nestingLevel' => $this->_nestingLevel,
'token' => $token,
);
$this->_queryComponents[$aliasIdentificationVariable] = $queryComponent;
return $deleteClause;
}
/**
* FromClause ::= "FROM" IdentificationVariableDeclaration {"," IdentificationVariableDeclaration}*
*
* @return \Doctrine\ORM\Query\AST\FromClause
*/
public function FromClause()
{
$this->match(Lexer::T_FROM);
$identificationVariableDeclarations = array();
$identificationVariableDeclarations[] = $this->IdentificationVariableDeclaration();
while ($this->_lexer->isNextToken(Lexer::T_COMMA)) {
$this->match(Lexer::T_COMMA);
$identificationVariableDeclarations[] = $this->IdentificationVariableDeclaration();
}
return new AST\FromClause($identificationVariableDeclarations);
}
/**
* SubselectFromClause ::= "FROM" SubselectIdentificationVariableDeclaration {"," SubselectIdentificationVariableDeclaration}*
*
* @return \Doctrine\ORM\Query\AST\SubselectFromClause
*/
public function SubselectFromClause()
{
$this->match(Lexer::T_FROM);
$identificationVariables = array();
$identificationVariables[] = $this->SubselectIdentificationVariableDeclaration();
while ($this->_lexer->isNextToken(Lexer::T_COMMA)) {
$this->match(Lexer::T_COMMA);
$identificationVariables[] = $this->SubselectIdentificationVariableDeclaration();
}
return new AST\SubselectFromClause($identificationVariables);
}
/**
* WhereClause ::= "WHERE" ConditionalExpression
*
* @return \Doctrine\ORM\Query\AST\WhereClause
*/
public function WhereClause()
{
$this->match(Lexer::T_WHERE);
return new AST\WhereClause($this->ConditionalExpression());
}
/**
* HavingClause ::= "HAVING" ConditionalExpression
*
* @return \Doctrine\ORM\Query\AST\HavingClause
*/
public function HavingClause()
{
$this->match(Lexer::T_HAVING);
return new AST\HavingClause($this->ConditionalExpression());
}
/**
* GroupByClause ::= "GROUP" "BY" GroupByItem {"," GroupByItem}*
*
* @return \Doctrine\ORM\Query\AST\GroupByClause
*/
public function GroupByClause()
{
$this->match(Lexer::T_GROUP);
$this->match(Lexer::T_BY);
$groupByItems = array($this->GroupByItem());
while ($this->_lexer->isNextToken(Lexer::T_COMMA)) {
$this->match(Lexer::T_COMMA);
$groupByItems[] = $this->GroupByItem();
}
return new AST\GroupByClause($groupByItems);
}
/**
* OrderByClause ::= "ORDER" "BY" OrderByItem {"," OrderByItem}*
*
* @return \Doctrine\ORM\Query\AST\OrderByClause
*/
public function OrderByClause()
{
$this->match(Lexer::T_ORDER);
$this->match(Lexer::T_BY);
$orderByItems = array();
$orderByItems[] = $this->OrderByItem();
while ($this->_lexer->isNextToken(Lexer::T_COMMA)) {
$this->match(Lexer::T_COMMA);
$orderByItems[] = $this->OrderByItem();
}
return new AST\OrderByClause($orderByItems);
}
/**
* Subselect ::= SimpleSelectClause SubselectFromClause [WhereClause] [GroupByClause] [HavingClause] [OrderByClause]
*
* @return \Doctrine\ORM\Query\AST\Subselect
*/
public function Subselect()
{
// Increase query nesting level
$this->_nestingLevel++;
$subselect = new AST\Subselect($this->SimpleSelectClause(), $this->SubselectFromClause());
$subselect->whereClause = $this->_lexer->isNextToken(Lexer::T_WHERE) ? $this->WhereClause() : null;
$subselect->groupByClause = $this->_lexer->isNextToken(Lexer::T_GROUP) ? $this->GroupByClause() : null;
$subselect->havingClause = $this->_lexer->isNextToken(Lexer::T_HAVING) ? $this->HavingClause() : null;
$subselect->orderByClause = $this->_lexer->isNextToken(Lexer::T_ORDER) ? $this->OrderByClause() : null;
// Decrease query nesting level
$this->_nestingLevel--;
return $subselect;
}
/**
* UpdateItem ::= SingleValuedPathExpression "=" NewValue
*
* @return \Doctrine\ORM\Query\AST\UpdateItem
*/
public function UpdateItem()
{
$pathExpr = $this->SingleValuedPathExpression();
$this->match(Lexer::T_EQUALS);
$updateItem = new AST\UpdateItem($pathExpr, $this->NewValue());
return $updateItem;
}
/**
* GroupByItem ::= IdentificationVariable | ResultVariable | SingleValuedPathExpression
*
* @return string | \Doctrine\ORM\Query\AST\PathExpression
*/
public function GroupByItem()
{
// We need to check if we are in a IdentificationVariable or SingleValuedPathExpression
$glimpse = $this->_lexer->glimpse();
if ($glimpse['type'] === Lexer::T_DOT) {
return $this->SingleValuedPathExpression();
}
// Still need to decide between IdentificationVariable or ResultVariable
$lookaheadValue = $this->_lexer->lookahead['value'];
if ( ! isset($this->_queryComponents[$lookaheadValue])) {
$this->semanticalError('Cannot group by undefined identification or result variable.');
}
return (isset($this->_queryComponents[$lookaheadValue]['metadata']))
? $this->IdentificationVariable()
: $this->ResultVariable();
}
/**
* OrderByItem ::= (
* SimpleArithmeticExpression | SingleValuedPathExpression |
* ScalarExpression | ResultVariable
* ) ["ASC" | "DESC"]
*
* @return \Doctrine\ORM\Query\AST\OrderByItem
*/
public function OrderByItem()
{
$this->_lexer->peek(); // lookahead => '.'
$this->_lexer->peek(); // lookahead => token after '.'
$peek = $this->_lexer->peek(); // lookahead => token after the token after the '.'
$this->_lexer->resetPeek();
$glimpse = $this->_lexer->glimpse();
switch (true) {
case ($this->_isMathOperator($peek)):
$expr = $this->SimpleArithmeticExpression();
break;
case ($glimpse['type'] === Lexer::T_DOT):
$expr = $this->SingleValuedPathExpression();
break;
case ($this->_lexer->peek() && $this->_isMathOperator($this->_peekBeyondClosingParenthesis())):
$expr = $this->ScalarExpression();
break;
default:
$expr = $this->ResultVariable();
break;
}
$type = 'ASC';
$item = new AST\OrderByItem($expr);
switch (true) {
case ($this->_lexer->isNextToken(Lexer::T_DESC)):
$this->match(Lexer::T_DESC);
$type = 'DESC';
break;
case ($this->_lexer->isNextToken(Lexer::T_ASC)):
$this->match(Lexer::T_ASC);
break;
default:
// Do nothing
}
$item->type = $type;
return $item;
}
/**
* NewValue ::= SimpleArithmeticExpression | StringPrimary | DatetimePrimary | BooleanPrimary |
* EnumPrimary | SimpleEntityExpression | "NULL"
*
* NOTE: Since it is not possible to correctly recognize individual types, here is the full
* grammar that needs to be supported:
*
* NewValue ::= SimpleArithmeticExpression | "NULL"
*
* SimpleArithmeticExpression covers all *Primary grammar rules and also SimpleEntityExpression
*/
public function NewValue()
{
if ($this->_lexer->isNextToken(Lexer::T_NULL)) {
$this->match(Lexer::T_NULL);
return null;
}
if ($this->_lexer->isNextToken(Lexer::T_INPUT_PARAMETER)) {
$this->match(Lexer::T_INPUT_PARAMETER);
return new AST\InputParameter($this->_lexer->token['value']);
}
return $this->SimpleArithmeticExpression();
}
/**
* IdentificationVariableDeclaration ::= RangeVariableDeclaration [IndexBy] {Join}*
*
* @return \Doctrine\ORM\Query\AST\IdentificationVariableDeclaration
*/
public function IdentificationVariableDeclaration()
{
$rangeVariableDeclaration = $this->RangeVariableDeclaration();
$indexBy = $this->_lexer->isNextToken(Lexer::T_INDEX) ? $this->IndexBy() : null;
$joins = array();
while (
$this->_lexer->isNextToken(Lexer::T_LEFT) ||
$this->_lexer->isNextToken(Lexer::T_INNER) ||
$this->_lexer->isNextToken(Lexer::T_JOIN)
) {
$joins[] = $this->Join();
}
return new AST\IdentificationVariableDeclaration(
$rangeVariableDeclaration, $indexBy, $joins
);
}
/**
* SubselectIdentificationVariableDeclaration ::= IdentificationVariableDeclaration | (AssociationPathExpression ["AS"] AliasIdentificationVariable)
*
* @return \Doctrine\ORM\Query\AST\SubselectIdentificationVariableDeclaration |
* \Doctrine\ORM\Query\AST\IdentificationVariableDeclaration
*/
public function SubselectIdentificationVariableDeclaration()
{
$this->_lexer->glimpse();
/* NOT YET IMPLEMENTED!
if ($glimpse['type'] == Lexer::T_DOT) {
$subselectIdVarDecl = new AST\SubselectIdentificationVariableDeclaration();
$subselectIdVarDecl->associationPathExpression = $this->AssociationPathExpression();
$this->match(Lexer::T_AS);
$subselectIdVarDecl->aliasIdentificationVariable = $this->AliasIdentificationVariable();
return $subselectIdVarDecl;
}
*/
return $this->IdentificationVariableDeclaration();
}
/**
* Join ::= ["LEFT" ["OUTER"] | "INNER"] "JOIN"
* (JoinAssociationDeclaration | RangeVariableDeclaration)
* ["WITH" ConditionalExpression]
*
* @return \Doctrine\ORM\Query\AST\Join
*/
public function Join()
{
// Check Join type
$joinType = AST\Join::JOIN_TYPE_INNER;
switch (true) {
case ($this->_lexer->isNextToken(Lexer::T_LEFT)):
$this->match(Lexer::T_LEFT);
$joinType = AST\Join::JOIN_TYPE_LEFT;
// Possible LEFT OUTER join
if ($this->_lexer->isNextToken(Lexer::T_OUTER)) {
$this->match(Lexer::T_OUTER);
$joinType = AST\Join::JOIN_TYPE_LEFTOUTER;
}
break;
case ($this->_lexer->isNextToken(Lexer::T_INNER)):
$this->match(Lexer::T_INNER);
break;
default:
// Do nothing
}
$this->match(Lexer::T_JOIN);
$next = $this->_lexer->glimpse();
$joinDeclaration = ($next['type'] === Lexer::T_DOT)
? $this->JoinAssociationDeclaration()
: $this->RangeVariableDeclaration();
// Create AST node
$join = new AST\Join($joinType, $joinDeclaration);
// Check for ad-hoc Join conditions
if ($this->_lexer->isNextToken(Lexer::T_WITH) || $joinDeclaration instanceof AST\RangeVariableDeclaration) {
$this->match(Lexer::T_WITH);
$join->conditionalExpression = $this->ConditionalExpression();
}
return $join;
}
/**
* RangeVariableDeclaration ::= AbstractSchemaName ["AS"] AliasIdentificationVariable
*
* @return \Doctrine\ORM\Query\AST\RangeVariableDeclaration
*/
public function RangeVariableDeclaration()
{
$abstractSchemaName = $this->AbstractSchemaName();
if ($this->_lexer->isNextToken(Lexer::T_AS)) {
$this->match(Lexer::T_AS);
}
$token = $this->_lexer->lookahead;
$aliasIdentificationVariable = $this->AliasIdentificationVariable();
$classMetadata = $this->_em->getClassMetadata($abstractSchemaName);
// Building queryComponent
$queryComponent = array(
'metadata' => $classMetadata,
'parent' => null,
'relation' => null,
'map' => null,
'nestingLevel' => $this->_nestingLevel,
'token' => $token
);
$this->_queryComponents[$aliasIdentificationVariable] = $queryComponent;
return new AST\RangeVariableDeclaration($abstractSchemaName, $aliasIdentificationVariable);
}
/**
* JoinAssociationDeclaration ::= JoinAssociationPathExpression ["AS"] AliasIdentificationVariable [IndexBy]
*
* @return \Doctrine\ORM\Query\AST\JoinAssociationPathExpression
*/
public function JoinAssociationDeclaration()
{
$joinAssociationPathExpression = $this->JoinAssociationPathExpression();
if ($this->_lexer->isNextToken(Lexer::T_AS)) {
$this->match(Lexer::T_AS);
}
$aliasIdentificationVariable = $this->AliasIdentificationVariable();
$indexBy = $this->_lexer->isNextToken(Lexer::T_INDEX) ? $this->IndexBy() : null;
$identificationVariable = $joinAssociationPathExpression->identificationVariable;
$field = $joinAssociationPathExpression->associationField;
$class = $this->_queryComponents[$identificationVariable]['metadata'];
$targetClass = $this->_em->getClassMetadata($class->associationMappings[$field]['targetEntity']);
// Building queryComponent
$joinQueryComponent = array(
'metadata' => $targetClass,
'parent' => $joinAssociationPathExpression->identificationVariable,
'relation' => $class->getAssociationMapping($field),
'map' => null,
'nestingLevel' => $this->_nestingLevel,
'token' => $this->_lexer->lookahead
);
$this->_queryComponents[$aliasIdentificationVariable] = $joinQueryComponent;
return new AST\JoinAssociationDeclaration($joinAssociationPathExpression, $aliasIdentificationVariable, $indexBy);
}
/**
* PartialObjectExpression ::= "PARTIAL" IdentificationVariable "." PartialFieldSet
* PartialFieldSet ::= "{" SimpleStateField {"," SimpleStateField}* "}"
*
* @return array
*/
public function PartialObjectExpression()
{
$this->match(Lexer::T_PARTIAL);
$partialFieldSet = array();
$identificationVariable = $this->IdentificationVariable();
$this->match(Lexer::T_DOT);
$this->match(Lexer::T_OPEN_CURLY_BRACE);
$this->match(Lexer::T_IDENTIFIER);
$partialFieldSet[] = $this->_lexer->token['value'];
while ($this->_lexer->isNextToken(Lexer::T_COMMA)) {
$this->match(Lexer::T_COMMA);
$this->match(Lexer::T_IDENTIFIER);
$partialFieldSet[] = $this->_lexer->token['value'];
}
$this->match(Lexer::T_CLOSE_CURLY_BRACE);
$partialObjectExpression = new AST\PartialObjectExpression($identificationVariable, $partialFieldSet);
// Defer PartialObjectExpression validation
$this->_deferredPartialObjectExpressions[] = array(
'expression' => $partialObjectExpression,
'nestingLevel' => $this->_nestingLevel,
'token' => $this->_lexer->token,
);
return $partialObjectExpression;
}
/**
* IndexBy ::= "INDEX" "BY" StateFieldPathExpression
*
* @return \Doctrine\ORM\Query\AST\IndexBy
*/
public function IndexBy()
{
$this->match(Lexer::T_INDEX);
$this->match(Lexer::T_BY);
$pathExpr = $this->StateFieldPathExpression();
// Add the INDEX BY info to the query component
$this->_queryComponents[$pathExpr->identificationVariable]['map'] = $pathExpr->field;
return new AST\IndexBy($pathExpr);
}
/**
* ScalarExpression ::= SimpleArithmeticExpression | StringPrimary | DateTimePrimary |
* StateFieldPathExpression | BooleanPrimary | CaseExpression |
* InstanceOfExpression
*
* @return mixed One of the possible expressions or subexpressions.
*/
public function ScalarExpression()
{
$lookahead = $this->_lexer->lookahead['type'];
switch ($lookahead) {
case Lexer::T_IDENTIFIER:
$this->_lexer->peek(); // lookahead => '.'
$this->_lexer->peek(); // lookahead => token after '.'
$peek = $this->_lexer->peek(); // lookahead => token after the token after the '.'
$this->_lexer->resetPeek();
if ($this->_isMathOperator($peek)) {
return $this->SimpleArithmeticExpression();
}
return $this->StateFieldPathExpression();
case Lexer::T_INTEGER:
case Lexer::T_FLOAT:
return $this->SimpleArithmeticExpression();
case Lexer::T_STRING:
return $this->StringPrimary();
case Lexer::T_TRUE:
case Lexer::T_FALSE:
$this->match($lookahead);
return new AST\Literal(AST\Literal::BOOLEAN, $this->_lexer->token['value']);
case Lexer::T_INPUT_PARAMETER:
return $this->InputParameter();
case Lexer::T_CASE:
case Lexer::T_COALESCE:
case Lexer::T_NULLIF:
// Since NULLIF and COALESCE can be identified as a function,
// we need to check if before check for FunctionDeclaration
return $this->CaseExpression();
default:
if ( ! ($this->_isFunction() || $this->_isAggregateFunction($lookahead))) {
$this->syntaxError();
}
// We may be in an ArithmeticExpression (find the matching ")" and inspect for Math operator)
$this->_lexer->peek(); // "("
$peek = $this->_peekBeyondClosingParenthesis();
if ($this->_isMathOperator($peek)) {
return $this->SimpleArithmeticExpression();
}
if ($this->_isAggregateFunction($this->_lexer->lookahead['type'])) {
return $this->AggregateExpression();
}
return $this->FunctionDeclaration();
}
}
/**
* CaseExpression ::= GeneralCaseExpression | SimpleCaseExpression | CoalesceExpression | NullifExpression
* GeneralCaseExpression ::= "CASE" WhenClause {WhenClause}* "ELSE" ScalarExpression "END"
* WhenClause ::= "WHEN" ConditionalExpression "THEN" ScalarExpression
* SimpleCaseExpression ::= "CASE" CaseOperand SimpleWhenClause {SimpleWhenClause}* "ELSE" ScalarExpression "END"
* CaseOperand ::= StateFieldPathExpression | TypeDiscriminator
* SimpleWhenClause ::= "WHEN" ScalarExpression "THEN" ScalarExpression
* CoalesceExpression ::= "COALESCE" "(" ScalarExpression {"," ScalarExpression}* ")"
* NullifExpression ::= "NULLIF" "(" ScalarExpression "," ScalarExpression ")"
*
* @return mixed One of the possible expressions or subexpressions.
*/
public function CaseExpression()
{
$lookahead = $this->_lexer->lookahead['type'];
switch ($lookahead) {
case Lexer::T_NULLIF:
return $this->NullIfExpression();
case Lexer::T_COALESCE:
return $this->CoalesceExpression();
case Lexer::T_CASE:
$this->_lexer->resetPeek();
$peek = $this->_lexer->peek();
if ($peek['type'] === Lexer::T_WHEN) {
return $this->GeneralCaseExpression();
}
return $this->SimpleCaseExpression();
default:
// Do nothing
break;
}
$this->syntaxError();
}
/**
* CoalesceExpression ::= "COALESCE" "(" ScalarExpression {"," ScalarExpression}* ")"
*
* @return \Doctrine\ORM\Query\AST\CoalesceExpression
*/
public function CoalesceExpression()
{
$this->match(Lexer::T_COALESCE);
$this->match(Lexer::T_OPEN_PARENTHESIS);
// Process ScalarExpressions (1..N)
$scalarExpressions = array();
$scalarExpressions[] = $this->ScalarExpression();
while ($this->_lexer->isNextToken(Lexer::T_COMMA)) {
$this->match(Lexer::T_COMMA);
$scalarExpressions[] = $this->ScalarExpression();
}
$this->match(Lexer::T_CLOSE_PARENTHESIS);
return new AST\CoalesceExpression($scalarExpressions);
}
/**
* NullIfExpression ::= "NULLIF" "(" ScalarExpression "," ScalarExpression ")"
*
* @return \Doctrine\ORM\Query\AST\NullIfExpression
*/
public function NullIfExpression()
{
$this->match(Lexer::T_NULLIF);
$this->match(Lexer::T_OPEN_PARENTHESIS);
$firstExpression = $this->ScalarExpression();
$this->match(Lexer::T_COMMA);
$secondExpression = $this->ScalarExpression();
$this->match(Lexer::T_CLOSE_PARENTHESIS);
return new AST\NullIfExpression($firstExpression, $secondExpression);
}
/**
* GeneralCaseExpression ::= "CASE" WhenClause {WhenClause}* "ELSE" ScalarExpression "END"
*
* @return \Doctrine\ORM\Query\AST\GeneralExpression
*/
public function GeneralCaseExpression()
{
$this->match(Lexer::T_CASE);
// Process WhenClause (1..N)
$whenClauses = array();
do {
$whenClauses[] = $this->WhenClause();
} while ($this->_lexer->isNextToken(Lexer::T_WHEN));
$this->match(Lexer::T_ELSE);
$scalarExpression = $this->ScalarExpression();
$this->match(Lexer::T_END);
return new AST\GeneralCaseExpression($whenClauses, $scalarExpression);
}
/**
* SimpleCaseExpression ::= "CASE" CaseOperand SimpleWhenClause {SimpleWhenClause}* "ELSE" ScalarExpression "END"
* CaseOperand ::= StateFieldPathExpression | TypeDiscriminator
*/
public function SimpleCaseExpression()
{
$this->match(Lexer::T_CASE);
$caseOperand = $this->StateFieldPathExpression();
// Process SimpleWhenClause (1..N)
$simpleWhenClauses = array();
do {
$simpleWhenClauses[] = $this->SimpleWhenClause();
} while ($this->_lexer->isNextToken(Lexer::T_WHEN));
$this->match(Lexer::T_ELSE);
$scalarExpression = $this->ScalarExpression();
$this->match(Lexer::T_END);
return new AST\SimpleCaseExpression($caseOperand, $simpleWhenClauses, $scalarExpression);
}
/**
* WhenClause ::= "WHEN" ConditionalExpression "THEN" ScalarExpression
*
* @return \Doctrine\ORM\Query\AST\WhenExpression
*/
public function WhenClause()
{
$this->match(Lexer::T_WHEN);
$conditionalExpression = $this->ConditionalExpression();
$this->match(Lexer::T_THEN);
return new AST\WhenClause($conditionalExpression, $this->ScalarExpression());
}
/**
* SimpleWhenClause ::= "WHEN" ScalarExpression "THEN" ScalarExpression
*
* @return \Doctrine\ORM\Query\AST\SimpleWhenExpression
*/
public function SimpleWhenClause()
{
$this->match(Lexer::T_WHEN);
$conditionalExpression = $this->ScalarExpression();
$this->match(Lexer::T_THEN);
return new AST\SimpleWhenClause($conditionalExpression, $this->ScalarExpression());
}
/**
* SelectExpression ::= (
* IdentificationVariable | ScalarExpression | AggregateExpression | FunctionDeclaration |
* PartialObjectExpression | "(" Subselect ")" | CaseExpression
* ) [["AS"] ["HIDDEN"] AliasResultVariable]
*
* @return \Doctrine\ORM\Query\AST\SelectExpression
*/
public function SelectExpression()
{
$expression = null;
$identVariable = null;
$peek = $this->_lexer->glimpse();
$lookaheadType = $this->_lexer->lookahead['type'];
switch (true) {
// ScalarExpression (u.name)
case ($lookaheadType === Lexer::T_IDENTIFIER && $peek['type'] === Lexer::T_DOT):
$expression = $this->ScalarExpression();
break;
// IdentificationVariable (u)
case ($lookaheadType === Lexer::T_IDENTIFIER && $peek['type'] !== Lexer::T_OPEN_PARENTHESIS):
$expression = $identVariable = $this->IdentificationVariable();
break;
// CaseExpression (CASE ... or NULLIF(...) or COALESCE(...))
case ($lookaheadType === Lexer::T_CASE):
case ($lookaheadType === Lexer::T_COALESCE):
case ($lookaheadType === Lexer::T_NULLIF):
$expression = $this->CaseExpression();
break;
// DQL Function (SUM(u.value) or SUM(u.value) + 1)
case ($this->_isFunction()):
$this->_lexer->peek(); // "("
switch (true) {
case ($this->_isMathOperator($this->_peekBeyondClosingParenthesis())):
// SUM(u.id) + COUNT(u.id)
$expression = $this->ScalarExpression();
break;
case ($this->_isAggregateFunction($lookaheadType)):
// COUNT(u.id)
$expression = $this->AggregateExpression();
break;
default:
// IDENTITY(u)
$expression = $this->FunctionDeclaration();
break;
}
break;
// PartialObjectExpression (PARTIAL u.{id, name})
case ($lookaheadType === Lexer::T_PARTIAL):
$expression = $this->PartialObjectExpression();
$identVariable = $expression->identificationVariable;
break;
// Subselect
case ($lookaheadType === Lexer::T_OPEN_PARENTHESIS && $peek['type'] === Lexer::T_SELECT):
$this->match(Lexer::T_OPEN_PARENTHESIS);
$expression = $this->Subselect();
$this->match(Lexer::T_CLOSE_PARENTHESIS);
break;
// Shortcut: ScalarExpression => SimpleArithmeticExpression
case ($lookaheadType === Lexer::T_OPEN_PARENTHESIS):
case ($lookaheadType === Lexer::T_INTEGER):
case ($lookaheadType === Lexer::T_STRING):
case ($lookaheadType === Lexer::T_FLOAT):
// SimpleArithmeticExpression : (- u.value ) or ( + u.value )
case ($lookaheadType === Lexer::T_MINUS):
case ($lookaheadType === Lexer::T_PLUS):
$expression = $this->SimpleArithmeticExpression();
break;
default:
$this->syntaxError(
'IdentificationVariable | ScalarExpression | AggregateExpression | FunctionDeclaration | PartialObjectExpression | "(" Subselect ")" | CaseExpression',
$this->_lexer->lookahead
);
}
// [["AS"] ["HIDDEN"] AliasResultVariable]
if ($this->_lexer->isNextToken(Lexer::T_AS)) {
$this->match(Lexer::T_AS);
}
$hiddenAliasResultVariable = false;
if ($this->_lexer->isNextToken(Lexer::T_HIDDEN)) {
$this->match(Lexer::T_HIDDEN);
$hiddenAliasResultVariable = true;
}
$aliasResultVariable = null;
if ($this->_lexer->isNextToken(Lexer::T_IDENTIFIER)) {
$token = $this->_lexer->lookahead;
$aliasResultVariable = $this->AliasResultVariable();
// Include AliasResultVariable in query components.
$this->_queryComponents[$aliasResultVariable] = array(
'resultVariable' => $expression,
'nestingLevel' => $this->_nestingLevel,
'token' => $token,
);
}
// AST
$expr = new AST\SelectExpression($expression, $aliasResultVariable, $hiddenAliasResultVariable);
if ($identVariable) {
$this->_identVariableExpressions[$identVariable] = $expr;
}
return $expr;
}
/**
* SimpleSelectExpression ::= (
* StateFieldPathExpression | IdentificationVariable | FunctionDeclaration |
* AggregateExpression | "(" Subselect ")" | ScalarExpression
* ) [["AS"] AliasResultVariable]
*
* @return \Doctrine\ORM\Query\AST\SimpleSelectExpression
*/
public function SimpleSelectExpression()
{
$peek = $this->_lexer->glimpse();
switch ($this->_lexer->lookahead['type']) {
case Lexer::T_IDENTIFIER:
switch (true) {
case ($peek['type'] === Lexer::T_DOT):
$expression = $this->StateFieldPathExpression();
return new AST\SimpleSelectExpression($expression);
case ($peek['type'] !== Lexer::T_OPEN_PARENTHESIS):
$expression = $this->IdentificationVariable();
return new AST\SimpleSelectExpression($expression);
case ($this->_isFunction()):
// SUM(u.id) + COUNT(u.id)
if ($this->_isMathOperator($this->_peekBeyondClosingParenthesis())) {
return new AST\SimpleSelectExpression($this->ScalarExpression());
}
// COUNT(u.id)
if ($this->_isAggregateFunction($this->_lexer->lookahead['type'])) {
return new AST\SimpleSelectExpression($this->AggregateExpression());
}
// IDENTITY(u)
return new AST\SimpleSelectExpression($this->FunctionDeclaration());
default:
// Do nothing
}
break;
case Lexer::T_OPEN_PARENTHESIS:
if ($peek['type'] !== Lexer::T_SELECT) {
// Shortcut: ScalarExpression => SimpleArithmeticExpression
$expression = $this->SimpleArithmeticExpression();
return new AST\SimpleSelectExpression($expression);
}
// Subselect
$this->match(Lexer::T_OPEN_PARENTHESIS);
$expression = $this->Subselect();
$this->match(Lexer::T_CLOSE_PARENTHESIS);
return new AST\SimpleSelectExpression($expression);
default:
// Do nothing
}
$this->_lexer->peek();
$expression = $this->ScalarExpression();
$expr = new AST\SimpleSelectExpression($expression);
if ($this->_lexer->isNextToken(Lexer::T_AS)) {
$this->match(Lexer::T_AS);
}
if ($this->_lexer->isNextToken(Lexer::T_IDENTIFIER)) {
$token = $this->_lexer->lookahead;
$resultVariable = $this->AliasResultVariable();
$expr->fieldIdentificationVariable = $resultVariable;
// Include AliasResultVariable in query components.
$this->_queryComponents[$resultVariable] = array(
'resultvariable' => $expr,
'nestingLevel' => $this->_nestingLevel,
'token' => $token,
);
}
return $expr;
}
/**
* ConditionalExpression ::= ConditionalTerm {"OR" ConditionalTerm}*
*
* @return \Doctrine\ORM\Query\AST\ConditionalExpression
*/
public function ConditionalExpression()
{
$conditionalTerms = array();
$conditionalTerms[] = $this->ConditionalTerm();
while ($this->_lexer->isNextToken(Lexer::T_OR)) {
$this->match(Lexer::T_OR);
$conditionalTerms[] = $this->ConditionalTerm();
}
// Phase 1 AST optimization: Prevent AST\ConditionalExpression
// if only one AST\ConditionalTerm is defined
if (count($conditionalTerms) == 1) {
return $conditionalTerms[0];
}
return new AST\ConditionalExpression($conditionalTerms);
}
/**
* ConditionalTerm ::= ConditionalFactor {"AND" ConditionalFactor}*
*
* @return \Doctrine\ORM\Query\AST\ConditionalTerm
*/
public function ConditionalTerm()
{
$conditionalFactors = array();
$conditionalFactors[] = $this->ConditionalFactor();
while ($this->_lexer->isNextToken(Lexer::T_AND)) {
$this->match(Lexer::T_AND);
$conditionalFactors[] = $this->ConditionalFactor();
}
// Phase 1 AST optimization: Prevent AST\ConditionalTerm
// if only one AST\ConditionalFactor is defined
if (count($conditionalFactors) == 1) {
return $conditionalFactors[0];
}
return new AST\ConditionalTerm($conditionalFactors);
}
/**
* ConditionalFactor ::= ["NOT"] ConditionalPrimary
*
* @return \Doctrine\ORM\Query\AST\ConditionalFactor
*/
public function ConditionalFactor()
{
$not = false;
if ($this->_lexer->isNextToken(Lexer::T_NOT)) {
$this->match(Lexer::T_NOT);
$not = true;
}
$conditionalPrimary = $this->ConditionalPrimary();
// Phase 1 AST optimization: Prevent AST\ConditionalFactor
// if only one AST\ConditionalPrimary is defined
if ( ! $not) {
return $conditionalPrimary;
}
$conditionalFactor = new AST\ConditionalFactor($conditionalPrimary);
$conditionalFactor->not = $not;
return $conditionalFactor;
}
/**
* ConditionalPrimary ::= SimpleConditionalExpression | "(" ConditionalExpression ")"
*
* @return \Doctrine\ORM\Query\AST\ConditionalPrimary
*/
public function ConditionalPrimary()
{
$condPrimary = new AST\ConditionalPrimary;
if ( ! $this->_lexer->isNextToken(Lexer::T_OPEN_PARENTHESIS)) {
$condPrimary->simpleConditionalExpression = $this->SimpleConditionalExpression();
return $condPrimary;
}
// Peek beyond the matching closing paranthesis ')'
$peek = $this->_peekBeyondClosingParenthesis();
if (in_array($peek['value'], array("=", "<", "<=", "<>", ">", ">=", "!=")) ||
in_array($peek['type'], array(Lexer::T_NOT, Lexer::T_BETWEEN, Lexer::T_LIKE, Lexer::T_IN, Lexer::T_IS, Lexer::T_EXISTS)) ||
$this->_isMathOperator($peek)) {
$condPrimary->simpleConditionalExpression = $this->SimpleConditionalExpression();
return $condPrimary;
}
$this->match(Lexer::T_OPEN_PARENTHESIS);
$condPrimary->conditionalExpression = $this->ConditionalExpression();
$this->match(Lexer::T_CLOSE_PARENTHESIS);
return $condPrimary;
}
/**
* SimpleConditionalExpression ::=
* ComparisonExpression | BetweenExpression | LikeExpression |
* InExpression | NullComparisonExpression | ExistsExpression |
* EmptyCollectionComparisonExpression | CollectionMemberExpression |
* InstanceOfExpression
*/
public function SimpleConditionalExpression()
{
$token = $this->_lexer->lookahead;
if ($this->_lexer->isNextToken(Lexer::T_NOT)) {
$token = $this->_lexer->glimpse();
}
if ($token['type'] === Lexer::T_EXISTS) {
return $this->ExistsExpression();
}
$peek = $this->_lexer->glimpse();
if ($token['type'] === Lexer::T_IDENTIFIER || $token['type'] === Lexer::T_INPUT_PARAMETER) {
if ($peek['value'] == '(') {
// Peek beyond the matching closing paranthesis ')'
$this->_lexer->peek();
$token = $this->_peekBeyondClosingParenthesis(false);
if ($token['type'] === Lexer::T_NOT) {
$token = $this->_lexer->peek();
}
$this->_lexer->resetPeek();
} else {
// Peek beyond the PathExpression (or InputParameter)
$peek = $this->_lexer->peek();
while ($peek['value'] === '.') {
$this->_lexer->peek();
$peek = $this->_lexer->peek();
}
// Also peek beyond a NOT if there is one
if ($peek['type'] === Lexer::T_NOT) {
$peek = $this->_lexer->peek();
}
$token = $peek;
// We need to go even further in case of IS (differenciate between NULL and EMPTY)
$lookahead = $this->_lexer->peek();
// Also peek beyond a NOT if there is one
if ($lookahead['type'] === Lexer::T_NOT) {
$lookahead = $this->_lexer->peek();
}
$this->_lexer->resetPeek();
}
}
switch ($token['type']) {
case Lexer::T_BETWEEN:
return $this->BetweenExpression();
case Lexer::T_LIKE:
return $this->LikeExpression();
case Lexer::T_IN:
return $this->InExpression();
case Lexer::T_INSTANCE:
return $this->InstanceOfExpression();
case Lexer::T_IS:
if ($lookahead['type'] == Lexer::T_NULL) {
return $this->NullComparisonExpression();
}
return $this->EmptyCollectionComparisonExpression();
case Lexer::T_MEMBER:
return $this->CollectionMemberExpression();
default:
return $this->ComparisonExpression();
}
}
/**
* EmptyCollectionComparisonExpression ::= CollectionValuedPathExpression "IS" ["NOT"] "EMPTY"
*
* @return \Doctrine\ORM\Query\AST\EmptyCollectionComparisonExpression
*/
public function EmptyCollectionComparisonExpression()
{
$emptyColletionCompExpr = new AST\EmptyCollectionComparisonExpression(
$this->CollectionValuedPathExpression()
);
$this->match(Lexer::T_IS);
if ($this->_lexer->isNextToken(Lexer::T_NOT)) {
$this->match(Lexer::T_NOT);
$emptyColletionCompExpr->not = true;
}
$this->match(Lexer::T_EMPTY);
return $emptyColletionCompExpr;
}
/**
* CollectionMemberExpression ::= EntityExpression ["NOT"] "MEMBER" ["OF"] CollectionValuedPathExpression
*
* EntityExpression ::= SingleValuedAssociationPathExpression | SimpleEntityExpression
* SimpleEntityExpression ::= IdentificationVariable | InputParameter
*
* @return \Doctrine\ORM\Query\AST\CollectionMemberExpression
*/
public function CollectionMemberExpression()
{
$not = false;
$entityExpr = $this->EntityExpression();
if ($this->_lexer->isNextToken(Lexer::T_NOT)) {
$this->match(Lexer::T_NOT);
$not = true;
}
$this->match(Lexer::T_MEMBER);
if ($this->_lexer->isNextToken(Lexer::T_OF)) {
$this->match(Lexer::T_OF);
}
$collMemberExpr = new AST\CollectionMemberExpression(
$entityExpr, $this->CollectionValuedPathExpression()
);
$collMemberExpr->not = $not;
return $collMemberExpr;
}
/**
* Literal ::= string | char | integer | float | boolean
*
* @return string
*/
public function Literal()
{
switch ($this->_lexer->lookahead['type']) {
case Lexer::T_STRING:
$this->match(Lexer::T_STRING);
return new AST\Literal(AST\Literal::STRING, $this->_lexer->token['value']);
case Lexer::T_INTEGER:
case Lexer::T_FLOAT:
$this->match(
$this->_lexer->isNextToken(Lexer::T_INTEGER) ? Lexer::T_INTEGER : Lexer::T_FLOAT
);
return new AST\Literal(AST\Literal::NUMERIC, $this->_lexer->token['value']);
case Lexer::T_TRUE:
case Lexer::T_FALSE:
$this->match(
$this->_lexer->isNextToken(Lexer::T_TRUE) ? Lexer::T_TRUE : Lexer::T_FALSE
);
return new AST\Literal(AST\Literal::BOOLEAN, $this->_lexer->token['value']);
default:
$this->syntaxError('Literal');
}
}
/**
* InParameter ::= Literal | InputParameter
*
* @return string | \Doctrine\ORM\Query\AST\InputParameter
*/
public function InParameter()
{
if ($this->_lexer->lookahead['type'] == Lexer::T_INPUT_PARAMETER) {
return $this->InputParameter();
}
return $this->Literal();
}
/**
* InputParameter ::= PositionalParameter | NamedParameter
*
* @return \Doctrine\ORM\Query\AST\InputParameter
*/
public function InputParameter()
{
$this->match(Lexer::T_INPUT_PARAMETER);
return new AST\InputParameter($this->_lexer->token['value']);
}
/**
* ArithmeticExpression ::= SimpleArithmeticExpression | "(" Subselect ")"
*
* @return \Doctrine\ORM\Query\AST\ArithmeticExpression
*/
public function ArithmeticExpression()
{
$expr = new AST\ArithmeticExpression;
if ($this->_lexer->isNextToken(Lexer::T_OPEN_PARENTHESIS)) {
$peek = $this->_lexer->glimpse();
if ($peek['type'] === Lexer::T_SELECT) {
$this->match(Lexer::T_OPEN_PARENTHESIS);
$expr->subselect = $this->Subselect();
$this->match(Lexer::T_CLOSE_PARENTHESIS);
return $expr;
}
}
$expr->simpleArithmeticExpression = $this->SimpleArithmeticExpression();
return $expr;
}
/**
* SimpleArithmeticExpression ::= ArithmeticTerm {("+" | "-") ArithmeticTerm}*
*
* @return \Doctrine\ORM\Query\AST\SimpleArithmeticExpression
*/
public function SimpleArithmeticExpression()
{
$terms = array();
$terms[] = $this->ArithmeticTerm();
while (($isPlus = $this->_lexer->isNextToken(Lexer::T_PLUS)) || $this->_lexer->isNextToken(Lexer::T_MINUS)) {
$this->match(($isPlus) ? Lexer::T_PLUS : Lexer::T_MINUS);
$terms[] = $this->_lexer->token['value'];
$terms[] = $this->ArithmeticTerm();
}
// Phase 1 AST optimization: Prevent AST\SimpleArithmeticExpression
// if only one AST\ArithmeticTerm is defined
if (count($terms) == 1) {
return $terms[0];
}
return new AST\SimpleArithmeticExpression($terms);
}
/**
* ArithmeticTerm ::= ArithmeticFactor {("*" | "/") ArithmeticFactor}*
*
* @return \Doctrine\ORM\Query\AST\ArithmeticTerm
*/
public function ArithmeticTerm()
{
$factors = array();
$factors[] = $this->ArithmeticFactor();
while (($isMult = $this->_lexer->isNextToken(Lexer::T_MULTIPLY)) || $this->_lexer->isNextToken(Lexer::T_DIVIDE)) {
$this->match(($isMult) ? Lexer::T_MULTIPLY : Lexer::T_DIVIDE);
$factors[] = $this->_lexer->token['value'];
$factors[] = $this->ArithmeticFactor();
}
// Phase 1 AST optimization: Prevent AST\ArithmeticTerm
// if only one AST\ArithmeticFactor is defined
if (count($factors) == 1) {
return $factors[0];
}
return new AST\ArithmeticTerm($factors);
}
/**
* ArithmeticFactor ::= [("+" | "-")] ArithmeticPrimary
*
* @return \Doctrine\ORM\Query\AST\ArithmeticFactor
*/
public function ArithmeticFactor()
{
$sign = null;
if (($isPlus = $this->_lexer->isNextToken(Lexer::T_PLUS)) || $this->_lexer->isNextToken(Lexer::T_MINUS)) {
$this->match(($isPlus) ? Lexer::T_PLUS : Lexer::T_MINUS);
$sign = $isPlus;
}
$primary = $this->ArithmeticPrimary();
// Phase 1 AST optimization: Prevent AST\ArithmeticFactor
// if only one AST\ArithmeticPrimary is defined
if ($sign === null) {
return $primary;
}
return new AST\ArithmeticFactor($primary, $sign);
}
/**
* ArithmeticPrimary ::= SingleValuedPathExpression | Literal | "(" SimpleArithmeticExpression ")"
* | FunctionsReturningNumerics | AggregateExpression | FunctionsReturningStrings
* | FunctionsReturningDatetime | IdentificationVariable | ResultVariable
* | InputParameter | CaseExpression
*/
public function ArithmeticPrimary()
{
if ($this->_lexer->isNextToken(Lexer::T_OPEN_PARENTHESIS)) {
$this->match(Lexer::T_OPEN_PARENTHESIS);
$expr = $this->SimpleArithmeticExpression();
$this->match(Lexer::T_CLOSE_PARENTHESIS);
return $expr;
}
switch ($this->_lexer->lookahead['type']) {
case Lexer::T_COALESCE:
case Lexer::T_NULLIF:
case Lexer::T_CASE:
return $this->CaseExpression();
case Lexer::T_IDENTIFIER:
$peek = $this->_lexer->glimpse();
if ($peek['value'] == '(') {
return $this->FunctionDeclaration();
}
if ($peek['value'] == '.') {
return $this->SingleValuedPathExpression();
}
if (isset($this->_queryComponents[$this->_lexer->lookahead['value']]['resultVariable'])) {
return $this->ResultVariable();
}
return $this->StateFieldPathExpression();
case Lexer::T_INPUT_PARAMETER:
return $this->InputParameter();
default:
$peek = $this->_lexer->glimpse();
if ($peek['value'] == '(') {
if ($this->_isAggregateFunction($this->_lexer->lookahead['type'])) {
return $this->AggregateExpression();
}
return $this->FunctionDeclaration();
}
return $this->Literal();
}
}
/**
* StringExpression ::= StringPrimary | "(" Subselect ")"
*
* @return \Doctrine\ORM\Query\AST\StringPrimary |
* \Doctrine]ORM\Query\AST\Subselect
*/
public function StringExpression()
{
if ($this->_lexer->isNextToken(Lexer::T_OPEN_PARENTHESIS)) {
$peek = $this->_lexer->glimpse();
if ($peek['type'] === Lexer::T_SELECT) {
$this->match(Lexer::T_OPEN_PARENTHESIS);
$expr = $this->Subselect();
$this->match(Lexer::T_CLOSE_PARENTHESIS);
return $expr;
}
}
return $this->StringPrimary();
}
/**
* StringPrimary ::= StateFieldPathExpression | string | InputParameter | FunctionsReturningStrings | AggregateExpression | CaseExpression
*/
public function StringPrimary()
{
$lookaheadType = $this->_lexer->lookahead['type'];
switch ($lookaheadType) {
case Lexer::T_IDENTIFIER:
$peek = $this->_lexer->glimpse();
if ($peek['value'] == '.') {
return $this->StateFieldPathExpression();
}
if ($peek['value'] == '(') {
// do NOT directly go to FunctionsReturningString() because it doesnt check for custom functions.
return $this->FunctionDeclaration();
}
$this->syntaxError("'.' or '('");
break;
case Lexer::T_STRING:
$this->match(Lexer::T_STRING);
return new AST\Literal(AST\Literal::STRING, $this->_lexer->token['value']);
case Lexer::T_INPUT_PARAMETER:
return $this->InputParameter();
case Lexer::T_CASE:
case Lexer::T_COALESCE:
case Lexer::T_NULLIF:
return $this->CaseExpression();
default:
if ($this->_isAggregateFunction($lookaheadType)) {
return $this->AggregateExpression();
}
}
$this->syntaxError(
'StateFieldPathExpression | string | InputParameter | FunctionsReturningStrings | AggregateExpression'
);
}
/**
* EntityExpression ::= SingleValuedAssociationPathExpression | SimpleEntityExpression
*
* @return \Doctrine\ORM\Query\AST\SingleValuedAssociationPathExpression |
* \Doctrine\ORM\Query\AST\SimpleEntityExpression
*/
public function EntityExpression()
{
$glimpse = $this->_lexer->glimpse();
if ($this->_lexer->isNextToken(Lexer::T_IDENTIFIER) && $glimpse['value'] === '.') {
return $this->SingleValuedAssociationPathExpression();
}
return $this->SimpleEntityExpression();
}
/**
* SimpleEntityExpression ::= IdentificationVariable | InputParameter
*
* @return string | \Doctrine\ORM\Query\AST\InputParameter
*/
public function SimpleEntityExpression()
{
if ($this->_lexer->isNextToken(Lexer::T_INPUT_PARAMETER)) {
return $this->InputParameter();
}
return $this->StateFieldPathExpression();
}
/**
* AggregateExpression ::=
* ("AVG" | "MAX" | "MIN" | "SUM") "(" ["DISTINCT"] StateFieldPathExpression ")" |
* "COUNT" "(" ["DISTINCT"] (IdentificationVariable | SingleValuedPathExpression) ")"
*
* @return \Doctrine\ORM\Query\AST\AggregateExpression
*/
public function AggregateExpression()
{
$lookaheadType = $this->_lexer->lookahead['type'];
$isDistinct = false;
if ( ! in_array($lookaheadType, array(Lexer::T_COUNT, Lexer::T_AVG, Lexer::T_MAX, Lexer::T_MIN, Lexer::T_SUM))) {
$this->syntaxError('One of: MAX, MIN, AVG, SUM, COUNT');
}
$this->match($lookaheadType);
$functionName = $this->_lexer->token['value'];
$this->match(Lexer::T_OPEN_PARENTHESIS);
if ($this->_lexer->isNextToken(Lexer::T_DISTINCT)) {
$this->match(Lexer::T_DISTINCT);
$isDistinct = true;
}
$pathExp = ($lookaheadType === Lexer::T_COUNT)
? $this->SingleValuedPathExpression()
: $this->SimpleArithmeticExpression();
$this->match(Lexer::T_CLOSE_PARENTHESIS);
return new AST\AggregateExpression($functionName, $pathExp, $isDistinct);
}
/**
* QuantifiedExpression ::= ("ALL" | "ANY" | "SOME") "(" Subselect ")"
*
* @return \Doctrine\ORM\Query\AST\QuantifiedExpression
*/
public function QuantifiedExpression()
{
$lookaheadType = $this->_lexer->lookahead['type'];
$value = $this->_lexer->lookahead['value'];
if ( ! in_array($lookaheadType, array(Lexer::T_ALL, Lexer::T_ANY, Lexer::T_SOME))) {
$this->syntaxError('ALL, ANY or SOME');
}
$this->match($lookaheadType);
$this->match(Lexer::T_OPEN_PARENTHESIS);
$qExpr = new AST\QuantifiedExpression($this->Subselect());
$qExpr->type = $value;
$this->match(Lexer::T_CLOSE_PARENTHESIS);
return $qExpr;
}
/**
* BetweenExpression ::= ArithmeticExpression ["NOT"] "BETWEEN" ArithmeticExpression "AND" ArithmeticExpression
*
* @return \Doctrine\ORM\Query\AST\BetweenExpression
*/
public function BetweenExpression()
{
$not = false;
$arithExpr1 = $this->ArithmeticExpression();
if ($this->_lexer->isNextToken(Lexer::T_NOT)) {
$this->match(Lexer::T_NOT);
$not = true;
}
$this->match(Lexer::T_BETWEEN);
$arithExpr2 = $this->ArithmeticExpression();
$this->match(Lexer::T_AND);
$arithExpr3 = $this->ArithmeticExpression();
$betweenExpr = new AST\BetweenExpression($arithExpr1, $arithExpr2, $arithExpr3);
$betweenExpr->not = $not;
return $betweenExpr;
}
/**
* ComparisonExpression ::= ArithmeticExpression ComparisonOperator ( QuantifiedExpression | ArithmeticExpression )
*
* @return \Doctrine\ORM\Query\AST\ComparisonExpression
*/
public function ComparisonExpression()
{
$this->_lexer->glimpse();
$leftExpr = $this->ArithmeticExpression();
$operator = $this->ComparisonOperator();
$rightExpr = ($this->_isNextAllAnySome())
? $this->QuantifiedExpression()
: $this->ArithmeticExpression();
return new AST\ComparisonExpression($leftExpr, $operator, $rightExpr);
}
/**
* InExpression ::= SingleValuedPathExpression ["NOT"] "IN" "(" (InParameter {"," InParameter}* | Subselect) ")"
*
* @return \Doctrine\ORM\Query\AST\InExpression
*/
public function InExpression()
{
$inExpression = new AST\InExpression($this->ArithmeticExpression());
if ($this->_lexer->isNextToken(Lexer::T_NOT)) {
$this->match(Lexer::T_NOT);
$inExpression->not = true;
}
$this->match(Lexer::T_IN);
$this->match(Lexer::T_OPEN_PARENTHESIS);
if ($this->_lexer->isNextToken(Lexer::T_SELECT)) {
$inExpression->subselect = $this->Subselect();
} else {
$literals = array();
$literals[] = $this->InParameter();
while ($this->_lexer->isNextToken(Lexer::T_COMMA)) {
$this->match(Lexer::T_COMMA);
$literals[] = $this->InParameter();
}
$inExpression->literals = $literals;
}
$this->match(Lexer::T_CLOSE_PARENTHESIS);
return $inExpression;
}
/**
* InstanceOfExpression ::= IdentificationVariable ["NOT"] "INSTANCE" ["OF"] (InstanceOfParameter | "(" InstanceOfParameter {"," InstanceOfParameter}* ")")
*
* @return \Doctrine\ORM\Query\AST\InstanceOfExpression
*/
public function InstanceOfExpression()
{
$instanceOfExpression = new AST\InstanceOfExpression($this->IdentificationVariable());
if ($this->_lexer->isNextToken(Lexer::T_NOT)) {
$this->match(Lexer::T_NOT);
$instanceOfExpression->not = true;
}
$this->match(Lexer::T_INSTANCE);
$this->match(Lexer::T_OF);
$exprValues = array();
if ($this->_lexer->isNextToken(Lexer::T_OPEN_PARENTHESIS)) {
$this->match(Lexer::T_OPEN_PARENTHESIS);
$exprValues[] = $this->InstanceOfParameter();
while ($this->_lexer->isNextToken(Lexer::T_COMMA)) {
$this->match(Lexer::T_COMMA);
$exprValues[] = $this->InstanceOfParameter();
}
$this->match(Lexer::T_CLOSE_PARENTHESIS);
$instanceOfExpression->value = $exprValues;
return $instanceOfExpression;
}
$exprValues[] = $this->InstanceOfParameter();
$instanceOfExpression->value = $exprValues;
return $instanceOfExpression;
}
/**
* InstanceOfParameter ::= AbstractSchemaName | InputParameter
*
* @return mixed
*/
public function InstanceOfParameter()
{
if ($this->_lexer->isNextToken(Lexer::T_INPUT_PARAMETER)) {
$this->match(Lexer::T_INPUT_PARAMETER);
return new AST\InputParameter($this->_lexer->token['value']);
}
return $this->AliasIdentificationVariable();
}
/**
* LikeExpression ::= StringExpression ["NOT"] "LIKE" StringPrimary ["ESCAPE" char]
*
* @return \Doctrine\ORM\Query\AST\LikeExpression
*/
public function LikeExpression()
{
$stringExpr = $this->StringExpression();
$not = false;
if ($this->_lexer->isNextToken(Lexer::T_NOT)) {
$this->match(Lexer::T_NOT);
$not = true;
}
$this->match(Lexer::T_LIKE);
if ($this->_lexer->isNextToken(Lexer::T_INPUT_PARAMETER)) {
$this->match(Lexer::T_INPUT_PARAMETER);
$stringPattern = new AST\InputParameter($this->_lexer->token['value']);
} else {
$stringPattern = $this->StringPrimary();
}
$escapeChar = null;
if ($this->_lexer->lookahead['type'] === Lexer::T_ESCAPE) {
$this->match(Lexer::T_ESCAPE);
$this->match(Lexer::T_STRING);
$escapeChar = new AST\Literal(AST\Literal::STRING, $this->_lexer->token['value']);
}
$likeExpr = new AST\LikeExpression($stringExpr, $stringPattern, $escapeChar);
$likeExpr->not = $not;
return $likeExpr;
}
/**
* NullComparisonExpression ::= (SingleValuedPathExpression | InputParameter) "IS" ["NOT"] "NULL"
*
* @return \Doctrine\ORM\Query\AST\NullComparisonExpression
*/
public function NullComparisonExpression()
{
if ($this->_lexer->isNextToken(Lexer::T_INPUT_PARAMETER)) {
$this->match(Lexer::T_INPUT_PARAMETER);
$expr = new AST\InputParameter($this->_lexer->token['value']);
} else {
$expr = $this->SingleValuedPathExpression();
}
$nullCompExpr = new AST\NullComparisonExpression($expr);
$this->match(Lexer::T_IS);
if ($this->_lexer->isNextToken(Lexer::T_NOT)) {
$this->match(Lexer::T_NOT);
$nullCompExpr->not = true;
}
$this->match(Lexer::T_NULL);
return $nullCompExpr;
}
/**
* ExistsExpression ::= ["NOT"] "EXISTS" "(" Subselect ")"
*
* @return \Doctrine\ORM\Query\AST\ExistsExpression
*/
public function ExistsExpression()
{
$not = false;
if ($this->_lexer->isNextToken(Lexer::T_NOT)) {
$this->match(Lexer::T_NOT);
$not = true;
}
$this->match(Lexer::T_EXISTS);
$this->match(Lexer::T_OPEN_PARENTHESIS);
$existsExpression = new AST\ExistsExpression($this->Subselect());
$existsExpression->not = $not;
$this->match(Lexer::T_CLOSE_PARENTHESIS);
return $existsExpression;
}
/**
* ComparisonOperator ::= "=" | "<" | "<=" | "<>" | ">" | ">=" | "!="
*
* @return string
*/
public function ComparisonOperator()
{
switch ($this->_lexer->lookahead['value']) {
case '=':
$this->match(Lexer::T_EQUALS);
return '=';
case '<':
$this->match(Lexer::T_LOWER_THAN);
$operator = '<';
if ($this->_lexer->isNextToken(Lexer::T_EQUALS)) {
$this->match(Lexer::T_EQUALS);
$operator .= '=';
} else if ($this->_lexer->isNextToken(Lexer::T_GREATER_THAN)) {
$this->match(Lexer::T_GREATER_THAN);
$operator .= '>';
}
return $operator;
case '>':
$this->match(Lexer::T_GREATER_THAN);
$operator = '>';
if ($this->_lexer->isNextToken(Lexer::T_EQUALS)) {
$this->match(Lexer::T_EQUALS);
$operator .= '=';
}
return $operator;
case '!':
$this->match(Lexer::T_NEGATE);
$this->match(Lexer::T_EQUALS);
return '<>';
default:
$this->syntaxError('=, <, <=, <>, >, >=, !=');
}
}
/**
* FunctionDeclaration ::= FunctionsReturningStrings | FunctionsReturningNumerics | FunctionsReturningDatetime
*/
public function FunctionDeclaration()
{
$token = $this->_lexer->lookahead;
$funcName = strtolower($token['value']);
// Check for built-in functions first!
switch (true) {
case (isset(self::$_STRING_FUNCTIONS[$funcName])):
return $this->FunctionsReturningStrings();
case (isset(self::$_NUMERIC_FUNCTIONS[$funcName])):
return $this->FunctionsReturningNumerics();
case (isset(self::$_DATETIME_FUNCTIONS[$funcName])):
return $this->FunctionsReturningDatetime();
default:
return $this->CustomFunctionDeclaration();
}
}
/**
* Helper function for FunctionDeclaration grammar rule
*/
private function CustomFunctionDeclaration()
{
$token = $this->_lexer->lookahead;
$funcName = strtolower($token['value']);
// Check for custom functions afterwards
$config = $this->_em->getConfiguration();
switch (true) {
case ($config->getCustomStringFunction($funcName) !== null):
return $this->CustomFunctionsReturningStrings();
case ($config->getCustomNumericFunction($funcName) !== null):
return $this->CustomFunctionsReturningNumerics();
case ($config->getCustomDatetimeFunction($funcName) !== null):
return $this->CustomFunctionsReturningDatetime();
default:
$this->syntaxError('known function', $token);
}
}
/**
* FunctionsReturningNumerics ::=
* "LENGTH" "(" StringPrimary ")" |
* "LOCATE" "(" StringPrimary "," StringPrimary ["," SimpleArithmeticExpression]")" |
* "ABS" "(" SimpleArithmeticExpression ")" |
* "SQRT" "(" SimpleArithmeticExpression ")" |
* "MOD" "(" SimpleArithmeticExpression "," SimpleArithmeticExpression ")" |
* "SIZE" "(" CollectionValuedPathExpression ")"
*/
public function FunctionsReturningNumerics()
{
$funcNameLower = strtolower($this->_lexer->lookahead['value']);
$funcClass = self::$_NUMERIC_FUNCTIONS[$funcNameLower];
$function = new $funcClass($funcNameLower);
$function->parse($this);
return $function;
}
public function CustomFunctionsReturningNumerics()
{
// getCustomNumericFunction is case-insensitive
$funcName = strtolower($this->_lexer->lookahead['value']);
$funcClass = $this->_em->getConfiguration()->getCustomNumericFunction($funcName);
$function = new $funcClass($funcName);
$function->parse($this);
return $function;
}
/**
* FunctionsReturningDateTime ::= "CURRENT_DATE" | "CURRENT_TIME" | "CURRENT_TIMESTAMP"
*/
public function FunctionsReturningDatetime()
{
$funcNameLower = strtolower($this->_lexer->lookahead['value']);
$funcClass = self::$_DATETIME_FUNCTIONS[$funcNameLower];
$function = new $funcClass($funcNameLower);
$function->parse($this);
return $function;
}
public function CustomFunctionsReturningDatetime()
{
// getCustomDatetimeFunction is case-insensitive
$funcName = $this->_lexer->lookahead['value'];
$funcClass = $this->_em->getConfiguration()->getCustomDatetimeFunction($funcName);
$function = new $funcClass($funcName);
$function->parse($this);
return $function;
}
/**
* FunctionsReturningStrings ::=
* "CONCAT" "(" StringPrimary "," StringPrimary ")" |
* "SUBSTRING" "(" StringPrimary "," SimpleArithmeticExpression "," SimpleArithmeticExpression ")" |
* "TRIM" "(" [["LEADING" | "TRAILING" | "BOTH"] [char] "FROM"] StringPrimary ")" |
* "LOWER" "(" StringPrimary ")" |
* "UPPER" "(" StringPrimary ")"
*/
public function FunctionsReturningStrings()
{
$funcNameLower = strtolower($this->_lexer->lookahead['value']);
$funcClass = self::$_STRING_FUNCTIONS[$funcNameLower];
$function = new $funcClass($funcNameLower);
$function->parse($this);
return $function;
}
public function CustomFunctionsReturningStrings()
{
// getCustomStringFunction is case-insensitive
$funcName = $this->_lexer->lookahead['value'];
$funcClass = $this->_em->getConfiguration()->getCustomStringFunction($funcName);
$function = new $funcClass($funcName);
$function->parse($this);
return $function;
}
}
DoctrineORM-2.3.3/Doctrine/ORM/Query/ParserResult.php 0000644 0001751 0001751 00000010232 12143374607 022434 0 ustar beberlei beberlei .
*/
namespace Doctrine\ORM\Query;
/**
* Encapsulates the resulting components from a DQL query parsing process that
* can be serialized.
*
* @author Guilherme Blanco
* @author Janne Vanhala
* @author Roman Borschel
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link http://www.doctrine-project.org
* @since 2.0
*/
class ParserResult
{
/**
* The SQL executor used for executing the SQL.
*
* @var \Doctrine\ORM\Query\Exec\AbstractSqlExecutor
*/
private $_sqlExecutor;
/**
* The ResultSetMapping that describes how to map the SQL result set.
*
* @var \Doctrine\ORM\Query\ResultSetMapping
*/
private $_resultSetMapping;
/**
* The mappings of DQL parameter names/positions to SQL parameter positions.
*
* @var array
*/
private $_parameterMappings = array();
/**
* Initializes a new instance of the ParserResult class.
* The new instance is initialized with an empty ResultSetMapping.
*/
public function __construct()
{
$this->_resultSetMapping = new ResultSetMapping;
}
/**
* Gets the ResultSetMapping for the parsed query.
*
* @return ResultSetMapping The result set mapping of the parsed query or NULL
* if the query is not a SELECT query.
*/
public function getResultSetMapping()
{
return $this->_resultSetMapping;
}
/**
* Sets the ResultSetMapping of the parsed query.
*
* @param ResultSetMapping $rsm
*/
public function setResultSetMapping(ResultSetMapping $rsm)
{
$this->_resultSetMapping = $rsm;
}
/**
* Sets the SQL executor that should be used for this ParserResult.
*
* @param \Doctrine\ORM\Query\Exec\AbstractSqlExecutor $executor
*/
public function setSqlExecutor($executor)
{
$this->_sqlExecutor = $executor;
}
/**
* Gets the SQL executor used by this ParserResult.
*
* @return \Doctrine\ORM\Query\Exec\AbstractSqlExecutor
*/
public function getSqlExecutor()
{
return $this->_sqlExecutor;
}
/**
* Adds a DQL to SQL parameter mapping. One DQL parameter name/position can map to
* several SQL parameter positions.
*
* @param string|integer $dqlPosition
* @param integer $sqlPosition
*/
public function addParameterMapping($dqlPosition, $sqlPosition)
{
$this->_parameterMappings[$dqlPosition][] = $sqlPosition;
}
/**
* Gets all DQL to SQL parameter mappings.
*
* @return array The parameter mappings.
*/
public function getParameterMappings()
{
return $this->_parameterMappings;
}
/**
* Gets the SQL parameter positions for a DQL parameter name/position.
*
* @param string|integer $dqlPosition The name or position of the DQL parameter.
* @return array The positions of the corresponding SQL parameters.
*/
public function getSqlParameterPositions($dqlPosition)
{
return $this->_parameterMappings[$dqlPosition];
}
}
DoctrineORM-2.3.3/Doctrine/ORM/Query/Printer.php 0000644 0001751 0001751 00000005173 12143374607 021434 0 ustar beberlei beberlei .
*/
namespace Doctrine\ORM\Query;
/**
* A parse tree printer for Doctrine Query Language parser.
*
* @author Janne Vanhala
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link http://www.phpdoctrine.org
* @since 2.0
*/
class Printer
{
/**
* Current indentation level
*
* @var int
*/
protected $_indent = 0;
/**
* Defines whether parse tree is printed (default, false) or not (true).
*
* @var bool
*/
protected $_silent;
/**
* Constructs a new parse tree printer.
*
* @param bool $silent Parse tree will not be printed if true.
*/
public function __construct($silent = false)
{
$this->_silent = $silent;
}
/**
* Prints an opening parenthesis followed by production name and increases
* indentation level by one.
*
* This method is called before executing a production.
*
* @param string $name production name
*/
public function startProduction($name)
{
$this->println('(' . $name);
$this->_indent++;
}
/**
* Decreases indentation level by one and prints a closing parenthesis.
*
* This method is called after executing a production.
*/
public function endProduction()
{
$this->_indent--;
$this->println(')');
}
/**
* Prints text indented with spaces depending on current indentation level.
*
* @param string $str text
*/
public function println($str)
{
if ( ! $this->_silent) {
echo str_repeat(' ', $this->_indent), $str, "\n";
}
}
}
DoctrineORM-2.3.3/Doctrine/ORM/Query/QueryException.php 0000644 0001751 0001751 00000012611 12143374607 022770 0 ustar beberlei beberlei .
*/
namespace Doctrine\ORM\Query;
use Doctrine\ORM\Query\AST\PathExpression;
/**
* Description of QueryException
*
*
* @link www.doctrine-project.org
* @since 2.0
* @author Guilherme Blanco
* @author Jonathan Wage
* @author Roman Borschel
* @author Benjamin Eberlei
*/
class QueryException extends \Doctrine\ORM\ORMException
{
public static function dqlError($dql)
{
return new self($dql);
}
public static function syntaxError($message, $previous = null)
{
return new self('[Syntax Error] ' . $message, 0, $previous);
}
public static function semanticalError($message, $previous = null)
{
return new self('[Semantical Error] ' . $message, 0, $previous);
}
public static function invalidLockMode()
{
return new self('Invalid lock mode hint provided.');
}
public static function invalidParameterType($expected, $received)
{
return new self('Invalid parameter type, ' . $received . ' given, but ' . $expected . ' expected.');
}
public static function invalidParameterPosition($pos)
{
return new self('Invalid parameter position: ' . $pos);
}
public static function invalidParameterNumber()
{
return new self("Invalid parameter number: number of bound variables does not match number of tokens");
}
public static function invalidParameterFormat($value)
{
return new self('Invalid parameter format, '.$value.' given, but : or ? expected.');
}
public static function unknownParameter($key)
{
return new self("Invalid parameter: token ".$key." is not defined in the query.");
}
public static function parameterTypeMissmatch()
{
return new self("DQL Query parameter and type numbers missmatch, but have to be exactly equal.");
}
public static function invalidPathExpression($pathExpr)
{
return new self(
"Invalid PathExpression '" . $pathExpr->identificationVariable . "." . $pathExpr->field . "'."
);
}
public static function invalidLiteral($literal) {
return new self("Invalid literal '$literal'");
}
/**
* @param array $assoc
*/
public static function iterateWithFetchJoinCollectionNotAllowed($assoc)
{
return new self(
"Invalid query operation: Not allowed to iterate over fetch join collections ".
"in class ".$assoc['sourceEntity']." assocation ".$assoc['fieldName']
);
}
public static function partialObjectsAreDangerous()
{
return new self(
"Loading partial objects is dangerous. Fetch full objects or consider " .
"using a different fetch mode. If you really want partial objects, " .
"set the doctrine.forcePartialLoad query hint to TRUE."
);
}
public static function overwritingJoinConditionsNotYetSupported($assoc)
{
return new self(
"Unsupported query operation: It is not yet possible to overwrite the join ".
"conditions in class ".$assoc['sourceEntityName']." assocation ".$assoc['fieldName'].". ".
"Use WITH to append additional join conditions to the association."
);
}
public static function associationPathInverseSideNotSupported()
{
return new self(
"A single-valued association path expression to an inverse side is not supported".
" in DQL queries. Use an explicit join instead."
);
}
public static function iterateWithFetchJoinNotAllowed($assoc) {
return new self(
"Iterate with fetch join in class " . $assoc['sourceEntity'] .
" using association " . $assoc['fieldName'] . " not allowed."
);
}
public static function associationPathCompositeKeyNotSupported()
{
return new self(
"A single-valued association path expression to an entity with a composite primary ".
"key is not supported. Explicitly name the components of the composite primary key ".
"in the query."
);
}
public static function instanceOfUnrelatedClass($className, $rootClass)
{
return new self("Cannot check if a child of '" . $rootClass . "' is instanceof '" . $className . "', " .
"inheritance hierachy exists between these two classes.");
}
}
DoctrineORM-2.3.3/Doctrine/ORM/Query/ResultSetMapping.php 0000644 0001751 0001751 00000035222 12143374607 023255 0 ustar beberlei beberlei .
*/
namespace Doctrine\ORM\Query;
/**
* A ResultSetMapping describes how a result set of an SQL query maps to a Doctrine result.
*
* IMPORTANT NOTE:
* The properties of this class are only public for fast internal READ access and to (drastically)
* reduce the size of serialized instances for more effective caching due to better (un-)serialization
* performance.
*
* Users should use the public methods.
*
* @author Roman Borschel
* @since 2.0
* @todo Think about whether the number of lookup maps can be reduced.
*/
class ResultSetMapping
{
/**
* @ignore
* @var boolean Whether the result is mixed (contains scalar values together with field values).
*/
public $isMixed = false;
/**
* @ignore
* @var array Maps alias names to class names.
*/
public $aliasMap = array();
/**
* @ignore
* @var array Maps alias names to related association field names.
*/
public $relationMap = array();
/**
* @ignore
* @var array Maps alias names to parent alias names.
*/
public $parentAliasMap = array();
/**
* @ignore
* @var array Maps column names in the result set to field names for each class.
*/
public $fieldMappings = array();
/**
* @ignore
* @var array Maps column names in the result set to the alias/field name to use in the mapped result.
*/
public $scalarMappings = array();
/**
* @ignore
* @var array Maps column names in the result set to the alias/field type to use in the mapped result.
*/
public $typeMappings = array();
/**
* @ignore
* @var array Maps entities in the result set to the alias name to use in the mapped result.
*/
public $entityMappings = array();
/**
* @ignore
* @var array Maps column names of meta columns (foreign keys, discriminator columns, ...) to field names.
*/
public $metaMappings = array();
/**
* @ignore
* @var array Maps column names in the result set to the alias they belong to.
*/
public $columnOwnerMap = array();
/**
* @ignore
* @var array List of columns in the result set that are used as discriminator columns.
*/
public $discriminatorColumns = array();
/**
* @ignore
* @var array Maps alias names to field names that should be used for indexing.
*/
public $indexByMap = array();
/**
* @ignore
* @var array Map from column names to class names that declare the field the column is mapped to.
*/
public $declaringClasses = array();
/**
* @var array This is necessary to hydrate derivate foreign keys correctly.
*/
public $isIdentifierColumn = array();
/**
* Adds an entity result to this ResultSetMapping.
*
* @param string $class The class name of the entity.
* @param string $alias The alias for the class. The alias must be unique among all entity
* results or joined entity results within this ResultSetMapping.
* @param string $resultAlias The result alias with which the entity result should be
* placed in the result structure.
* @return ResultSetMapping This ResultSetMapping instance.
* @todo Rename: addRootEntity
*/
public function addEntityResult($class, $alias, $resultAlias = null)
{
$this->aliasMap[$alias] = $class;
$this->entityMappings[$alias] = $resultAlias;
if ($resultAlias !== null) {
$this->isMixed = true;
}
return $this;
}
/**
* Sets a discriminator column for an entity result or joined entity result.
* The discriminator column will be used to determine the concrete class name to
* instantiate.
*
* @param string $alias The alias of the entity result or joined entity result the discriminator
* column should be used for.
* @param string $discrColumn The name of the discriminator column in the SQL result set.
* @return ResultSetMapping This ResultSetMapping instance.
* @todo Rename: addDiscriminatorColumn
*/
public function setDiscriminatorColumn($alias, $discrColumn)
{
$this->discriminatorColumns[$alias] = $discrColumn;
$this->columnOwnerMap[$discrColumn] = $alias;
return $this;
}
/**
* Sets a field to use for indexing an entity result or joined entity result.
*
* @param string $alias The alias of an entity result or joined entity result.
* @param string $fieldName The name of the field to use for indexing.
* @return ResultSetMapping This ResultSetMapping instance.
*/
public function addIndexBy($alias, $fieldName)
{
$found = false;
foreach ($this->fieldMappings as $columnName => $columnFieldName) {
if ( ! ($columnFieldName === $fieldName && $this->columnOwnerMap[$columnName] === $alias)) continue;
$this->addIndexByColumn($alias, $columnName);
$found = true;
break;
}
/* TODO: check if this exception can be put back, for now it's gone because of assumptions made by some ORM internals
if ( ! $found) {
$message = sprintf(
'Cannot add index by for DQL alias %s and field %s without calling addFieldResult() for them before.',
$alias,
$fieldName
);
throw new \LogicException($message);
}
*/
return $this;
}
/**
* Set to index by a scalar result column name
*
* @param $resultColumnName
* @return ResultSetMapping This ResultSetMapping instance.
*/
public function addIndexByScalar($resultColumnName)
{
$this->indexByMap['scalars'] = $resultColumnName;
return $this;
}
/**
* Sets a column to use for indexing an entity or joined entity result by the given alias name.
*
* @param $alias
* @param $resultColumnName
* @return ResultSetMapping This ResultSetMapping instance.
*/
public function addIndexByColumn($alias, $resultColumnName)
{
$this->indexByMap[$alias] = $resultColumnName;
return $this;
}
/**
* Checks whether an entity result or joined entity result with a given alias has
* a field set for indexing.
*
* @param string $alias
* @return boolean
* @todo Rename: isIndexed($alias)
*/
public function hasIndexBy($alias)
{
return isset($this->indexByMap[$alias]);
}
/**
* Checks whether the column with the given name is mapped as a field result
* as part of an entity result or joined entity result.
*
* @param string $columnName The name of the column in the SQL result set.
* @return boolean
* @todo Rename: isField
*/
public function isFieldResult($columnName)
{
return isset($this->fieldMappings[$columnName]);
}
/**
* Adds a field to the result that belongs to an entity or joined entity.
*
* @param string $alias The alias of the root entity or joined entity to which the field belongs.
* @param string $columnName The name of the column in the SQL result set.
* @param string $fieldName The name of the field on the declaring class.
* @param string $declaringClass The name of the class that declares/owns the specified field.
* When $alias refers to a superclass in a mapped hierarchy but
* the field $fieldName is defined on a subclass, specify that here.
* If not specified, the field is assumed to belong to the class
* designated by $alias.
* @return ResultSetMapping This ResultSetMapping instance.
* @todo Rename: addField
*/
public function addFieldResult($alias, $columnName, $fieldName, $declaringClass = null)
{
// column name (in result set) => field name
$this->fieldMappings[$columnName] = $fieldName;
// column name => alias of owner
$this->columnOwnerMap[$columnName] = $alias;
// field name => class name of declaring class
$this->declaringClasses[$columnName] = $declaringClass ?: $this->aliasMap[$alias];
if ( ! $this->isMixed && $this->scalarMappings) {
$this->isMixed = true;
}
return $this;
}
/**
* Adds a joined entity result.
*
* @param string $class The class name of the joined entity.
* @param string $alias The unique alias to use for the joined entity.
* @param string $parentAlias The alias of the entity result that is the parent of this joined result.
* @param object $relation The association field that connects the parent entity result with the joined entity result.
* @return ResultSetMapping This ResultSetMapping instance.
* @todo Rename: addJoinedEntity
*/
public function addJoinedEntityResult($class, $alias, $parentAlias, $relation)
{
$this->aliasMap[$alias] = $class;
$this->parentAliasMap[$alias] = $parentAlias;
$this->relationMap[$alias] = $relation;
return $this;
}
/**
* Adds a scalar result mapping.
*
* @param string $columnName The name of the column in the SQL result set.
* @param string $alias The result alias with which the scalar result should be placed in the result structure.
* @param string $type The column type
*
* @return ResultSetMapping This ResultSetMapping instance.
*
* @todo Rename: addScalar
*/
public function addScalarResult($columnName, $alias, $type = 'string')
{
$this->scalarMappings[$columnName] = $alias;
$this->typeMappings[$columnName] = $type;
if ( ! $this->isMixed && $this->fieldMappings) {
$this->isMixed = true;
}
return $this;
}
/**
* Checks whether a column with a given name is mapped as a scalar result.
*
* @param string $columName The name of the column in the SQL result set.
* @return boolean
* @todo Rename: isScalar
*/
public function isScalarResult($columnName)
{
return isset($this->scalarMappings[$columnName]);
}
/**
* Gets the name of the class of an entity result or joined entity result,
* identified by the given unique alias.
*
* @param string $alias
* @return string
*/
public function getClassName($alias)
{
return $this->aliasMap[$alias];
}
/**
* Gets the field alias for a column that is mapped as a scalar value.
*
* @param string $columnName The name of the column in the SQL result set.
* @return string
*/
public function getScalarAlias($columnName)
{
return $this->scalarMappings[$columnName];
}
/**
* Gets the name of the class that owns a field mapping for the specified column.
*
* @param string $columnName
* @return string
*/
public function getDeclaringClass($columnName)
{
return $this->declaringClasses[$columnName];
}
/**
*
* @param string $alias
* @return AssociationMapping
*/
public function getRelation($alias)
{
return $this->relationMap[$alias];
}
/**
*
* @param string $alias
* @return boolean
*/
public function isRelation($alias)
{
return isset($this->relationMap[$alias]);
}
/**
* Gets the alias of the class that owns a field mapping for the specified column.
*
* @param string $columnName
* @return string
*/
public function getEntityAlias($columnName)
{
return $this->columnOwnerMap[$columnName];
}
/**
* Gets the parent alias of the given alias.
*
* @param string $alias
* @return string
*/
public function getParentAlias($alias)
{
return $this->parentAliasMap[$alias];
}
/**
* Checks whether the given alias has a parent alias.
*
* @param string $alias
* @return boolean
*/
public function hasParentAlias($alias)
{
return isset($this->parentAliasMap[$alias]);
}
/**
* Gets the field name for a column name.
*
* @param string $columnName
* @return string
*/
public function getFieldName($columnName)
{
return $this->fieldMappings[$columnName];
}
/**
*
* @return array
*/
public function getAliasMap()
{
return $this->aliasMap;
}
/**
* Gets the number of different entities that appear in the mapped result.
*
* @return integer
*/
public function getEntityResultCount()
{
return count($this->aliasMap);
}
/**
* Checks whether this ResultSetMapping defines a mixed result.
* Mixed results can only occur in object and array (graph) hydration. In such a
* case a mixed result means that scalar values are mixed with objects/array in
* the result.
*
* @return boolean
*/
public function isMixedResult()
{
return $this->isMixed;
}
/**
* Adds a meta column (foreign key or discriminator column) to the result set.
*
* @param string $alias
* @param string $columnName
* @param string $fieldName
* @param bool
* @return ResultSetMapping This ResultSetMapping instance.
*/
public function addMetaResult($alias, $columnName, $fieldName, $isIdentifierColumn = false)
{
$this->metaMappings[$columnName] = $fieldName;
$this->columnOwnerMap[$columnName] = $alias;
if ($isIdentifierColumn) {
$this->isIdentifierColumn[$alias][$columnName] = true;
}
return $this;
}
}
DoctrineORM-2.3.3/Doctrine/ORM/Query/ResultSetMappingBuilder.php 0000644 0001751 0001751 00000026744 12143374607 024575 0 ustar beberlei beberlei .
*/
namespace Doctrine\ORM\Query;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\Mapping\ClassMetadataInfo;
/**
* A ResultSetMappingBuilder uses the EntityManager to automatically populate entity fields
*
* @author Michael Ridgway
* @since 2.1
*/
class ResultSetMappingBuilder extends ResultSetMapping
{
/**
* @var EntityManager
*/
private $em;
/**
* @param EntityManager
*/
public function __construct(EntityManager $em)
{
$this->em = $em;
}
/**
* Adds a root entity and all of its fields to the result set.
*
* @param string $class The class name of the root entity.
* @param string $alias The unique alias to use for the root entity.
* @param array $renamedColumns Columns that have been renamed (tableColumnName => queryColumnName)
*/
public function addRootEntityFromClassMetadata($class, $alias, $renamedColumns = array())
{
$this->addEntityResult($class, $alias);
$this->addAllClassFields($class, $alias, $renamedColumns);
}
/**
* Adds a joined entity and all of its fields to the result set.
*
* @param string $class The class name of the joined entity.
* @param string $alias The unique alias to use for the joined entity.
* @param string $parentAlias The alias of the entity result that is the parent of this joined result.
* @param object $relation The association field that connects the parent entity result with the joined entity result.
* @param array $renamedColumns Columns that have been renamed (tableColumnName => queryColumnName)
*/
public function addJoinedEntityFromClassMetadata($class, $alias, $parentAlias, $relation, $renamedColumns = array())
{
$this->addJoinedEntityResult($class, $alias, $parentAlias, $relation);
$this->addAllClassFields($class, $alias, $renamedColumns);
}
/**
* Adds all fields of the given class to the result set mapping (columns and meta fields)
*/
protected function addAllClassFields($class, $alias, $renamedColumns = array())
{
$classMetadata = $this->em->getClassMetadata($class);
if ($classMetadata->isInheritanceTypeSingleTable() || $classMetadata->isInheritanceTypeJoined()) {
throw new \InvalidArgumentException('ResultSetMapping builder does not currently support inheritance.');
}
$platform = $this->em->getConnection()->getDatabasePlatform();
foreach ($classMetadata->getColumnNames() as $columnName) {
$propertyName = $classMetadata->getFieldName($columnName);
if (isset($renamedColumns[$columnName])) {
$columnName = $renamedColumns[$columnName];
}
$columnName = $platform->getSQLResultCasing($columnName);
if (isset($this->fieldMappings[$columnName])) {
throw new \InvalidArgumentException("The column '$columnName' conflicts with another column in the mapper.");
}
$this->addFieldResult($alias, $columnName, $propertyName);
}
foreach ($classMetadata->associationMappings as $associationMapping) {
if ($associationMapping['isOwningSide'] && $associationMapping['type'] & ClassMetadataInfo::TO_ONE) {
foreach ($associationMapping['joinColumns'] as $joinColumn) {
$columnName = $joinColumn['name'];
$renamedColumnName = isset($renamedColumns[$columnName]) ? $renamedColumns[$columnName] : $columnName;
$renamedColumnName = $platform->getSQLResultCasing($renamedColumnName);
if (isset($this->metaMappings[$renamedColumnName])) {
throw new \InvalidArgumentException("The column '$renamedColumnName' conflicts with another column in the mapper.");
}
$this->addMetaResult($alias, $renamedColumnName, $columnName);
}
}
}
}
/**
* Adds the mappings of the results of native SQL queries to the result set.
*
* @param ClassMetadataInfo $class
* @param array $queryMapping
* @return ResultSetMappingBuilder
*/
public function addNamedNativeQueryMapping(ClassMetadataInfo $class, array $queryMapping)
{
if (isset($queryMapping['resultClass'])) {
return $this->addNamedNativeQueryResultClassMapping($class, $queryMapping['resultClass']);
}
return $this->addNamedNativeQueryResultSetMapping($class, $queryMapping['resultSetMapping']);
}
/**
* Adds the class mapping of the results of native SQL queries to the result set.
*
* @param ClassMetadataInfo $class
* @param string $resultClassName
* @return ResultSetMappingBuilder
*/
public function addNamedNativeQueryResultClassMapping(ClassMetadataInfo $class, $resultClassName)
{
$classMetadata = $this->em->getClassMetadata($resultClassName);
$shortName = $classMetadata->reflClass->getShortName();
$alias = strtolower($shortName[0]).'0';
$this->addEntityResult($class->name, $alias);
if ($classMetadata->discriminatorColumn) {
$discriminatorColumn = $classMetadata->discriminatorColumn;
$this->setDiscriminatorColumn($alias, $discriminatorColumn['name']);
$this->addMetaResult($alias, $discriminatorColumn['name'], $discriminatorColumn['fieldName']);
}
foreach ($classMetadata->getColumnNames() as $key => $columnName) {
$propertyName = $classMetadata->getFieldName($columnName);
$this->addFieldResult($alias, $columnName, $propertyName);
}
foreach ($classMetadata->associationMappings as $associationMapping) {
if ($associationMapping['isOwningSide'] && $associationMapping['type'] & ClassMetadataInfo::TO_ONE) {
foreach ($associationMapping['joinColumns'] as $joinColumn) {
$columnName = $joinColumn['name'];
$this->addMetaResult($alias, $columnName, $columnName, $classMetadata->isIdentifier($columnName));
}
}
}
return $this;
}
/**
* Adds the result set mapping of the results of native SQL queries to the result set.
*
* @param ClassMetadataInfo $class
* @param string $resultSetMappingName
* @return ResultSetMappingBuilder
*/
public function addNamedNativeQueryResultSetMapping(ClassMetadataInfo $class, $resultSetMappingName)
{
$counter = 0;
$resultMapping = $class->getSqlResultSetMapping($resultSetMappingName);
$rooShortName = $class->reflClass->getShortName();
$rootAlias = strtolower($rooShortName[0]) . $counter;
if (isset($resultMapping['entities'])) {
foreach ($resultMapping['entities'] as $key => $entityMapping) {
$classMetadata = $this->em->getClassMetadata($entityMapping['entityClass']);
if ($class->reflClass->name == $classMetadata->reflClass->name) {
$this->addEntityResult($classMetadata->name, $rootAlias);
$this->addNamedNativeQueryEntityResultMapping($classMetadata, $entityMapping, $rootAlias);
} else {
$shortName = $classMetadata->reflClass->getShortName();
$joinAlias = strtolower($shortName[0]) . ++ $counter;
$associations = $class->getAssociationsByTargetClass($classMetadata->name);
foreach ($associations as $relation => $mapping) {
$this->addJoinedEntityResult($mapping['targetEntity'], $joinAlias, $rootAlias, $relation);
$this->addNamedNativeQueryEntityResultMapping($classMetadata, $entityMapping, $joinAlias);
}
}
}
}
if (isset($resultMapping['columns'])) {
foreach ($resultMapping['columns'] as $entityMapping) {
$this->addScalarResult($entityMapping['name'], $entityMapping['name']);
}
}
return $this;
}
/**
* Adds the entity result mapping of the results of native SQL queries to the result set.
*
* @param ClassMetadataInfo $classMetadata
* @param array $entityMapping
* @param string $alias
* @return ResultSetMappingBuilder
*/
public function addNamedNativeQueryEntityResultMapping(ClassMetadataInfo $classMetadata, array $entityMapping, $alias)
{
if (isset($entityMapping['discriminatorColumn']) && $entityMapping['discriminatorColumn']) {
$discriminatorColumn = $entityMapping['discriminatorColumn'];
$this->setDiscriminatorColumn($alias, $discriminatorColumn);
$this->addMetaResult($alias, $discriminatorColumn, $discriminatorColumn);
}
if (isset($entityMapping['fields']) && !empty($entityMapping['fields'])) {
foreach ($entityMapping['fields'] as $field) {
$fieldName = $field['name'];
$relation = null;
if(strpos($fieldName, '.')){
list($relation, $fieldName) = explode('.', $fieldName);
}
if (isset($classMetadata->associationMappings[$relation])) {
if($relation) {
$associationMapping = $classMetadata->associationMappings[$relation];
$joinAlias = $alias.$relation;
$parentAlias = $alias;
$this->addJoinedEntityResult($associationMapping['targetEntity'], $joinAlias, $parentAlias, $relation);
$this->addFieldResult($joinAlias, $field['column'], $fieldName);
}else {
$this->addFieldResult($alias, $field['column'], $fieldName, $classMetadata->name);
}
} else {
if(!isset($classMetadata->fieldMappings[$fieldName])) {
throw new \InvalidArgumentException("Entity '".$classMetadata->name."' has no field '".$fieldName."'. ");
}
$this->addFieldResult($alias, $field['column'], $fieldName, $classMetadata->name);
}
}
} else {
foreach ($classMetadata->getColumnNames() as $columnName) {
$propertyName = $classMetadata->getFieldName($columnName);
$this->addFieldResult($alias, $columnName, $propertyName);
}
}
return $this;
}
}
DoctrineORM-2.3.3/Doctrine/ORM/Query/SqlWalker.php 0000644 0001751 0001751 00000241350 12143374607 021715 0 ustar beberlei beberlei .
*/
namespace Doctrine\ORM\Query;
use Doctrine\DBAL\LockMode,
Doctrine\DBAL\Types\Type,
Doctrine\ORM\Mapping\ClassMetadata,
Doctrine\ORM\Query,
Doctrine\ORM\Query\QueryException,
Doctrine\ORM\Mapping\ClassMetadataInfo;
/**
* The SqlWalker is a TreeWalker that walks over a DQL AST and constructs
* the corresponding SQL.
*
* @author Guilherme Blanco
* @author Roman Borschel
* @author Benjamin Eberlei
* @author Alexander
* @since 2.0
* @todo Rename: SQLWalker
*/
class SqlWalker implements TreeWalker
{
/**
* @var string
*/
const HINT_DISTINCT = 'doctrine.distinct';
/**
* @var ResultSetMapping
*/
private $rsm;
/**
* Counters for generating unique column aliases.
*
* @var integer
*/
private $aliasCounter = 0;
/**
* Counters for generating unique table aliases.
*
* @var integer
*/
private $tableAliasCounter = 0;
/**
* Counters for generating unique scalar result.
*
* @var integer
*/
private $scalarResultCounter = 1;
/**
* Counters for generating unique parameter indexes.
*
* @var integer
*/
private $sqlParamIndex = 0;
/**
* @var ParserResult
*/
private $parserResult;
/**
* @var EntityManager
*/
private $em;
/**
* @var \Doctrine\DBAL\Connection
*/
private $conn;
/**
* @var AbstractQuery
*/
private $query;
/**
* @var array
*/
private $tableAliasMap = array();
/**
* Map from result variable names to their SQL column alias names.
*
* @var array
*/
private $scalarResultAliasMap = array();
/**
* Map from DQL-Alias + Field-Name to SQL Column Alias
*
* @var array
*/
private $scalarFields = array();
/**
* Map of all components/classes that appear in the DQL query.
*
* @var array
*/
private $queryComponents;
/**
* A list of classes that appear in non-scalar SelectExpressions.
*
* @var array
*/
private $selectedClasses = array();
/**
* The DQL alias of the root class of the currently traversed query.
*
* @var array
*/
private $rootAliases = array();
/**
* Flag that indicates whether to generate SQL table aliases in the SQL.
* These should only be generated for SELECT queries, not for UPDATE/DELETE.
*
* @var boolean
*/
private $useSqlTableAliases = true;
/**
* The database platform abstraction.
*
* @var AbstractPlatform
*/
private $platform;
/**
* The quote strategy.
*
* @var \Doctrine\ORM\Mapping\QuoteStrategy
*/
private $quoteStrategy;
/**
* {@inheritDoc}
*/
public function __construct($query, $parserResult, array $queryComponents)
{
$this->query = $query;
$this->parserResult = $parserResult;
$this->queryComponents = $queryComponents;
$this->rsm = $parserResult->getResultSetMapping();
$this->em = $query->getEntityManager();
$this->conn = $this->em->getConnection();
$this->platform = $this->conn->getDatabasePlatform();
$this->quoteStrategy = $this->em->getConfiguration()->getQuoteStrategy();
}
/**
* Gets the Query instance used by the walker.
*
* @return Query.
*/
public function getQuery()
{
return $this->query;
}
/**
* Gets the Connection used by the walker.
*
* @return Connection
*/
public function getConnection()
{
return $this->conn;
}
/**
* Gets the EntityManager used by the walker.
*
* @return EntityManager
*/
public function getEntityManager()
{
return $this->em;
}
/**
* Gets the information about a single query component.
*
* @param string $dqlAlias The DQL alias.
* @return array
*/
public function getQueryComponent($dqlAlias)
{
return $this->queryComponents[$dqlAlias];
}
/**
* Gets an executor that can be used to execute the result of this walker.
*
* @return AbstractExecutor
*/
public function getExecutor($AST)
{
switch (true) {
case ($AST instanceof AST\DeleteStatement):
$primaryClass = $this->em->getClassMetadata($AST->deleteClause->abstractSchemaName);
return ($primaryClass->isInheritanceTypeJoined())
? new Exec\MultiTableDeleteExecutor($AST, $this)
: new Exec\SingleTableDeleteUpdateExecutor($AST, $this);
case ($AST instanceof AST\UpdateStatement):
$primaryClass = $this->em->getClassMetadata($AST->updateClause->abstractSchemaName);
return ($primaryClass->isInheritanceTypeJoined())
? new Exec\MultiTableUpdateExecutor($AST, $this)
: new Exec\SingleTableDeleteUpdateExecutor($AST, $this);
default:
return new Exec\SingleSelectExecutor($AST, $this);
}
}
/**
* Generates a unique, short SQL table alias.
*
* @param string $tableName Table name
* @param string $dqlAlias The DQL alias.
* @return string Generated table alias.
*/
public function getSQLTableAlias($tableName, $dqlAlias = '')
{
$tableName .= ($dqlAlias) ? '@[' . $dqlAlias . ']' : '';
if ( ! isset($this->tableAliasMap[$tableName])) {
$this->tableAliasMap[$tableName] = strtolower(substr($tableName, 0, 1)) . $this->tableAliasCounter++ . '_';
}
return $this->tableAliasMap[$tableName];
}
/**
* Forces the SqlWalker to use a specific alias for a table name, rather than
* generating an alias on its own.
*
* @param string $tableName
* @param string $alias
* @param string $dqlAlias
* @return string
*/
public function setSQLTableAlias($tableName, $alias, $dqlAlias = '')
{
$tableName .= ($dqlAlias) ? '@[' . $dqlAlias . ']' : '';
$this->tableAliasMap[$tableName] = $alias;
return $alias;
}
/**
* Gets an SQL column alias for a column name.
*
* @param string $columnName
* @return string
*/
public function getSQLColumnAlias($columnName)
{
return $this->quoteStrategy->getColumnAlias($columnName, $this->aliasCounter++, $this->platform);
}
/**
* Generates the SQL JOINs that are necessary for Class Table Inheritance
* for the given class.
*
* @param ClassMetadata $class The class for which to generate the joins.
* @param string $dqlAlias The DQL alias of the class.
* @return string The SQL.
*/
private function _generateClassTableInheritanceJoins($class, $dqlAlias)
{
$sql = '';
$baseTableAlias = $this->getSQLTableAlias($class->getTableName(), $dqlAlias);
// INNER JOIN parent class tables
foreach ($class->parentClasses as $parentClassName) {
$parentClass = $this->em->getClassMetadata($parentClassName);
$tableAlias = $this->getSQLTableAlias($parentClass->getTableName(), $dqlAlias);
// If this is a joined association we must use left joins to preserve the correct result.
$sql .= isset($this->queryComponents[$dqlAlias]['relation']) ? ' LEFT ' : ' INNER ';
$sql .= 'JOIN ' . $this->quoteStrategy->getTableName($parentClass, $this->platform) . ' ' . $tableAlias . ' ON ';
$sqlParts = array();
foreach ($this->quoteStrategy->getIdentifierColumnNames($class, $this->platform) as $columnName) {
$sqlParts[] = $baseTableAlias . '.' . $columnName . ' = ' . $tableAlias . '.' . $columnName;
}
// Add filters on the root class
if ($filterSql = $this->generateFilterConditionSQL($parentClass, $tableAlias)) {
$sqlParts[] = $filterSql;
}
$sql .= implode(' AND ', $sqlParts);
}
// Ignore subclassing inclusion if partial objects is disallowed
if ($this->query->getHint(Query::HINT_FORCE_PARTIAL_LOAD)) {
return $sql;
}
// LEFT JOIN child class tables
foreach ($class->subClasses as $subClassName) {
$subClass = $this->em->getClassMetadata($subClassName);
$tableAlias = $this->getSQLTableAlias($subClass->getTableName(), $dqlAlias);
$sql .= ' LEFT JOIN ' . $this->quoteStrategy->getTableName($subClass, $this->platform) . ' ' . $tableAlias . ' ON ';
$sqlParts = array();
foreach ($this->quoteStrategy->getIdentifierColumnNames($subClass, $this->platform) as $columnName) {
$sqlParts[] = $baseTableAlias . '.' . $columnName . ' = ' . $tableAlias . '.' . $columnName;
}
$sql .= implode(' AND ', $sqlParts);
}
return $sql;
}
private function _generateOrderedCollectionOrderByItems()
{
$sqlParts = array();
foreach ($this->selectedClasses as $selectedClass) {
$dqlAlias = $selectedClass['dqlAlias'];
$qComp = $this->queryComponents[$dqlAlias];
if ( ! isset($qComp['relation']['orderBy'])) continue;
foreach ($qComp['relation']['orderBy'] as $fieldName => $orientation) {
$columnName = $this->quoteStrategy->getColumnName($fieldName, $qComp['metadata'], $this->platform);
$tableName = ($qComp['metadata']->isInheritanceTypeJoined())
? $this->em->getUnitOfWork()->getEntityPersister($qComp['metadata']->name)->getOwningTable($fieldName)
: $qComp['metadata']->getTableName();
$sqlParts[] = $this->getSQLTableAlias($tableName, $dqlAlias) . '.' . $columnName . ' ' . $orientation;
}
}
return implode(', ', $sqlParts);
}
/**
* Generates a discriminator column SQL condition for the class with the given DQL alias.
*
* @param array $dqlAliases List of root DQL aliases to inspect for discriminator restrictions.
* @return string
*/
private function _generateDiscriminatorColumnConditionSQL(array $dqlAliases)
{
$sqlParts = array();
foreach ($dqlAliases as $dqlAlias) {
$class = $this->queryComponents[$dqlAlias]['metadata'];
if ( ! $class->isInheritanceTypeSingleTable()) continue;
$conn = $this->em->getConnection();
$values = array();
if ($class->discriminatorValue !== null) { // discrimnators can be 0
$values[] = $conn->quote($class->discriminatorValue);
}
foreach ($class->subClasses as $subclassName) {
$values[] = $conn->quote($this->em->getClassMetadata($subclassName)->discriminatorValue);
}
$sqlParts[] = (($this->useSqlTableAliases) ? $this->getSQLTableAlias($class->getTableName(), $dqlAlias) . '.' : '')
. $class->discriminatorColumn['name'] . ' IN (' . implode(', ', $values) . ')';
}
$sql = implode(' AND ', $sqlParts);
return (count($sqlParts) > 1) ? '(' . $sql . ')' : $sql;
}
/**
* Generates the filter SQL for a given entity and table alias.
*
* @param ClassMetadata $targetEntity Metadata of the target entity.
* @param string $targetTableAlias The table alias of the joined/selected table.
*
* @return string The SQL query part to add to a query.
*/
private function generateFilterConditionSQL(ClassMetadata $targetEntity, $targetTableAlias)
{
if (!$this->em->hasFilters()) {
return '';
}
switch($targetEntity->inheritanceType) {
case ClassMetadata::INHERITANCE_TYPE_NONE:
break;
case ClassMetadata::INHERITANCE_TYPE_JOINED:
// The classes in the inheritance will be added to the query one by one,
// but only the root node is getting filtered
if ($targetEntity->name !== $targetEntity->rootEntityName) {
return '';
}
break;
case ClassMetadata::INHERITANCE_TYPE_SINGLE_TABLE:
// With STI the table will only be queried once, make sure that the filters
// are added to the root entity
$targetEntity = $this->em->getClassMetadata($targetEntity->rootEntityName);
break;
default:
//@todo: throw exception?
return '';
break;
}
$filterClauses = array();
foreach ($this->em->getFilters()->getEnabledFilters() as $filter) {
if ('' !== $filterExpr = $filter->addFilterConstraint($targetEntity, $targetTableAlias)) {
$filterClauses[] = '(' . $filterExpr . ')';
}
}
return implode(' AND ', $filterClauses);
}
/**
* Walks down a SelectStatement AST node, thereby generating the appropriate SQL.
*
* @return string The SQL.
*/
public function walkSelectStatement(AST\SelectStatement $AST)
{
$sql = $this->walkSelectClause($AST->selectClause);
$sql .= $this->walkFromClause($AST->fromClause);
$sql .= $this->walkWhereClause($AST->whereClause);
$sql .= $AST->groupByClause ? $this->walkGroupByClause($AST->groupByClause) : '';
$sql .= $AST->havingClause ? $this->walkHavingClause($AST->havingClause) : '';
if (($orderByClause = $AST->orderByClause) !== null) {
$sql .= $AST->orderByClause ? $this->walkOrderByClause($AST->orderByClause) : '';
} else if (($orderBySql = $this->_generateOrderedCollectionOrderByItems()) !== '') {
$sql .= ' ORDER BY ' . $orderBySql;
}
$sql = $this->platform->modifyLimitQuery(
$sql, $this->query->getMaxResults(), $this->query->getFirstResult()
);
if (($lockMode = $this->query->getHint(Query::HINT_LOCK_MODE)) !== false) {
switch ($lockMode) {
case LockMode::PESSIMISTIC_READ:
$sql .= ' ' . $this->platform->getReadLockSQL();
break;
case LockMode::PESSIMISTIC_WRITE:
$sql .= ' ' . $this->platform->getWriteLockSQL();
break;
case LockMode::OPTIMISTIC:
foreach ($this->selectedClasses as $selectedClass) {
if ( ! $selectedClass['class']->isVersioned) {
throw \Doctrine\ORM\OptimisticLockException::lockFailed($selectedClass['class']->name);
}
}
break;
case LockMode::NONE:
break;
default:
throw \Doctrine\ORM\Query\QueryException::invalidLockMode();
}
}
return $sql;
}
/**
* Walks down an UpdateStatement AST node, thereby generating the appropriate SQL.
*
* @param UpdateStatement
* @return string The SQL.
*/
public function walkUpdateStatement(AST\UpdateStatement $AST)
{
$this->useSqlTableAliases = false;
return $this->walkUpdateClause($AST->updateClause)
. $this->walkWhereClause($AST->whereClause);
}
/**
* Walks down a DeleteStatement AST node, thereby generating the appropriate SQL.
*
* @param DeleteStatement
* @return string The SQL.
*/
public function walkDeleteStatement(AST\DeleteStatement $AST)
{
$this->useSqlTableAliases = false;
return $this->walkDeleteClause($AST->deleteClause)
. $this->walkWhereClause($AST->whereClause);
}
/**
* Walks down an IdentificationVariable AST node, thereby generating the appropriate SQL.
* This one differs of ->walkIdentificationVariable() because it generates the entity identifiers.
*
* @param string $identVariable
* @return string
*/
public function walkEntityIdentificationVariable($identVariable)
{
$class = $this->queryComponents[$identVariable]['metadata'];
$tableAlias = $this->getSQLTableAlias($class->getTableName(), $identVariable);
$sqlParts = array();
foreach ($this->quoteStrategy->getIdentifierColumnNames($class, $this->platform) as $columnName) {
$sqlParts[] = $tableAlias . '.' . $columnName;
}
return implode(', ', $sqlParts);
}
/**
* Walks down an IdentificationVariable (no AST node associated), thereby generating the SQL.
*
* @param string $identificationVariable
* @param string $fieldName
* @return string The SQL.
*/
public function walkIdentificationVariable($identificationVariable, $fieldName = null)
{
$class = $this->queryComponents[$identificationVariable]['metadata'];
if (
$fieldName !== null && $class->isInheritanceTypeJoined() &&
isset($class->fieldMappings[$fieldName]['inherited'])
) {
$class = $this->em->getClassMetadata($class->fieldMappings[$fieldName]['inherited']);
}
return $this->getSQLTableAlias($class->getTableName(), $identificationVariable);
}
/**
* Walks down a PathExpression AST node, thereby generating the appropriate SQL.
*
* @param mixed
* @return string The SQL.
*/
public function walkPathExpression($pathExpr)
{
$sql = '';
switch ($pathExpr->type) {
case AST\PathExpression::TYPE_STATE_FIELD:
$fieldName = $pathExpr->field;
$dqlAlias = $pathExpr->identificationVariable;
$class = $this->queryComponents[$dqlAlias]['metadata'];
if ($this->useSqlTableAliases) {
$sql .= $this->walkIdentificationVariable($dqlAlias, $fieldName) . '.';
}
$sql .= $this->quoteStrategy->getColumnName($fieldName, $class, $this->platform);
break;
case AST\PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION:
// 1- the owning side:
// Just use the foreign key, i.e. u.group_id
$fieldName = $pathExpr->field;
$dqlAlias = $pathExpr->identificationVariable;
$class = $this->queryComponents[$dqlAlias]['metadata'];
if (isset($class->associationMappings[$fieldName]['inherited'])) {
$class = $this->em->getClassMetadata($class->associationMappings[$fieldName]['inherited']);
}
$assoc = $class->associationMappings[$fieldName];
if ( ! $assoc['isOwningSide']) {
throw QueryException::associationPathInverseSideNotSupported();
}
// COMPOSITE KEYS NOT (YET?) SUPPORTED
if (count($assoc['sourceToTargetKeyColumns']) > 1) {
throw QueryException::associationPathCompositeKeyNotSupported();
}
if ($this->useSqlTableAliases) {
$sql .= $this->getSQLTableAlias($class->getTableName(), $dqlAlias) . '.';
}
$sql .= reset($assoc['targetToSourceKeyColumns']);
break;
default:
throw QueryException::invalidPathExpression($pathExpr);
}
return $sql;
}
/**
* Walks down a SelectClause AST node, thereby generating the appropriate SQL.
*
* @param $selectClause
* @return string The SQL.
*/
public function walkSelectClause($selectClause)
{
$sql = 'SELECT ' . (($selectClause->isDistinct) ? 'DISTINCT ' : '');
$sqlSelectExpressions = array_filter(array_map(array($this, 'walkSelectExpression'), $selectClause->selectExpressions));
if ($this->query->getHint(Query::HINT_INTERNAL_ITERATION) == true && $selectClause->isDistinct) {
$this->query->setHint(self::HINT_DISTINCT, true);
}
$addMetaColumns = ! $this->query->getHint(Query::HINT_FORCE_PARTIAL_LOAD) &&
$this->query->getHydrationMode() == Query::HYDRATE_OBJECT
||
$this->query->getHydrationMode() != Query::HYDRATE_OBJECT &&
$this->query->getHint(Query::HINT_INCLUDE_META_COLUMNS);
foreach ($this->selectedClasses as $selectedClass) {
$class = $selectedClass['class'];
$dqlAlias = $selectedClass['dqlAlias'];
$resultAlias = $selectedClass['resultAlias'];
// Register as entity or joined entity result
if ($this->queryComponents[$dqlAlias]['relation'] === null) {
$this->rsm->addEntityResult($class->name, $dqlAlias, $resultAlias);
} else {
$this->rsm->addJoinedEntityResult(
$class->name,
$dqlAlias,
$this->queryComponents[$dqlAlias]['parent'],
$this->queryComponents[$dqlAlias]['relation']['fieldName']
);
}
if ($class->isInheritanceTypeSingleTable() || $class->isInheritanceTypeJoined()) {
// Add discriminator columns to SQL
$rootClass = $this->em->getClassMetadata($class->rootEntityName);
$tblAlias = $this->getSQLTableAlias($rootClass->getTableName(), $dqlAlias);
$discrColumn = $rootClass->discriminatorColumn;
$columnAlias = $this->getSQLColumnAlias($discrColumn['name']);
$sqlSelectExpressions[] = $tblAlias . '.' . $discrColumn['name'] . ' AS ' . $columnAlias;
$this->rsm->setDiscriminatorColumn($dqlAlias, $columnAlias);
$this->rsm->addMetaResult($dqlAlias, $columnAlias, $discrColumn['fieldName']);
}
// Add foreign key columns to SQL, if necessary
if ( ! $addMetaColumns && ! $class->containsForeignIdentifier) {
continue;
}
// Add foreign key columns of class and also parent classes
foreach ($class->associationMappings as $assoc) {
if ( ! ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE)) {
continue;
} else if ( !$addMetaColumns && !isset($assoc['id'])) {
continue;
}
$owningClass = (isset($assoc['inherited'])) ? $this->em->getClassMetadata($assoc['inherited']) : $class;
$sqlTableAlias = $this->getSQLTableAlias($owningClass->getTableName(), $dqlAlias);
foreach ($assoc['targetToSourceKeyColumns'] as $srcColumn) {
$columnAlias = $this->getSQLColumnAlias($srcColumn);
$sqlSelectExpressions[] = $sqlTableAlias . '.' . $srcColumn . ' AS ' . $columnAlias;
$this->rsm->addMetaResult($dqlAlias, $columnAlias, $srcColumn, (isset($assoc['id']) && $assoc['id'] === true));
}
}
// Add foreign key columns to SQL, if necessary
if ( ! $addMetaColumns) {
continue;
}
// Add foreign key columns of subclasses
foreach ($class->subClasses as $subClassName) {
$subClass = $this->em->getClassMetadata($subClassName);
$sqlTableAlias = $this->getSQLTableAlias($subClass->getTableName(), $dqlAlias);
foreach ($subClass->associationMappings as $assoc) {
// Skip if association is inherited
if (isset($assoc['inherited'])) continue;
if ( ! ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE)) continue;
foreach ($assoc['targetToSourceKeyColumns'] as $srcColumn) {
$columnAlias = $this->getSQLColumnAlias($srcColumn);
$sqlSelectExpressions[] = $sqlTableAlias . '.' . $srcColumn . ' AS ' . $columnAlias;
$this->rsm->addMetaResult($dqlAlias, $columnAlias, $srcColumn);
}
}
}
}
$sql .= implode(', ', $sqlSelectExpressions);
return $sql;
}
/**
* Walks down a FromClause AST node, thereby generating the appropriate SQL.
*
* @return string The SQL.
*/
public function walkFromClause($fromClause)
{
$identificationVarDecls = $fromClause->identificationVariableDeclarations;
$sqlParts = array();
foreach ($identificationVarDecls as $identificationVariableDecl) {
$sql = $this->platform->appendLockHint(
$this->walkRangeVariableDeclaration($identificationVariableDecl->rangeVariableDeclaration),
$this->query->getHint(Query::HINT_LOCK_MODE)
);
foreach ($identificationVariableDecl->joins as $join) {
$sql .= $this->walkJoin($join);
}
if ($identificationVariableDecl->indexBy) {
$alias = $identificationVariableDecl->indexBy->simpleStateFieldPathExpression->identificationVariable;
$field = $identificationVariableDecl->indexBy->simpleStateFieldPathExpression->field;
if (isset($this->scalarFields[$alias][$field])) {
$this->rsm->addIndexByScalar($this->scalarFields[$alias][$field]);
} else {
$this->rsm->addIndexBy(
$identificationVariableDecl->indexBy->simpleStateFieldPathExpression->identificationVariable,
$identificationVariableDecl->indexBy->simpleStateFieldPathExpression->field
);
}
}
$sqlParts[] = $sql;
}
return ' FROM ' . implode(', ', $sqlParts);
}
/**
* Walks down a RangeVariableDeclaration AST node, thereby generating the appropriate SQL.
*
* @return string
*/
public function walkRangeVariableDeclaration($rangeVariableDeclaration)
{
$class = $this->em->getClassMetadata($rangeVariableDeclaration->abstractSchemaName);
$dqlAlias = $rangeVariableDeclaration->aliasIdentificationVariable;
$this->rootAliases[] = $dqlAlias;
$sql = $class->getQuotedTableName($this->platform) . ' '
. $this->getSQLTableAlias($class->getTableName(), $dqlAlias);
if ($class->isInheritanceTypeJoined()) {
$sql .= $this->_generateClassTableInheritanceJoins($class, $dqlAlias);
}
return $sql;
}
/**
* Walks down a JoinAssociationDeclaration AST node, thereby generating the appropriate SQL.
*
* @return string
*/
public function walkJoinAssociationDeclaration($joinAssociationDeclaration, $joinType = AST\Join::JOIN_TYPE_INNER)
{
$sql = '';
$associationPathExpression = $joinAssociationDeclaration->joinAssociationPathExpression;
$joinedDqlAlias = $joinAssociationDeclaration->aliasIdentificationVariable;
$indexBy = $joinAssociationDeclaration->indexBy;
$relation = $this->queryComponents[$joinedDqlAlias]['relation'];
$targetClass = $this->em->getClassMetadata($relation['targetEntity']);
$sourceClass = $this->em->getClassMetadata($relation['sourceEntity']);
$targetTableName = $targetClass->getQuotedTableName($this->platform);
$targetTableAlias = $this->getSQLTableAlias($targetClass->getTableName(), $joinedDqlAlias);
$sourceTableAlias = $this->getSQLTableAlias($sourceClass->getTableName(), $associationPathExpression->identificationVariable);
// Ensure we got the owning side, since it has all mapping info
$assoc = ( ! $relation['isOwningSide']) ? $targetClass->associationMappings[$relation['mappedBy']] : $relation;
if ($this->query->getHint(Query::HINT_INTERNAL_ITERATION) == true && (!$this->query->getHint(self::HINT_DISTINCT) || isset($this->selectedClasses[$joinedDqlAlias]))) {
if ($relation['type'] == ClassMetadata::ONE_TO_MANY || $relation['type'] == ClassMetadata::MANY_TO_MANY) {
throw QueryException::iterateWithFetchJoinNotAllowed($assoc);
}
}
// This condition is not checking ClassMetadata::MANY_TO_ONE, because by definition it cannot
// be the owning side and previously we ensured that $assoc is always the owning side of the associations.
// The owning side is necessary at this point because only it contains the JoinColumn information.
switch (true) {
case ($assoc['type'] & ClassMetadata::TO_ONE):
$conditions = array();
foreach ($assoc['joinColumns'] as $joinColumn) {
$quotedSourceColumn = $this->quoteStrategy->getJoinColumnName($joinColumn, $targetClass, $this->platform);
$quotedTargetColumn = $this->quoteStrategy->getReferencedJoinColumnName($joinColumn, $targetClass, $this->platform);
if ($relation['isOwningSide']) {
$conditions[] = $sourceTableAlias . '.' . $quotedSourceColumn . ' = ' . $targetTableAlias . '.' . $quotedTargetColumn;
continue;
}
$conditions[] = $sourceTableAlias . '.' . $quotedTargetColumn . ' = ' . $targetTableAlias . '.' . $quotedSourceColumn;
}
// Apply remaining inheritance restrictions
$discrSql = $this->_generateDiscriminatorColumnConditionSQL(array($joinedDqlAlias));
if ($discrSql) {
$conditions[] = $discrSql;
}
// Apply the filters
$filterExpr = $this->generateFilterConditionSQL($targetClass, $targetTableAlias);
if ($filterExpr) {
$conditions[] = $filterExpr;
}
$sql .= $targetTableName . ' ' . $targetTableAlias . ' ON ' . implode(' AND ', $conditions);
break;
case ($assoc['type'] == ClassMetadata::MANY_TO_MANY):
// Join relation table
$joinTable = $assoc['joinTable'];
$joinTableAlias = $this->getSQLTableAlias($joinTable['name'], $joinedDqlAlias);
$joinTableName = $sourceClass->getQuotedJoinTableName($assoc, $this->platform);
$conditions = array();
$relationColumns = ($relation['isOwningSide'])
? $assoc['joinTable']['joinColumns']
: $assoc['joinTable']['inverseJoinColumns'];
foreach ($relationColumns as $joinColumn) {
$quotedSourceColumn = $this->quoteStrategy->getJoinColumnName($joinColumn, $targetClass, $this->platform);
$quotedTargetColumn = $this->quoteStrategy->getReferencedJoinColumnName($joinColumn, $targetClass, $this->platform);
$conditions[] = $sourceTableAlias . '.' . $quotedTargetColumn . ' = ' . $joinTableAlias . '.' . $quotedSourceColumn;
}
$sql .= $joinTableName . ' ' . $joinTableAlias . ' ON ' . implode(' AND ', $conditions);
// Join target table
$sql .= ($joinType == AST\Join::JOIN_TYPE_LEFT || $joinType == AST\Join::JOIN_TYPE_LEFTOUTER) ? ' LEFT JOIN ' : ' INNER JOIN ';
$conditions = array();
$relationColumns = ($relation['isOwningSide'])
? $assoc['joinTable']['inverseJoinColumns']
: $assoc['joinTable']['joinColumns'];
foreach ($relationColumns as $joinColumn) {
$quotedSourceColumn = $this->quoteStrategy->getJoinColumnName($joinColumn, $targetClass, $this->platform);
$quotedTargetColumn = $this->quoteStrategy->getReferencedJoinColumnName($joinColumn, $targetClass, $this->platform);
$conditions[] = $targetTableAlias . '.' . $quotedTargetColumn . ' = ' . $joinTableAlias . '.' . $quotedSourceColumn;
}
// Apply remaining inheritance restrictions
$discrSql = $this->_generateDiscriminatorColumnConditionSQL(array($joinedDqlAlias));
if ($discrSql) {
$conditions[] = $discrSql;
}
// Apply the filters
$filterExpr = $this->generateFilterConditionSQL($targetClass, $targetTableAlias);
if ($filterExpr) {
$conditions[] = $filterExpr;
}
$sql .= $targetTableName . ' ' . $targetTableAlias . ' ON ' . implode(' AND ', $conditions);
break;
}
// FIXME: these should either be nested or all forced to be left joins (DDC-XXX)
if ($targetClass->isInheritanceTypeJoined()) {
$sql .= $this->_generateClassTableInheritanceJoins($targetClass, $joinedDqlAlias);
}
// Apply the indexes
if ($indexBy) {
// For Many-To-One or One-To-One associations this obviously makes no sense, but is ignored silently.
$this->rsm->addIndexBy(
$indexBy->simpleStateFieldPathExpression->identificationVariable,
$indexBy->simpleStateFieldPathExpression->field
);
} else if (isset($relation['indexBy'])) {
$this->rsm->addIndexBy($joinedDqlAlias, $relation['indexBy']);
}
return $sql;
}
/**
* Walks down a FunctionNode AST node, thereby generating the appropriate SQL.
*
* @return string The SQL.
*/
public function walkFunction($function)
{
return $function->getSql($this);
}
/**
* Walks down an OrderByClause AST node, thereby generating the appropriate SQL.
*
* @param OrderByClause
* @return string The SQL.
*/
public function walkOrderByClause($orderByClause)
{
$orderByItems = array_map(array($this, 'walkOrderByItem'), $orderByClause->orderByItems);
if (($collectionOrderByItems = $this->_generateOrderedCollectionOrderByItems()) !== '') {
$orderByItems = array_merge($orderByItems, (array) $collectionOrderByItems);
}
return ' ORDER BY ' . implode(', ', $orderByItems);
}
/**
* Walks down an OrderByItem AST node, thereby generating the appropriate SQL.
*
* @param OrderByItem
* @return string The SQL.
*/
public function walkOrderByItem($orderByItem)
{
$expr = $orderByItem->expression;
$sql = ($expr instanceof AST\Node)
? $expr->dispatch($this)
: $this->walkResultVariable($this->queryComponents[$expr]['token']['value']);
return $sql . ' ' . strtoupper($orderByItem->type);
}
/**
* Walks down a HavingClause AST node, thereby generating the appropriate SQL.
*
* @param HavingClause
* @return string The SQL.
*/
public function walkHavingClause($havingClause)
{
return ' HAVING ' . $this->walkConditionalExpression($havingClause->conditionalExpression);
}
/**
* Walks down a Join AST node and creates the corresponding SQL.
*
* @return string The SQL.
*/
public function walkJoin($join)
{
$joinType = $join->joinType;
$joinDeclaration = $join->joinAssociationDeclaration;
$sql = ($joinType == AST\Join::JOIN_TYPE_LEFT || $joinType == AST\Join::JOIN_TYPE_LEFTOUTER)
? ' LEFT JOIN '
: ' INNER JOIN ';
switch (true) {
case ($joinDeclaration instanceof \Doctrine\ORM\Query\AST\RangeVariableDeclaration):
$class = $this->em->getClassMetadata($joinDeclaration->abstractSchemaName);
$condExprConjunction = $class->isInheritanceTypeJoined() && $joinType != AST\Join::JOIN_TYPE_LEFT && $joinType != AST\Join::JOIN_TYPE_LEFTOUTER
? ' AND '
: ' ON ';
$sql .= $this->walkRangeVariableDeclaration($joinDeclaration)
. $condExprConjunction . '(' . $this->walkConditionalExpression($join->conditionalExpression) . ')';
break;
case ($joinDeclaration instanceof \Doctrine\ORM\Query\AST\JoinAssociationDeclaration):
$sql .= $this->walkJoinAssociationDeclaration($joinDeclaration, $joinType);
// Handle WITH clause
if (($condExpr = $join->conditionalExpression) !== null) {
// Phase 2 AST optimization: Skip processment of ConditionalExpression
// if only one ConditionalTerm is defined
$sql .= ' AND (' . $this->walkConditionalExpression($condExpr) . ')';
}
break;
}
return $sql;
}
/**
* Walks down a CaseExpression AST node and generates the corresponding SQL.
*
* @param CoalesceExpression|NullIfExpression|GeneralCaseExpression|SimpleCaseExpression $expression
* @return string The SQL.
*/
public function walkCaseExpression($expression)
{
switch (true) {
case ($expression instanceof AST\CoalesceExpression):
return $this->walkCoalesceExpression($expression);
case ($expression instanceof AST\NullIfExpression):
return $this->walkNullIfExpression($expression);
case ($expression instanceof AST\GeneralCaseExpression):
return $this->walkGeneralCaseExpression($expression);
case ($expression instanceof AST\SimpleCaseExpression):
return $this->walkSimpleCaseExpression($expression);
default:
return '';
}
}
/**
* Walks down a CoalesceExpression AST node and generates the corresponding SQL.
*
* @param CoalesceExpression $coalesceExpression
* @return string The SQL.
*/
public function walkCoalesceExpression($coalesceExpression)
{
$sql = 'COALESCE(';
$scalarExpressions = array();
foreach ($coalesceExpression->scalarExpressions as $scalarExpression) {
$scalarExpressions[] = $this->walkSimpleArithmeticExpression($scalarExpression);
}
$sql .= implode(', ', $scalarExpressions) . ')';
return $sql;
}
/**
* Walks down a NullIfExpression AST node and generates the corresponding SQL.
*
* @param NullIfExpression $nullIfExpression
* @return string The SQL.
*/
public function walkNullIfExpression($nullIfExpression)
{
$firstExpression = is_string($nullIfExpression->firstExpression)
? $this->conn->quote($nullIfExpression->firstExpression)
: $this->walkSimpleArithmeticExpression($nullIfExpression->firstExpression);
$secondExpression = is_string($nullIfExpression->secondExpression)
? $this->conn->quote($nullIfExpression->secondExpression)
: $this->walkSimpleArithmeticExpression($nullIfExpression->secondExpression);
return 'NULLIF(' . $firstExpression . ', ' . $secondExpression . ')';
}
/**
* Walks down a GeneralCaseExpression AST node and generates the corresponding SQL.
*
* @param GeneralCaseExpression $generalCaseExpression
* @return string The SQL.
*/
public function walkGeneralCaseExpression(AST\GeneralCaseExpression $generalCaseExpression)
{
$sql = 'CASE';
foreach ($generalCaseExpression->whenClauses as $whenClause) {
$sql .= ' WHEN ' . $this->walkConditionalExpression($whenClause->caseConditionExpression);
$sql .= ' THEN ' . $this->walkSimpleArithmeticExpression($whenClause->thenScalarExpression);
}
$sql .= ' ELSE ' . $this->walkSimpleArithmeticExpression($generalCaseExpression->elseScalarExpression) . ' END';
return $sql;
}
/**
* Walks down a SimpleCaseExpression AST node and generates the corresponding SQL.
*
* @param SimpleCaseExpression $simpleCaseExpression
* @return string The SQL.
*/
public function walkSimpleCaseExpression($simpleCaseExpression)
{
$sql = 'CASE ' . $this->walkStateFieldPathExpression($simpleCaseExpression->caseOperand);
foreach ($simpleCaseExpression->simpleWhenClauses as $simpleWhenClause) {
$sql .= ' WHEN ' . $this->walkSimpleArithmeticExpression($simpleWhenClause->caseScalarExpression);
$sql .= ' THEN ' . $this->walkSimpleArithmeticExpression($simpleWhenClause->thenScalarExpression);
}
$sql .= ' ELSE ' . $this->walkSimpleArithmeticExpression($simpleCaseExpression->elseScalarExpression) . ' END';
return $sql;
}
/**
* Walks down a SelectExpression AST node and generates the corresponding SQL.
*
* @param SelectExpression $selectExpression
* @return string The SQL.
*/
public function walkSelectExpression($selectExpression)
{
$sql = '';
$expr = $selectExpression->expression;
$hidden = $selectExpression->hiddenAliasResultVariable;
switch (true) {
case ($expr instanceof AST\PathExpression):
if ($expr->type !== AST\PathExpression::TYPE_STATE_FIELD) {
throw QueryException::invalidPathExpression($expr);
}
$fieldName = $expr->field;
$dqlAlias = $expr->identificationVariable;
$qComp = $this->queryComponents[$dqlAlias];
$class = $qComp['metadata'];
$resultAlias = $selectExpression->fieldIdentificationVariable ?: $fieldName;
$tableName = ($class->isInheritanceTypeJoined())
? $this->em->getUnitOfWork()->getEntityPersister($class->name)->getOwningTable($fieldName)
: $class->getTableName();
$sqlTableAlias = $this->getSQLTableAlias($tableName, $dqlAlias);
$columnName = $this->quoteStrategy->getColumnName($fieldName, $class, $this->platform);
$columnAlias = $this->getSQLColumnAlias($class->fieldMappings[$fieldName]['columnName']);
$col = $sqlTableAlias . '.' . $columnName;
$fieldType = $class->getTypeOfField($fieldName);
if (isset($class->fieldMappings[$fieldName]['requireSQLConversion'])) {
$type = Type::getType($fieldType);
$col = $type->convertToPHPValueSQL($col, $this->conn->getDatabasePlatform());
}
$sql .= $col . ' AS ' . $columnAlias;
$this->scalarResultAliasMap[$resultAlias] = $columnAlias;
if ( ! $hidden) {
$this->rsm->addScalarResult($columnAlias, $resultAlias, $fieldType);
$this->scalarFields[$dqlAlias][$fieldName] = $columnAlias;
}
break;
case ($expr instanceof AST\AggregateExpression):
case ($expr instanceof AST\Functions\FunctionNode):
case ($expr instanceof AST\SimpleArithmeticExpression):
case ($expr instanceof AST\ArithmeticTerm):
case ($expr instanceof AST\ArithmeticFactor):
case ($expr instanceof AST\Literal):
case ($expr instanceof AST\NullIfExpression):
case ($expr instanceof AST\CoalesceExpression):
case ($expr instanceof AST\GeneralCaseExpression):
case ($expr instanceof AST\SimpleCaseExpression):
$columnAlias = $this->getSQLColumnAlias('sclr');
$resultAlias = $selectExpression->fieldIdentificationVariable ?: $this->scalarResultCounter++;
$sql .= $expr->dispatch($this) . ' AS ' . $columnAlias;
$this->scalarResultAliasMap[$resultAlias] = $columnAlias;
if ( ! $hidden) {
// We cannot resolve field type here; assume 'string'.
$this->rsm->addScalarResult($columnAlias, $resultAlias, 'string');
}
break;
case ($expr instanceof AST\Subselect):
$columnAlias = $this->getSQLColumnAlias('sclr');
$resultAlias = $selectExpression->fieldIdentificationVariable ?: $this->scalarResultCounter++;
$sql .= '(' . $this->walkSubselect($expr) . ') AS ' . $columnAlias;
$this->scalarResultAliasMap[$resultAlias] = $columnAlias;
if ( ! $hidden) {
// We cannot resolve field type here; assume 'string'.
$this->rsm->addScalarResult($columnAlias, $resultAlias, 'string');
}
break;
default:
// IdentificationVariable or PartialObjectExpression
if ($expr instanceof AST\PartialObjectExpression) {
$dqlAlias = $expr->identificationVariable;
$partialFieldSet = $expr->partialFieldSet;
} else {
$dqlAlias = $expr;
$partialFieldSet = array();
}
$queryComp = $this->queryComponents[$dqlAlias];
$class = $queryComp['metadata'];
$resultAlias = $selectExpression->fieldIdentificationVariable ?: null;
if ( ! isset($this->selectedClasses[$dqlAlias])) {
$this->selectedClasses[$dqlAlias] = array(
'class' => $class,
'dqlAlias' => $dqlAlias,
'resultAlias' => $resultAlias
);
}
$sqlParts = array();
// Select all fields from the queried class
foreach ($class->fieldMappings as $fieldName => $mapping) {
if ($partialFieldSet && ! in_array($fieldName, $partialFieldSet)) {
continue;
}
$tableName = (isset($mapping['inherited']))
? $this->em->getClassMetadata($mapping['inherited'])->getTableName()
: $class->getTableName();
$sqlTableAlias = $this->getSQLTableAlias($tableName, $dqlAlias);
$columnAlias = $this->getSQLColumnAlias($mapping['columnName']);
$quotedColumnName = $this->quoteStrategy->getColumnName($fieldName, $class, $this->platform);
$col = $sqlTableAlias . '.' . $quotedColumnName;
if (isset($class->fieldMappings[$fieldName]['requireSQLConversion'])) {
$type = Type::getType($class->getTypeOfField($fieldName));
$col = $type->convertToPHPValueSQL($col, $this->platform);
}
$sqlParts[] = $col . ' AS '. $columnAlias;
$this->scalarResultAliasMap[$resultAlias][] = $columnAlias;
$this->rsm->addFieldResult($dqlAlias, $columnAlias, $fieldName, $class->name);
}
// Add any additional fields of subclasses (excluding inherited fields)
// 1) on Single Table Inheritance: always, since its marginal overhead
// 2) on Class Table Inheritance only if partial objects are disallowed,
// since it requires outer joining subtables.
if ($class->isInheritanceTypeSingleTable() || ! $this->query->getHint(Query::HINT_FORCE_PARTIAL_LOAD)) {
foreach ($class->subClasses as $subClassName) {
$subClass = $this->em->getClassMetadata($subClassName);
$sqlTableAlias = $this->getSQLTableAlias($subClass->getTableName(), $dqlAlias);
foreach ($subClass->fieldMappings as $fieldName => $mapping) {
if (isset($mapping['inherited']) || $partialFieldSet && !in_array($fieldName, $partialFieldSet)) {
continue;
}
$columnAlias = $this->getSQLColumnAlias($mapping['columnName']);
$quotedColumnName = $this->quoteStrategy->getColumnName($fieldName, $subClass, $this->platform);
$col = $sqlTableAlias . '.' . $quotedColumnName;
if (isset($subClass->fieldMappings[$fieldName]['requireSQLConversion'])) {
$type = Type::getType($subClass->getTypeOfField($fieldName));
$col = $type->convertToPHPValueSQL($col, $this->platform);
}
$sqlParts[] = $col . ' AS ' . $columnAlias;
$this->scalarResultAliasMap[$resultAlias][] = $columnAlias;
$this->rsm->addFieldResult($dqlAlias, $columnAlias, $fieldName, $subClassName);
}
}
}
$sql .= implode(', ', $sqlParts);
}
return $sql;
}
/**
* Walks down a QuantifiedExpression AST node, thereby generating the appropriate SQL.
*
* @param QuantifiedExpression
* @return string The SQL.
*/
public function walkQuantifiedExpression($qExpr)
{
return ' ' . strtoupper($qExpr->type) . '(' . $this->walkSubselect($qExpr->subselect) . ')';
}
/**
* Walks down a Subselect AST node, thereby generating the appropriate SQL.
*
* @param Subselect
* @return string The SQL.
*/
public function walkSubselect($subselect)
{
$useAliasesBefore = $this->useSqlTableAliases;
$rootAliasesBefore = $this->rootAliases;
$this->rootAliases = array(); // reset the rootAliases for the subselect
$this->useSqlTableAliases = true;
$sql = $this->walkSimpleSelectClause($subselect->simpleSelectClause);
$sql .= $this->walkSubselectFromClause($subselect->subselectFromClause);
$sql .= $this->walkWhereClause($subselect->whereClause);
$sql .= $subselect->groupByClause ? $this->walkGroupByClause($subselect->groupByClause) : '';
$sql .= $subselect->havingClause ? $this->walkHavingClause($subselect->havingClause) : '';
$sql .= $subselect->orderByClause ? $this->walkOrderByClause($subselect->orderByClause) : '';
$this->rootAliases = $rootAliasesBefore; // put the main aliases back
$this->useSqlTableAliases = $useAliasesBefore;
return $sql;
}
/**
* Walks down a SubselectFromClause AST node, thereby generating the appropriate SQL.
*
* @param SubselectFromClause
* @return string The SQL.
*/
public function walkSubselectFromClause($subselectFromClause)
{
$identificationVarDecls = $subselectFromClause->identificationVariableDeclarations;
$sqlParts = array ();
foreach ($identificationVarDecls as $subselectIdVarDecl) {
$sql = $this->platform->appendLockHint(
$this->walkRangeVariableDeclaration($subselectIdVarDecl->rangeVariableDeclaration),
$this->query->getHint(Query::HINT_LOCK_MODE)
);
foreach ($subselectIdVarDecl->joins as $join) {
$sql .= $this->walkJoin($join);
}
$sqlParts[] = $sql;
}
return ' FROM ' . implode(', ', $sqlParts);
}
/**
* Walks down a SimpleSelectClause AST node, thereby generating the appropriate SQL.
*
* @param SimpleSelectClause
* @return string The SQL.
*/
public function walkSimpleSelectClause($simpleSelectClause)
{
return 'SELECT' . ($simpleSelectClause->isDistinct ? ' DISTINCT' : '')
. $this->walkSimpleSelectExpression($simpleSelectClause->simpleSelectExpression);
}
/**
* Walks down a SimpleSelectExpression AST node, thereby generating the appropriate SQL.
*
* @param SimpleSelectExpression
* @return string The SQL.
*/
public function walkSimpleSelectExpression($simpleSelectExpression)
{
$expr = $simpleSelectExpression->expression;
$sql = ' ';
switch (true) {
case ($expr instanceof AST\PathExpression):
$sql .= $this->walkPathExpression($expr);
break;
case ($expr instanceof AST\AggregateExpression):
$alias = $simpleSelectExpression->fieldIdentificationVariable ?: $this->scalarResultCounter++;
$sql .= $this->walkAggregateExpression($expr) . ' AS dctrn__' . $alias;
break;
case ($expr instanceof AST\Subselect):
$alias = $simpleSelectExpression->fieldIdentificationVariable ?: $this->scalarResultCounter++;
$columnAlias = 'sclr' . $this->aliasCounter++;
$this->scalarResultAliasMap[$alias] = $columnAlias;
$sql .= '(' . $this->walkSubselect($expr) . ') AS ' . $columnAlias;
break;
case ($expr instanceof AST\Functions\FunctionNode):
case ($expr instanceof AST\SimpleArithmeticExpression):
case ($expr instanceof AST\ArithmeticTerm):
case ($expr instanceof AST\ArithmeticFactor):
case ($expr instanceof AST\Literal):
case ($expr instanceof AST\NullIfExpression):
case ($expr instanceof AST\CoalesceExpression):
case ($expr instanceof AST\GeneralCaseExpression):
case ($expr instanceof AST\SimpleCaseExpression):
$alias = $simpleSelectExpression->fieldIdentificationVariable ?: $this->scalarResultCounter++;
$columnAlias = $this->getSQLColumnAlias('sclr');
$this->scalarResultAliasMap[$alias] = $columnAlias;
$sql .= $expr->dispatch($this) . ' AS ' . $columnAlias;
break;
default: // IdentificationVariable
$sql .= $this->walkEntityIdentificationVariable($expr);
break;
}
return $sql;
}
/**
* Walks down an AggregateExpression AST node, thereby generating the appropriate SQL.
*
* @param AggregateExpression
* @return string The SQL.
*/
public function walkAggregateExpression($aggExpression)
{
return $aggExpression->functionName . '(' . ($aggExpression->isDistinct ? 'DISTINCT ' : '')
. $this->walkSimpleArithmeticExpression($aggExpression->pathExpression) . ')';
}
/**
* Walks down a GroupByClause AST node, thereby generating the appropriate SQL.
*
* @param GroupByClause
* @return string The SQL.
*/
public function walkGroupByClause($groupByClause)
{
$sqlParts = array();
foreach ($groupByClause->groupByItems as $groupByItem) {
$sqlParts[] = $this->walkGroupByItem($groupByItem);
}
return ' GROUP BY ' . implode(', ', $sqlParts);
}
/**
* Walks down a GroupByItem AST node, thereby generating the appropriate SQL.
*
* @param GroupByItem
* @return string The SQL.
*/
public function walkGroupByItem($groupByItem)
{
// StateFieldPathExpression
if ( ! is_string($groupByItem)) {
return $this->walkPathExpression($groupByItem);
}
// ResultVariable
if (isset($this->queryComponents[$groupByItem]['resultVariable'])) {
return $this->walkResultVariable($groupByItem);
}
// IdentificationVariable
$sqlParts = array();
foreach ($this->queryComponents[$groupByItem]['metadata']->fieldNames as $field) {
$item = new AST\PathExpression(AST\PathExpression::TYPE_STATE_FIELD, $groupByItem, $field);
$item->type = AST\PathExpression::TYPE_STATE_FIELD;
$sqlParts[] = $this->walkPathExpression($item);
}
foreach ($this->queryComponents[$groupByItem]['metadata']->associationMappings as $mapping) {
if ($mapping['isOwningSide'] && $mapping['type'] & ClassMetadataInfo::TO_ONE) {
$item = new AST\PathExpression(AST\PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION, $groupByItem, $mapping['fieldName']);
$item->type = AST\PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION;
$sqlParts[] = $this->walkPathExpression($item);
}
}
return implode(', ', $sqlParts);
}
/**
* Walks down a DeleteClause AST node, thereby generating the appropriate SQL.
*
* @param DeleteClause
* @return string The SQL.
*/
public function walkDeleteClause(AST\DeleteClause $deleteClause)
{
$class = $this->em->getClassMetadata($deleteClause->abstractSchemaName);
$tableName = $class->getTableName();
$sql = 'DELETE FROM ' . $this->quoteStrategy->getTableName($class, $this->platform);
$this->setSQLTableAlias($tableName, $tableName, $deleteClause->aliasIdentificationVariable);
$this->rootAliases[] = $deleteClause->aliasIdentificationVariable;
return $sql;
}
/**
* Walks down an UpdateClause AST node, thereby generating the appropriate SQL.
*
* @param UpdateClause
* @return string The SQL.
*/
public function walkUpdateClause($updateClause)
{
$class = $this->em->getClassMetadata($updateClause->abstractSchemaName);
$tableName = $class->getTableName();
$sql = 'UPDATE ' . $this->quoteStrategy->getTableName($class, $this->platform);
$this->setSQLTableAlias($tableName, $tableName, $updateClause->aliasIdentificationVariable);
$this->rootAliases[] = $updateClause->aliasIdentificationVariable;
$sql .= ' SET ' . implode(', ', array_map(array($this, 'walkUpdateItem'), $updateClause->updateItems));
return $sql;
}
/**
* Walks down an UpdateItem AST node, thereby generating the appropriate SQL.
*
* @param UpdateItem
* @return string The SQL.
*/
public function walkUpdateItem($updateItem)
{
$useTableAliasesBefore = $this->useSqlTableAliases;
$this->useSqlTableAliases = false;
$sql = $this->walkPathExpression($updateItem->pathExpression) . ' = ';
$newValue = $updateItem->newValue;
switch (true) {
case ($newValue instanceof AST\Node):
$sql .= $newValue->dispatch($this);
break;
case ($newValue === null):
$sql .= 'NULL';
break;
default:
$sql .= $this->conn->quote($newValue);
break;
}
$this->useSqlTableAliases = $useTableAliasesBefore;
return $sql;
}
/**
* Walks down a WhereClause AST node, thereby generating the appropriate SQL.
* WhereClause or not, the appropriate discriminator sql is added.
*
* @param WhereClause
* @return string The SQL.
*/
public function walkWhereClause($whereClause)
{
$condSql = null !== $whereClause ? $this->walkConditionalExpression($whereClause->conditionalExpression) : '';
$discrSql = $this->_generateDiscriminatorColumnConditionSql($this->rootAliases);
if ($this->em->hasFilters()) {
$filterClauses = array();
foreach ($this->rootAliases as $dqlAlias) {
$class = $this->queryComponents[$dqlAlias]['metadata'];
$tableAlias = $this->getSQLTableAlias($class->table['name'], $dqlAlias);
if ($filterExpr = $this->generateFilterConditionSQL($class, $tableAlias)) {
$filterClauses[] = $filterExpr;
}
}
if (count($filterClauses)) {
if ($condSql) {
$condSql = '(' . $condSql . ') AND ';
}
$condSql .= implode(' AND ', $filterClauses);
}
}
if ($condSql) {
return ' WHERE ' . (( ! $discrSql) ? $condSql : '(' . $condSql . ') AND ' . $discrSql);
}
if ($discrSql) {
return ' WHERE ' . $discrSql;
}
return '';
}
/**
* Walk down a ConditionalExpression AST node, thereby generating the appropriate SQL.
*
* @param ConditionalExpression
* @return string The SQL.
*/
public function walkConditionalExpression($condExpr)
{
// Phase 2 AST optimization: Skip processment of ConditionalExpression
// if only one ConditionalTerm is defined
if ( ! ($condExpr instanceof AST\ConditionalExpression)) {
return $this->walkConditionalTerm($condExpr);
}
return implode(' OR ', array_map(array($this, 'walkConditionalTerm'), $condExpr->conditionalTerms));
}
/**
* Walks down a ConditionalTerm AST node, thereby generating the appropriate SQL.
*
* @param ConditionalTerm
* @return string The SQL.
*/
public function walkConditionalTerm($condTerm)
{
// Phase 2 AST optimization: Skip processment of ConditionalTerm
// if only one ConditionalFactor is defined
if ( ! ($condTerm instanceof AST\ConditionalTerm)) {
return $this->walkConditionalFactor($condTerm);
}
return implode(' AND ', array_map(array($this, 'walkConditionalFactor'), $condTerm->conditionalFactors));
}
/**
* Walks down a ConditionalFactor AST node, thereby generating the appropriate SQL.
*
* @param ConditionalFactor
* @return string The SQL.
*/
public function walkConditionalFactor($factor)
{
// Phase 2 AST optimization: Skip processment of ConditionalFactor
// if only one ConditionalPrimary is defined
return ( ! ($factor instanceof AST\ConditionalFactor))
? $this->walkConditionalPrimary($factor)
: ($factor->not ? 'NOT ' : '') . $this->walkConditionalPrimary($factor->conditionalPrimary);
}
/**
* Walks down a ConditionalPrimary AST node, thereby generating the appropriate SQL.
*
* @param ConditionalPrimary
* @return string The SQL.
*/
public function walkConditionalPrimary($primary)
{
if ($primary->isSimpleConditionalExpression()) {
return $primary->simpleConditionalExpression->dispatch($this);
}
if ($primary->isConditionalExpression()) {
$condExpr = $primary->conditionalExpression;
return '(' . $this->walkConditionalExpression($condExpr) . ')';
}
}
/**
* Walks down an ExistsExpression AST node, thereby generating the appropriate SQL.
*
* @param ExistsExpression
* @return string The SQL.
*/
public function walkExistsExpression($existsExpr)
{
$sql = ($existsExpr->not) ? 'NOT ' : '';
$sql .= 'EXISTS (' . $this->walkSubselect($existsExpr->subselect) . ')';
return $sql;
}
/**
* Walks down a CollectionMemberExpression AST node, thereby generating the appropriate SQL.
*
* @param CollectionMemberExpression
* @return string The SQL.
*/
public function walkCollectionMemberExpression($collMemberExpr)
{
$sql = $collMemberExpr->not ? 'NOT ' : '';
$sql .= 'EXISTS (SELECT 1 FROM ';
$entityExpr = $collMemberExpr->entityExpression;
$collPathExpr = $collMemberExpr->collectionValuedPathExpression;
$fieldName = $collPathExpr->field;
$dqlAlias = $collPathExpr->identificationVariable;
$class = $this->queryComponents[$dqlAlias]['metadata'];
switch (true) {
// InputParameter
case ($entityExpr instanceof AST\InputParameter):
$dqlParamKey = $entityExpr->name;
$entitySql = '?';
break;
// SingleValuedAssociationPathExpression | IdentificationVariable
case ($entityExpr instanceof AST\PathExpression):
$entitySql = $this->walkPathExpression($entityExpr);
break;
default:
throw new \BadMethodCallException("Not implemented");
}
$assoc = $class->associationMappings[$fieldName];
if ($assoc['type'] == ClassMetadata::ONE_TO_MANY) {
$targetClass = $this->em->getClassMetadata($assoc['targetEntity']);
$targetTableAlias = $this->getSQLTableAlias($targetClass->getTableName());
$sourceTableAlias = $this->getSQLTableAlias($class->getTableName(), $dqlAlias);
$sql .= $this->quoteStrategy->getTableName($targetClass, $this->platform) . ' ' . $targetTableAlias . ' WHERE ';
$owningAssoc = $targetClass->associationMappings[$assoc['mappedBy']];
$sqlParts = array();
foreach ($owningAssoc['targetToSourceKeyColumns'] as $targetColumn => $sourceColumn) {
$targetColumn = $this->quoteStrategy->getColumnName($class->fieldNames[$targetColumn], $class, $this->platform);
$sqlParts[] = $sourceTableAlias . '.' . $targetColumn . ' = ' . $targetTableAlias . '.' . $sourceColumn;
}
foreach ($this->quoteStrategy->getIdentifierColumnNames($targetClass, $this->platform) as $targetColumnName) {
if (isset($dqlParamKey)) {
$this->parserResult->addParameterMapping($dqlParamKey, $this->sqlParamIndex++);
}
$sqlParts[] = $targetTableAlias . '.' . $targetColumnName . ' = ' . $entitySql;
}
$sql .= implode(' AND ', $sqlParts);
} else { // many-to-many
$targetClass = $this->em->getClassMetadata($assoc['targetEntity']);
$owningAssoc = $assoc['isOwningSide'] ? $assoc : $targetClass->associationMappings[$assoc['mappedBy']];
$joinTable = $owningAssoc['joinTable'];
// SQL table aliases
$joinTableAlias = $this->getSQLTableAlias($joinTable['name']);
$targetTableAlias = $this->getSQLTableAlias($targetClass->getTableName());
$sourceTableAlias = $this->getSQLTableAlias($class->getTableName(), $dqlAlias);
// join to target table
$sql .= $this->quoteStrategy->getJoinTableName($owningAssoc, $targetClass, $this->platform) . ' ' . $joinTableAlias
. ' INNER JOIN ' . $this->quoteStrategy->getTableName($targetClass, $this->platform) . ' ' . $targetTableAlias . ' ON ';
// join conditions
$joinColumns = $assoc['isOwningSide'] ? $joinTable['inverseJoinColumns'] : $joinTable['joinColumns'];
$joinSqlParts = array();
foreach ($joinColumns as $joinColumn) {
$targetColumn = $this->quoteStrategy->getColumnName($targetClass->fieldNames[$joinColumn['referencedColumnName']], $targetClass, $this->platform);
$joinSqlParts[] = $joinTableAlias . '.' . $joinColumn['name'] . ' = ' . $targetTableAlias . '.' . $targetColumn;
}
$sql .= implode(' AND ', $joinSqlParts);
$sql .= ' WHERE ';
$joinColumns = $assoc['isOwningSide'] ? $joinTable['joinColumns'] : $joinTable['inverseJoinColumns'];
$sqlParts = array();
foreach ($joinColumns as $joinColumn) {
$targetColumn = $this->quoteStrategy->getColumnName($class->fieldNames[$joinColumn['referencedColumnName']], $class, $this->platform);
$sqlParts[] = $joinTableAlias . '.' . $joinColumn['name'] . ' = ' . $sourceTableAlias . '.' . $targetColumn;
}
foreach ($this->quoteStrategy->getIdentifierColumnNames($targetClass, $this->platform) as $targetColumnName) {
if (isset($dqlParamKey)) {
$this->parserResult->addParameterMapping($dqlParamKey, $this->sqlParamIndex++);
}
$sqlParts[] = $targetTableAlias . '.' . $targetColumnName . ' = ' . $entitySql;
}
$sql .= implode(' AND ', $sqlParts);
}
return $sql . ')';
}
/**
* Walks down an EmptyCollectionComparisonExpression AST node, thereby generating the appropriate SQL.
*
* @param EmptyCollectionComparisonExpression
* @return string The SQL.
*/
public function walkEmptyCollectionComparisonExpression($emptyCollCompExpr)
{
$sizeFunc = new AST\Functions\SizeFunction('size');
$sizeFunc->collectionPathExpression = $emptyCollCompExpr->expression;
return $sizeFunc->getSql($this) . ($emptyCollCompExpr->not ? ' > 0' : ' = 0');
}
/**
* Walks down a NullComparisonExpression AST node, thereby generating the appropriate SQL.
*
* @param NullComparisonExpression
* @return string The SQL.
*/
public function walkNullComparisonExpression($nullCompExpr)
{
$sql = '';
$innerExpr = $nullCompExpr->expression;
if ($innerExpr instanceof AST\InputParameter) {
$dqlParamKey = $innerExpr->name;
$this->parserResult->addParameterMapping($dqlParamKey, $this->sqlParamIndex++);
$sql .= ' ?';
} else {
$sql .= $this->walkPathExpression($innerExpr);
}
$sql .= ' IS' . ($nullCompExpr->not ? ' NOT' : '') . ' NULL';
return $sql;
}
/**
* Walks down an InExpression AST node, thereby generating the appropriate SQL.
*
* @param InExpression
* @return string The SQL.
*/
public function walkInExpression($inExpr)
{
$sql = $this->walkArithmeticExpression($inExpr->expression) . ($inExpr->not ? ' NOT' : '') . ' IN (';
$sql .= ($inExpr->subselect)
? $this->walkSubselect($inExpr->subselect)
: implode(', ', array_map(array($this, 'walkInParameter'), $inExpr->literals));
$sql .= ')';
return $sql;
}
/**
* Walks down an InstanceOfExpression AST node, thereby generating the appropriate SQL.
*
* @param InstanceOfExpression
* @return string The SQL.
*/
public function walkInstanceOfExpression($instanceOfExpr)
{
$sql = '';
$dqlAlias = $instanceOfExpr->identificationVariable;
$discrClass = $class = $this->queryComponents[$dqlAlias]['metadata'];
if ($class->discriminatorColumn) {
$discrClass = $this->em->getClassMetadata($class->rootEntityName);
}
if ($this->useSqlTableAliases) {
$sql .= $this->getSQLTableAlias($discrClass->getTableName(), $dqlAlias) . '.';
}
$sql .= $class->discriminatorColumn['name'] . ($instanceOfExpr->not ? ' NOT IN ' : ' IN ');
$sqlParameterList = array();
foreach ($instanceOfExpr->value as $parameter) {
if ($parameter instanceof AST\InputParameter) {
// We need to modify the parameter value to be its correspondent mapped value
$dqlParamKey = $parameter->name;
$dqlParam = $this->query->getParameter($dqlParamKey);
$paramValue = $this->query->processParameterValue($dqlParam->getValue());
if ( ! ($paramValue instanceof \Doctrine\ORM\Mapping\ClassMetadata)) {
throw QueryException::invalidParameterType('ClassMetadata', get_class($paramValue));
}
$entityClassName = $paramValue->name;
} else {
// Get name from ClassMetadata to resolve aliases.
$entityClassName = $this->em->getClassMetadata($parameter)->name;
}
if ($entityClassName == $class->name) {
$sqlParameterList[] = $this->conn->quote($class->discriminatorValue);
} else {
$discrMap = array_flip($class->discriminatorMap);
if (!isset($discrMap[$entityClassName])) {
throw QueryException::instanceOfUnrelatedClass($entityClassName, $class->rootEntityName);
}
$sqlParameterList[] = $this->conn->quote($discrMap[$entityClassName]);
}
}
$sql .= '(' . implode(', ', $sqlParameterList) . ')';
return $sql;
}
/**
* Walks down an InParameter AST node, thereby generating the appropriate SQL.
*
* @param InParameter
* @return string The SQL.
*/
public function walkInParameter($inParam)
{
return $inParam instanceof AST\InputParameter
? $this->walkInputParameter($inParam)
: $this->walkLiteral($inParam);
}
/**
* Walks down a literal that represents an AST node, thereby generating the appropriate SQL.
*
* @param mixed
* @return string The SQL.
*/
public function walkLiteral($literal)
{
switch ($literal->type) {
case AST\Literal::STRING:
return $this->conn->quote($literal->value);
case AST\Literal::BOOLEAN:
$bool = strtolower($literal->value) == 'true' ? true : false;
$boolVal = $this->conn->getDatabasePlatform()->convertBooleans($bool);
return $boolVal;
case AST\Literal::NUMERIC:
return $literal->value;
default:
throw QueryException::invalidLiteral($literal);
}
}
/**
* Walks down a BetweenExpression AST node, thereby generating the appropriate SQL.
*
* @param BetweenExpression
* @return string The SQL.
*/
public function walkBetweenExpression($betweenExpr)
{
$sql = $this->walkArithmeticExpression($betweenExpr->expression);
if ($betweenExpr->not) $sql .= ' NOT';
$sql .= ' BETWEEN ' . $this->walkArithmeticExpression($betweenExpr->leftBetweenExpression)
. ' AND ' . $this->walkArithmeticExpression($betweenExpr->rightBetweenExpression);
return $sql;
}
/**
* Walks down a LikeExpression AST node, thereby generating the appropriate SQL.
*
* @param LikeExpression
* @return string The SQL.
*/
public function walkLikeExpression($likeExpr)
{
$stringExpr = $likeExpr->stringExpression;
$sql = $stringExpr->dispatch($this) . ($likeExpr->not ? ' NOT' : '') . ' LIKE ';
if ($likeExpr->stringPattern instanceof AST\InputParameter) {
$inputParam = $likeExpr->stringPattern;
$dqlParamKey = $inputParam->name;
$this->parserResult->addParameterMapping($dqlParamKey, $this->sqlParamIndex++);
$sql .= '?';
} elseif ($likeExpr->stringPattern instanceof AST\Functions\FunctionNode ) {
$sql .= $this->walkFunction($likeExpr->stringPattern);
} elseif ($likeExpr->stringPattern instanceof AST\PathExpression) {
$sql .= $this->walkPathExpression($likeExpr->stringPattern);
} else {
$sql .= $this->walkLiteral($likeExpr->stringPattern);
}
if ($likeExpr->escapeChar) {
$sql .= ' ESCAPE ' . $this->walkLiteral($likeExpr->escapeChar);
}
return $sql;
}
/**
* Walks down a StateFieldPathExpression AST node, thereby generating the appropriate SQL.
*
* @param StateFieldPathExpression
* @return string The SQL.
*/
public function walkStateFieldPathExpression($stateFieldPathExpression)
{
return $this->walkPathExpression($stateFieldPathExpression);
}
/**
* Walks down a ComparisonExpression AST node, thereby generating the appropriate SQL.
*
* @param ComparisonExpression
* @return string The SQL.
*/
public function walkComparisonExpression($compExpr)
{
$leftExpr = $compExpr->leftExpression;
$rightExpr = $compExpr->rightExpression;
$sql = '';
$sql .= ($leftExpr instanceof AST\Node)
? $leftExpr->dispatch($this)
: (is_numeric($leftExpr) ? $leftExpr : $this->conn->quote($leftExpr));
$sql .= ' ' . $compExpr->operator . ' ';
$sql .= ($rightExpr instanceof AST\Node)
? $rightExpr->dispatch($this)
: (is_numeric($rightExpr) ? $rightExpr : $this->conn->quote($rightExpr));
return $sql;
}
/**
* Walks down an InputParameter AST node, thereby generating the appropriate SQL.
*
* @param InputParameter
* @return string The SQL.
*/
public function walkInputParameter($inputParam)
{
$this->parserResult->addParameterMapping($inputParam->name, $this->sqlParamIndex++);
return '?';
}
/**
* Walks down an ArithmeticExpression AST node, thereby generating the appropriate SQL.
*
* @param ArithmeticExpression
* @return string The SQL.
*/
public function walkArithmeticExpression($arithmeticExpr)
{
return ($arithmeticExpr->isSimpleArithmeticExpression())
? $this->walkSimpleArithmeticExpression($arithmeticExpr->simpleArithmeticExpression)
: '(' . $this->walkSubselect($arithmeticExpr->subselect) . ')';
}
/**
* Walks down an SimpleArithmeticExpression AST node, thereby generating the appropriate SQL.
*
* @param SimpleArithmeticExpression
* @return string The SQL.
*/
public function walkSimpleArithmeticExpression($simpleArithmeticExpr)
{
if ( ! ($simpleArithmeticExpr instanceof AST\SimpleArithmeticExpression)) {
return $this->walkArithmeticTerm($simpleArithmeticExpr);
}
return implode(' ', array_map(array($this, 'walkArithmeticTerm'), $simpleArithmeticExpr->arithmeticTerms));
}
/**
* Walks down an ArithmeticTerm AST node, thereby generating the appropriate SQL.
*
* @param mixed
* @return string The SQL.
*/
public function walkArithmeticTerm($term)
{
if (is_string($term)) {
return (isset($this->queryComponents[$term]))
? $this->walkResultVariable($this->queryComponents[$term]['token']['value'])
: $term;
}
// Phase 2 AST optimization: Skip processment of ArithmeticTerm
// if only one ArithmeticFactor is defined
if ( ! ($term instanceof AST\ArithmeticTerm)) {
return $this->walkArithmeticFactor($term);
}
return implode(' ', array_map(array($this, 'walkArithmeticFactor'), $term->arithmeticFactors));
}
/**
* Walks down an ArithmeticFactor that represents an AST node, thereby generating the appropriate SQL.
*
* @param mixed
* @return string The SQL.
*/
public function walkArithmeticFactor($factor)
{
if (is_string($factor)) {
return $factor;
}
// Phase 2 AST optimization: Skip processment of ArithmeticFactor
// if only one ArithmeticPrimary is defined
if ( ! ($factor instanceof AST\ArithmeticFactor)) {
return $this->walkArithmeticPrimary($factor);
}
$sign = $factor->isNegativeSigned() ? '-' : ($factor->isPositiveSigned() ? '+' : '');
return $sign . $this->walkArithmeticPrimary($factor->arithmeticPrimary);
}
/**
* Walks down an ArithmeticPrimary that represents an AST node, thereby generating the appropriate SQL.
*
* @param mixed
* @return string The SQL.
*/
public function walkArithmeticPrimary($primary)
{
if ($primary instanceof AST\SimpleArithmeticExpression) {
return '(' . $this->walkSimpleArithmeticExpression($primary) . ')';
}
if ($primary instanceof AST\Node) {
return $primary->dispatch($this);
}
return $this->walkEntityIdentificationVariable($primary);
}
/**
* Walks down a StringPrimary that represents an AST node, thereby generating the appropriate SQL.
*
* @param mixed
* @return string The SQL.
*/
public function walkStringPrimary($stringPrimary)
{
return (is_string($stringPrimary))
? $this->conn->quote($stringPrimary)
: $stringPrimary->dispatch($this);
}
/**
* Walks down a ResultVriable that represents an AST node, thereby generating the appropriate SQL.
*
* @param string $resultVariable
* @return string The SQL.
*/
public function walkResultVariable($resultVariable)
{
$resultAlias = $this->scalarResultAliasMap[$resultVariable];
if (is_array($resultAlias)) {
return implode(', ', $resultAlias);
}
return $resultAlias;
}
}
DoctrineORM-2.3.3/Doctrine/ORM/Query/TreeWalker.php 0000644 0001751 0001751 00000027241 12143374607 022056 0 ustar beberlei beberlei .
*/
namespace Doctrine\ORM\Query;
/**
* Interface for walkers of DQL ASTs (abstract syntax trees).
*
* @author Roman Borschel
* @since 2.0
*/
interface TreeWalker
{
/**
* Initializes TreeWalker with important information about the ASTs to be walked
*
* @param Query $query The parsed Query.
* @param ParserResult $parserResult The result of the parsing process.
* @param array $queryComponents Query components (symbol table)
*/
public function __construct($query, $parserResult, array $queryComponents);
/**
* Walks down a SelectStatement AST node, thereby generating the appropriate SQL.
*
* @return string The SQL.
*/
function walkSelectStatement(AST\SelectStatement $AST);
/**
* Walks down a SelectClause AST node, thereby generating the appropriate SQL.
*
* @return string The SQL.
*/
function walkSelectClause($selectClause);
/**
* Walks down a FromClause AST node, thereby generating the appropriate SQL.
*
* @return string The SQL.
*/
function walkFromClause($fromClause);
/**
* Walks down a FunctionNode AST node, thereby generating the appropriate SQL.
*
* @return string The SQL.
*/
function walkFunction($function);
/**
* Walks down an OrderByClause AST node, thereby generating the appropriate SQL.
*
* @param OrderByClause
* @return string The SQL.
*/
function walkOrderByClause($orderByClause);
/**
* Walks down an OrderByItem AST node, thereby generating the appropriate SQL.
*
* @param OrderByItem
* @return string The SQL.
*/
function walkOrderByItem($orderByItem);
/**
* Walks down a HavingClause AST node, thereby generating the appropriate SQL.
*
* @param HavingClause
* @return string The SQL.
*/
function walkHavingClause($havingClause);
/**
* Walks down a Join AST node and creates the corresponding SQL.
*
* @param Join $joinVarDecl
* @return string The SQL.
*/
function walkJoin($join);
/**
* Walks down a SelectExpression AST node and generates the corresponding SQL.
*
* @param SelectExpression $selectExpression
* @return string The SQL.
*/
function walkSelectExpression($selectExpression);
/**
* Walks down a QuantifiedExpression AST node, thereby generating the appropriate SQL.
*
* @param QuantifiedExpression
* @return string The SQL.
*/
function walkQuantifiedExpression($qExpr);
/**
* Walks down a Subselect AST node, thereby generating the appropriate SQL.
*
* @param Subselect
* @return string The SQL.
*/
function walkSubselect($subselect);
/**
* Walks down a SubselectFromClause AST node, thereby generating the appropriate SQL.
*
* @param SubselectFromClause
* @return string The SQL.
*/
function walkSubselectFromClause($subselectFromClause);
/**
* Walks down a SimpleSelectClause AST node, thereby generating the appropriate SQL.
*
* @param SimpleSelectClause
* @return string The SQL.
*/
function walkSimpleSelectClause($simpleSelectClause);
/**
* Walks down a SimpleSelectExpression AST node, thereby generating the appropriate SQL.
*
* @param SimpleSelectExpression
* @return string The SQL.
*/
function walkSimpleSelectExpression($simpleSelectExpression);
/**
* Walks down an AggregateExpression AST node, thereby generating the appropriate SQL.
*
* @param AggregateExpression
* @return string The SQL.
*/
function walkAggregateExpression($aggExpression);
/**
* Walks down a GroupByClause AST node, thereby generating the appropriate SQL.
*
* @param GroupByClause
* @return string The SQL.
*/
function walkGroupByClause($groupByClause);
/**
* Walks down a GroupByItem AST node, thereby generating the appropriate SQL.
*
* @param GroupByItem
* @return string The SQL.
*/
function walkGroupByItem($groupByItem);
/**
* Walks down an UpdateStatement AST node, thereby generating the appropriate SQL.
*
* @param UpdateStatement
* @return string The SQL.
*/
function walkUpdateStatement(AST\UpdateStatement $AST);
/**
* Walks down a DeleteStatement AST node, thereby generating the appropriate SQL.
*
* @param DeleteStatement
* @return string The SQL.
*/
function walkDeleteStatement(AST\DeleteStatement $AST);
/**
* Walks down a DeleteClause AST node, thereby generating the appropriate SQL.
*
* @param DeleteClause
* @return string The SQL.
*/
function walkDeleteClause(AST\DeleteClause $deleteClause);
/**
* Walks down an UpdateClause AST node, thereby generating the appropriate SQL.
*
* @param UpdateClause
* @return string The SQL.
*/
function walkUpdateClause($updateClause);
/**
* Walks down an UpdateItem AST node, thereby generating the appropriate SQL.
*
* @param UpdateItem
* @return string The SQL.
*/
function walkUpdateItem($updateItem);
/**
* Walks down a WhereClause AST node, thereby generating the appropriate SQL.
*
* @param WhereClause
* @return string The SQL.
*/
function walkWhereClause($whereClause);
/**
* Walks down a ConditionalExpression AST node, thereby generating the appropriate SQL.
*
* @param ConditionalExpression
* @return string The SQL.
*/
function walkConditionalExpression($condExpr);
/**
* Walks down a ConditionalTerm AST node, thereby generating the appropriate SQL.
*
* @param ConditionalTerm
* @return string The SQL.
*/
function walkConditionalTerm($condTerm);
/**
* Walks down a ConditionalFactor AST node, thereby generating the appropriate SQL.
*
* @param ConditionalFactor
* @return string The SQL.
*/
function walkConditionalFactor($factor);
/**
* Walks down a ConditionalPrimary AST node, thereby generating the appropriate SQL.
*
* @param ConditionalPrimary
* @return string The SQL.
*/
function walkConditionalPrimary($primary);
/**
* Walks down an ExistsExpression AST node, thereby generating the appropriate SQL.
*
* @param ExistsExpression
* @return string The SQL.
*/
function walkExistsExpression($existsExpr);
/**
* Walks down a CollectionMemberExpression AST node, thereby generating the appropriate SQL.
*
* @param CollectionMemberExpression
* @return string The SQL.
*/
function walkCollectionMemberExpression($collMemberExpr);
/**
* Walks down an EmptyCollectionComparisonExpression AST node, thereby generating the appropriate SQL.
*
* @param EmptyCollectionComparisonExpression
* @return string The SQL.
*/
function walkEmptyCollectionComparisonExpression($emptyCollCompExpr);
/**
* Walks down a NullComparisonExpression AST node, thereby generating the appropriate SQL.
*
* @param NullComparisonExpression
* @return string The SQL.
*/
function walkNullComparisonExpression($nullCompExpr);
/**
* Walks down an InExpression AST node, thereby generating the appropriate SQL.
*
* @param InExpression
* @return string The SQL.
*/
function walkInExpression($inExpr);
/**
* Walks down an InstanceOfExpression AST node, thereby generating the appropriate SQL.
*
* @param InstanceOfExpression
* @return string The SQL.
*/
function walkInstanceOfExpression($instanceOfExpr);
/**
* Walks down a literal that represents an AST node, thereby generating the appropriate SQL.
*
* @param mixed
* @return string The SQL.
*/
function walkLiteral($literal);
/**
* Walks down a BetweenExpression AST node, thereby generating the appropriate SQL.
*
* @param BetweenExpression
* @return string The SQL.
*/
function walkBetweenExpression($betweenExpr);
/**
* Walks down a LikeExpression AST node, thereby generating the appropriate SQL.
*
* @param LikeExpression
* @return string The SQL.
*/
function walkLikeExpression($likeExpr);
/**
* Walks down a StateFieldPathExpression AST node, thereby generating the appropriate SQL.
*
* @param StateFieldPathExpression
* @return string The SQL.
*/
function walkStateFieldPathExpression($stateFieldPathExpression);
/**
* Walks down a ComparisonExpression AST node, thereby generating the appropriate SQL.
*
* @param ComparisonExpression
* @return string The SQL.
*/
function walkComparisonExpression($compExpr);
/**
* Walks down an InputParameter AST node, thereby generating the appropriate SQL.
*
* @param InputParameter
* @return string The SQL.
*/
function walkInputParameter($inputParam);
/**
* Walks down an ArithmeticExpression AST node, thereby generating the appropriate SQL.
*
* @param ArithmeticExpression
* @return string The SQL.
*/
function walkArithmeticExpression($arithmeticExpr);
/**
* Walks down an ArithmeticTerm AST node, thereby generating the appropriate SQL.
*
* @param mixed
* @return string The SQL.
*/
function walkArithmeticTerm($term);
/**
* Walks down a StringPrimary that represents an AST node, thereby generating the appropriate SQL.
*
* @param mixed
* @return string The SQL.
*/
function walkStringPrimary($stringPrimary);
/**
* Walks down an ArithmeticFactor that represents an AST node, thereby generating the appropriate SQL.
*
* @param mixed
* @return string The SQL.
*/
function walkArithmeticFactor($factor);
/**
* Walks down an SimpleArithmeticExpression AST node, thereby generating the appropriate SQL.
*
* @param SimpleArithmeticExpression
* @return string The SQL.
*/
function walkSimpleArithmeticExpression($simpleArithmeticExpr);
/**
* Walks down an PathExpression AST node, thereby generating the appropriate SQL.
*
* @param mixed
* @return string The SQL.
*/
function walkPathExpression($pathExpr);
/**
* Walks down an ResultVariable AST node, thereby generating the appropriate SQL.
*
* @param string $resultVariable
* @return string The SQL.
*/
function walkResultVariable($resultVariable);
/**
* Gets an executor that can be used to execute the result of this walker.
*
* @return AbstractExecutor
*/
function getExecutor($AST);
}
DoctrineORM-2.3.3/Doctrine/ORM/Query/TreeWalkerAdapter.php 0000644 0001751 0001751 00000031264 12143374607 023357 0 ustar beberlei beberlei .
*/
namespace Doctrine\ORM\Query;
/**
* An adapter implementation of the TreeWalker interface. The methods in this class
* are empty. This class exists as convenience for creating tree walkers.
*
* @author Roman Borschel
* @since 2.0
*/
abstract class TreeWalkerAdapter implements TreeWalker
{
private $_query;
private $_parserResult;
private $_queryComponents;
/**
* {@inheritdoc}
*/
public function __construct($query, $parserResult, array $queryComponents)
{
$this->_query = $query;
$this->_parserResult = $parserResult;
$this->_queryComponents = $queryComponents;
}
/**
* @return array
*/
protected function _getQueryComponents()
{
return $this->_queryComponents;
}
/**
* Retrieve Query Instance reponsible for the current walkers execution.
*
* @return \Doctrine\ORM\Query
*/
protected function _getQuery()
{
return $this->_query;
}
/**
* Retrieve ParserResult
*
* @return \Doctrine\ORM\Query\ParserResult
*/
protected function _getParserResult()
{
return $this->_parserResult;
}
/**
* Walks down a SelectStatement AST node, thereby generating the appropriate SQL.
*
* @return string The SQL.
*/
public function walkSelectStatement(AST\SelectStatement $AST) {}
/**
* Walks down a SelectClause AST node, thereby generating the appropriate SQL.
*
* @return string The SQL.
*/
public function walkSelectClause($selectClause) {}
/**
* Walks down a FromClause AST node, thereby generating the appropriate SQL.
*
* @return string The SQL.
*/
public function walkFromClause($fromClause) {}
/**
* Walks down a FunctionNode AST node, thereby generating the appropriate SQL.
*
* @return string The SQL.
*/
public function walkFunction($function) {}
/**
* Walks down an OrderByClause AST node, thereby generating the appropriate SQL.
*
* @param OrderByClause
* @return string The SQL.
*/
public function walkOrderByClause($orderByClause) {}
/**
* Walks down an OrderByItem AST node, thereby generating the appropriate SQL.
*
* @param OrderByItem
* @return string The SQL.
*/
public function walkOrderByItem($orderByItem) {}
/**
* Walks down a HavingClause AST node, thereby generating the appropriate SQL.
*
* @param HavingClause
* @return string The SQL.
*/
public function walkHavingClause($havingClause) {}
/**
* Walks down a Join AST node and creates the corresponding SQL.
*
* @param Join $join
* @return string The SQL.
*/
public function walkJoin($join) {}
/**
* Walks down a SelectExpression AST node and generates the corresponding SQL.
*
* @param SelectExpression $selectExpression
* @return string The SQL.
*/
public function walkSelectExpression($selectExpression) {}
/**
* Walks down a QuantifiedExpression AST node, thereby generating the appropriate SQL.
*
* @param QuantifiedExpression
* @return string The SQL.
*/
public function walkQuantifiedExpression($qExpr) {}
/**
* Walks down a Subselect AST node, thereby generating the appropriate SQL.
*
* @param Subselect
* @return string The SQL.
*/
public function walkSubselect($subselect) {}
/**
* Walks down a SubselectFromClause AST node, thereby generating the appropriate SQL.
*
* @param SubselectFromClause
* @return string The SQL.
*/
public function walkSubselectFromClause($subselectFromClause) {}
/**
* Walks down a SimpleSelectClause AST node, thereby generating the appropriate SQL.
*
* @param SimpleSelectClause
* @return string The SQL.
*/
public function walkSimpleSelectClause($simpleSelectClause) {}
/**
* Walks down a SimpleSelectExpression AST node, thereby generating the appropriate SQL.
*
* @param SimpleSelectExpression
* @return string The SQL.
*/
public function walkSimpleSelectExpression($simpleSelectExpression) {}
/**
* Walks down an AggregateExpression AST node, thereby generating the appropriate SQL.
*
* @param AggregateExpression
* @return string The SQL.
*/
public function walkAggregateExpression($aggExpression) {}
/**
* Walks down a GroupByClause AST node, thereby generating the appropriate SQL.
*
* @param GroupByClause
* @return string The SQL.
*/
public function walkGroupByClause($groupByClause) {}
/**
* Walks down a GroupByItem AST node, thereby generating the appropriate SQL.
*
* @param GroupByItem
* @return string The SQL.
*/
public function walkGroupByItem($groupByItem) {}
/**
* Walks down an UpdateStatement AST node, thereby generating the appropriate SQL.
*
* @param UpdateStatement
* @return string The SQL.
*/
public function walkUpdateStatement(AST\UpdateStatement $AST) {}
/**
* Walks down a DeleteStatement AST node, thereby generating the appropriate SQL.
*
* @param DeleteStatement
* @return string The SQL.
*/
public function walkDeleteStatement(AST\DeleteStatement $AST) {}
/**
* Walks down a DeleteClause AST node, thereby generating the appropriate SQL.
*
* @param DeleteClause
* @return string The SQL.
*/
public function walkDeleteClause(AST\DeleteClause $deleteClause) {}
/**
* Walks down an UpdateClause AST node, thereby generating the appropriate SQL.
*
* @param UpdateClause
* @return string The SQL.
*/
public function walkUpdateClause($updateClause) {}
/**
* Walks down an UpdateItem AST node, thereby generating the appropriate SQL.
*
* @param UpdateItem
* @return string The SQL.
*/
public function walkUpdateItem($updateItem) {}
/**
* Walks down a WhereClause AST node, thereby generating the appropriate SQL.
*
* @param WhereClause
* @return string The SQL.
*/
public function walkWhereClause($whereClause) {}
/**
* Walks down a ConditionalExpression AST node, thereby generating the appropriate SQL.
*
* @param ConditionalExpression
* @return string The SQL.
*/
public function walkConditionalExpression($condExpr) {}
/**
* Walks down a ConditionalTerm AST node, thereby generating the appropriate SQL.
*
* @param ConditionalTerm
* @return string The SQL.
*/
public function walkConditionalTerm($condTerm) {}
/**
* Walks down a ConditionalFactor AST node, thereby generating the appropriate SQL.
*
* @param ConditionalFactor
* @return string The SQL.
*/
public function walkConditionalFactor($factor) {}
/**
* Walks down a ConditionalPrimary AST node, thereby generating the appropriate SQL.
*
* @param ConditionalPrimary
* @return string The SQL.
*/
public function walkConditionalPrimary($primary) {}
/**
* Walks down an ExistsExpression AST node, thereby generating the appropriate SQL.
*
* @param ExistsExpression
* @return string The SQL.
*/
public function walkExistsExpression($existsExpr) {}
/**
* Walks down a CollectionMemberExpression AST node, thereby generating the appropriate SQL.
*
* @param CollectionMemberExpression
* @return string The SQL.
*/
public function walkCollectionMemberExpression($collMemberExpr) {}
/**
* Walks down an EmptyCollectionComparisonExpression AST node, thereby generating the appropriate SQL.
*
* @param EmptyCollectionComparisonExpression
* @return string The SQL.
*/
public function walkEmptyCollectionComparisonExpression($emptyCollCompExpr) {}
/**
* Walks down a NullComparisonExpression AST node, thereby generating the appropriate SQL.
*
* @param NullComparisonExpression
* @return string The SQL.
*/
public function walkNullComparisonExpression($nullCompExpr) {}
/**
* Walks down an InExpression AST node, thereby generating the appropriate SQL.
*
* @param InExpression
* @return string The SQL.
*/
public function walkInExpression($inExpr) {}
/**
* Walks down an InstanceOfExpression AST node, thereby generating the appropriate SQL.
*
* @param InstanceOfExpression
* @return string The SQL.
*/
function walkInstanceOfExpression($instanceOfExpr) {}
/**
* Walks down a literal that represents an AST node, thereby generating the appropriate SQL.
*
* @param mixed
* @return string The SQL.
*/
public function walkLiteral($literal) {}
/**
* Walks down a BetweenExpression AST node, thereby generating the appropriate SQL.
*
* @param BetweenExpression
* @return string The SQL.
*/
public function walkBetweenExpression($betweenExpr) {}
/**
* Walks down a LikeExpression AST node, thereby generating the appropriate SQL.
*
* @param LikeExpression
* @return string The SQL.
*/
public function walkLikeExpression($likeExpr) {}
/**
* Walks down a StateFieldPathExpression AST node, thereby generating the appropriate SQL.
*
* @param StateFieldPathExpression
* @return string The SQL.
*/
public function walkStateFieldPathExpression($stateFieldPathExpression) {}
/**
* Walks down a ComparisonExpression AST node, thereby generating the appropriate SQL.
*
* @param ComparisonExpression
* @return string The SQL.
*/
public function walkComparisonExpression($compExpr) {}
/**
* Walks down an InputParameter AST node, thereby generating the appropriate SQL.
*
* @param InputParameter
* @return string The SQL.
*/
public function walkInputParameter($inputParam) {}
/**
* Walks down an ArithmeticExpression AST node, thereby generating the appropriate SQL.
*
* @param ArithmeticExpression
* @return string The SQL.
*/
public function walkArithmeticExpression($arithmeticExpr) {}
/**
* Walks down an ArithmeticTerm AST node, thereby generating the appropriate SQL.
*
* @param mixed
* @return string The SQL.
*/
public function walkArithmeticTerm($term) {}
/**
* Walks down a StringPrimary that represents an AST node, thereby generating the appropriate SQL.
*
* @param mixed
* @return string The SQL.
*/
public function walkStringPrimary($stringPrimary) {}
/**
* Walks down an ArithmeticFactor that represents an AST node, thereby generating the appropriate SQL.
*
* @param mixed
* @return string The SQL.
*/
public function walkArithmeticFactor($factor) {}
/**
* Walks down an SimpleArithmeticExpression AST node, thereby generating the appropriate SQL.
*
* @param SimpleArithmeticExpression
* @return string The SQL.
*/
public function walkSimpleArithmeticExpression($simpleArithmeticExpr) {}
/**
* Walks down an PathExpression AST node, thereby generating the appropriate SQL.
*
* @param mixed
* @return string The SQL.
*/
public function walkPathExpression($pathExpr) {}
/**
* Walks down an ResultVariable AST node, thereby generating the appropriate SQL.
*
* @param string $resultVariable
* @return string The SQL.
*/
public function walkResultVariable($resultVariable) {}
/**
* Gets an executor that can be used to execute the result of this walker.
*
* @return AbstractExecutor
*/
public function getExecutor($AST) {}
}
DoctrineORM-2.3.3/Doctrine/ORM/Query/TreeWalkerChain.php 0000644 0001751 0001751 00000044471 12143374607 023025 0 ustar beberlei beberlei .
*/
namespace Doctrine\ORM\Query;
/**
* Represents a chain of tree walkers that modify an AST and finally emit output.
* Only the last walker in the chain can emit output. Any previous walkers can modify
* the AST to influence the final output produced by the last walker.
*
* @author Roman Borschel
* @since 2.0
*/
class TreeWalkerChain implements TreeWalker
{
/** The tree walkers. */
private $_walkers = array();
/** The original Query. */
private $_query;
/** The ParserResult of the original query that was produced by the Parser. */
private $_parserResult;
/** The query components of the original query (the "symbol table") that was produced by the Parser. */
private $_queryComponents;
/**
* @inheritdoc
*/
public function __construct($query, $parserResult, array $queryComponents)
{
$this->_query = $query;
$this->_parserResult = $parserResult;
$this->_queryComponents = $queryComponents;
}
/**
* Adds a tree walker to the chain.
*
* @param string $walkerClass The class of the walker to instantiate.
*/
public function addTreeWalker($walkerClass)
{
$this->_walkers[] = new $walkerClass($this->_query, $this->_parserResult, $this->_queryComponents);
}
/**
* Walks down a SelectStatement AST node, thereby generating the appropriate SQL.
*
* @return string The SQL.
*/
public function walkSelectStatement(AST\SelectStatement $AST)
{
foreach ($this->_walkers as $walker) {
$walker->walkSelectStatement($AST);
}
}
/**
* Walks down a SelectClause AST node, thereby generating the appropriate SQL.
*
* @return string The SQL.
*/
public function walkSelectClause($selectClause)
{
foreach ($this->_walkers as $walker) {
$walker->walkSelectClause($selectClause);
}
}
/**
* Walks down a FromClause AST node, thereby generating the appropriate SQL.
*
* @return string The SQL.
*/
public function walkFromClause($fromClause)
{
foreach ($this->_walkers as $walker) {
$walker->walkFromClause($fromClause);
}
}
/**
* Walks down a FunctionNode AST node, thereby generating the appropriate SQL.
*
* @return string The SQL.
*/
public function walkFunction($function)
{
foreach ($this->_walkers as $walker) {
$walker->walkFunction($function);
}
}
/**
* Walks down an OrderByClause AST node, thereby generating the appropriate SQL.
*
* @param OrderByClause
* @return string The SQL.
*/
public function walkOrderByClause($orderByClause)
{
foreach ($this->_walkers as $walker) {
$walker->walkOrderByClause($orderByClause);
}
}
/**
* Walks down an OrderByItem AST node, thereby generating the appropriate SQL.
*
* @param OrderByItem
* @return string The SQL.
*/
public function walkOrderByItem($orderByItem)
{
foreach ($this->_walkers as $walker) {
$walker->walkOrderByItem($orderByItem);
}
}
/**
* Walks down a HavingClause AST node, thereby generating the appropriate SQL.
*
* @param HavingClause
* @return string The SQL.
*/
public function walkHavingClause($havingClause)
{
foreach ($this->_walkers as $walker) {
$walker->walkHavingClause($havingClause);
}
}
/**
* Walks down a Join AST node and creates the corresponding SQL.
*
* @param Join $join
* @return string The SQL.
*/
public function walkJoin($join)
{
foreach ($this->_walkers as $walker) {
$walker->walkJoin($join);
}
}
/**
* Walks down a SelectExpression AST node and generates the corresponding SQL.
*
* @param SelectExpression $selectExpression
* @return string The SQL.
*/
public function walkSelectExpression($selectExpression)
{
foreach ($this->_walkers as $walker) {
$walker->walkSelectExpression($selectExpression);
}
}
/**
* Walks down a QuantifiedExpression AST node, thereby generating the appropriate SQL.
*
* @param QuantifiedExpression
* @return string The SQL.
*/
public function walkQuantifiedExpression($qExpr)
{
foreach ($this->_walkers as $walker) {
$walker->walkQuantifiedExpression($qExpr);
}
}
/**
* Walks down a Subselect AST node, thereby generating the appropriate SQL.
*
* @param Subselect
* @return string The SQL.
*/
public function walkSubselect($subselect)
{
foreach ($this->_walkers as $walker) {
$walker->walkSubselect($subselect);
}
}
/**
* Walks down a SubselectFromClause AST node, thereby generating the appropriate SQL.
*
* @param SubselectFromClause
* @return string The SQL.
*/
public function walkSubselectFromClause($subselectFromClause)
{
foreach ($this->_walkers as $walker) {
$walker->walkSubselectFromClause($subselectFromClause);
}
}
/**
* Walks down a SimpleSelectClause AST node, thereby generating the appropriate SQL.
*
* @param SimpleSelectClause
* @return string The SQL.
*/
public function walkSimpleSelectClause($simpleSelectClause)
{
foreach ($this->_walkers as $walker) {
$walker->walkSimpleSelectClause($simpleSelectClause);
}
}
/**
* Walks down a SimpleSelectExpression AST node, thereby generating the appropriate SQL.
*
* @param SimpleSelectExpression
* @return string The SQL.
*/
public function walkSimpleSelectExpression($simpleSelectExpression)
{
foreach ($this->_walkers as $walker) {
$walker->walkSimpleSelectExpression($simpleSelectExpression);
}
}
/**
* Walks down an AggregateExpression AST node, thereby generating the appropriate SQL.
*
* @param AggregateExpression
* @return string The SQL.
*/
public function walkAggregateExpression($aggExpression)
{
foreach ($this->_walkers as $walker) {
$walker->walkAggregateExpression($aggExpression);
}
}
/**
* Walks down a GroupByClause AST node, thereby generating the appropriate SQL.
*
* @param GroupByClause
* @return string The SQL.
*/
public function walkGroupByClause($groupByClause)
{
foreach ($this->_walkers as $walker) {
$walker->walkGroupByClause($groupByClause);
}
}
/**
* Walks down a GroupByItem AST node, thereby generating the appropriate SQL.
*
* @param GroupByItem
* @return string The SQL.
*/
public function walkGroupByItem($groupByItem)
{
foreach ($this->_walkers as $walker) {
$walker->walkGroupByItem($groupByItem);
}
}
/**
* Walks down an UpdateStatement AST node, thereby generating the appropriate SQL.
*
* @param UpdateStatement
* @return string The SQL.
*/
public function walkUpdateStatement(AST\UpdateStatement $AST)
{
foreach ($this->_walkers as $walker) {
$walker->walkUpdateStatement($AST);
}
}
/**
* Walks down a DeleteStatement AST node, thereby generating the appropriate SQL.
*
* @param DeleteStatement
* @return string The SQL.
*/
public function walkDeleteStatement(AST\DeleteStatement $AST)
{
foreach ($this->_walkers as $walker) {
$walker->walkDeleteStatement($AST);
}
}
/**
* Walks down a DeleteClause AST node, thereby generating the appropriate SQL.
*
* @param DeleteClause
* @return string The SQL.
*/
public function walkDeleteClause(AST\DeleteClause $deleteClause)
{
foreach ($this->_walkers as $walker) {
$walker->walkDeleteClause($deleteClause);
}
}
/**
* Walks down an UpdateClause AST node, thereby generating the appropriate SQL.
*
* @param UpdateClause
* @return string The SQL.
*/
public function walkUpdateClause($updateClause)
{
foreach ($this->_walkers as $walker) {
$walker->walkUpdateClause($updateClause);
}
}
/**
* Walks down an UpdateItem AST node, thereby generating the appropriate SQL.
*
* @param UpdateItem
* @return string The SQL.
*/
public function walkUpdateItem($updateItem)
{
foreach ($this->_walkers as $walker) {
$walker->walkUpdateItem($updateItem);
}
}
/**
* Walks down a WhereClause AST node, thereby generating the appropriate SQL.
*
* @param WhereClause
* @return string The SQL.
*/
public function walkWhereClause($whereClause)
{
foreach ($this->_walkers as $walker) {
$walker->walkWhereClause($whereClause);
}
}
/**
* Walks down a ConditionalExpression AST node, thereby generating the appropriate SQL.
*
* @param ConditionalExpression
* @return string The SQL.
*/
public function walkConditionalExpression($condExpr)
{
foreach ($this->_walkers as $walker) {
$walker->walkConditionalExpression($condExpr);
}
}
/**
* Walks down a ConditionalTerm AST node, thereby generating the appropriate SQL.
*
* @param ConditionalTerm
* @return string The SQL.
*/
public function walkConditionalTerm($condTerm)
{
foreach ($this->_walkers as $walker) {
$walker->walkConditionalTerm($condTerm);
}
}
/**
* Walks down a ConditionalFactor AST node, thereby generating the appropriate SQL.
*
* @param ConditionalFactor
* @return string The SQL.
*/
public function walkConditionalFactor($factor)
{
foreach ($this->_walkers as $walker) {
$walker->walkConditionalFactor($factor);
}
}
/**
* Walks down a ConditionalPrimary AST node, thereby generating the appropriate SQL.
*
* @param ConditionalPrimary
* @return string The SQL.
*/
public function walkConditionalPrimary($condPrimary)
{
foreach ($this->_walkers as $walker) {
$walker->walkConditionalPrimary($condPrimary);
}
}
/**
* Walks down an ExistsExpression AST node, thereby generating the appropriate SQL.
*
* @param ExistsExpression
* @return string The SQL.
*/
public function walkExistsExpression($existsExpr)
{
foreach ($this->_walkers as $walker) {
$walker->walkExistsExpression($existsExpr);
}
}
/**
* Walks down a CollectionMemberExpression AST node, thereby generating the appropriate SQL.
*
* @param CollectionMemberExpression
* @return string The SQL.
*/
public function walkCollectionMemberExpression($collMemberExpr)
{
foreach ($this->_walkers as $walker) {
$walker->walkCollectionMemberExpression($collMemberExpr);
}
}
/**
* Walks down an EmptyCollectionComparisonExpression AST node, thereby generating the appropriate SQL.
*
* @param EmptyCollectionComparisonExpression
* @return string The SQL.
*/
public function walkEmptyCollectionComparisonExpression($emptyCollCompExpr)
{
foreach ($this->_walkers as $walker) {
$walker->walkEmptyCollectionComparisonExpression($emptyCollCompExpr);
}
}
/**
* Walks down a NullComparisonExpression AST node, thereby generating the appropriate SQL.
*
* @param NullComparisonExpression
* @return string The SQL.
*/
public function walkNullComparisonExpression($nullCompExpr)
{
foreach ($this->_walkers as $walker) {
$walker->walkNullComparisonExpression($nullCompExpr);
}
}
/**
* Walks down an InExpression AST node, thereby generating the appropriate SQL.
*
* @param InExpression
* @return string The SQL.
*/
public function walkInExpression($inExpr)
{
foreach ($this->_walkers as $walker) {
$walker->walkInExpression($inExpr);
}
}
/**
* Walks down an InstanceOfExpression AST node, thereby generating the appropriate SQL.
*
* @param InstanceOfExpression
* @return string The SQL.
*/
function walkInstanceOfExpression($instanceOfExpr)
{
foreach ($this->_walkers as $walker) {
$walker->walkInstanceOfExpression($instanceOfExpr);
}
}
/**
* Walks down a literal that represents an AST node, thereby generating the appropriate SQL.
*
* @param mixed
* @return string The SQL.
*/
public function walkLiteral($literal)
{
foreach ($this->_walkers as $walker) {
$walker->walkLiteral($literal);
}
}
/**
* Walks down a BetweenExpression AST node, thereby generating the appropriate SQL.
*
* @param BetweenExpression
* @return string The SQL.
*/
public function walkBetweenExpression($betweenExpr)
{
foreach ($this->_walkers as $walker) {
$walker->walkBetweenExpression($betweenExpr);
}
}
/**
* Walks down a LikeExpression AST node, thereby generating the appropriate SQL.
*
* @param LikeExpression
* @return string The SQL.
*/
public function walkLikeExpression($likeExpr)
{
foreach ($this->_walkers as $walker) {
$walker->walkLikeExpression($likeExpr);
}
}
/**
* Walks down a StateFieldPathExpression AST node, thereby generating the appropriate SQL.
*
* @param StateFieldPathExpression
* @return string The SQL.
*/
public function walkStateFieldPathExpression($stateFieldPathExpression)
{
foreach ($this->_walkers as $walker) {
$walker->walkStateFieldPathExpression($stateFieldPathExpression);
}
}
/**
* Walks down a ComparisonExpression AST node, thereby generating the appropriate SQL.
*
* @param ComparisonExpression
* @return string The SQL.
*/
public function walkComparisonExpression($compExpr)
{
foreach ($this->_walkers as $walker) {
$walker->walkComparisonExpression($compExpr);
}
}
/**
* Walks down an InputParameter AST node, thereby generating the appropriate SQL.
*
* @param InputParameter
* @return string The SQL.
*/
public function walkInputParameter($inputParam)
{
foreach ($this->_walkers as $walker) {
$walker->walkInputParameter($inputParam);
}
}
/**
* Walks down an ArithmeticExpression AST node, thereby generating the appropriate SQL.
*
* @param ArithmeticExpression
* @return string The SQL.
*/
public function walkArithmeticExpression($arithmeticExpr)
{
foreach ($this->_walkers as $walker) {
$walker->walkArithmeticExpression($arithmeticExpr);
}
}
/**
* Walks down an ArithmeticTerm AST node, thereby generating the appropriate SQL.
*
* @param mixed
* @return string The SQL.
*/
public function walkArithmeticTerm($term)
{
foreach ($this->_walkers as $walker) {
$walker->walkArithmeticTerm($term);
}
}
/**
* Walks down a StringPrimary that represents an AST node, thereby generating the appropriate SQL.
*
* @param mixed
* @return string The SQL.
*/
public function walkStringPrimary($stringPrimary)
{
foreach ($this->_walkers as $walker) {
$walker->walkStringPrimary($stringPrimary);
}
}
/**
* Walks down an ArithmeticFactor that represents an AST node, thereby generating the appropriate SQL.
*
* @param mixed
* @return string The SQL.
*/
public function walkArithmeticFactor($factor)
{
foreach ($this->_walkers as $walker) {
$walker->walkArithmeticFactor($factor);
}
}
/**
* Walks down an SimpleArithmeticExpression AST node, thereby generating the appropriate SQL.
*
* @param SimpleArithmeticExpression
* @return string The SQL.
*/
public function walkSimpleArithmeticExpression($simpleArithmeticExpr)
{
foreach ($this->_walkers as $walker) {
$walker->walkSimpleArithmeticExpression($simpleArithmeticExpr);
}
}
/**
* Walks down an PathExpression AST node, thereby generating the appropriate SQL.
*
* @param mixed
* @return string The SQL.
*/
public function walkPathExpression($pathExpr)
{
foreach ($this->_walkers as $walker) {
$walker->walkPathExpression($pathExpr);
}
}
/**
* Walks down an ResultVariable AST node, thereby generating the appropriate SQL.
*
* @param string $resultVariable
* @return string The SQL.
*/
public function walkResultVariable($resultVariable)
{
foreach ($this->_walkers as $walker) {
$walker->walkResultVariable($resultVariable);
}
}
/**
* Gets an executor that can be used to execute the result of this walker.
*
* @return AbstractExecutor
*/
public function getExecutor($AST)
{}
}
DoctrineORM-2.3.3/Doctrine/ORM/Query/AST/AggregateExpression.php 0000644 0001751 0001751 00000003415 12143374607 024403 0 ustar beberlei beberlei .
*/
namespace Doctrine\ORM\Query\AST;
/**
* Description of AggregateExpression
*
*
* @link www.doctrine-project.org
* @since 2.0
* @author Guilherme Blanco
* @author Jonathan Wage
* @author Roman Borschel
*/
class AggregateExpression extends Node
{
public $functionName;
public $pathExpression;
public $isDistinct = false; // Some aggregate expressions support distinct, eg COUNT
public function __construct($functionName, $pathExpression, $isDistinct)
{
$this->functionName = $functionName;
$this->pathExpression = $pathExpression;
$this->isDistinct = $isDistinct;
}
public function dispatch($walker)
{
return $walker->walkAggregateExpression($this);
}
}
DoctrineORM-2.3.3/Doctrine/ORM/Query/AST/ArithmeticExpression.php 0000644 0001751 0001751 00000003323 12143374607 024604 0 ustar beberlei beberlei .
*/
namespace Doctrine\ORM\Query\AST;
/**
* ArithmeticExpression ::= SimpleArithmeticExpression | "(" Subselect ")"
*
*
* @link www.doctrine-project.org
* @since 2.0
* @author Guilherme Blanco
* @author Jonathan Wage
* @author Roman Borschel