Commit 320d21e2 authored by romanb's avatar romanb

[2.0] Refactored classloader architecture. Renamed ClassLoader =>...

[2.0] Refactored classloader architecture. Renamed ClassLoader => GlobalClassLoader. Introduced IsolatedClassLoader that is suitable for participating in autoload stacks. Added 2 example entity classes to the sandbox with 2 xml mappings and 2 yaml mappings. Simplified sandbox setup.
parent 51987764
......@@ -22,41 +22,22 @@
namespace Doctrine\Common;
/**
* A class loader used to load class files on demand.
* A <tt>GlobalClassLoader</tt> is an autoloader for class files that can be
* installed on the SPL autoload stack. A GlobalClassLoader must be the only
* autoloader on the stack and be used for all classes.
*
* IMPORTANT:
* The <tt>GlobalClassLoader</tt> assumes the PHP 5.3 namespace separator but
* is also compatible with the underscore "_" namespace separator.
*
* This class loader is NOT meant to be put into a "chain" of autoloaders.
* It is not meant to load only Doctrine class file. It is a one-classloader-for-all-classes
* solution. It may not be useable with frameworks that do not follow basic pear/zend
* conventions where the namespace+class name reflects the physical location of the source
* file. This is not a bug. This class loader is, however, compatible with the
* old namespace separator '_' (underscore), so any classes using that convention
* instead of the 5.3 builtin namespaces can be loaded as well.
* A recommended class loading setup for optimal performance looks as follows:
*
* The only way to put this classloader into an autoloader chain is to use
* setCheckFileExists(true) which is not recommended.
*
* Here is the recommended usage:
* 1) Use only 1 class loader instance.
* 1) Use a GlobalClassLoader.
* 2) Reduce the include_path to only the path to the PEAR packages.
* 2) Set the base paths to any non-pear class libraries through
* $classLoader->setBasePath($prefix, $basePath);
* 3) DO NOT setCheckFileExists(true). Doing so is expensive in terms of performance.
* 4) Use an opcode-cache (i.e. APC) (STRONGLY RECOMMENDED).
*
* The "prefix" is the part of a class name before the very first namespace separator
* character. If the class is named "Foo_Bar_Baz" then the prefix is "Foo".
* 2) Register the namespaces of any other (non-pear) class library with their
* absolute base paths, like this: $gcl->registerNamespace('Zend', '/path/to/zf-lib');
*
* If no base path is configured for a certain class prefix, the classloader relies on
* the include_path. That's why classes of any pear packages can be loaded without
* registering their base paths. However, since a long include_path has a negative effect
* on performance it is recommended to have only the path to the pear packages in the
* include_path.
*
* For any other class libraries you always have the choice between registering the base
* path on the classloader or putting the base path into the include_path. The former
* should be preferred but the latter is fine also.
* If no base path is configured for a certain namespace, the GlobalClassLoader relies on
* the include_path.
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org
......@@ -66,80 +47,62 @@ namespace Doctrine\Common;
* @author Jonathan Wage <jonwage@gmail.com>
* @author Roman Borschel <roman@code-factory.org>
*/
class ClassLoader
class GlobalClassLoader
{
/**
* @var string Namespace separator
*/
private $_namespaceSeparator = '\\';
/**
* @var string File extension used for classes
*/
private $_fileExtension = '.php';
private $_defaultFileExtension = '.php';
/**
* @var boolean Flag to inspect if file exists in codebase before include it
* @var array The custom file extensions of class libraries.
*/
private $_checkFileExists = false;
private $_fileExtensions = array();
/**
* @var array Hashmap of base paths that Autoloader will look into
* @var array Hashmap of base paths to class libraries.
*/
private $_basePaths = array();
/**
* Constructor registers the autoloader automatically
* Installs this class loader on the SPL autoload stack as the only class loader.
*
* @throws DoctrineException If the SPL autoload stack already contains other autoloaders.
*/
public function __construct()
public function register()
{
spl_autoload_register(array($this, 'loadClass'));
if (spl_autoload_functions() !== false) {
throw new DoctrineException("Autoload stack is not empty. GlobalClassLoader does not work "
. "in an autoload stack.");
}
/**
* Set check file exists
*
* @param boolean $bool
* @return void
*/
public function setCheckFileExists($bool)
{
$this->_checkFileExists = $bool;
spl_autoload_register(array($this, 'loadClass'));
}
/**
* Set class file extension
* Sets the default file extension of class files.
*
* @param string $extension
* @return void
*/
public function setClassFileExtension($extension)
public function setDefaultFileExtension($extension)
{
$this->_fileExtension = $extension;
}
/**
* Sets the namespace separator to use.
*
* @param string $separator
* @return void
*/
public function setNamespaceSeparator($separator)
{
$this->_namespaceSeparator = $separator;
}
/**
* Sets a static base path for classes with a certain prefix that is prepended
* to the path derived from the class itself.
*
* @param string $classPrefix
* @param string $basePath
* @param string $classPrefix The prefix (root namespace) of the class library.
* @param string $basePath The base path to the class library.
* @param string $fileExtension The custom file extension used by the class files in the namespace.
*/
public function setBasePath($classPrefix, $basePath)
public function registerNamespace($namespace, $basePath, $fileExtension = null)
{
$this->_basePaths[$classPrefix] = $basePath;
$this->_basePaths[$namespace] = $basePath;
if ($fileExtension !== null) {
$this->_fileExtensions[$namespace] = $fileExtension;
}
}
/**
......@@ -150,35 +113,24 @@ class ClassLoader
*/
public function loadClass($className)
{
if (class_exists($className, false) || interface_exists($className, false)) {
return false;
}
$prefix = '';
$separator = $this->_namespaceSeparator;
if (($pos = strpos($className, $this->_namespaceSeparator)) !== false) {
$prefix = substr($className, 0, strpos($className, $this->_namespaceSeparator));
$separator = '\\';
if (($pos = strpos($className, $separator)) !== false) {
$prefix = substr($className, 0, strpos($className, $separator));
} else if (($pos = strpos($className, '_')) !== false) {
// Support for '_' namespace separator for compatibility with Zend/PEAR/...
$prefix = substr($className, 0, strpos($className, '_'));
$separator = '_';
}
// If we have a custom path for namespace, use it
$class = ((isset($this->_basePaths[$prefix])) ? $this->_basePaths[$prefix] . DIRECTORY_SEPARATOR : '')
. str_replace($separator, DIRECTORY_SEPARATOR, $className) . $this->_fileExtension;
// Assure file exists in codebase before require if flag is active
if ($this->_checkFileExists) {
if (($fh = @fopen($class, 'r', true)) === false) {
return false;
}
@fclose($fh);
}
// Build the class file name
$class = ((isset($this->_basePaths[$prefix])) ?
$this->_basePaths[$prefix] . DIRECTORY_SEPARATOR : '')
. str_replace($separator, DIRECTORY_SEPARATOR, $className)
. (isset($this->_fileExtensions[$prefix]) ?
$this->_fileExtensions[$prefix] : $this->_defaultFileExtension);
require $class;
return true;
}
}
\ No newline at end of file
<?php
/*
* $Id$
*
* 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\Common;
/**
* An <tt>IsolatedClassLoader</tt> is an autoloader for class files that can be
* installed on the SPL autoload stack. It is a class loader that loads only classes
* of a specific namespace and is suitable for working together with other autoloaders
* in the SPL autoload stack.
*
* If no base path is configured, an IsolatedClassLoader relies on the include_path.
*
* @author Roman Borschel <roman@code-factory.org>
* @since 2.0
*/
class IsolatedClassLoader
{
private $_fileExtension = '.php';
private $_namespace;
private $_basePath;
private $_namespaceSeparator = '\\';
/**
* Creates a new <tt>IsolatedClassLoader</tt> that loads classes of the
* specified namespace.
*
* @param string $ns The namespace to use.
*/
public function __construct($ns)
{
$this->_namespace = $ns;
}
/**
* Sets the namespace separator used by classes in the namespace of this class loader.
*
* @param string $sep The separator to use.
*/
public function setNamespaceSeparator($sep)
{
$this->_namespaceSeparator = $sep;
}
/**
* Sets the base include path for all class files in the namespace of this class loader.
*
* @param string $basePath
*/
public function setBasePath($basePath)
{
$this->_basePath = $basePath;
}
/**
* Sets the file extension of class files in the namespace of this class loader.
*
* @param string $fileExtension
*/
public function setFileExtension($fileExtension)
{
$this->_fileExtension = $fileExtension;
}
/**
* Installs this class loader on the SPL autoload stack.
*/
public function register()
{
spl_autoload_register(array($this, 'loadClass'));
}
/**
* Loads the given class or interface.
*
* @param string $classname The name of the class to load.
* @return boolean TRUE if the class has been successfully loaded, FALSE otherwise.
*/
public function loadClass($className)
{
if (class_exists($className, false) || interface_exists($className, false)) {
return false;
}
if (strpos($className, $this->_namespace) !== 0) {
return false;
}
// Build the class file name
$class = ($this->_basePath !== null ? $this->_basePath . DIRECTORY_SEPARATOR : '')
. str_replace($this->_namespaceSeparator, DIRECTORY_SEPARATOR, $className)
. $this->_fileExtension;
require $class;
return true;
}
}
\ No newline at end of file
......@@ -113,6 +113,7 @@ class ClassMetadataFactory
$this->_loadMetadata($className);
}
}
return $this->_loadedMetadata[$className];
}
......
......@@ -21,7 +21,8 @@
namespace Doctrine\ORM\Mapping\Driver;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\Mapping\ClassMetadata,
Doctrine\Common\DoctrineException;
if ( ! class_exists('sfYaml', false)) {
require_once __DIR__ . '/../../../../vendor/sfYaml/sfYaml.class.php';
......
......@@ -1286,10 +1286,10 @@ class Parser
if ($glimpse['value'] != '.') {
$token = $this->_lexer->lookahead;
$resultVariable = $this->ResultVariable();
$expr = $this->ResultVariable();
// Check if ResultVariable is defined in query components
$queryComponent = $this->_validateIdentificationVariable($resultVariable, null, $token);
$queryComponent = $this->_validateIdentificationVariable($expr, null, $token);
// Outer defininition used in inner subselect is not enough.
// ResultVariable exists in queryComponents, check nesting level
......@@ -1562,10 +1562,10 @@ class Parser
if ($this->_lexer->isNextToken(Lexer::T_IDENTIFIER)) {
$token = $this->_lexer->lookahead;
$resultVariable = $this->ResultVariable();
$fieldAliasIdentificationVariable = $this->ResultVariable();
// Include ResultVariable in query components.
$this->_queryComponents[$resultVariable] = array(
$this->_queryComponents[$fieldAliasIdentificationVariable] = array(
'resultvariable' => $expression,
'nestingLevel' => $this->_nestingLevel,
'token' => $token,
......
......@@ -59,6 +59,9 @@ class SqlWalker implements TreeWalker
private $_query;
private $_dqlToSqlAliasMap = array();
/** Map from result variable names to their SQL column alias names. */
private $_scalarResultAliasMap = array();
/** Map of all components/classes that appear in the DQL query. */
private $_queryComponents;
......@@ -581,6 +584,7 @@ class SqlWalker implements TreeWalker
public function walkOrderByItem($orderByItem)
{
$expr = $orderByItem->expression;
if ($expr instanceof AST\PathExpression) {
$parts = $expr->parts;
$dqlAlias = $expr->identificationVariable;
$class = $this->_queryComponents[$dqlAlias]['metadata'];
......@@ -588,6 +592,11 @@ class SqlWalker implements TreeWalker
return $this->getSqlTableAlias($class->getTableName(), $dqlAlias) . '.'
. $columnName . ' ' . strtoupper($orderByItem->type);
} else {
$columnName = $this->_queryComponents[$expr]['token']['value'];
return $this->_scalarResultAliasMap[$columnName] . ' ' . strtoupper($orderByItem->type);
}
}
/**
......@@ -762,6 +771,7 @@ class SqlWalker implements TreeWalker
$columnAlias = 'sclr' . $this->_aliasCounter++;
$sql .= $this->walkAggregateExpression($expr) . ' AS ' . $columnAlias;
$this->_scalarResultAliasMap[$resultAlias] = $columnAlias;
$columnAlias = $this->_platform->getSqlResultCasing($columnAlias);
$this->_rsm->addScalarResult($columnAlias, $resultAlias);
......@@ -776,6 +786,7 @@ class SqlWalker implements TreeWalker
$columnAlias = 'sclr' . $this->_aliasCounter++;
$sql .= $this->walkFunction($expr) . ' AS ' . $columnAlias;
$this->_scalarResultAliasMap[$resultAlias] = $columnAlias;
$columnAlias = $this->_platform->getSqlResultCasing($columnAlias);
$this->_rsm->addScalarResult($columnAlias, $resultAlias);
......
......@@ -164,6 +164,11 @@ class SchemaToolTask extends AbstractTask
$printer = $this->getPrinter();
$tool = new SchemaTool($this->_em);
if (empty($classes)) {
$printer->writeln('No classes to process.', 'INFO');
return;
}
if ($isCreate) {
if (isset($args['dump-sql'])) {
foreach ($tool->getCreateSchemaSql($classes) as $sql) {
......
......@@ -2,41 +2,32 @@
namespace Doctrine\Tests\Common;
use Doctrine\Common\ClassLoader;
use Doctrine\Common\GlobalClassLoader,
Doctrine\Common\IsolatedClassLoader;
require_once __DIR__ . '/../TestInit.php';
class ClassLoaderTest extends \Doctrine\Tests\DoctrineTestCase
{
public function testCustomFileExtensionAndNamespaceSeparator()
public function testGlobalClassLoaderThrowsExceptionIfPutInChain()
{
$classLoader = new \Doctrine\Common\ClassLoader();
$classLoader->setBasePath('ClassLoaderTest', __DIR__);
$classLoader->setClassFileExtension('.class.php');
$classLoader->setNamespaceSeparator('_');
$this->assertEquals($classLoader->loadClass('ClassLoaderTest_ClassA'), true);
$this->assertEquals($classLoader->loadClass('ClassLoaderTest_ClassB'), true);
}
$this->setExpectedException('Doctrine\Common\DoctrineException');
public function testClassLoaderCheckFileExists()
{
$classLoader = new \Doctrine\Common\ClassLoader();
$classLoader->setBasePath('ClassLoaderTest', __DIR__);
$classLoader->setCheckFileExists(true);
$classLoader1 = new IsolatedClassLoader('Foo');
$classLoader1->register();
// This would return a fatal error without check file exists true
$this->assertEquals($classLoader->loadClass('SomeInvalidClass'), false);
$globalClassLoader = new GlobalClassLoader;
$globalClassLoader->register();
}
public function testAlreadyLoadedClassReturnsFalse()
public function testIsolatedClassLoaderReturnsFalseOnClassExists()
{
$classLoader = new \Doctrine\Common\ClassLoader();
$classLoader->setBasePath('ClassLoaderTest', __DIR__);
$classLoader->setClassFileExtension('.class.php');
$classLoader = new IsolatedClassLoader('ClassLoaderTest');
$classLoader->setBasePath( __DIR__);
$classLoader->setFileExtension('.class.php');
$classLoader->setNamespaceSeparator('_');
$classLoader->setCheckFileExists(true);
$this->assertEquals($classLoader->loadClass('ClassLoaderTest_ClassA'), true);
$this->assertEquals($classLoader->loadClass('ClassLoaderTest_ClassA'), false);
$this->assertEquals($classLoader->loadClass('ClassLoaderTest_ClassC'), true);
}
......
......@@ -408,6 +408,14 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase
);
}
public function testOrderByCollectionAssociationSize()
{
$this->assertSqlGeneration(
"select u, size(u.articles) as numArticles from Doctrine\Tests\Models\CMS\CmsUser u order by numArticles",
"SELECT c0_.id AS id0, c0_.status AS status1, c0_.username AS username2, c0_.name AS name3, (SELECT COUNT(c1_.user_id) FROM cms_articles c1_ WHERE c1_.user_id = c0_.id) AS sclr4 FROM cms_users c0_ ORDER BY sclr4 ASC"
);
}
/* Not yet implemented, needs more thought
public function testSingleValuedAssociationFieldInWhere()
{
......
......@@ -8,11 +8,13 @@ error_reporting(E_ALL | E_STRICT);
require_once 'PHPUnit/Framework.php';
require_once 'PHPUnit/TextUI/TestRunner.php';
require_once __DIR__ . '/../../../lib/Doctrine/Common/ClassLoader.php';
require_once __DIR__ . '/../../../lib/Doctrine/Common/IsolatedClassLoader.php';
$classLoader = new \Doctrine\Common\ClassLoader();
$classLoader = new \Doctrine\Common\IsolatedClassLoader('Doctrine');
$classLoader->register();
set_include_path(
get_include_path()
'.'
. PATH_SEPARATOR . __DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'lib'
);
<?php
namespace Entities;
/** @Entity @Table(name="addresses") */
class Address {
/**
* @Id @Column(type="integer")
* @GeneratedValue(strategy="AUTO")
*/
private $id;
/** @Column(type="string", length=255) */
private $street;
/** @OneToOne(targetEntity="User", mappedBy="address") */
private $user;
public function getId() {
return $this->id;
}
public function getStreet() {
return $this->street;
}
public function setStreet($street) {
$this->street = $street;
}
public function getUser() {
return $this->user;
}
public function setUser(User $user) {
if ($this->user !== $user) {
$this->user = $user;
$user->setAddress($this);
}
}
}
<?php
namespace Entities;
/** @Entity @Table(name="users") */
class User {
/**
* @Id @Column(type="integer")
* @GeneratedValue(strategy="AUTO")
*/
private $id;
/** @Column(type="string", length=50) */
private $name;
/**
* @OneToOne(targetEntity="Address")
* @JoinColumn(name="address_id", referencedColumnName="id")
*/
private $address;
public function getId() {
return $this->id;
}
public function getName() {
return $this->name;
}
public function setName($name) {
$this->name = $name;
}
public function getAddress() {
return $this->address;
}
public function setAddress(Address $address) {
if ($this->address !== $address) {
$this->address = $address;
$address->setUser($this);
}
}
}
<?php
#
# This configuration file is loaded by the Doctrine CLI whenever you execute
# a task. A CLI configuration file usually initializes two local variables:
#
# $em - An EntityManager instance that the CLI tasks should use.
# $args - An array of default command line arguments that take effect when an
# argument is not specifically set on the command line.
#
# You can create several CLI configuration files with different names, for different databases.
# Every CLI task recognizes the --config=<path> option where you can specify the configuration
# file to use for a particular task. If this option is not given, the CLI looks for a file
# named "cli-config.php" (this one) in the same directory and uses that by default.
#
require_once __DIR__ . '/../../lib/Doctrine/Common/IsolatedClassLoader.php';
$classLoader = new \Doctrine\Common\IsolatedClassLoader('Entities');
$classLoader->setBasePath(__DIR__);
$classLoader->register();
$config = new \Doctrine\ORM\Configuration();
$config->setMetadataCacheImpl(new \Doctrine\Common\Cache\ArrayCache);
$connectionOptions = array(
'driver' => 'pdo_sqlite',
'path' => 'database.sqlite'
);
$em = \Doctrine\ORM\EntityManager::create($connectionOptions, $config);
$args = array(
'classdir' => './Entities'
);
\ No newline at end of file
<?php
require '../../lib/Doctrine/Common/ClassLoader.php';
$classLoader = new \Doctrine\Common\ClassLoader();
$classLoader->setBasePath('Doctrine', realpath(__DIR__ . '/../../lib'));
$classLoader->setBasePath('Entities', __DIR__);
$config = new \Doctrine\ORM\Configuration;
$cache = new \Doctrine\Common\Cache\ApcCache;
// Use ArrayCache is APC is not available
// Warning without APC Doctrine will not perform as well
$cache = new \Doctrine\Common\Cache\ArrayCache;
$config->setMetadataCacheImpl($cache);
$config->setQueryCacheImpl($cache);
# EXAMPLE FOR YAML DRIVER
#$config->setMetadataDriverImpl(new \Doctrine\ORM\Mapping\Driver\YamlDriver(__DIR__ . '/yaml'));
# EXAMPLE FOR XML DRIVER
#$config->setMetadataDriverImpl(new \Doctrine\ORM\Mapping\Driver\XmlDriver(__DIR__ . '/xml'));
$eventManager = new \Doctrine\Common\EventManager();
$connectionOptions = array(
'driver' => 'pdo_sqlite',
'path' => 'database.sqlite'
);
$em = \Doctrine\ORM\EntityManager::create($connectionOptions, $config, $eventManager);
<?php
require __DIR__ . '/../../lib/Doctrine/Common/ClassLoader.php';
require __DIR__ . '/../../lib/Doctrine/Common/IsolatedClassLoader.php';
$classLoader = new \Doctrine\Common\ClassLoader();
$classLoader->setBasePath('Doctrine', __DIR__ . '/../../lib');
$classLoader = new \Doctrine\Common\IsolatedClassLoader('Doctrine');
$classLoader->setBasePath(__DIR__ . '/../../lib');
$classLoader->register();
$cli = new \Doctrine\ORM\Tools\Cli();
$cli->run($_SERVER['argv']);
\ No newline at end of file
<?php
require 'config.php';
namespace Sandbox;
// Place your code here. $em is at your service.
use Entities\User, Entities\Address;
require '../../lib/Doctrine/Common/GlobalClassLoader.php';
// Set up class loading, we could alternatively use 2 IsolatedClassLoaders
$classLoader = new \Doctrine\Common\GlobalClassLoader();
$classLoader->registerNamespace('Doctrine', realpath(__DIR__ . '/../../lib'));
$classLoader->registerNamespace('Entities', __DIR__);
$classLoader->register();
// Set up caches
$config = new \Doctrine\ORM\Configuration;
$cache = new \Doctrine\Common\Cache\ApcCache;
$config->setMetadataCacheImpl($cache);
$config->setQueryCacheImpl($cache);
// Database connection information
$connectionOptions = array(
'driver' => 'pdo_sqlite',
'path' => 'database.sqlite'
);
// Create EntityManager
$em = \Doctrine\ORM\EntityManager::create($connectionOptions, $config);
## PUT YOUR TEST CODE BELOW
$user = new User;
$address = new Address;
echo "Hello World!";
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
http://doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
<entity name="Entities\Address" table="addresses">
<id name="id" type="integer">
<generator strategy="AUTO"/>
</id>
<field name="street" type="string" length="255"/>
<one-to-one field="user" target-entity="User" mapped-by="address"/>
</entity>
</doctrine-mapping>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
http://doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
<entity name="Entities\User" table="users">
<id name="id" type="integer">
<generator strategy="AUTO"/>
</id>
<field name="name" type="string" length="50"/>
<one-to-one field="address" target-entity="Address">
<join-column name="address_id" referenced-column-name="id"/>
</one-to-one>
</entity>
</doctrine-mapping>
Entities\Address:
type: entity
table: addresses
id:
id:
type: integer
generator:
strategy: AUTO
fields:
street:
type: string
length: 255
oneToOne:
user:
targetEntity: User
mappedBy: address
\ No newline at end of file
Entities\User:
type: entity
table: users
id:
id:
type: integer
generator:
strategy: AUTO
fields:
name:
type: string
length: 50
oneToOne:
address:
targetEntity: Address
joinColumn:
name: address_id
referencedColumnName: id
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment