Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions resources/dtd/database.dtd
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ PHP class or method name.
lazyLoad (true|false) "false"
primaryString (true|false) "false"
valueSet CDATA #IMPLIED
valueEnum CDATA #IMPLIED
>

<!ELEMENT inheritance EMPTY>
Expand Down
7 changes: 7 additions & 0 deletions resources/xsd/database.xsd
Original file line number Diff line number Diff line change
Expand Up @@ -473,6 +473,13 @@
</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="valueEnum" type="xs:string" use="optional">
<xs:annotation>
<xs:documentation xml:lang="en">
Reference to PHP enum class used to generate the list of values for an ENUM or SET column.
</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>

<xs:complexType name="foreign-key">
Expand Down
1 change: 1 addition & 0 deletions src/Propel/Common/Config/PropelConfiguration.php
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,7 @@ protected function addGeneratorSection(ArrayNodeDefinition $node): void
->booleanNode('packageObjectModel')->defaultTrue()->end()
->booleanNode('namespaceAutoPackage')->defaultTrue()->end()
->booleanNode('recursive')->defaultFalse()->end()
->booleanNode('defaultToNativeEnumeratedColumnTypes')->defaultFalse()->end()
->arrayNode('connections')
->prototype('scalar')->end()
->end()
Expand Down
131 changes: 125 additions & 6 deletions src/Propel/Common/Util/SetColumnConverter.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,23 @@

namespace Propel\Common\Util;

use BackedEnum;
use Propel\Common\Exception\SetColumnConverterException;
use Propel\Runtime\Exception\PropelException;
use UnitEnum;
use function array_diff;
use function array_filter;
use function array_intersect;
use function array_keys;
use function array_map;
use function array_reduce;
use function array_values;
use function count;
use function explode;
use function gettype;
use function implode;
use function sprintf;
use function trim;
use const ARRAY_FILTER_USE_KEY;

