2009-10-07 08:07:23 +04:00
< ? php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* " AS IS " AND ANY EXPRESS OR IMPLIED WARRANTIES , INCLUDING , BUT NOT
* LIMITED TO , THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED . IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT , INDIRECT , INCIDENTAL ,
* SPECIAL , EXEMPLARY , OR CONSEQUENTIAL DAMAGES ( INCLUDING , BUT NOT
* LIMITED TO , PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES ; LOSS OF USE ,
* DATA , OR PROFITS ; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY , WHETHER IN CONTRACT , STRICT LIABILITY , OR TORT
* ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE .
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL . For more information , see
* < http :// www . doctrine - project . org >.
*/
namespace Doctrine\ORM\Mapping\Driver ;
2010-02-03 00:17:00 +03:00
use Doctrine\Common\Cache\ArrayCache ,
2009-10-07 08:07:23 +04:00
Doctrine\Common\Annotations\AnnotationReader ,
Doctrine\DBAL\Schema\AbstractSchemaManager ,
2010-12-31 01:18:00 +03:00
Doctrine\DBAL\Schema\SchemaException ,
2009-10-07 08:07:23 +04:00
Doctrine\ORM\Mapping\ClassMetadataInfo ,
Doctrine\ORM\Mapping\MappingException ,
2011-02-16 19:24:42 +03:00
Doctrine\Common\Util\Inflector ;
2009-10-07 08:07:23 +04:00
/**
2010-04-10 02:00:36 +04:00
* The DatabaseDriver reverse engineers the mapping metadata from a database .
2009-10-07 08:07:23 +04:00
*
* @ license http :// www . opensource . org / licenses / lgpl - license . php LGPL
* @ link www . doctrine - project . org
* @ since 2.0
* @ author Guilherme Blanco < guilhermeblanco @ hotmail . com >
* @ author Jonathan Wage < jonwage @ gmail . com >
2010-06-14 01:02:18 +04:00
* @ author Benjamin Eberlei < kontakt @ beberlei . de >
2009-10-07 08:07:23 +04:00
*/
class DatabaseDriver implements Driver
{
2010-06-20 21:34:09 +04:00
/**
* @ var AbstractSchemaManager
*/
2009-10-07 08:07:23 +04:00
private $_sm ;
2010-06-20 21:34:09 +04:00
/**
* @ var array
*/
private $tables = null ;
2010-10-30 14:35:22 +04:00
private $classToTableNames = array ();
2010-06-21 01:39:21 +04:00
2010-06-20 21:34:09 +04:00
/**
* @ var array
*/
private $manyToManyTables = array ();
2009-10-07 08:07:23 +04:00
/**
* Initializes a new AnnotationDriver that uses the given AnnotationReader for reading
* docblock annotations .
*
* @ param AnnotationReader $reader The AnnotationReader to use .
*/
public function __construct ( AbstractSchemaManager $schemaManager )
{
$this -> _sm = $schemaManager ;
}
2010-06-20 21:34:09 +04:00
private function reverseEngineerMappingFromDatabase ()
{
if ( $this -> tables !== null ) {
return ;
}
foreach ( $this -> _sm -> listTableNames () as $tableName ) {
2010-10-30 14:35:22 +04:00
$tables [ $tableName ] = $this -> _sm -> listTableDetails ( $tableName );
2010-06-20 21:34:09 +04:00
}
$this -> tables = array ();
2010-10-30 14:35:22 +04:00
foreach ( $tables AS $tableName => $table ) {
2010-06-20 21:34:09 +04:00
/* @var $table Table */
if ( $this -> _sm -> getDatabasePlatform () -> supportsForeignKeyConstraints ()) {
$foreignKeys = $table -> getForeignKeys ();
} else {
$foreignKeys = array ();
}
$allForeignKeyColumns = array ();
foreach ( $foreignKeys AS $foreignKey ) {
$allForeignKeyColumns = array_merge ( $allForeignKeyColumns , $foreignKey -> getLocalColumns ());
}
2010-11-19 01:07:32 +03:00
2010-06-20 21:34:09 +04:00
$pkColumns = $table -> getPrimaryKey () -> getColumns ();
sort ( $pkColumns );
sort ( $allForeignKeyColumns );
if ( $pkColumns == $allForeignKeyColumns ) {
2010-06-21 01:39:21 +04:00
if ( count ( $table -> getForeignKeys ()) > 2 ) {
2010-10-30 14:35:22 +04:00
throw new \InvalidArgumentException ( " ManyToMany table ' " . $tableName . " ' with more or less than two foreign keys are not supported by the Database Reverese Engineering Driver. " );
2010-06-20 21:34:09 +04:00
}
2010-10-30 14:35:22 +04:00
$this -> manyToManyTables [ $tableName ] = $table ;
2010-06-20 21:34:09 +04:00
} else {
2010-10-30 14:35:22 +04:00
// lower-casing is necessary because of Oracle Uppercase Tablenames,
// assumption is lower-case + underscore separated.
$className = Inflector :: classify ( strtolower ( $tableName ));
$this -> tables [ $tableName ] = $table ;
$this -> classToTableNames [ $className ] = $tableName ;
2010-06-20 21:34:09 +04:00
}
2010-06-21 01:39:21 +04:00
}
2010-06-20 21:34:09 +04:00
}
2009-10-07 08:07:23 +04:00
/**
* { @ inheritdoc }
*/
2011-02-16 19:24:42 +03:00
public function loadMetadataForClass ( $className , ClassMetadataInfo $metadata )
2009-10-07 08:07:23 +04:00
{
2010-06-20 21:34:09 +04:00
$this -> reverseEngineerMappingFromDatabase ();
2010-10-30 14:35:22 +04:00
if ( ! isset ( $this -> classToTableNames [ $className ])) {
2010-06-21 01:39:21 +04:00
throw new \InvalidArgumentException ( " Unknown class " . $className );
}
2010-10-30 14:35:22 +04:00
$tableName = $this -> classToTableNames [ $className ];
2009-10-07 08:07:23 +04:00
$metadata -> name = $className ;
2010-03-29 17:20:41 +04:00
$metadata -> table [ 'name' ] = $tableName ;
2009-10-07 08:07:23 +04:00
2010-06-20 21:34:09 +04:00
$columns = $this -> tables [ $tableName ] -> getColumns ();
$indexes = $this -> tables [ $tableName ] -> getIndexes ();
2010-12-31 01:18:00 +03:00
try {
$primaryKeyColumns = $this -> tables [ $tableName ] -> getPrimaryKey () -> getColumns ();
} catch ( SchemaException $e ) {
$primaryKeyColumns = array ();
}
2009-12-05 00:40:03 +03:00
2010-04-10 02:00:36 +04:00
if ( $this -> _sm -> getDatabasePlatform () -> supportsForeignKeyConstraints ()) {
2010-06-20 21:34:09 +04:00
$foreignKeys = $this -> tables [ $tableName ] -> getForeignKeys ();
2009-12-05 00:40:03 +03:00
} else {
2009-10-08 22:54:19 +04:00
$foreignKeys = array ();
}
2009-10-07 08:07:23 +04:00
2010-06-13 21:36:49 +04:00
$allForeignKeyColumns = array ();
foreach ( $foreignKeys AS $foreignKey ) {
$allForeignKeyColumns = array_merge ( $allForeignKeyColumns , $foreignKey -> getLocalColumns ());
}
2009-10-07 08:07:23 +04:00
$ids = array ();
$fieldMappings = array ();
foreach ( $columns as $column ) {
$fieldMapping = array ();
2010-12-31 01:18:00 +03:00
if ( $primaryKeyColumns && in_array ( $column -> getName (), $primaryKeyColumns )) {
2009-10-07 08:07:23 +04:00
$fieldMapping [ 'id' ] = true ;
2010-06-14 01:02:18 +04:00
} else if ( in_array ( $column -> getName (), $allForeignKeyColumns )) {
continue ;
2009-10-07 08:07:23 +04:00
}
2010-02-07 15:36:30 +03:00
$fieldMapping [ 'fieldName' ] = Inflector :: camelize ( strtolower ( $column -> getName ()));
2009-12-05 00:40:03 +03:00
$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 ();
}
2010-05-14 20:31:25 +04:00
$fieldMapping [ 'nullable' ] = $column -> getNotNull () ? false : true ;
2009-10-07 08:07:23 +04:00
if ( isset ( $fieldMapping [ 'id' ])) {
$ids [] = $fieldMapping ;
} else {
$fieldMappings [] = $fieldMapping ;
}
}
if ( $ids ) {
2009-10-08 22:54:19 +04:00
if ( count ( $ids ) == 1 ) {
$metadata -> setIdGeneratorType ( ClassMetadataInfo :: GENERATOR_TYPE_AUTO );
}
2009-10-07 08:07:23 +04:00
foreach ( $ids as $id ) {
$metadata -> mapField ( $id );
}
}
foreach ( $fieldMappings as $fieldMapping ) {
$metadata -> mapField ( $fieldMapping );
}
2010-06-20 21:34:09 +04:00
foreach ( $this -> manyToManyTables AS $manyTable ) {
foreach ( $manyTable -> getForeignKeys () AS $foreignKey ) {
2010-10-30 14:35:22 +04:00
if ( strtolower ( $tableName ) == strtolower ( $foreignKey -> getForeignTableName ())) {
2010-06-20 21:34:09 +04:00
$myFk = $foreignKey ;
foreach ( $manyTable -> getForeignKeys () AS $foreignKey ) {
if ( $foreignKey != $myFk ) {
$otherFk = $foreignKey ;
break ;
}
}
$localColumn = current ( $myFk -> getColumns ());
$associationMapping = array ();
$associationMapping [ 'fieldName' ] = Inflector :: camelize ( str_replace ( '_id' , '' , strtolower ( current ( $otherFk -> getColumns ()))));
$associationMapping [ 'targetEntity' ] = Inflector :: classify ( strtolower ( $otherFk -> getForeignTableName ()));
if ( current ( $manyTable -> getColumns ()) -> getName () == $localColumn ) {
$associationMapping [ 'inversedBy' ] = Inflector :: camelize ( str_replace ( '_id' , '' , strtolower ( current ( $myFk -> getColumns ()))));
$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' ] = Inflector :: camelize ( str_replace ( '_id' , '' , strtolower ( current ( $myFk -> getColumns ()))));
}
$metadata -> mapManyToMany ( $associationMapping );
break ;
}
}
}
2009-10-07 08:07:23 +04:00
foreach ( $foreignKeys as $foreignKey ) {
2010-06-20 21:34:09 +04:00
$foreignTable = $foreignKey -> getForeignTableName ();
2009-12-07 16:04:54 +03:00
$cols = $foreignKey -> getColumns ();
$fkCols = $foreignKey -> getForeignColumns ();
2009-12-05 00:40:03 +03:00
2010-06-20 21:34:09 +04:00
$localColumn = current ( $cols );
2009-10-07 08:07:23 +04:00
$associationMapping = array ();
2010-06-13 21:36:49 +04:00
$associationMapping [ 'fieldName' ] = Inflector :: camelize ( str_replace ( '_id' , '' , strtolower ( $localColumn )));
2010-06-20 21:34:09 +04:00
$associationMapping [ 'targetEntity' ] = Inflector :: classify ( $foreignTable );
2010-02-14 01:28:33 +03:00
for ( $i = 0 ; $i < count ( $cols ); $i ++ ) {
$associationMapping [ 'joinColumns' ][] = array (
'name' => $cols [ $i ],
'referencedColumnName' => $fkCols [ $i ],
);
}
2009-10-07 08:07:23 +04:00
$metadata -> mapManyToOne ( $associationMapping );
}
}
/**
2010-01-28 15:46:12 +03:00
* { @ inheritdoc }
2009-10-07 08:07:23 +04:00
*/
public function isTransient ( $className )
{
return true ;
}
/**
2010-06-13 21:36:49 +04:00
* Return all the class names supported by this driver .
*
2010-06-20 21:34:09 +04:00
* IMPORTANT : This method must return an array of class not tables names .
2010-06-13 21:36:49 +04:00
*
* @ return array
2009-10-07 08:07:23 +04:00
*/
2009-12-16 00:06:32 +03:00
public function getAllClassNames ()
2009-10-07 08:07:23 +04:00
{
2010-06-20 21:34:09 +04:00
$this -> reverseEngineerMappingFromDatabase ();
2009-10-07 08:07:23 +04:00
2010-10-30 14:35:22 +04:00
return array_keys ( $this -> classToTableNames );
2009-10-07 08:07:23 +04:00
}
}