diff --git a/main/inc/lib/autoload.class.php b/main/inc/lib/autoload.class.php index cd666fabc8..35a3ed42cb 100644 --- a/main/inc/lib/autoload.class.php +++ b/main/inc/lib/autoload.class.php @@ -109,6 +109,7 @@ class Autoload $result['DokeosIndexer'] = '/main/inc/lib/search/DokeosIndexer.class.php'; $result['DropboxLink'] = '/main/gradebook/lib/be/dropboxlink.class.php'; $result['DummyCourseCreator'] = '/main/coursecopy/classes/DummyCourseCreator.class.php'; + $result['EntityGenerator'] = '/main/inc/lib/tools/entity_generator.class.php'; $result['EvalForm'] = '/main/gradebook/lib/fe/evalform.class.php'; $result['EvalLink'] = '/main/gradebook/lib/be/evallink.class.php'; $result['Evaluation'] = '/main/gradebook/lib/be/evaluation.class.php'; @@ -216,7 +217,6 @@ class Autoload $result['KeyAuth'] = '/main/auth/key/key_auth.class.php'; $result['LearnpathLink'] = '/main/gradebook/lib/be/learnpathlink.class.php'; $result['LegalManager'] = '/main/inc/lib/legal.lib.php'; - $result['Link'] = '/main/coursecopy/classes/Link.class.php'; $result['LinkAddEditForm'] = '/main/gradebook/lib/fe/linkaddeditform.class.php'; $result['LinkCategory'] = '/main/coursecopy/classes/LinkCategory.class.php'; $result['LinkFactory'] = '/main/gradebook/lib/be/linkfactory.class.php'; diff --git a/main/inc/lib/chamilo.class.php b/main/inc/lib/chamilo.class.php index 7f5212bef3..84bcc7e9f9 100644 --- a/main/inc/lib/chamilo.class.php +++ b/main/inc/lib/chamilo.class.php @@ -30,7 +30,7 @@ class Chamilo { return Uri::www(); } - + /** * File system root for Chamilo * @@ -40,15 +40,24 @@ class Chamilo { return api_get_path(SYS_PATH); } - + public static function path($path = '') { $root = self::root(); - if(empty($path)) - { + if (empty($path)) { return $root; } return $root . $path; } + static function is_test_server() + { + return api_get_setting('server_type') == 'test'; + } + + static function is_production_server() + { + return api_get_setting('server_type') == 'production'; + } + } \ No newline at end of file diff --git a/main/inc/lib/db.lib.php b/main/inc/lib/db.lib.php index 6f8da3d41d..eb613d0060 100644 --- a/main/inc/lib/db.lib.php +++ b/main/inc/lib/db.lib.php @@ -1,78 +1,242 @@ save($data); +} -use Doctrine\ORM\EntityManager, - Doctrine\ORM\Configuration; +class db +{ + /** + * @return db + */ + public static function instance() + { + static $result = null; + if (empty($result)) { + $result = new self(); + } + return $result; + } -class db { - - function __construct() { - global $_configuration; - $connectionParams = array( - 'dbname' => $_configuration['main_database'], - 'user' => $_configuration['db_user'], - 'password' => $_configuration['db_password'], - 'host' => $_configuration['db_host'], - 'driver' => 'pdo_mysql', - ); - - /* $classLoader = new ClassLoader('Doctrine', api_get_path(LIBRARY_PATH).'symfony'); - $classLoader->register();*/ - /* $config = new Doctrine\DBAL\Configuration(); - - $conn = DriverManager::getConnection($connectionParams, $config); - - $sql = "SELECT * FROM user"; - $stmt = $conn->query($sql); // Simple, but has several drawbacks - */ - - $lib = api_get_path(LIBRARY_PATH).'symfony'; - Doctrine\ORM\Tools\Setup::registerAutoloadDirectory($lib); - - $config = new Configuration; - $applicationMode = 'development'; - - if ($applicationMode == "development") { - $cache = new \Doctrine\Common\Cache\ArrayCache; - } else { - $cache = new \Doctrine\Common\Cache\ApcCache; + private static function register_autoload() + { + static $has_run = false; + if ($has_run) { + return true; } - $config->setMetadataCacheImpl($cache); - $driverImpl = $config->newDefaultAnnotationDriver(api_get_path(LIBRARY_PATH).'symfony/app_obj/entities'); - $config->setMetadataDriverImpl($driverImpl); - $config->setQueryCacheImpl($cache); - $config->setProxyDir(api_get_path(LIBRARY_PATH).'proxies'); - $config->setProxyNamespace('App\Proxies'); - - if ($applicationMode == "development") { - $config->setAutoGenerateProxyClasses(true); - } else { - $config->setAutoGenerateProxyClasses(false); + require_once api_get_path(LIBRARY_PATH) . 'symfony/Doctrine/ORM/Tools/Setup.php'; + $directory = api_get_path(LIBRARY_PATH) . 'symfony'; + //Doctrine\ORM\Tools\Setup::registerAutoloadDirectory($lib); + + if (!class_exists('Doctrine\Common\ClassLoader', false)) { + require_once $directory . '/doctrine/Common/ClassLoader.php'; } - - $config = Setup::createYAMLMetadataConfiguration(array(api_get_path(LIBRARY_PATH).'symfony/app_obj/metadata'), true); - - $em = EntityManager::create($connectionParams, $config); - - require_once api_get_path(LIBRARY_PATH).'symfony/app_obj/entities/user.php'; - + + $loader = new Doctrine\Common\ClassLoader('Doctrine', $directory); + $loader->register(); + + $loader = new Doctrine\Common\ClassLoader('Symfony\Component', $directory); + $loader->register(); + + $has_run = true; + } + + protected $config; + protected $em; + + protected function __construct() + { + self::register_autoload(); + $connection_parameters = $this->get_connection_parameters(); + $this->config = Setup::createYAMLMetadataConfiguration(array(api_get_path(LIBRARY_PATH) . 'symfony/app_obj/metadata'), true); + $this->em = EntityManager::create($connection_parameters, $this->config); + } + + public function save($data) + { + $em = $this->em; + $em->persist($data); + } + + function test() + { + /* $classLoader = new ClassLoader('Doctrine', api_get_path(LIBRARY_PATH).'symfony'); + $classLoader->register(); */ + /* $config = new Doctrine\DBAL\Configuration(); + + $conn = DriverManager::getConnection($connectionParams, $config); + + $sql = "SELECT * FROM user"; + $stmt = $conn->query($sql); // Simple, but has several drawbacks + */ + +// $lib = api_get_path(LIBRARY_PATH) . 'symfony'; +// Doctrine\ORM\Tools\Setup::registerAutoloadDirectory($lib); +// +// $config = new Configuration; +// $applicationMode = 'development'; +// +// if ($applicationMode == "development") { +// $cache = new \Doctrine\Common\Cache\ArrayCache; +// } else { +// $cache = new \Doctrine\Common\Cache\ApcCache; +// } +// $config->setMetadataCacheImpl($cache); +// $driverImpl = $config->newDefaultAnnotationDriver(api_get_path(LIBRARY_PATH) . 'symfony/app_obj/entities'); +// $config->setMetadataDriverImpl($driverImpl); +// $config->setQueryCacheImpl($cache); +// $config->setProxyDir(api_get_path(LIBRARY_PATH) . 'proxies'); +// $config->setProxyNamespace('App\Proxies'); +// +// if ($applicationMode == "development") { +// $config->setAutoGenerateProxyClasses(true); +// } else { +// $config->setAutoGenerateProxyClasses(false); +// } + + require_once api_get_path(LIBRARY_PATH) . 'symfony/app_obj/entities/user.php'; $user = new User(); $user->setUsername('salut'); $em->persist($user); $em->flush(); echo "Created User with ID " . $user->getId() . "\n"; } -} + /** + * + * @return EntityManager The created EntityManager. + */ + public function em() + { + return $this->em; + } + + /** + * Pathes?? do we put everything in one place or in several places? + * @return type + */ + public function get_model_path() + { + $result = __DIR__ . '/../model'; + $result = realpath($result); + return $result; + } + + public function is_production() + { + return Chamilo::is_production_server(); + } + + public function is_dev() + { + return !Chamilo::is_production_server(); + } + + /** + * Reverse engineering of the model from the database structure. + * Write result to the entity folder + * + * WARNING THIS WILL OVERWRITE EXISTING MODEL. + * + * @return boolean + */ + public function generate_model() + { + if (!$this->is_dev()) { + return false; + } + + $model_path = $this->get_model_path(); + + $connection_parameters = $this->get_connection_parameters(); + $connection = Doctrine\DBAL\DriverManager::getConnection($connection_parameters); + $platform = $connection->getDatabasePlatform(); + $platform->registerDoctrineTypeMapping('enum', 'string'); + $platform->registerDoctrineTypeMapping('set', 'string'); + + $config = Setup::createConfiguration($this->is_dev()); + $config->setMetadataDriverImpl(new Doctrine\ORM\Mapping\Driver\DatabaseDriver(new Doctrine\DBAL\Schema\MySqlSchemaManager($connection))); + + $em = EntityManager::create($connection, $config); + + $cmf = new Doctrine\ORM\Tools\DisconnectedClassMetadataFactory(); + $cmf->setEntityManager($em); + $metadatas = $cmf->getAllMetadata(); + + foreach ($metadatas as $metadata) { + echo sprintf('Processing entity "%s"', $metadata->name) . '
'; + } + + $generator = new EntityGenerator(); + $generator->setGenerateAnnotations(false); + $generator->setGenerateStubMethods(true); + $generator->setRegenerateEntityIfExists(true); + $generator->setUpdateEntityIfExists(false); + $generator->setExtension('.class.php'); + $generator->setNumSpaces(4); + + // Generating Entities + $generator->generate($metadatas, $model_path); + + $exporter = new \Doctrine\ORM\Tools\Export\Driver\YamlExporter(); + $exporter->setOutputDir($model_path . '/mapping'); + $exporter->setMetadata($metadatas); + $exporter->export(); + } + + public function update_schema($paths, $save_mode) + { + $is_dev = $this->is_dev(); + + $ext = explode('.', $path); + $ext = end($ext); + if ($ext == 'yml') { + $config = Setup::createYAMLMetadataConfiguration($paths, $is_dev); + } else if ($ext == 'xml') { + $config = Setup::createXMLMetadataConfiguration($paths, $is_dev); + } else if ($ext == 'php') { + $config = Setup::createAnnotationMetadataConfiguration($paths, $is_dev); + } else { + return false; + } + + $em = $this->em; + + $cmf = new Doctrine\ORM\Tools\DisconnectedClassMetadataFactory(); + $cmf->setEntityManager($em); + $classes = $cmf->getAllMetadata(); + + $tool = new Doctrine\ORM\Tools\SchemaTool($em); + return $tool->updateSchema($classes, $save_mode); + } + + protected function get_config() + { + return $this->config; + } + + protected function get_connection_parameters() + { + global $_configuration; + $result = array( + 'dbname' => $_configuration['main_database'], + 'user' => $_configuration['db_user'], + 'password' => $_configuration['db_password'], + 'host' => $_configuration['db_host'], + 'driver' => 'pdo_mysql', + ); + return $result; + } + +} diff --git a/main/inc/lib/tools/entity_generator.class.php b/main/inc/lib/tools/entity_generator.class.php new file mode 100644 index 0000000000..7caecc79a6 --- /dev/null +++ b/main/inc/lib/tools/entity_generator.class.php @@ -0,0 +1,1145 @@ +getClassMetadataFactory()->getAllMetadata(); + * + * $generator = new \Doctrine\ORM\Tools\EntityGenerator(); + * $generator->setGenerateAnnotations(true); + * $generator->setGenerateStubMethods(true); + * $generator->setRegenerateEntityIfExists(false); + * $generator->setUpdateEntityIfExists(true); + * $generator->generate($classes, '/path/to/generate/entities'); + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision$ + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class EntityGenerator +{ + /** + * @var bool + */ + private $_backupExisting = true; + + /** The extension to use for written php files */ + private $_extension = '.php'; + + /** Whether or not the current ClassMetadataInfo instance is new or old */ + private $_isNew = true; + + private $_staticReflection = array(); + + /** Number of spaces to use for indention in generated code */ + private $_numSpaces = 4; + + /** The actual spaces to use for indention */ + private $_spaces = ' '; + + /** The class all generated entities should extend */ + private $_classToExtend; + + /** Whether or not to generation annotations */ + private $_generateAnnotations = false; + + /** + * @var string + */ + private $_annotationsPrefix = ''; + + /** Whether or not to generated sub methods */ + private $_generateEntityStubMethods = false; + + /** Whether or not to update the entity class if it exists already */ + private $_updateEntityIfExists = false; + + /** Whether or not to re-generate entity class if it exists already */ + private $_regenerateEntityIfExists = false; + + private static $_classTemplate = +' + +use Doctrine\ORM\Mapping as ORM; + + + +{ + +}'; + + private static $_getMethodTemplate = +'/** + * + * + * @return + */ +public function () +{ +return $this->; +}'; + + private static $_setMethodTemplate = +'/** + * + * + * @param $ + * @return + */ +public function ($) +{ +$this-> = $; +return $this; +}'; + + private static $_addMethodTemplate = +'/** + * + * + * @param $ + * @return + */ +public function ($) +{ +$this->[] = $; +return $this; +}'; + + private static $_lifecycleCallbackMethodTemplate = +'/** + * @ + */ +public function () +{ +// Add your code here +}'; + + private static $_constructorMethodTemplate = +'public function __construct() +{ + +} +'; + + public function __construct() + { + if (version_compare(\Doctrine\Common\Version::VERSION, '2.2.0-DEV', '>=')) { + $this->_annotationsPrefix = 'ORM\\'; + } + } + + /** + * Generate and write entity classes for the given array of ClassMetadataInfo instances + * + * @param array $metadatas + * @param string $outputDirectory + * @return void + */ + public function generate(array $metadatas, $outputDirectory) + { + foreach ($metadatas as $metadata) { + $this->writeEntityClass($metadata, $outputDirectory); + } + } + + /** + * Generated and write entity class to disk for the given ClassMetadataInfo instance + * + * @param ClassMetadataInfo $metadata + * @param string $outputDirectory + * @return void + */ + public function writeEntityClass(ClassMetadataInfo $metadata, $outputDirectory) + { + //change + $name = $metadata->name; + $name = Inflector::tableize($name); + $is_course_table = (strpos($name, 'c_') === 0); + if($is_course_table) + { + $name = substr($name, 2, strlen($name) -2); + } + $name = Inflector::tableize($name); + // + + $path = $outputDirectory . '/' . str_replace('\\', DIRECTORY_SEPARATOR, $name) . $this->_extension; + $dir = dirname($path); + + if ( ! is_dir($dir)) { + mkdir($dir, 0777, true); + } + + $this->_isNew = !file_exists($path) || (file_exists($path) && $this->_regenerateEntityIfExists); + + if ( ! $this->_isNew) { + $this->_parseTokensInEntityFile(file_get_contents($path)); + } else { + $this->_staticReflection[$metadata->name] = array('properties' => array(), 'methods' => array()); + } + + if ($this->_backupExisting && file_exists($path)) { + $backupPath = dirname($path) . DIRECTORY_SEPARATOR . basename($path) . "~"; + if (!copy($path, $backupPath)) { + throw new \RuntimeException("Attempt to backup overwritten entity file but copy operation failed."); + } + } + + // If entity doesn't exist or we're re-generating the entities entirely + if ($this->_isNew) { + file_put_contents($path, $this->generateEntityClass($metadata)); + // If entity exists and we're allowed to update the entity class + } else if ( ! $this->_isNew && $this->_updateEntityIfExists) { + file_put_contents($path, $this->generateUpdatedEntityClass($metadata, $path)); + } + } + + /** + * Generate a PHP5 Doctrine 2 entity class from the given ClassMetadataInfo instance + * + * @param ClassMetadataInfo $metadata + * @return string $code + */ + public function generateEntityClass(ClassMetadataInfo $metadata) + { + $placeHolders = array( + '', + '', + '', + '' + ); + + $replacements = array( + $this->_generateEntityNamespace($metadata), + $this->_generateEntityDocBlock($metadata), + $this->_generateEntityClassName($metadata), + $this->_generateEntityBody($metadata) + ); + + $code = str_replace($placeHolders, $replacements, self::$_classTemplate); + return str_replace('', $this->_spaces, $code); + } + + /** + * Generate the updated code for the given ClassMetadataInfo and entity at path + * + * @param ClassMetadataInfo $metadata + * @param string $path + * @return string $code; + */ + public function generateUpdatedEntityClass(ClassMetadataInfo $metadata, $path) + { + $currentCode = file_get_contents($path); + + $body = $this->_generateEntityBody($metadata); + $body = str_replace('', $this->_spaces, $body); + $last = strrpos($currentCode, '}'); + + return substr($currentCode, 0, $last) . $body . (strlen($body) > 0 ? "\n" : ''). "}"; + } + + /** + * Set the number of spaces the exported class should have + * + * @param integer $numSpaces + * @return void + */ + public function setNumSpaces($numSpaces) + { + $this->_spaces = str_repeat(' ', $numSpaces); + $this->_numSpaces = $numSpaces; + } + + /** + * Set the extension to use when writing php files to disk + * + * @param string $extension + * @return void + */ + public function setExtension($extension) + { + $this->_extension = $extension; + } + + /** + * Set the name of the class the generated classes should extend from + * + * @return void + */ + public function setClassToExtend($classToExtend) + { + $this->_classToExtend = $classToExtend; + } + + /** + * Set whether or not to generate annotations for the entity + * + * @param bool $bool + * @return void + */ + public function setGenerateAnnotations($bool) + { + $this->_generateAnnotations = $bool; + } + + /** + * Set an annotation prefix. + * + * @param string $prefix + */ + public function setAnnotationPrefix($prefix) + { + $this->_annotationsPrefix = $prefix; + } + + /** + * Set whether or not to try and update the entity if it already exists + * + * @param bool $bool + * @return void + */ + public function setUpdateEntityIfExists($bool) + { + $this->_updateEntityIfExists = $bool; + } + + /** + * Set whether or not to regenerate the entity if it exists + * + * @param bool $bool + * @return void + */ + public function setRegenerateEntityIfExists($bool) + { + $this->_regenerateEntityIfExists = $bool; + } + + /** + * Set whether or not to generate stub methods for the entity + * + * @param bool $bool + * @return void + */ + public function setGenerateStubMethods($bool) + { + $this->_generateEntityStubMethods = $bool; + } + + /** + * Should an existing entity be backed up if it already exists? + */ + public function setBackupExisting($bool) + { + $this->_backupExisting = $bool; + } + + private function _generateEntityNamespace(ClassMetadataInfo $metadata) + { + if ($this->_hasNamespace($metadata)) { + return 'namespace ' . $this->_getNamespace($metadata) .';'; + } + } + + private function _generateEntityClassName(ClassMetadataInfo $metadata) + { + return 'class ' . $this->_getClassName($metadata) . + ($this->_extendsClass() ? ' extends ' . $this->_getClassToExtendName() : null); + } + + private function _generateEntityBody(ClassMetadataInfo $metadata) + { + $fieldMappingProperties = $this->_generateEntityFieldMappingProperties($metadata); + $associationMappingProperties = $this->_generateEntityAssociationMappingProperties($metadata); + $stubMethods = $this->_generateEntityStubMethods ? $this->_generateEntityStubMethods($metadata) : null; + $lifecycleCallbackMethods = $this->_generateEntityLifecycleCallbackMethods($metadata); + + $code = array(); + + if ($fieldMappingProperties) { + $code[] = $fieldMappingProperties; + } + + if ($associationMappingProperties) { + $code[] = $associationMappingProperties; + } + + $code[] = $this->_generateEntityConstructor($metadata); + + if ($stubMethods) { + $code[] = $stubMethods; + } + + if ($lifecycleCallbackMethods) { + $code[] = $lifecycleCallbackMethods; + } + + return implode("\n", $code); + } + + private function _generateEntityConstructor(ClassMetadataInfo $metadata) + { + if ($this->_hasMethod('__construct', $metadata)) { + return ''; + } + + $collections = array(); + + foreach ($metadata->associationMappings AS $mapping) { + if ($mapping['type'] & ClassMetadataInfo::TO_MANY) { + $collections[] = '$this->'.$mapping['fieldName'].' = new \Doctrine\Common\Collections\ArrayCollection();'; + } + } + + if ($collections) { + return $this->_prefixCodeWithSpaces(str_replace("", implode("\n".$this->_spaces, $collections), self::$_constructorMethodTemplate)); + } + + return ''; + } + + /** + * @todo this won't work if there is a namespace in brackets and a class outside of it. + * @param string $src + */ + private function _parseTokensInEntityFile($src) + { + $tokens = token_get_all($src); + $lastSeenNamespace = ""; + $lastSeenClass = false; + + $inNamespace = false; + $inClass = false; + for ($i = 0; $i < count($tokens); $i++) { + $token = $tokens[$i]; + if (in_array($token[0], array(T_WHITESPACE, T_COMMENT, T_DOC_COMMENT))) { + continue; + } + + if ($inNamespace) { + if ($token[0] == T_NS_SEPARATOR || $token[0] == T_STRING) { + $lastSeenNamespace .= $token[1]; + } else if (is_string($token) && in_array($token, array(';', '{'))) { + $inNamespace = false; + } + } + + if ($inClass) { + $inClass = false; + $lastSeenClass = $lastSeenNamespace . ($lastSeenNamespace ? '\\' : '') . $token[1]; + $this->_staticReflection[$lastSeenClass]['properties'] = array(); + $this->_staticReflection[$lastSeenClass]['methods'] = array(); + } + + if ($token[0] == T_NAMESPACE) { + $lastSeenNamespace = ""; + $inNamespace = true; + } else if ($token[0] == T_CLASS) { + $inClass = true; + } else if ($token[0] == T_FUNCTION) { + if ($tokens[$i+2][0] == T_STRING) { + $this->_staticReflection[$lastSeenClass]['methods'][] = $tokens[$i+2][1]; + } else if ($tokens[$i+2] == "&" && $tokens[$i+3][0] == T_STRING) { + $this->_staticReflection[$lastSeenClass]['methods'][] = $tokens[$i+3][1]; + } + } else if (in_array($token[0], array(T_VAR, T_PUBLIC, T_PRIVATE, T_PROTECTED)) && $tokens[$i+2][0] != T_FUNCTION) { + $this->_staticReflection[$lastSeenClass]['properties'][] = substr($tokens[$i+2][1], 1); + } + } + } + + private function _hasProperty($property, ClassMetadataInfo $metadata) + { + if ($this->_extendsClass()) { + // don't generate property if its already on the base class. + $reflClass = new \ReflectionClass($this->_getClassToExtend()); + if ($reflClass->hasProperty($property)) { + return true; + } + } + + return ( + isset($this->_staticReflection[$metadata->name]) && + in_array($property, $this->_staticReflection[$metadata->name]['properties']) + ); + } + + private function _hasMethod($method, ClassMetadataInfo $metadata) + { + if ($this->_extendsClass()) { + // don't generate method if its already on the base class. + $reflClass = new \ReflectionClass($this->_getClassToExtend()); + if ($reflClass->hasMethod($method)) { + return true; + } + } + + return ( + isset($this->_staticReflection[$metadata->name]) && + in_array($method, $this->_staticReflection[$metadata->name]['methods']) + ); + } + + private function _hasNamespace(ClassMetadataInfo $metadata) + { + return strpos($metadata->name, '\\') ? true : false; + } + + private function _extendsClass() + { + return $this->_classToExtend ? true : false; + } + + private function _getClassToExtend() + { + return $this->_classToExtend; + } + + private function _getClassToExtendName() + { + $refl = new \ReflectionClass($this->_getClassToExtend()); + + return '\\' . $refl->getName(); + } + + private function _getClassName(ClassMetadataInfo $metadata) + { + //changed + $name = $metadata->name; + $name = Inflector::tableize($name); + $is_course_table = (strpos($name, 'c_') === 0); + if($is_course_table) + { + $name = substr($name, 2, strlen($name) -2); + } + $name = Inflector::classify($name); + + $result = ($pos = strrpos($name, '\\')) ? substr($name, $pos + 1, strlen($name)) : $name; + return $result; + } + + private function _getNamespace(ClassMetadataInfo $metadata) + { + return substr($metadata->name, 0, strrpos($metadata->name, '\\')); + } + + private function _generateEntityDocBlock(ClassMetadataInfo $metadata) + { + $lines = array(); + $lines[] = '/**'; + $lines[] = ' * '.$metadata->name; + + if ($this->_generateAnnotations) { + $lines[] = ' *'; + + $methods = array( + '_generateTableAnnotation', + '_generateInheritanceAnnotation', + '_generateDiscriminatorColumnAnnotation', + '_generateDiscriminatorMapAnnotation' + ); + + foreach ($methods as $method) { + if ($code = $this->$method($metadata)) { + $lines[] = ' * ' . $code; + } + } + + if ($metadata->isMappedSuperclass) { + $lines[] = ' * @' . $this->_annotationsPrefix . 'MappedSuperClass'; + } else { + $lines[] = ' * @' . $this->_annotationsPrefix . 'Entity'; + } + + if ($metadata->customRepositoryClassName) { + $lines[count($lines) - 1] .= '(repositoryClass="' . $metadata->customRepositoryClassName . '")'; + } + + if (isset($metadata->lifecycleCallbacks) && $metadata->lifecycleCallbacks) { + $lines[] = ' * @' . $this->_annotationsPrefix . 'HasLifecycleCallbacks'; + } + } + + $lines[] = ' */'; + + return implode("\n", $lines); + } + + private function _generateTableAnnotation($metadata) + { + $table = array(); + + if (isset($metadata->table['schema'])) { + $table[] = 'schema="' . $metadata->table['schema'] . '"'; + } + + if (isset($metadata->table['name'])) { + $table[] = 'name="' . $metadata->table['name'] . '"'; + } + + if (isset($metadata->table['uniqueConstraints']) && $metadata->table['uniqueConstraints']) { + $constraints = $this->_generateTableConstraints('UniqueConstraint', $metadata->table['uniqueConstraints']); + $table[] = 'uniqueConstraints={' . $constraints . '}'; + } + + if (isset($metadata->table['indexes']) && $metadata->table['indexes']) { + $constraints = $this->_generateTableConstraints('Index', $metadata->table['indexes']); + $table[] = 'indexes={' . $constraints . '}'; + } + + return '@' . $this->_annotationsPrefix . 'Table(' . implode(', ', $table) . ')'; + } + + private function _generateTableConstraints($constraintName, $constraints) + { + $annotations = array(); + foreach ($constraints as $name => $constraint) { + $columns = array(); + foreach ($constraint['columns'] as $column) { + $columns[] = '"' . $column . '"'; + } + $annotations[] = '@' . $this->_annotationsPrefix . $constraintName . '(name="' . $name . '", columns={' . implode(', ', $columns) . '})'; + } + return implode(', ', $annotations); + } + + private function _generateInheritanceAnnotation($metadata) + { + if ($metadata->inheritanceType != ClassMetadataInfo::INHERITANCE_TYPE_NONE) { + return '@' . $this->_annotationsPrefix . 'InheritanceType("'.$this->_getInheritanceTypeString($metadata->inheritanceType).'")'; + } + } + + private function _generateDiscriminatorColumnAnnotation($metadata) + { + if ($metadata->inheritanceType != ClassMetadataInfo::INHERITANCE_TYPE_NONE) { + $discrColumn = $metadata->discriminatorValue; + $columnDefinition = 'name="' . $discrColumn['name'] + . '", type="' . $discrColumn['type'] + . '", length=' . $discrColumn['length']; + + return '@' . $this->_annotationsPrefix . 'DiscriminatorColumn(' . $columnDefinition . ')'; + } + } + + private function _generateDiscriminatorMapAnnotation($metadata) + { + if ($metadata->inheritanceType != ClassMetadataInfo::INHERITANCE_TYPE_NONE) { + $inheritanceClassMap = array(); + + foreach ($metadata->discriminatorMap as $type => $class) { + $inheritanceClassMap[] .= '"' . $type . '" = "' . $class . '"'; + } + + return '@' . $this->_annotationsPrefix . 'DiscriminatorMap({' . implode(', ', $inheritanceClassMap) . '})'; + } + } + + private function _generateEntityStubMethods(ClassMetadataInfo $metadata) + { + $methods = array(); + + foreach ($metadata->fieldMappings as $fieldMapping) { + if ( ! isset($fieldMapping['id']) || ! $fieldMapping['id'] || $metadata->generatorType == ClassMetadataInfo::GENERATOR_TYPE_NONE) { + if ($code = $this->_generateEntityStubMethod($metadata, 'set', $fieldMapping['fieldName'], $fieldMapping['type'])) { + $methods[] = $code; + } + } + + if ($code = $this->_generateEntityStubMethod($metadata, 'get', $fieldMapping['fieldName'], $fieldMapping['type'])) { + $methods[] = $code; + } + } + + foreach ($metadata->associationMappings as $associationMapping) { + if ($associationMapping['type'] & ClassMetadataInfo::TO_ONE) { + $nullable = $this->_isAssociationIsNullable($associationMapping) ? 'null' : null; + if ($code = $this->_generateEntityStubMethod($metadata, 'set', $associationMapping['fieldName'], $associationMapping['targetEntity'], $nullable)) { + $methods[] = $code; + } + if ($code = $this->_generateEntityStubMethod($metadata, 'get', $associationMapping['fieldName'], $associationMapping['targetEntity'])) { + $methods[] = $code; + } + } else if ($associationMapping['type'] & ClassMetadataInfo::TO_MANY) { + if ($code = $this->_generateEntityStubMethod($metadata, 'add', $associationMapping['fieldName'], $associationMapping['targetEntity'])) { + $methods[] = $code; + } + if ($code = $this->_generateEntityStubMethod($metadata, 'get', $associationMapping['fieldName'], 'Doctrine\Common\Collections\Collection')) { + $methods[] = $code; + } + } + } + + return implode("\n\n", $methods); + } + + private function _isAssociationIsNullable($associationMapping) + { + if (isset($associationMapping['id']) && $associationMapping['id']) { + return false; + } + if (isset($associationMapping['joinColumns'])) { + $joinColumns = $associationMapping['joinColumns']; + } else { + //@todo thereis no way to retreive targetEntity metadata + $joinColumns = array(); + } + foreach ($joinColumns as $joinColumn) { + if(isset($joinColumn['nullable']) && !$joinColumn['nullable']) { + return false; + } + } + return true; + } + + private function _generateEntityLifecycleCallbackMethods(ClassMetadataInfo $metadata) + { + if (isset($metadata->lifecycleCallbacks) && $metadata->lifecycleCallbacks) { + $methods = array(); + + foreach ($metadata->lifecycleCallbacks as $name => $callbacks) { + foreach ($callbacks as $callback) { + if ($code = $this->_generateLifecycleCallbackMethod($name, $callback, $metadata)) { + $methods[] = $code; + } + } + } + + return implode("\n\n", $methods); + } + + return ""; + } + + private function _generateEntityAssociationMappingProperties(ClassMetadataInfo $metadata) + { + $lines = array(); + + foreach ($metadata->associationMappings as $associationMapping) { + if ($this->_hasProperty($associationMapping['fieldName'], $metadata)) { + continue; + } + + $lines[] = $this->_generateAssociationMappingPropertyDocBlock($associationMapping, $metadata); + $lines[] = $this->_spaces . 'private $' . $associationMapping['fieldName'] + . ($associationMapping['type'] == 'manyToMany' ? ' = array()' : null) . ";\n"; + } + + return implode("\n", $lines); + } + + private function _generateEntityFieldMappingProperties(ClassMetadataInfo $metadata) + { + $lines = array(); + + foreach ($metadata->fieldMappings as $fieldMapping) { + if ($this->_hasProperty($fieldMapping['fieldName'], $metadata) || + $metadata->isInheritedField($fieldMapping['fieldName'])) { + continue; + } + + $lines[] = $this->_generateFieldMappingPropertyDocBlock($fieldMapping, $metadata); + $lines[] = $this->_spaces . 'private $' . $fieldMapping['fieldName'] + . (isset($fieldMapping['default']) ? ' = ' . var_export($fieldMapping['default'], true) : null) . ";\n"; + } + + return implode("\n", $lines); + } + + private function _generateEntityStubMethod(ClassMetadataInfo $metadata, $type, $fieldName, $typeHint = null, $defaultValue = null) + { + if ($type == "add") { + $addMethod = explode("\\", $typeHint); + $addMethod = end($addMethod); + $methodName = $type . $addMethod; + } else { + //change + $methodName = $type . '_' . Inflector::tableize($fieldName); + // + } + + if ($this->_hasMethod($methodName, $metadata)) { + return; + } + $this->_staticReflection[$metadata->name]['methods'][] = $methodName; + + $var = sprintf('_%sMethodTemplate', $type); + $template = self::$$var; + + $variableType = $typeHint ? $typeHint . ' ' : null; + + $types = \Doctrine\DBAL\Types\Type::getTypesMap(); + $methodTypeHint = $typeHint && ! isset($types[$typeHint]) ? '\\' . $typeHint . ' ' : null; + + $replacements = array( + '' => ucfirst($type) . ' ' . $fieldName, + '' => $methodTypeHint, + '' => $variableType, + '' => Inflector::camelize($fieldName), + '' => $methodName, + '' => $fieldName, + '' => ($defaultValue !== null ) ? (' = '.$defaultValue) : '', + '' => $this->_getClassName($metadata) + ); + + $method = str_replace( + array_keys($replacements), + array_values($replacements), + $template + ); + + return $this->_prefixCodeWithSpaces($method); + } + + private function _generateLifecycleCallbackMethod($name, $methodName, $metadata) + { + if ($this->_hasMethod($methodName, $metadata)) { + return; + } + $this->_staticReflection[$metadata->name]['methods'][] = $methodName; + + $replacements = array( + '' => $this->_annotationsPrefix . $name, + '' => $methodName, + ); + + $method = str_replace( + array_keys($replacements), + array_values($replacements), + self::$_lifecycleCallbackMethodTemplate + ); + + return $this->_prefixCodeWithSpaces($method); + } + + private function _generateJoinColumnAnnotation(array $joinColumn) + { + $joinColumnAnnot = array(); + + if (isset($joinColumn['name'])) { + $joinColumnAnnot[] = 'name="' . $joinColumn['name'] . '"'; + } + + if (isset($joinColumn['referencedColumnName'])) { + $joinColumnAnnot[] = 'referencedColumnName="' . $joinColumn['referencedColumnName'] . '"'; + } + + if (isset($joinColumn['unique']) && $joinColumn['unique']) { + $joinColumnAnnot[] = 'unique=' . ($joinColumn['unique'] ? 'true' : 'false'); + } + + if (isset($joinColumn['nullable'])) { + $joinColumnAnnot[] = 'nullable=' . ($joinColumn['nullable'] ? 'true' : 'false'); + } + + if (isset($joinColumn['onDelete'])) { + $joinColumnAnnot[] = 'onDelete="' . ($joinColumn['onDelete'] . '"'); + } + + if (isset($joinColumn['columnDefinition'])) { + $joinColumnAnnot[] = 'columnDefinition="' . $joinColumn['columnDefinition'] . '"'; + } + + return '@' . $this->_annotationsPrefix . 'JoinColumn(' . implode(', ', $joinColumnAnnot) . ')'; + } + + private function _generateAssociationMappingPropertyDocBlock(array $associationMapping, ClassMetadataInfo $metadata) + { + $lines = array(); + $lines[] = $this->_spaces . '/**'; + + if ($associationMapping['type'] & ClassMetadataInfo::TO_MANY) { + $lines[] = $this->_spaces . ' * @var \Doctrine\Common\Collections\ArrayCollection'; + } else { + $lines[] = $this->_spaces . ' * @var ' . $associationMapping['targetEntity']; + } + + if ($this->_generateAnnotations) { + $lines[] = $this->_spaces . ' *'; + + if (isset($associationMapping['id']) && $associationMapping['id']) { + $lines[] = $this->_spaces . ' * @' . $this->_annotationsPrefix . 'Id'; + + if ($generatorType = $this->_getIdGeneratorTypeString($metadata->generatorType)) { + $lines[] = $this->_spaces . ' * @' . $this->_annotationsPrefix . 'GeneratedValue(strategy="' . $generatorType . '")'; + } + } + + $type = null; + switch ($associationMapping['type']) { + case ClassMetadataInfo::ONE_TO_ONE: + $type = 'OneToOne'; + break; + case ClassMetadataInfo::MANY_TO_ONE: + $type = 'ManyToOne'; + break; + case ClassMetadataInfo::ONE_TO_MANY: + $type = 'OneToMany'; + break; + case ClassMetadataInfo::MANY_TO_MANY: + $type = 'ManyToMany'; + break; + } + $typeOptions = array(); + + if (isset($associationMapping['targetEntity'])) { + $typeOptions[] = 'targetEntity="' . $associationMapping['targetEntity'] . '"'; + } + + if (isset($associationMapping['inversedBy'])) { + $typeOptions[] = 'inversedBy="' . $associationMapping['inversedBy'] . '"'; + } + + if (isset($associationMapping['mappedBy'])) { + $typeOptions[] = 'mappedBy="' . $associationMapping['mappedBy'] . '"'; + } + + if ($associationMapping['cascade']) { + $cascades = array(); + + if ($associationMapping['isCascadePersist']) $cascades[] = '"persist"'; + if ($associationMapping['isCascadeRemove']) $cascades[] = '"remove"'; + if ($associationMapping['isCascadeDetach']) $cascades[] = '"detach"'; + if ($associationMapping['isCascadeMerge']) $cascades[] = '"merge"'; + if ($associationMapping['isCascadeRefresh']) $cascades[] = '"refresh"'; + + $typeOptions[] = 'cascade={' . implode(',', $cascades) . '}'; + } + + if (isset($associationMapping['orphanRemoval']) && $associationMapping['orphanRemoval']) { + $typeOptions[] = 'orphanRemoval=' . ($associationMapping['orphanRemoval'] ? 'true' : 'false'); + } + + $lines[] = $this->_spaces . ' * @' . $this->_annotationsPrefix . '' . $type . '(' . implode(', ', $typeOptions) . ')'; + + if (isset($associationMapping['joinColumns']) && $associationMapping['joinColumns']) { + $lines[] = $this->_spaces . ' * @' . $this->_annotationsPrefix . 'JoinColumns({'; + + $joinColumnsLines = array(); + + foreach ($associationMapping['joinColumns'] as $joinColumn) { + if ($joinColumnAnnot = $this->_generateJoinColumnAnnotation($joinColumn)) { + $joinColumnsLines[] = $this->_spaces . ' * ' . $joinColumnAnnot; + } + } + + $lines[] = implode(",\n", $joinColumnsLines); + $lines[] = $this->_spaces . ' * })'; + } + + if (isset($associationMapping['joinTable']) && $associationMapping['joinTable']) { + $joinTable = array(); + $joinTable[] = 'name="' . $associationMapping['joinTable']['name'] . '"'; + + if (isset($associationMapping['joinTable']['schema'])) { + $joinTable[] = 'schema="' . $associationMapping['joinTable']['schema'] . '"'; + } + + $lines[] = $this->_spaces . ' * @' . $this->_annotationsPrefix . 'JoinTable(' . implode(', ', $joinTable) . ','; + $lines[] = $this->_spaces . ' * joinColumns={'; + + foreach ($associationMapping['joinTable']['joinColumns'] as $joinColumn) { + $lines[] = $this->_spaces . ' * ' . $this->_generateJoinColumnAnnotation($joinColumn); + } + + $lines[] = $this->_spaces . ' * },'; + $lines[] = $this->_spaces . ' * inverseJoinColumns={'; + + foreach ($associationMapping['joinTable']['inverseJoinColumns'] as $joinColumn) { + $lines[] = $this->_spaces . ' * ' . $this->_generateJoinColumnAnnotation($joinColumn); + } + + $lines[] = $this->_spaces . ' * }'; + $lines[] = $this->_spaces . ' * )'; + } + + if (isset($associationMapping['orderBy'])) { + $lines[] = $this->_spaces . ' * @' . $this->_annotationsPrefix . 'OrderBy({'; + + foreach ($associationMapping['orderBy'] as $name => $direction) { + $lines[] = $this->_spaces . ' * "' . $name . '"="' . $direction . '",'; + } + + $lines[count($lines) - 1] = substr($lines[count($lines) - 1], 0, strlen($lines[count($lines) - 1]) - 1); + $lines[] = $this->_spaces . ' * })'; + } + } + + $lines[] = $this->_spaces . ' */'; + + return implode("\n", $lines); + } + + private function _generateFieldMappingPropertyDocBlock(array $fieldMapping, ClassMetadataInfo $metadata) + { + $lines = array(); + $lines[] = $this->_spaces . '/**'; + $lines[] = $this->_spaces . ' * @var ' . $fieldMapping['type'] . ' $' . $fieldMapping['fieldName']; + + if ($this->_generateAnnotations) { + $lines[] = $this->_spaces . ' *'; + + $column = array(); + if (isset($fieldMapping['columnName'])) { + $column[] = 'name="' . $fieldMapping['columnName'] . '"'; + } + + if (isset($fieldMapping['type'])) { + $column[] = 'type="' . $fieldMapping['type'] . '"'; + } + + if (isset($fieldMapping['length'])) { + $column[] = 'length=' . $fieldMapping['length']; + } + + if (isset($fieldMapping['precision'])) { + $column[] = 'precision=' . $fieldMapping['precision']; + } + + if (isset($fieldMapping['scale'])) { + $column[] = 'scale=' . $fieldMapping['scale']; + } + + if (isset($fieldMapping['nullable'])) { + $column[] = 'nullable=' . var_export($fieldMapping['nullable'], true); + } + + if (isset($fieldMapping['columnDefinition'])) { + $column[] = 'columnDefinition="' . $fieldMapping['columnDefinition'] . '"'; + } + + if (isset($fieldMapping['unique'])) { + $column[] = 'unique=' . var_export($fieldMapping['unique'], true); + } + + $lines[] = $this->_spaces . ' * @' . $this->_annotationsPrefix . 'Column(' . implode(', ', $column) . ')'; + + if (isset($fieldMapping['id']) && $fieldMapping['id']) { + $lines[] = $this->_spaces . ' * @' . $this->_annotationsPrefix . 'Id'; + + if ($generatorType = $this->_getIdGeneratorTypeString($metadata->generatorType)) { + $lines[] = $this->_spaces.' * @' . $this->_annotationsPrefix . 'GeneratedValue(strategy="' . $generatorType . '")'; + } + + if ($metadata->sequenceGeneratorDefinition) { + $sequenceGenerator = array(); + + if (isset($metadata->sequenceGeneratorDefinition['sequenceName'])) { + $sequenceGenerator[] = 'sequenceName="' . $metadata->sequenceGeneratorDefinition['sequenceName'] . '"'; + } + + if (isset($metadata->sequenceGeneratorDefinition['allocationSize'])) { + $sequenceGenerator[] = 'allocationSize="' . $metadata->sequenceGeneratorDefinition['allocationSize'] . '"'; + } + + if (isset($metadata->sequenceGeneratorDefinition['initialValue'])) { + $sequenceGenerator[] = 'initialValue="' . $metadata->sequenceGeneratorDefinition['initialValue'] . '"'; + } + + $lines[] = $this->_spaces . ' * @' . $this->_annotationsPrefix . 'SequenceGenerator(' . implode(', ', $sequenceGenerator) . ')'; + } + } + + if (isset($fieldMapping['version']) && $fieldMapping['version']) { + $lines[] = $this->_spaces . ' * @' . $this->_annotationsPrefix . 'Version'; + } + } + + $lines[] = $this->_spaces . ' */'; + + return implode("\n", $lines); + } + + private function _prefixCodeWithSpaces($code, $num = 1) + { + $lines = explode("\n", $code); + + foreach ($lines as $key => $value) { + $lines[$key] = str_repeat($this->_spaces, $num) . $lines[$key]; + } + + return implode("\n", $lines); + } + + private function _getInheritanceTypeString($type) + { + switch ($type) { + case ClassMetadataInfo::INHERITANCE_TYPE_NONE: + return 'NONE'; + + case ClassMetadataInfo::INHERITANCE_TYPE_JOINED: + return 'JOINED'; + + case ClassMetadataInfo::INHERITANCE_TYPE_SINGLE_TABLE: + return 'SINGLE_TABLE'; + + case ClassMetadataInfo::INHERITANCE_TYPE_TABLE_PER_CLASS: + return 'PER_CLASS'; + + default: + throw new \InvalidArgumentException('Invalid provided InheritanceType: ' . $type); + } + } + + private function _getChangeTrackingPolicyString($policy) + { + switch ($policy) { + case ClassMetadataInfo::CHANGETRACKING_DEFERRED_IMPLICIT: + return 'DEFERRED_IMPLICIT'; + + case ClassMetadataInfo::CHANGETRACKING_DEFERRED_EXPLICIT: + return 'DEFERRED_EXPLICIT'; + + case ClassMetadataInfo::CHANGETRACKING_NOTIFY: + return 'NOTIFY'; + + default: + throw new \InvalidArgumentException('Invalid provided ChangeTrackingPolicy: ' . $policy); + } + } + + private function _getIdGeneratorTypeString($type) + { + switch ($type) { + case ClassMetadataInfo::GENERATOR_TYPE_AUTO: + return 'AUTO'; + + case ClassMetadataInfo::GENERATOR_TYPE_SEQUENCE: + return 'SEQUENCE'; + + case ClassMetadataInfo::GENERATOR_TYPE_TABLE: + return 'TABLE'; + + case ClassMetadataInfo::GENERATOR_TYPE_IDENTITY: + return 'IDENTITY'; + + case ClassMetadataInfo::GENERATOR_TYPE_NONE: + return 'NONE'; + + default: + throw new \InvalidArgumentException('Invalid provided IdGeneratorType: ' . $type); + } + } +}