/**
Expand All @@ -23,21 +31,21 @@ class SetColumnConverter
/**
* Converts set column values to the corresponding integer.
*
* @param array<string>|string|null $val
* @param array<string>|string|null $items
* @param array<int, string> $valueSet
*
* @throws \Propel\Common\Exception\SetColumnConverterException
*
* @return int
*/
public static function convertToInt($val, array $valueSet): int
public static function convertToBitmask($items, array $valueSet): int
{
if ($val === null) {
if ($items === null) {
return 0;
}
$setValues = array_intersect($valueSet, (array)$val);
$setValues = array_intersect($valueSet, (array)$items);

$missingValues = array_diff((array)$val, $setValues);
$missingValues = array_diff((array)$items, $setValues);
if ($missingValues) {
throw new SetColumnConverterException(sprintf('Value "%s" is not among the valueSet', $missingValues[0]), $missingValues[0]);
}
Expand All @@ -46,6 +54,19 @@ public static function convertToInt($val, array $valueSet): int
return array_reduce($keys, fn (int $bitVector, int $ix): int => $bitVector | (1 << $ix), 0);
}

/**
* @deprecated Use aptly named {@see static::convertToBitmask()}.
*
* @param array<string>|string|null $val
* @param array<int, string> $valueSet
*
* @return int
*/
public static function convertToInt($val, array $valueSet): int
{
return static::convertToBitmask($val, $valueSet);
}

/**
* Converts set column integer value to corresponding array.
*
Expand All @@ -56,7 +77,7 @@ public static function convertToInt($val, array $valueSet): int
*
* @return list<string>
*/
public static function convertIntToArray(?int $val, array $valueSet): array
public static function convertBitmaskToArray(?int $val, array $valueSet): array
{
if ($val === null) {
return [];
Expand All @@ -69,4 +90,102 @@ public static function convertIntToArray(?int $val, array $valueSet): array

return array_values(array_filter($valueSet, fn ($ix) => (bool)($val & (1 << $ix)), ARRAY_FILTER_USE_KEY));
}

/**
* @deprecated Use aptly named {@see static::convertBitmaskToArray()}
*
* @param int|null $val
* @param array<int, string> $valueSet
*
* @return list<string>
*/
public static function convertIntToArray(?int $val, array $valueSet): array
{
return static::convertBitmaskToArray($val, $valueSet);
}

/**
* @psalm-return ($items is null ? null : array)
*
* @param array|string $items
* @param array $setValues
*
* @return array|null
*/
public static function getItemsInOrder(array|string|null $items, array $setValues): array|null
{
if ($items === null) {
return null;
}
$items = (array)$items;
static::requireValuesInSet($items, $setValues);

return array_values(array_intersect($setValues, $items));
}

/**
* @param array|string $items
* @param array $setValues
* @param string $locationDescription
*
* @throws \Propel\Runtime\Exception\PropelException
*
* @return void
*/
public static function requireValuesInSet(array|string $items, array $setValues, string $locationDescription = ''): void
{
$unknownValues = array_diff((array)$items, $setValues);
if (!$unknownValues) {
return;
}

$unknownValuesCsv = implode(',', $unknownValues);
$allowedValuesCsv = implode(',', $setValues);

throw new PropelException("Illegal value in SET $locationDescription: Set '$allowedValuesCsv' does not contain '$unknownValuesCsv'");
}

/**
* @param string $itemsCsv
*
* @return array<string>
*/
public static function itemsCsvToArray(string $itemsCsv): array
{
if (!trim($itemsCsv)) {
return [];
}
$items = explode(',', $itemsCsv);

return array_filter(array_map('trim', $items));
}

/**
* Tries to resolve set items for unknown input type.
*
* @param array<string>|string|int $value
* @param array<string> $valueSet
*
* @return array<string>
*/
public static function rawInputToSetItems(array|string|int $value, array $valueSet): array
{
$items = match (gettype($value)) {
'string' => self::itemsCsvToArray($value),
'integer' => self::convertBitmaskToArray($value, $valueSet),
default => (array)$value,
};

return self::getItemsInOrder($items, $valueSet);
}

/**
* @param class-string<\UnitEnum> $enumClass
*
* @return array<string>
*/
public static function getItemsFromEnum(string $enumClass): array
{
return array_map(fn (UnitEnum $case) => (string)($case instanceof BackedEnum ? $case->value : $case->name), $enumClass::cases());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -372,16 +372,16 @@ public function getScopeValue(\$returnNulls = true)

return \$onlyNulls && \$returnNulls ? null : \$result;
";
} elseif ($this->behavior->getColumnForParameter('scope_column')->isEnumType()) {
} elseif ($this->behavior->getColumnForParameter('scope_column')->isBinaryEnumType()) {
$columnConstant = strtoupper(preg_replace('/[^a-zA-Z0-9_\x7f-\xff]/', '_', $this->getColumnAttribute('scope_column')));
$script .= "
return array_search(\$this->{$this->getColumnGetter('scope_column')}(), {$this->tableMapClassName}::getValueSet({$this->tableMapClassName}::COL_{$columnConstant}));
";
} elseif ($this->behavior->getColumnForParameter('scope_column')->isSetType()) {
} elseif ($this->behavior->getColumnForParameter('scope_column')->isBinarySetType()) {
$columnConstant = strtoupper(preg_replace('/[^a-zA-Z0-9_\x7f-\xff]/', '_', $this->getColumnAttribute('scope_column')));
$script .= "
try {
return SetColumnConverter::convertToInt(\$this->{$this->getColumnGetter('scope_column')}(), {$this->tableMapClassName}::getValueSet({$this->tableMapClassName}::COL_{$columnConstant}));
return SetColumnConverter::convertToBitmask(\$this->{$this->getColumnGetter('scope_column')}(), {$this->tableMapClassName}::getValueSet({$this->tableMapClassName}::COL_{$columnConstant}));
} catch (SetColumnConverterException \$e) {
throw new PropelException(sprintf('Value `%s` is not accepted in this set column', \$e->getValue()), \$e->getCode(), \$e);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@

namespace Propel\Generator\Behavior\Util;

use Propel\Common\Util\SetColumnConverter;
use Propel\Generator\Exception\SchemaException;
use Propel\Generator\Model\Behavior;
use function array_map;
use function count;
use function explode;
use function in_array;
use function is_array;
use function is_numeric;
Expand Down Expand Up @@ -130,9 +130,7 @@ public function getParameterTrueOrCsv(string $parameterName, ?array $defaultValu
*/
protected function explodeCsv(string $stringValue): ?array
{
$stringValue = trim($stringValue);

return trim($stringValue) ? array_map('trim', explode(',', $stringValue)) : null;
return SetColumnConverter::itemsCsvToArray($stringValue) ?: null;
}

/**
Expand Down
49 changes: 11 additions & 38 deletions src/Propel/Generator/Builder/Om/ObjectBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -732,27 +732,13 @@ public function applyDefaultValues(): void
*/
protected function addApplyDefaultValuesBody(string &$script): void
{
$table = $this->getTable();
// FIXME - Apply support for PHP default expressions here
// see: http://propel.phpdb.org/trac/ticket/378

foreach ($table->getColumns() as $column) {
$def = $column->getDefaultValue();
if ($def === null || $def->isExpression()) {
foreach ($this->columnCodeProducers as $columnCodeProducer) {
$isValueType = $columnCodeProducer->getColumn()->getDefaultValue()?->isValueType();
if (!$isValueType) {
continue;
}

$clo = $column->getLowercasedName();
$defaultValue = ColumnCodeProducerFactory::create($column, $this)->getDefaultValueString();
if ($column->isTemporalType()) {
$dateTimeClass = $this->resolveColumnDateTimeClass($column);
$defaultValue = "PropelDateTime::newInstance($defaultValue, null, '$dateTimeClass')";
} elseif ($column->isPhpObjectType()) {
$assumedClassName = $this->declareClass($column->getPhpType());
$defaultValue = "new $assumedClassName($defaultValue )";
}
$script .= "
\$this->{$clo} = $defaultValue;";
$script .= $columnCodeProducer->getApplyDefaultValueStatement();
}
}

Expand Down Expand Up @@ -1027,7 +1013,7 @@ protected function addHydrateBody(string &$script): void
$script .= "
\$this->$clo = \$columnValue;
\$this->$cloUnserialized = null;";
} elseif ($col->isSetType()) {
} elseif ($col->isBinarySetType()) {
$cloConverted = $clo . '_converted';
$script .= "
\$this->$clo = \$columnValue;
Expand Down Expand Up @@ -3112,7 +3098,7 @@ public function clear()
$script .= "
\$this->$cloUnserialized = null;";
}
if ($col->isSetType()) {
if ($col->isBinarySetType()) {
$cloConverted = $clo . '_converted';

$script .= "
Expand Down Expand Up @@ -3307,24 +3293,11 @@ public static function createFromFilters(FilterCollector \$filterCollector)
\$value = \$filter->getValue();

match (\$columnIdentifier) {";
foreach ($this->getTable()->getColumns() as $col) {
$columnName = $col->getFullyQualifiedName(true);
$cfc = $col->getPhpName();
$valueExpression = '$value';

if ($col->getType() === PropelTypes::PHP_ARRAY) {
$this->declareGlobalFunction('is_array');
$valueExpression = "is_array($valueExpression) ? $valueExpression : static::unserializeArray($valueExpression)";
} elseif ($col->getType() === PropelTypes::ENUM) {
$tableMapClassName = $this->getTableMapClassName();
$columnConstant = $this->getColumnConstant($col);
$valueExpression = "$tableMapClassName::getValueSet($columnConstant)[$valueExpression] ?? $valueExpression";
} elseif ($col->isSetType()) {
$this->declareClasses('Propel\Common\Util\SetColumnConverter');
$tableMapClassName = $this->getTableMapClassName();
$columnConstant = $this->getColumnConstant($col);
$valueExpression = "SetColumnConverter::convertIntToArray($valueExpression, $tableMapClassName::getValueSet($columnConstant))";
}
foreach ($this->columnCodeProducers as $columnCodeProducer) {
$column = $columnCodeProducer->getColumn();
$columnName = $column->getFullyQualifiedName(true);
$cfc = $column->getPhpName();
$valueExpression = $columnCodeProducer->buildCreateFromFilterValueExpression('$value');

$script .= "
'$columnName' => {$objectVar}->set$cfc($valueExpression),";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,4 +116,19 @@ protected function addMutatorBody(string &$script): void
\$this->modifiedColumns[" . $this->objectBuilder->getColumnConstant($col) . "] = true;
}\n";
}

/**
* @see \Propel\Generator\Builder\Om\ObjectBuilder::addCreateFromFilter()
*
* @param string $valueExpression The variable expression holding the value (i.e. '$value')
*
* @return string
*/
#[\Override]
public function buildCreateFromFilterValueExpression(string $valueExpression): string
{
$this->referencedClasses->registerFunction('is_array');

return "is_array($valueExpression) ? $valueExpression : static::unserializeArray($valueExpression)";
}
}
Loading