Skip to content
Permalink
Branch: master
Find file Copy path
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
470 lines (403 sloc) 14.3 KB
<?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>.
*/
/**
* Doctrine_Record_Generator
*
* @author Konsta Vesterinen <kvesteri@cc.hut.fi>
* @package Doctrine
* @subpackage Plugin
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @version $Revision$
* @link www.doctrine-project.org
* @since 1.0
*/
abstract class Doctrine_Record_Generator extends Doctrine_Record_Abstract
{
/**
* _options
*
* @var array $_options an array of plugin specific options
*/
protected $_options = array(
'generateFiles' => false,
'generatePath' => false,
'builderOptions' => array(),
'identifier' => false,
'table' => false,
'pluginTable' => false,
'children' => array(),
'cascadeDelete' => true,
'appLevelDelete' => false
);
/**
* Whether or not the generator has been initialized
*
* @var bool $_initialized
*/
protected $_initialized = false;
/**
* An alias for getOption
*
* @param string $option
*/
public function __get($option)
{
if (isset($this->_options[$option])) {
return $this->_options[$option];
}
return null;
}
/**
* __isset
*
* @param string $option
*/
public function __isset($option)
{
return isset($this->_options[$option]);
}
/**
* Returns the value of an option
*
* @param $option the name of the option to retrieve
* @return mixed the value of the option
*/
public function getOption($name)
{
if ( ! isset($this->_options[$name])) {
throw new Doctrine_Exception('Unknown option ' . $name);
}
return $this->_options[$name];
}
/**
* Sets given value to an option
*
* @param $option the name of the option to be changed
* @param $value the value of the option
* @return Doctrine_Plugin this object
*/
public function setOption($name, $value)
{
$this->_options[$name] = $value;
return $this;
}
/**
* Add child record generator
*
* @param Doctrine_Record_Generator $generator
* @return void
*/
public function addChild($generator)
{
$this->_options['children'][] = $generator;
}
/**
* Returns all options and their associated values
*
* @return array all options as an associative array
*/
public function getOptions()
{
return $this->_options;
}
/**
* Initialize the plugin. Call in Doctrine_Template setTableDefinition() in order to initiate a generator in a template
*
* @see Doctrine_Template_I18n
* @param Doctrine_Table $table
* @return void
*/
public function initialize(Doctrine_Table $table)
{
if ($this->_initialized) {
return false;
}
$this->_initialized = true;
$this->initOptions();
$table->addGenerator($this, get_class($this));
$this->_options['table'] = $table;
$ownerClassName = $this->_options['table']->getComponentName();
$className = $this->_options['className'];
$this->_options['className'] = str_replace('%CLASS%', $ownerClassName, $className);
if (isset($this->_options['tableName'])) {
$ownerTableName = $this->_options['table']->getTableName();
$tableName = $this->_options['tableName'];
$this->_options['tableName'] = str_replace('%TABLE%', $ownerTableName, $tableName);
}
// check that class doesn't exist (otherwise we cannot create it)
if ($this->_options['generateFiles'] === false && class_exists($this->_options['className'])) {
$this->_table = Doctrine_Core::getTable($this->_options['className']);
return false;
}
$this->buildTable();
$fk = $this->buildForeignKeys($this->_options['table']);
$this->_table->setColumns($fk);
$this->buildRelation();
$this->setTableDefinition();
$this->setUp();
$this->generateClassFromTable($this->_table);
$this->buildChildDefinitions();
$this->_table->initIdentifier();
}
/**
* Create the new Doctrine_Table instance in $this->_table based on the owning
* table.
*
* @return void
*/
public function buildTable()
{
// Bind model
$conn = $this->_options['table']->getConnection();
$bindConnName = $conn->getManager()->getConnectionForComponent($this->_options['table']->getComponentName())->getName();
if ($bindConnName) {
$conn->getManager()->bindComponent($this->_options['className'], $bindConnName);
} else {
$conn->getManager()->bindComponent($this->_options['className'], $conn->getName());
}
// Create table
$tableClass = $conn->getAttribute(Doctrine_Core::ATTR_TABLE_CLASS);
$this->_table = new $tableClass($this->_options['className'], $conn);
$this->_table->setGenerator($this);
// If custom table name set then lets use it
if (isset($this->_options['tableName']) && $this->_options['tableName']) {
$this->_table->setTableName($this->_options['tableName']);
}
// Maintain some options from the parent table
$options = $this->_options['table']->getOptions();
$newOptions = array();
$maintain = array('type', 'collate', 'charset'); // This list may need updating
foreach ($maintain as $key) {
if (isset($options[$key])) {
$newOptions[$key] = $options[$key];
}
}
$this->_table->setOptions($newOptions);
$conn->addTable($this->_table);
}
/**
* Empty template method for providing the concrete plugins the ability
* to initialize options before the actual definition is being built
*
* @return void
*/
public function initOptions()
{
}
/**
* Build the child behavior definitions that are attached to this generator
*
* @return void
*/
public function buildChildDefinitions()
{
if ( ! isset($this->_options['children'])) {
throw new Doctrine_Record_Exception("Unknown option 'children'.");
}
foreach ($this->_options['children'] as $child) {
if ($child instanceof Doctrine_Template) {
if ($child->getPlugin() !== null) {
$this->_table->addGenerator($child->getPlugin(), get_class($child->getPlugin()));
}
$this->_table->addTemplate(get_class($child), $child);
$child->setInvoker($this);
$child->setTable($this->_table);
$child->setTableDefinition();
$child->setUp();
} else {
$this->_table->addGenerator($child, get_class($child));
$child->initialize($this->_table);
}
}
}
/**
* Generates foreign keys for the plugin table based on the owner table.
* These columns are automatically added to the generated model so we can
* create foreign keys back to the table object that owns the plugin.
*
* @param Doctrine_Table $table the table object that owns the plugin
* @return array an array of foreign key definitions
*/
public function buildForeignKeys(Doctrine_Table $table)
{
$fk = array();
foreach ((array) $table->getIdentifier() as $field) {
$def = $table->getDefinitionOf($field);
unset($def['autoincrement']);
unset($def['sequence']);
unset($def['primary']);
$col = $table->hasColumn($field) ? $field : $table->getColumnName($field) . ' as ' . $field;
$def['primary'] = true;
$fk[$col] = $def;
}
return $fk;
}
/**
* Build the local relationship on the generated model for this generator
* instance which points to the invoking table in $this->_options['table']
*
* @param string $alias Alias of the foreign relation
* @return void
*/
public function buildLocalRelation($alias = null)
{
$options = array(
'local' => $this->getRelationLocalKey(),
'foreign' => $this->getRelationForeignKey(),
'owningSide' => true
);
if (isset($this->_options['cascadeDelete']) && $this->_options['cascadeDelete'] && ! $this->_options['appLevelDelete']) {
$options['onDelete'] = 'CASCADE';
$options['onUpdate'] = 'CASCADE';
}
$aliasStr = '';
if ($alias !== null) {
$aliasStr = ' as ' . $alias;
}
$this->hasOne($this->_options['table']->getComponentName() . $aliasStr, $options);
}
/**
* Add a Doctrine_Relation::MANY relationship to the generator owner table
*
* @param string $name
* @param array $options
* @return void
*/
public function ownerHasMany($name, $options)
{
$this->_options['table']->hasMany($name, $options);
}
/**
* Add a Doctrine_Relation::ONE relationship to the generator owner table
*
* @param string $name
* @param array $options
* @return void
*/
public function ownerHasOne($name, $options)
{
$this->_options['table']->hasOne($name, $options);
}
/**
* Build the foreign relationship on the invoking table in $this->_options['table']
* which points back to the model generated in this generator instance.
*
* @param string $alias Alias of the foreign relation
* @return void
*/
public function buildForeignRelation($alias = null)
{
$options = array(
'local' => $this->getRelationForeignKey(),
'foreign' => $this->getRelationLocalKey(),
'localKey' => false
);
if (isset($this->_options['cascadeDelete']) && $this->_options['cascadeDelete'] && $this->_options['appLevelDelete']) {
$options['cascade'] = array('delete');
}
$aliasStr = '';
if ($alias !== null) {
$aliasStr = ' as ' . $alias;
}
$this->ownerHasMany($this->_table->getComponentName() . $aliasStr, $options);
}
/**
* Get the local key of the generated relationship
*
* @return string $local
*/
public function getRelationLocalKey()
{
return $this->getRelationForeignKey();
}
/**
* Get the foreign key of the generated relationship
*
* @return string $foreign
*/
public function getRelationForeignKey()
{
$table = $this->_options['table'];
$identifier = $table->getIdentifier();
foreach ((array) $identifier as $column) {
$def = $table->getDefinitionOf($column);
if (isset($def['primary']) && $def['primary'] && isset($def['autoincrement']) && $def['autoincrement']) {
return $column;
}
}
return $identifier;
}
/**
* This method can be used for generating the relation from the plugin
* table to the owner table. By default buildForeignRelation() and buildLocalRelation() are called
* Those methods can be overridden or this entire method can be overridden
*
* @return void
*/
public function buildRelation()
{
$this->buildForeignRelation();
$this->buildLocalRelation();
}
/**
* Generate a Doctrine_Record from a populated Doctrine_Table instance
*
* @param Doctrine_Table $table
* @return void
*/
public function generateClassFromTable(Doctrine_Table $table)
{
$definition = array();
$definition['columns'] = $table->getColumns();
$definition['tableName'] = $table->getTableName();
$definition['actAs'] = $table->getTemplates();
return $this->generateClass($definition);
}
/**
* Generates the class definition for plugin class
*
* @param array $definition Definition array defining columns, relations and options
* for the model
* @return void
*/
public function generateClass(array $definition = array())
{
$definition['className'] = $this->_options['className'];
$definition['toString'] = isset($this->_options['toString']) ? $this->_options['toString'] : false;
if (isset($this->_options['listeners'])) {
$definition['listeners'] = $this->_options['listeners'];
}
$builder = new Doctrine_Import_Builder();
$builderOptions = isset($this->_options['builderOptions']) ? (array) $this->_options['builderOptions']:array();
$builder->setOptions($builderOptions);
if ($this->_options['generateFiles']) {
if (isset($this->_options['generatePath']) && $this->_options['generatePath']) {
$builder->setTargetPath($this->_options['generatePath']);
$builder->buildRecord($definition);
} else {
throw new Doctrine_Record_Exception('If you wish to generate files then you must specify the path to generate the files in.');
}
} else {
$def = $builder->buildDefinition($definition);
eval($def);
}
}
}
You can’t perform that action at this time.