src/Propel/Generator/Builder/Om/TableMapBuilder.php
<?php
/**
* MIT License. This file is part of the Propel package.
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Propel\Generator\Builder\Om;
use Propel\Generator\Model\ForeignKey;
use Propel\Generator\Model\IdMethod;
use Propel\Generator\Platform\PlatformInterface;
/**
* Generates the table map class for user object model (OM).
*
* @author Hans Lellelid <hans@xmpl.org>
*/
class TableMapBuilder extends AbstractOMBuilder
{
/**
* Gets the package for the map builder classes.
*
* @return string
*/
public function getPackage(): string
{
return parent::getPackage() . '.Map';
}
/**
* @return string|null
*/
public function getNamespace(): ?string
{
$namespace = parent::getNamespace();
if (!$namespace) {
return 'Map';
}
$namespaceMap = $this->getBuildProperty('generator.objectModel.namespaceMap');
if (!$namespaceMap) {
return $namespace . 'Map';
}
return $namespace . '\\' . $namespaceMap;
}
/**
* @return string
*/
public function getBaseTableMapClassName(): string
{
return 'TableMap';
}
/**
* Returns the name of the current class being built.
*
* @return string
*/
public function getUnprefixedClassName(): string
{
return $this->getTable()->getPhpName() . 'TableMap';
}
/**
* Adds class phpdoc comment and opening of class.
*
* @param string $script The script will be modified in this method.
*
* @return void
*/
protected function addClassOpen(string &$script): void
{
$table = $this->getTable();
$script .= "
/**
* This class defines the structure of the '" . $table->getName() . "' table.
*
*";
if ($this->getBuildProperty('generator.objectModel.addTimeStamp')) {
$now = strftime('%c');
$script .= "
* This class was autogenerated by Propel " . $this->getBuildProperty('general.version') . " on:
*
* $now
*";
}
$script .= "
*
* This map class is used by Propel to do runtime db structure discovery.
* For example, the createSelectSql() method checks the type of a given column used in an
* ORDER BY clause to know whether it needs to apply SQL to make the ORDER BY case-insensitive
* (i.e. if it's a text column type).
*/
class " . $this->getUnqualifiedClassName() . " extends TableMap
{
use InstancePoolTrait;
use TableMapTrait;
";
}
/**
* Specifies the methods that are added as part of the map builder class.
* This can be overridden by subclasses that wish to add more methods.
*
* @see ObjectBuilder::addClassBody()
*
* @param string $script
*
* @return void
*/
protected function addClassBody(string &$script): void
{
$table = $this->getTable();
$this->declareClasses(
'\Propel\Runtime\ActiveQuery\InstancePoolTrait',
'\Propel\Runtime\Map\TableMap',
'\Propel\Runtime\Map\TableMapTrait',
'\Propel\Runtime\Map\RelationMap',
'\Propel\Runtime\ActiveQuery\Criteria',
'\Propel\Runtime\Connection\ConnectionInterface',
'\Propel\Runtime\Exception\PropelException',
'\Propel\Runtime\DataFetcher\DataFetcherInterface',
'\Propel\Runtime\Propel',
);
$script .= $this->addConstants();
$this->addInheritanceColumnConstants($script);
if ($table->hasValueSetColumns()) {
$this->addValueSetColumnConstants($script);
}
// apply behaviors
$this->applyBehaviorModifier('staticConstants', $script, ' ');
$this->applyBehaviorModifier('staticAttributes', $script, ' ');
$this->applyBehaviorModifier('staticMethods', $script, ' ');
$this->addAttributes($script);
$script .= $this->addFieldsAttributes();
$this->addNormalizedColumnNameMap($script);
if ($table->hasValueSetColumns()) {
$this->addValueSetColumnAttributes($script);
$this->addGetValueSets($script);
$this->addGetValueSet($script);
}
$this->addInitialize($script);
$this->addBuildRelations($script);
$this->addGetBehaviors($script);
$script .= $this->addInstancePool();
$script .= $this->addClearRelatedInstancePool();
$this->addGetPrimaryKeyHash($script);
$this->addGetPrimaryKeyFromRow($script);
$this->addGetOMClassMethod($script);
$this->addPopulateObject($script);
$this->addPopulateObjects($script);
if (!$table->isAlias()) {
$this->addSelectMethods($script);
$this->addGetTableMap($script);
}
$this->addDoDelete($script);
$this->addDoDeleteAll($script);
$this->addDoInsert($script);
}
/**
* Adds the addSelectColumns(), doCount(), etc. methods.
*
* @param string $script The script will be modified in this method.
*
* @return void
*/
protected function addSelectMethods(string &$script): void
{
$this->addAddSelectColumns($script);
$this->addRemoveSelectColumns($script);
}
/**
* Adds any constants needed for this TableMap class.
*
* @return string
*/
protected function addConstants(): string
{
return $this->renderTemplate('tableMapConstants', [
'className' => $this->getClasspath(),
'dbName' => $this->getDatabase()->getName(),
'tableName' => $this->getTable()->getName(),
'tablePhpName' => $this->getTable()->getPhpName(),
'omClassName' => $this->getTable()->isAbstract() ? '' : addslashes($this->getStubObjectBuilder()->getFullyQualifiedClassName()),
'classPath' => $this->getStubObjectBuilder()->getClasspath(),
'nbColumns' => $this->getTable()->getNumColumns(),
'nbLazyLoadColumns' => $this->getTable()->getNumLazyLoadColumns(),
'nbHydrateColumns' => $this->getTable()->getNumColumns() - $this->getTable()->getNumLazyLoadColumns(),
'columns' => $this->getTable()->getColumns(),
'stringFormat' => $this->getTable()->getDefaultStringFormat(),
]);
}
/**
* Adds the COLUMN_NAME constant to the class definition.
*
* @param string $script The script will be modified in this method.
*
* @return void
*/
protected function addColumnNameConstants(string &$script): void
{
foreach ($this->getTable()->getColumns() as $col) {
$script .= "
/**
* The column name for the " . $col->getName() . " field
*/
public const " . $col->getConstantName() . " = '" . $this->getTable()->getName() . '.' . $col->getName() . "';
";
}
}
/**
* Adds the valueSet constants for ENUM and SET columns.
*
* @param string $script The script will be modified in this method.
*
* @return void
*/
protected function addValueSetColumnConstants(string &$script): void
{
foreach ($this->getTable()->getColumns() as $col) {
if ($col->isValueSetType()) {
$script .= "
/** The enumerated values for the " . $col->getName() . ' field */';
foreach ($col->getValueSet() as $value) {
$script .= "
public const " . $col->getConstantName() . '_' . $this->getValueSetConstant($value) . " = '" . $value . "';";
}
$script .= "
";
}
}
}
/**
* Adds the valueSet attributes for ENUM columns.
*
* @param string $script The script will be modified in this method.
*
* @return void
*/
protected function addValueSetColumnAttributes(string &$script): void
{
$script .= "
/**
* The enumerated values for this table
*
* @var array<string, array<string>>
*/
protected static \$enumValueSets = [";
foreach ($this->getTable()->getColumns() as $col) {
if ($col->isValueSetType()) {
$script .= "
{$col->getFQConstantName()} => [
";
foreach ($col->getValueSet() as $value) {
$script .= ' self::' . $col->getConstantName() . '_' . $this->getValueSetConstant($value) . ",
";
}
$script .= ' ],';
}
}
$script .= "
];
";
}
/**
* Adds the getValueSets() method.
*
* @param string $script The script will be modified in this method.
*
* @return void
*/
protected function addGetValueSets(string &$script): void
{
$script .= "
/**
* Gets the list of values for all ENUM and SET columns
* @return array
*/
public static function getValueSets(): array
{
return static::\$enumValueSets;
}
";
}
/**
* Adds the getValueSet() method.
*
* @param string $script The script will be modified in this method.
*
* @return void
*/
protected function addGetValueSet(string &$script): void
{
$script .= "
/**
* Gets the list of values for an ENUM or SET column
* @param string \$colname
* @return array list of possible values for the column
*/
public static function getValueSet(string \$colname): array
{
\$valueSets = self::getValueSets();
return \$valueSets[\$colname];
}
";
}
/**
* Adds the CLASSKEY_* and CLASSNAME_* constants used for inheritance.
*
* @param string $script The script will be modified in this method.
*
* @return void
*/
public function addInheritanceColumnConstants(string &$script): void
{
$col = $this->getTable()->getChildrenColumn();
if (!$col || !$col->isEnumeratedClasses()) {
return;
}
foreach ($col->getChildren() as $child) {
$childBuilder = $this->getMultiExtendObjectBuilder();
$childBuilder->setChild($child);
$fqcn = addslashes($childBuilder->getFullyQualifiedClassName());
$script .= "
/** A key representing a particular subclass */
public const CLASSKEY_" . $child->getConstantSuffix() . " = '" . $child->getKey() . "';
";
if (strtoupper($child->getClassName()) != $child->getConstantSuffix()) {
$script .= "
/** A key representing a particular subclass */
public const CLASSKEY_" . strtoupper($child->getClassname()) . " = '" . $fqcn . "';
";
}
$script .= "
/** A class that can be returned by this tableMap. */
public const CLASSNAME_" . $child->getConstantSuffix() . " = '" . $fqcn . "';
";
}
}
/**
* @param string $value
*
* @return string
*/
protected function getValueSetConstant(string $value): string
{
return strtoupper(preg_replace('/[^a-zA-Z0-9_\x7f-\xff]/', '_', $value));
}
/**
* Adds any attributes needed for this TableMap class.
*
* @param string $script The script will be modified in this method.
*
* @return void
*/
protected function addAttributes(string &$script): void
{
}
/**
* @return string
*/
protected function addFieldsAttributes(): string
{
$tableColumns = $this->getTable()->getColumns();
$fieldNamesPhpName = '';
$fieldNamesCamelCaseName = '';
$fieldNamesColname = '';
$fieldNamesRawColname = '';
$fieldNamesFieldName = '';
$fieldNamesNum = '';
$fieldKeysPhpName = '';
$fieldKeysCamelCaseName = '';
$fieldKeysColname = '';
$fieldKeysRawColname = '';
$fieldKeysFieldName = '';
$fieldKeysNum = '';
foreach ($tableColumns as $num => $col) {
$fieldNamesPhpName .= "'" . $col->getPhpName() . "', ";
$fieldNamesCamelCaseName .= "'" . $col->getCamelCaseName() . "', ";
$fieldNamesColname .= $this->getColumnConstant($col, $this->getTableMapClass()) . ', ';
$fieldNamesRawColname .= "'" . $col->getConstantName() . "', ";
$fieldNamesFieldName .= "'" . $col->getName() . "', ";
$fieldNamesNum .= "$num, ";
$fieldKeysPhpName .= "'" . $col->getPhpName() . "' => $num, ";
$fieldKeysCamelCaseName .= "'" . $col->getCamelCaseName() . "' => $num, ";
$fieldKeysColname .= $this->getColumnConstant($col, $this->getTableMapClass()) . " => $num, ";
$fieldKeysRawColname .= "'" . $col->getConstantName() . "' => $num, ";
$fieldKeysFieldName .= "'" . $col->getName() . "' => $num, ";
$fieldKeysNum .= "$num, ";
}
return $this->renderTemplate('tableMapFields', [
'fieldNamesPhpName' => $fieldNamesPhpName,
'fieldNamesCamelCaseName' => $fieldNamesCamelCaseName,
'fieldNamesColname' => $fieldNamesColname,
'fieldNamesRawColname' => $fieldNamesRawColname,
'fieldNamesFieldName' => $fieldNamesFieldName,
'fieldNamesNum' => $fieldNamesNum,
'fieldKeysPhpName' => $fieldKeysPhpName,
'fieldKeysCamelCaseName' => $fieldKeysCamelCaseName,
'fieldKeysColname' => $fieldKeysColname,
'fieldKeysRawColname' => $fieldKeysRawColname,
'fieldKeysFieldName' => $fieldKeysFieldName,
'fieldKeysNum' => $fieldKeysNum,
]);
}
/**
* @param string $script
*
* @return void
*/
protected function addNormalizedColumnNameMap(string &$script): void
{
$table = $this->getTable();
$tableColumns = $table->getColumns();
$arrayString = '';
foreach ($tableColumns as $column) {
$variants = [
$column->getPhpName(), // ColumnName => COLUMN_NAME
$table->getPhpName() . '.' . $column->getPhpName(), // TableName.ColumnName => COLUMN_NAME
$column->getCamelCaseName(), // columnName => COLUMN_NAME
$table->getCamelCaseName() . '.' . $column->getCamelCaseName(), // tableName.columnName => COLUMN_NAME
$this->getColumnConstant($column, $this->getTableMapClass()), // TableNameTableMap::COL_COLUMN_NAME => COLUMN_NAME
$column->getConstantName(), // COL_COLUMN_NAME => COLUMN_NAME
$column->getName(), // column_name => COLUMN_NAME
$table->getName() . '.' . $column->getName(), // table_name.column_name => COLUMN_NAME
];
$variants = array_unique($variants);
$normalizedName = strtoupper($column->getName());
array_walk($variants, static function ($variant) use (&$arrayString, $normalizedName): void {
$arrayString .= PHP_EOL . " '{$variant}' => '{$normalizedName}',";
});
}
$script .= '
/**
* Holds a list of column names and their normalized version.
*
* @var array<string>
*/
protected $normalizedColumnNameMap = [' . $arrayString . PHP_EOL
. ' ];' . PHP_EOL;
}
/**
* Closes class.
*
* @param string $script The script will be modified in this method.
*
* @return void
*/
protected function addClassClose(string &$script): void
{
$script .= "
}
";
$this->applyBehaviorModifier('tableMapFilter', $script, '');
}
/**
* Adds the addInitialize() method to the table map class.
*
* @param string $script The script will be modified in this method.
*
* @return void
*/
protected function addInitialize(string &$script): void
{
$table = $this->getTable();
/** @var \Propel\Generator\Platform\DefaultPlatform $platform */
$platform = $this->getPlatform();
$script .= "
/**
* Initialize the table attributes and columns
* Relations are not initialized by this method since they are lazy loaded
*
* @return void
* @throws \Propel\Runtime\Exception\PropelException
*/
public function initialize(): void
{
// attributes
\$this->setName('" . $table->getName() . "');
\$this->setPhpName('" . $table->getPhpName() . "');
\$this->setIdentifierQuoting(" . ($table->isIdentifierQuotingEnabled() ? 'true' : 'false') . ");
\$this->setClassName('" . addslashes($this->getStubObjectBuilder()->getFullyQualifiedClassName()) . "');
\$this->setPackage('" . parent::getPackage() . "');";
if ($table->getIdMethod() === 'native') {
$script .= "
\$this->setUseIdGenerator(true);";
} else {
$script .= "
\$this->setUseIdGenerator(false);";
}
if ($table->getIdMethodParameters()) {
$params = $table->getIdMethodParameters();
$imp = $params[0];
$script .= "
\$this->setPrimaryKeyMethodInfo('" . $imp->getValue() . "');";
} elseif ($table->getIdMethod() == IdMethod::NATIVE && ($platform->getNativeIdMethod() == PlatformInterface::SEQUENCE || $platform->getNativeIdMethod() == PlatformInterface::SERIAL)) {
$script .= "
\$this->setPrimaryKeyMethodInfo('" . $platform->getSequenceName($table) . "');";
}
if ($this->getTable()->getChildrenColumn()) {
$script .= "
\$this->setSingleTableInheritance(true);";
}
if ($this->getTable()->getIsCrossRef()) {
$script .= "
\$this->setIsCrossRef(true);";
}
// Add columns to map
$script .= "
// columns";
foreach ($table->getColumns() as $col) {
$columnName = $col->getName();
$cfc = $col->getPhpName();
if (!$col->getSize()) {
$size = 'null';
} else {
$size = $col->getSize();
}
$default = $col->getDefaultValueString();
if ($col->isPrimaryKey()) {
if ($col->isForeignKey()) {
foreach ($col->getForeignKeys() as $fk) {
$script .= "
\$this->addForeignPrimaryKey('$columnName', '$cfc', '" . $col->getType() . "' , '" . $fk->getForeignTableName() . "', '" . $fk->getMappedForeignColumn($col->getName()) . "', " . ($col->isNotNull() ? 'true' : 'false') . ', ' . $size . ", $default);";
}
} else {
$script .= "
\$this->addPrimaryKey('$columnName', '$cfc', '" . $col->getType() . "', " . var_export($col->isNotNull(), true) . ', ' . $size . ", $default);";
}
} else {
if ($col->isForeignKey()) {
foreach ($col->getForeignKeys() as $fk) {
$script .= "
\$this->addForeignKey('$columnName', '$cfc', '" . $col->getType() . "', '" . $fk->getForeignTableName() . "', '" . $fk->getMappedForeignColumn($col->getName()) . "', " . ($col->isNotNull() ? 'true' : 'false') . ', ' . $size . ", $default);";
}
} else {
$script .= "
\$this->addColumn('$columnName', '$cfc', '" . $col->getType() . "', " . var_export($col->isNotNull(), true) . ', ' . $size . ", $default);";
}
} // if col-is prim key
if ($col->isValueSetType()) {
$script .= "
\$this->getColumn('$columnName')->setValueSet(" . var_export($col->getValueSet(), true) . ');';
}
if ($col->isPrimaryString()) {
$script .= "
\$this->getColumn('$columnName')->setPrimaryString(true);";
}
}
$script .= "
}
";
}
/**
* Adds the method that build the RelationMap objects
*
* @param string $script The script will be modified in this method.
*
* @return void
*/
protected function addBuildRelations(string &$script): void
{
$script .= "
/**
* Build the RelationMap objects for this table relationships
*
* @return void
*/
public function buildRelations(): void
{";
foreach ($this->getTable()->getForeignKeys() as $fkey) {
$joinCondition = var_export($fkey->getNormalizedMap($fkey->getMapping()), true);
$onDelete = $fkey->hasOnDelete() ? "'" . $fkey->getOnDelete() . "'" : 'null';
$onUpdate = $fkey->hasOnUpdate() ? "'" . $fkey->getOnUpdate() . "'" : 'null';
$isPolymorphic = $fkey->isPolymorphic() ? 'true' : 'false';
$script .= "
\$this->addRelation('" . $this->getFKPhpNameAffix($fkey) . "', '" . addslashes($this->getNewStubObjectBuilder($fkey->getForeignTable())->getFullyQualifiedClassName()) . "', RelationMap::MANY_TO_ONE, $joinCondition, $onDelete, $onUpdate, null, $isPolymorphic);";
}
foreach ($this->getTable()->getReferrers() as $fkey) {
$relationName = $this->getRefFKPhpNameAffix($fkey);
$joinCondition = var_export($fkey->getNormalizedMap($fkey->getMapping()), true);
$onDelete = $fkey->hasOnDelete() ? "'" . $fkey->getOnDelete() . "'" : 'null';
$onUpdate = $fkey->hasOnUpdate() ? "'" . $fkey->getOnUpdate() . "'" : 'null';
$isPolymorphic = $fkey->isPolymorphic() ? 'true' : 'false';
$script .= "
\$this->addRelation('$relationName', '" . addslashes($this->getNewStubObjectBuilder($fkey->getTable())->getFullyQualifiedClassName()) . "', RelationMap::ONE_TO_" . ($fkey->isLocalPrimaryKey() ? 'ONE' : 'MANY') . ", $joinCondition, $onDelete, $onUpdate";
if ($fkey->isLocalPrimaryKey()) {
$script .= ', null';
} else {
$script .= ", '" . $this->getRefFKPhpNameAffix($fkey, true) . "'";
}
$script .= ", $isPolymorphic);";
}
foreach ($this->getTable()->getCrossFks() as $crossFKs) {
foreach ($crossFKs->getCrossForeignKeys() as $crossFK) {
$relationName = $this->getFKPhpNameAffix($crossFK);
$pluralName = "'" . $this->getFKPhpNameAffix($crossFK, true) . "'";
$onDelete = $crossFK->hasOnDelete() ? "'" . $crossFK->getOnDelete() . "'" : 'null';
$onUpdate = $crossFK->hasOnUpdate() ? "'" . $crossFK->getOnUpdate() . "'" : 'null';
$script .= "
\$this->addRelation('$relationName', '" . addslashes($this->getNewStubObjectBuilder($crossFK->getForeignTable())->getFullyQualifiedClassName()) . "', RelationMap::MANY_TO_MANY, array(), $onDelete, $onUpdate, $pluralName);";
}
}
$script .= "
}
";
}
/**
* Adds the behaviors getter
*
* @param string $script The script will be modified in this method.
*
* @return void
*/
protected function addGetBehaviors(string &$script): void
{
$behaviors = $this->getTable()->getBehaviors();
if (!$behaviors) {
return;
}
$stringifiedBehaviors = [];
foreach ($behaviors as $behavior) {
$id = $behavior->getId();
$params = $this->stringify($behavior->getParameters());
$stringifiedBehaviors[] = "'$id' => $params,";
}
$itemsString = implode(PHP_EOL . ' ', $stringifiedBehaviors);
$script .= "
/**
*
* Gets the list of behaviors registered for this table
*
* @return array<string, array> Associative array (name => parameters) of behaviors
*/
public function getBehaviors(): array
{
return [
$itemsString
];
}
";
}
/**
* @param array|string|float|int|bool|null $value
*
* @return string
*/
protected function stringify($value): string
{
if (!is_array($value)) {
return var_export($value, true);
}
$items = [];
foreach ($value as $key => $arrayValue) {
$keyString = var_export($key, true);
$valString = $this->stringify($arrayValue);
$items[] = "$keyString => $valString";
}
$itemsCsv = implode(', ', $items);
return "[$itemsCsv]";
}
/**
* Adds the PHP code to return a instance pool key for the passed-in primary key variable names.
*
* @param array<string>|string $pkphp An array of PHP var names / method calls representing complete pk.
*
* @return string
*/
public function getInstancePoolKeySnippet($pkphp): string
{
$pkphp = (array)$pkphp; // make it an array if it is not.
$script = '';
if (count($pkphp) > 1) {
$script .= 'serialize([';
$i = 0;
foreach ($pkphp as $pkvar) {
$script .= ($i++ ? ', ' : '') . "(null === {$pkvar} || is_scalar({$pkvar}) || is_callable([{$pkvar}, '__toString']) ? (string) {$pkvar} : {$pkvar})";
}
$script .= '])';
} else {
$script .= "null === {$pkphp[0]} || is_scalar({$pkphp[0]}) || is_callable([{$pkphp[0]}, '__toString']) ? (string) {$pkphp[0]} : {$pkphp[0]}";
}
return $script;
}
/**
* @return string
*/
public function addInstancePool(): string
{
// No need to override instancePool if the PK is not composite
if (!$this->getTable()->hasCompositePrimaryKey()) {
return '';
}
$pks = $this->getTable()->getPrimaryKey();
if (!count($pks)) {
return '';
}
$add = [];
$removeObjects = [];
foreach ($pks as $pk) {
$add[] = '$obj->get' . $pk->getPhpName() . '()';
$removeObjects[] = '$value->get' . $pk->getPhpName() . '()';
}
$addInstancePoolKeySnippet = $this->getInstancePoolKeySnippet($add);
$removeInstancePoolKeySnippetObjects = $this->getInstancePoolKeySnippet($removeObjects);
$removePks = [];
$nbPks = count($pks);
for ($i = 0; $i < $nbPks; $i++) {
$removePks[] = "\$value[$i]";
}
$removeInstancePoolKeySnippetPks = $this->getInstancePoolKeySnippet($removePks);
return $this->renderTemplate('tableMapInstancePool', [
'objectClassName' => $this->getStubObjectBuilder()->getClassName(),
'addInstancePoolKeySnippet' => $addInstancePoolKeySnippet,
'removeInstancePoolKeySnippetObjects' => $removeInstancePoolKeySnippetObjects,
'removeInstancePoolKeySnippetPks' => $removeInstancePoolKeySnippetPks,
'countPks' => count($pks),
]);
}
/**
* @return string
*/
public function addClearRelatedInstancePool(): string
{
$table = $this->getTable();
$relatedClassNames = [];
// Handle ON DELETE CASCADE for updating instance pool
foreach ($table->getReferrers() as $fk) {
// $fk is the foreign key in the other table, so localTableName will
// actually be the table name of other table
$tblFK = $fk->getTable();
$joinedTableTableMapBuilder = $this->getNewTableMapBuilder($tblFK)->getTableMapBuilder();
$tableMapClassName = $this->declareClassFromBuilder($joinedTableTableMapBuilder, true);
if (!$tblFK->isForReferenceOnly()) {
// we can't perform operations on tables that are
// not within the schema (i.e. that we have no map for, etc.)
if ($fk->getOnDelete() === ForeignKey::CASCADE || $fk->getOnDelete() === ForeignKey::SETNULL) {
$relatedClassNames[$tableMapClassName] = $tableMapClassName;
}
}
}
if (count($relatedClassNames) == 0) {
return '';
}
return $this->renderTemplate('tableMapClearRelatedInstancePool', [
'tableName' => $table->getName(),
'relatedClassNames' => $relatedClassNames,
]);
}
/**
* Checks whether any registered behavior on that table has a modifier for a hook
*
* @param string $hookName The name of the hook as called from one of this class methods, e.g. "preSave"
* @param string $modifier
*
* @return bool
*/
public function hasBehaviorModifier(string $hookName, string $modifier = ''): bool
{
return parent::hasBehaviorModifier($hookName, 'TableMapBuilderModifier');
}
/**
* Checks whether any registered behavior on that table has a modifier for a hook
*
* @param string $hookName The name of the hook as called from one of this class methods, e.g. "preSave"
* @param string $script The script will be modified in this method.
* @param string $tab
*
* @return void
*/
public function applyBehaviorModifier(string $hookName, string &$script, string $tab = ' '): void
{
$this->applyBehaviorModifierBase($hookName, 'TableMapBuilderModifier', $script, $tab);
}
/**
* Adds method to get a version of the primary key that can be used as a unique key for identifier map.
*
* @param string $script The script will be modified in this method.
*
* @return void
*/
protected function addGetPrimaryKeyHash(string &$script): void
{
// We have to iterate through all the columns so that we know the offset of the primary
// key columns.
$n = 0;
$pk = [];
$cond = [];
foreach ($this->getTable()->getColumns() as $col) {
if (!$col->isLazyLoad()) {
if ($col->isPrimaryKey()) {
$part = "\$row[TableMap::TYPE_NUM == \$indexType ? $n + \$offset : static::translateFieldName('{$col->getPhpName()}', TableMap::TYPE_PHPNAME, \$indexType)]";
$cond[] = $part . ' === null';
$pk[] = $part;
}
$n++;
}
}
$script .= "
/**
* Retrieves a string version of the primary key from the DB resultset row that can be used to uniquely identify a row in this table.
*
* For tables with a single-column primary key, that simple pkey value will be returned. For tables with
* a multi-column primary key, a serialize()d version of the primary key will be returned.
*
* @param array \$row Resultset row.
* @param int \$offset The 0-based offset for reading from the resultset row.
* @param string \$indexType One of the class type constants TableMap::TYPE_PHPNAME, TableMap::TYPE_CAMELNAME
* TableMap::TYPE_COLNAME, TableMap::TYPE_FIELDNAME, TableMap::TYPE_NUM
*
* @return string|null The primary key hash of the row
*/
public static function getPrimaryKeyHashFromRow(array \$row, int \$offset = 0, string \$indexType = TableMap::TYPE_NUM): ?string
{";
if (count($pk) > 0) {
$script .= "
// If the PK cannot be derived from the row, return NULL.
if (" . implode(' && ', $cond) . ") {
return null;
}
return " . $this->getInstancePoolKeySnippet($pk) . ";
}
";
} else {
$script .= "
return null;
}
";
}
}
/**
* Adds method to get the primary key from a row
*
* @param string $script The script will be modified in this method.
*
* @return void
*/
protected function addGetPrimaryKeyFromRow(string &$script): void
{
$script .= "
/**
* Retrieves the primary key from the DB resultset row
* For tables with a single-column primary key, that simple pkey value will be returned. For tables with
* a multi-column primary key, an array of the primary key columns will be returned.
*
* @param array \$row Resultset row.
* @param int \$offset The 0-based offset for reading from the resultset row.
* @param string \$indexType One of the class type constants TableMap::TYPE_PHPNAME, TableMap::TYPE_CAMELNAME
* TableMap::TYPE_COLNAME, TableMap::TYPE_FIELDNAME, TableMap::TYPE_NUM
*
* @return mixed The primary key of the row
*/
public static function getPrimaryKeyFromRow(array \$row, int \$offset = 0, string \$indexType = TableMap::TYPE_NUM)
{";
// We have to iterate through all the columns so that we
// know the offset of the primary key columns.
$table = $this->getTable();
$n = 0;
if ($table->hasCompositePrimaryKey()) {
$script .= "
\$pks = [];
";
foreach ($table->getColumns() as $col) {
if (!$col->isLazyLoad()) {
if ($col->isPrimaryKey()) {
$script .= '
$pks[] = ' . ($col->isPhpObjectType() ? 'new ' . $col->getPhpType() . '(' : '(' . $col->getPhpType() . ') ') . "\$row[
\$indexType == TableMap::TYPE_NUM
? $n + \$offset
: self::translateFieldName('{$col->getPhpName()}', TableMap::TYPE_PHPNAME, \$indexType)
]" . ($col->isPhpObjectType() ? ')' : '') . ';';
}
$n++;
}
}
$script .= "
return \$pks;";
} else {
$pk = "''";
foreach ($table->getColumns() as $col) {
if (!$col->isLazyLoad()) {
if ($col->isPrimaryKey()) {
$pk = ($col->isPhpObjectType() ? 'new ' . $col->getPhpType() . '(' : '(' . $col->getPhpType() . ') ') . "\$row[
\$indexType == TableMap::TYPE_NUM
? $n + \$offset
: self::translateFieldName('{$col->getPhpName()}', TableMap::TYPE_PHPNAME, \$indexType)
]" . ($col->isPhpObjectType() ? ')' : '');
}
$n++;
}
}
$script .= "
return " . $pk . ';';
}
$script .= "
}
";
}
/**
* Adds the correct getOMClass() method, depending on whether this table uses inheritance.
*
* @param string $script The script will be modified in this method.
*
* @return void
*/
protected function addGetOMClassMethod(string &$script): void
{
$table = $this->getTable();
if ($table->getChildrenColumn()) {
$this->addGetOMClassInheritance($script);
} else {
if ($table->isAbstract()) {
$this->addGetOMClassNoInheritanceAbstract($script);
} else {
$this->addGetOMClassNoInheritance($script);
}
}
}
/**
* Adds a getOMClass() for non-abstract tables that have inheritance.
*
* @param string $script The script will be modified in this method.
*
* @return void
*/
protected function addGetOMClassInheritance(string &$script): void
{
$col = $this->getTable()->getChildrenColumn();
$script .= "
/**
* The returned Class will contain objects of the default type or
* objects that inherit from the default.
*
* @param array \$row ConnectionInterface result row.
* @param int \$colNum Column to examine for OM class information (first is 0).
* @param bool \$withPrefix Whether to return the path with the class name
* @throws \Propel\Runtime\Exception\PropelException Any exceptions caught during processing will be
* rethrown wrapped into a PropelException.
*
* @return string The OM class
*/
public static function getOMClass(array \$row, int \$colNum, bool \$withPrefix = true): string
{
try {
";
if ($col->isEnumeratedClasses()) {
$script .= "
\$omClass = null;
\$classKey = \$row[\$colNum + " . ($col->getPosition() - 1) . "];
switch (\$classKey) {
";
foreach ($col->getChildren() as $child) {
$script .= "
case {$this->getTableMapClassName()}::CLASSKEY_" . $child->getConstantSuffix() . ":
\$omClass = {$this->getTableMapClassName()}::CLASSNAME_" . $child->getConstantSuffix() . ";
break;
";
} /* foreach */
$script .= "
default:
\$omClass = \$withPrefix
? {$this->getTableMapClassName()}::CLASS_DEFAULT
: {$this->getTableMapClassName()}::OM_CLASS;
";
$script .= "
} // switch
if (!\$withPrefix) {
\$omClass = preg_replace('#\.#', '\\\\', \$omClass);
}
";
} else { /* if not enumerated */
$script .= "
\$omClass = \$row[\$colNum + " . ($col->getPosition() - 1) . "];
\$omClass = preg_replace('#\.#', '\\\\', '.'.\$omClass);
";
}
$script .= "
} catch (\Exception \$e) {
throw new PropelException('Unable to get OM class.', 0, \$e);
}
return \$omClass;
}
";
}
/**
* Adds a getOMClass() for non-abstract tables that do not use inheritance.
*
* @param string $script The script will be modified in this method.
*
* @return void
*/
protected function addGetOMClassNoInheritance(string &$script): void
{
$script .= "
/**
* The class that the tableMap will make instances of.
*
* If \$withPrefix is true, the returned path
* uses a dot-path notation which is translated into a path
* relative to a location on the PHP include_path.
* (e.g. path.to.MyClass -> 'path/to/MyClass.php')
*
* @param bool \$withPrefix Whether to return the path with the class name
* @return string path.to.ClassName
*/
public static function getOMClass(bool \$withPrefix = true): string
{
return \$withPrefix ? " . $this->getTableMapClass() . '::CLASS_DEFAULT : ' . $this->getTableMapClass() . "::OM_CLASS;
}
";
}
/**
* Adds a getOMClass() signature for abstract tables that do not have inheritance.
*
* @param string $script The script will be modified in this method.
*
* @return void
*/
protected function addGetOMClassNoInheritanceAbstract(string &$script): void
{
$objectClassName = $this->getObjectClassName();
$script .= "
/**
* The class that the tableMap will make instances of.
*
* This method must be overridden by the stub subclass, because
* $objectClassName is declared abstract in the schema.
*
* @param bool \$withPrefix
* @return string
*/
public static function getOMClass(bool \$withPrefix = true): string
{
throw new PropelException('$objectClassName is declared abstract, it cannot be instantiated.');
}
";
}
/**
* Adds the populateObject() method.
*
* @param string $script The script will be modified in this method.
*
* @return void
*/
protected function addPopulateObject(string &$script): void
{
$table = $this->getTable();
$script .= "
/**
* Populates an object of the default type or an object that inherit from the default.
*
* @param array \$row Row returned by DataFetcher->fetch().
* @param int \$offset The 0-based offset for reading from the resultset row.
* @param string \$indexType The index type of \$row. Mostly DataFetcher->getIndexType().
One of the class type constants TableMap::TYPE_PHPNAME, TableMap::TYPE_CAMELNAME
* TableMap::TYPE_COLNAME, TableMap::TYPE_FIELDNAME, TableMap::TYPE_NUM.
*
* @throws \Propel\Runtime\Exception\PropelException Any exceptions caught during processing will be
* rethrown wrapped into a PropelException.
* @return array (" . $this->getObjectClassName() . " object, last column rank)
*/
public static function populateObject(array \$row, int \$offset = 0, string \$indexType = TableMap::TYPE_NUM): array
{
\$key = {$this->getTableMapClassName()}::getPrimaryKeyHashFromRow(\$row, \$offset, \$indexType);
if (null !== (\$obj = {$this->getTableMapClassName()}::getInstanceFromPool(\$key))) {
// We no longer rehydrate the object, since this can cause data loss.
// See http://www.propelorm.org/ticket/509
// \$obj->hydrate(\$row, \$offset, true); // rehydrate
\$col = \$offset + " . $this->getTableMapClass() . '::NUM_HYDRATE_COLUMNS;';
if ($table->isAbstract()) {
$script .= "
} elseif (null == \$key) {
// empty resultset, probably from a left join
// since this table is abstract, we can't hydrate an empty object
\$obj = null;
\$col = \$offset + " . $this->getTableMapClass() . '::NUM_HYDRATE_COLUMNS;';
}
$script .= "
} else {";
if (!$table->getChildrenColumn()) {
$script .= "
\$cls = " . $this->getTableMapClass() . '::OM_CLASS;';
} else {
$script .= "
\$cls = static::getOMClass(\$row, \$offset, false);";
}
$script .= "
/** @var {$this->getObjectClassName()} \$obj */
\$obj = new \$cls();
\$col = \$obj->hydrate(\$row, \$offset, false, \$indexType);
{$this->getTableMapClassName()}::addInstanceToPool(\$obj, \$key);
}
return [\$obj, \$col];
}
";
}
/**
* Adds the populateObjects() method.
*
* @param string $script The script will be modified in this method.
*
* @return void
*/
protected function addPopulateObjects(string &$script): void
{
$table = $this->getTable();
$script .= "
/**
* The returned array will contain objects of the default type or
* objects that inherit from the default.
*
* @param DataFetcherInterface \$dataFetcher
* @return array<object>
* @throws \Propel\Runtime\Exception\PropelException Any exceptions caught during processing will be
* rethrown wrapped into a PropelException.
*/
public static function populateObjects(DataFetcherInterface \$dataFetcher): array
{
\$results = [];
";
if (!$table->getChildrenColumn()) {
$script .= "
// set the class once to avoid overhead in the loop
\$cls = static::getOMClass(false);";
}
$script .= "
// populate the object(s)
while (\$row = \$dataFetcher->fetch()) {
\$key = {$this->getTableMapClassName()}::getPrimaryKeyHashFromRow(\$row, 0, \$dataFetcher->getIndexType());
if (null !== (\$obj = {$this->getTableMapClassName()}::getInstanceFromPool(\$key))) {
// We no longer rehydrate the object, since this can cause data loss.
// See http://www.propelorm.org/ticket/509
// \$obj->hydrate(\$row, 0, true); // rehydrate
\$results[] = \$obj;
} else {";
if ($table->getChildrenColumn()) {
$script .= "
// class must be set each time from the record row
\$cls = static::getOMClass(\$row, 0);
\$cls = preg_replace('#\.#', '\\\\', \$cls);
/** @var {$this->getObjectClassName()} \$obj */
" . $this->buildObjectInstanceCreationCode('$obj', '$cls') . "
\$obj->hydrate(\$row);
\$results[] = \$obj;
{$this->getTableMapClassName()}::addInstanceToPool(\$obj, \$key);";
} else {
$script .= "
/** @var {$this->getObjectClassName()} \$obj */
" . $this->buildObjectInstanceCreationCode('$obj', '$cls') . "
\$obj->hydrate(\$row);
\$results[] = \$obj;
{$this->getTableMapClassName()}::addInstanceToPool(\$obj, \$key);";
}
$script .= "
} // if key exists
}
return \$results;
}";
}
/**
* Adds the addSelectColumns() method.
*
* @param string $script The script will be modified in this method.
*
* @return void
*/
protected function addAddSelectColumns(string &$script): void
{
$script .= "
/**
* Add all the columns needed to create a new object.
*
* Note: any columns that were marked with lazyLoad=\"true\" in the
* XML schema will not be added to the select list and only loaded
* on demand.
*
* @param Criteria \$criteria Object containing the columns to add.
* @param string|null \$alias Optional table alias
* @throws \Propel\Runtime\Exception\PropelException Any exceptions caught during processing will be
* rethrown wrapped into a PropelException.
* @return void
*/
public static function addSelectColumns(Criteria \$criteria, ?string \$alias = null): void
{
if (null === \$alias) {";
foreach ($this->getTable()->getColumns() as $col) {
if (!$col->isLazyLoad()) {
$script .= "
\$criteria->addSelectColumn({$col->getFQConstantName()});";
} // if !col->isLazyLoad
}
$script .= "
} else {";
foreach ($this->getTable()->getColumns() as $col) {
if (!$col->isLazyLoad()) {
$script .= "
\$criteria->addSelectColumn(\$alias . '." . $col->getName() . "');";
} // if !col->isLazyLoad
}
$script .= "
}";
$script .= "
}
";
}
// addAddSelectColumns()
/**
* Adds the removeSelectColumns() method.
*
* @param string $script The script will be modified in this method.
*
* @return void
*/
protected function addRemoveSelectColumns(string &$script): void
{
$script .= "
/**
* Remove all the columns needed to create a new object.
*
* Note: any columns that were marked with lazyLoad=\"true\" in the
* XML schema will not be removed as they are only loaded on demand.
*
* @param Criteria \$criteria Object containing the columns to remove.
* @param string|null \$alias Optional table alias
* @throws \Propel\Runtime\Exception\PropelException Any exceptions caught during processing will be
* rethrown wrapped into a PropelException.
* @return void
*/
public static function removeSelectColumns(Criteria \$criteria, ?string \$alias = null): void
{
if (null === \$alias) {";
foreach ($this->getTable()->getColumns() as $col) {
if (!$col->isLazyLoad()) {
$script .= "
\$criteria->removeSelectColumn({$col->getFQConstantName()});";
} // if !col->isLazyLoad
} // foreach
$script .= "
} else {";
foreach ($this->getTable()->getColumns() as $col) {
if (!$col->isLazyLoad()) {
$script .= "
\$criteria->removeSelectColumn(\$alias . '." . $col->getName() . "');";
} // if !col->isLazyLoad
} // foreach
$script .= "
}";
$script .= "
}
";
}
// addRemoveSelectColumns()
/**
* Adds the getTableMap() method which is a convenience method for apps to get DB metadata.
*
* @param string $script The script will be modified in this method.
*
* @return void
*/
protected function addGetTableMap(string &$script): void
{
$script .= "
/**
* Returns the TableMap related to this object.
* This method is not needed for general use but a specific application could have a need.
* @return TableMap
* @throws \Propel\Runtime\Exception\PropelException Any exceptions caught during processing will be
* rethrown wrapped into a PropelException.
*/
public static function getTableMap(): TableMap
{
return Propel::getServiceContainer()->getDatabaseMap(" . $this->getTableMapClass() . '::DATABASE_NAME)->getTable(' . $this->getTableMapClass() . "::TABLE_NAME);
}
";
}
/**
* Adds the doDeleteAll() method.
*
* @param string $script The script will be modified in this method.
*
* @return void
*/
protected function addDoDeleteAll(string &$script): void
{
$table = $this->getTable();
$script .= "
/**
* Deletes all rows from the " . $table->getName() . " table.
*
* @param ConnectionInterface \$con the connection to use
* @return int The number of affected rows (if supported by underlying database driver).
*/
public static function doDeleteAll(?ConnectionInterface \$con = null): int
{
return " . $this->getQueryClassName() . "::create()->doDeleteAll(\$con);
}
";
}
/**
* Adds the doDelete() method.
*
* @param string $script The script will be modified in this method.
*
* @return void
*/
protected function addDoDelete(string &$script): void
{
$table = $this->getTable();
$script .= "
/**
* Performs a DELETE on the database, given a " . $this->getObjectClassName() . " or Criteria object OR a primary key value.
*
* @param mixed \$values Criteria or " . $this->getObjectClassName() . " object or primary key or array of primary keys
* which is used to create the DELETE statement
* @param ConnectionInterface \$con the connection to use
* @return int The number of affected rows (if supported by underlying database driver). This includes CASCADE-related rows
* if supported by native driver or if emulated using Propel.
* @throws \Propel\Runtime\Exception\PropelException Any exceptions caught during processing will be
* rethrown wrapped into a PropelException.
*/
public static function doDelete(\$values, ?ConnectionInterface \$con = null): int
{
if (null === \$con) {
\$con = Propel::getServiceContainer()->getWriteConnection(" . $this->getTableMapClass() . "::DATABASE_NAME);
}
if (\$values instanceof Criteria) {";
$script .= "
// rename for clarity
\$criteria = \$values;
} elseif (\$values instanceof \\" . $this->getStubObjectBuilder()->getQualifiedClassName() . ") { // it's a model object";
if (count($table->getPrimaryKey()) > 0) {
$script .= "
// create criteria based on pk values
\$criteria = \$values->buildPkeyCriteria();";
} else {
$script .= "
// create criteria based on pk value
\$criteria = \$values->buildCriteria();";
}
$script .= "
} else { // it's a primary key, or an array of pks";
if (!$table->getPrimaryKey()) {
$class = $this->getObjectName();
$this->declareClass('Propel\\Runtime\\Exception\\LogicException');
$script .= "
throw new LogicException('The $class object has no primary key');";
} else {
$script .= "
\$criteria = new Criteria(" . $this->getTableMapClass() . '::DATABASE_NAME);';
if (count($table->getPrimaryKey()) === 1) {
$pkey = $table->getPrimaryKey();
$col = array_shift($pkey);
$script .= "
\$criteria->add(" . $this->getColumnConstant($col) . ', (array) $values, Criteria::IN);';
} else {
$script .= "
// primary key is composite; we therefore, expect
// the primary key passed to be an array of pkey values
if (count(\$values) == count(\$values, COUNT_RECURSIVE)) {
// array is not multi-dimensional
\$values = [\$values];
}
foreach (\$values as \$value) {";
$i = 0;
foreach ($table->getPrimaryKey() as $col) {
if ($i === 0) {
$script .= "
\$criterion = \$criteria->getNewCriterion(" . $this->getColumnConstant($col) . ", \$value[$i]);";
} else {
$script .= "
\$criterion->addAnd(\$criteria->getNewCriterion(" . $this->getColumnConstant($col) . ", \$value[$i]));";
}
$i++;
}
$script .= "
\$criteria->addOr(\$criterion);";
$script .= "
}";
} /* if count(table->getPrimaryKeys()) */
}
$script .= "
}
\$query = " . $this->getQueryClassName() . "::create()->mergeWith(\$criteria);
if (\$values instanceof Criteria) {
{$this->getTableMapClassName()}::clearInstancePool();
} elseif (!is_object(\$values)) { // it's a primary key, or an array of pks
foreach ((array) \$values as \$singleval) {
{$this->getTableMapClassName()}::removeInstanceFromPool(\$singleval);
}
}
return \$query->delete(\$con);
}
";
}
/**
* Adds the doInsert() method.
*
* @param string $script The script will be modified in this method.
*
* @return void
*/
protected function addDoInsert(string &$script): void
{
$table = $this->getTable();
$tableMapClass = $this->getTableMapClass();
$script .= "
/**
* Performs an INSERT on the database, given a " . $this->getObjectClassName() . " or Criteria object.
*
* @param mixed \$criteria Criteria or " . $this->getObjectClassName() . " object containing data that is used to create the INSERT statement.
* @param ConnectionInterface \$con the ConnectionInterface connection to use
* @return mixed The new primary key.
* @throws \Propel\Runtime\Exception\PropelException Any exceptions caught during processing will be
* rethrown wrapped into a PropelException.
*/
public static function doInsert(\$criteria, ?ConnectionInterface \$con = null)
{
if (null === \$con) {
\$con = Propel::getServiceContainer()->getWriteConnection(" . $tableMapClass . "::DATABASE_NAME);
}
if (\$criteria instanceof Criteria) {
\$criteria = clone \$criteria; // rename for clarity
} else {
\$criteria = \$criteria->buildCriteria(); // build Criteria from " . $this->getObjectClassName() . " object
}
";
foreach ($table->getColumns() as $col) {
if (
$col->isPrimaryKey()
&& $col->isAutoIncrement()
&& $table->getIdMethod() !== 'none'
&& !$table->isAllowPkInsert()
) {
$script .= "
if (\$criteria->containsKey(" . $this->getColumnConstant($col) . ') && $criteria->keyContainsValue(' . $this->getColumnConstant($col) . ") ) {
throw new PropelException('Cannot insert a value for auto-increment primary key ('." . $this->getColumnConstant($col) . ".')');
}
";
if (!$this->getPlatform()->supportsInsertNullPk()) {
$script .= "
// remove pkey col since this table uses auto-increment and passing a null value for it is not valid
\$criteria->remove(" . $this->getColumnConstant($col) . ");
";
}
} elseif (
$col->isPrimaryKey()
&& $col->isAutoIncrement()
&& $table->getIdMethod() !== 'none'
&& $table->isAllowPkInsert()
&& !$this->getPlatform()->supportsInsertNullPk()
) {
$script .= "
// remove pkey col if it is null since this table does not accept that
if (\$criteria->containsKey(" . $this->getColumnConstant($col) . ') && !$criteria->keyContainsValue(' . $this->getColumnConstant($col) . ") ) {
\$criteria->remove(" . $this->getColumnConstant($col) . ");
}
";
}
}
$script .= "
// Set the correct dbName
\$query = " . $this->getQueryClassName() . "::create()->mergeWith(\$criteria);
// use transaction because \$criteria could contain info
// for more than one table (I guess, conceivably)
return \$con->transaction(function () use (\$con, \$query) {
return \$query->doInsert(\$con);
});
}
";
}
}