Skip to content

Commit df4e56c

Browse files
authored
Merge pull request #99 from perplorm/enum_fixup
Add native enumerated column types
2 parents ce610b8 + 3483c7f commit df4e56c

File tree

53 files changed

+2315
-586
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+2315
-586
lines changed

resources/dtd/database.dtd

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ PHP class or method name.
108108
lazyLoad (true|false) "false"
109109
primaryString (true|false) "false"
110110
valueSet CDATA #IMPLIED
111+
valueEnum CDATA #IMPLIED
111112
>
112113

113114
<!ELEMENT inheritance EMPTY>

resources/xsd/database.xsd

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -473,6 +473,13 @@
473473
</xs:documentation>
474474
</xs:annotation>
475475
</xs:attribute>
476+
<xs:attribute name="valueEnum" type="xs:string" use="optional">
477+
<xs:annotation>
478+
<xs:documentation xml:lang="en">
479+
Reference to PHP enum class used to generate the list of values for an ENUM or SET column.
480+
</xs:documentation>
481+
</xs:annotation>
482+
</xs:attribute>
476483
</xs:complexType>
477484

478485
<xs:complexType name="foreign-key">

src/Propel/Common/Config/PropelConfiguration.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -354,6 +354,7 @@ protected function addGeneratorSection(ArrayNodeDefinition $node): void
354354
->booleanNode('packageObjectModel')->defaultTrue()->end()
355355
->booleanNode('namespaceAutoPackage')->defaultTrue()->end()
356356
->booleanNode('recursive')->defaultFalse()->end()
357+
->booleanNode('defaultToNativeEnumeratedColumnTypes')->defaultFalse()->end()
357358
->arrayNode('connections')
358359
->prototype('scalar')->end()
359360
->end()

src/Propel/Common/Util/SetColumnConverter.php

Lines changed: 125 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,23 @@
44

55
namespace Propel\Common\Util;
66

7+
use BackedEnum;
78
use Propel\Common\Exception\SetColumnConverterException;
9+
use Propel\Runtime\Exception\PropelException;
10+
use UnitEnum;
811
use function array_diff;
912
use function array_filter;
1013
use function array_intersect;
1114
use function array_keys;
15+
use function array_map;
1216
use function array_reduce;
1317
use function array_values;
1418
use function count;
19+
use function explode;
20+
use function gettype;
21+
use function implode;
1522
use function sprintf;
23+
use function trim;
1624
use const ARRAY_FILTER_USE_KEY;
1725

1826
/**
@@ -23,21 +31,21 @@ class SetColumnConverter
2331
/**
2432
* Converts set column values to the corresponding integer.
2533
*
26-
* @param array<string>|string|null $val
34+
* @param array<string>|string|null $items
2735
* @param array<int, string> $valueSet
2836
*
2937
* @throws \Propel\Common\Exception\SetColumnConverterException
3038
*
3139
* @return int
3240
*/
33-
public static function convertToInt($val, array $valueSet): int
41+
public static function convertToBitmask($items, array $valueSet): int
3442
{
35-
if ($val === null) {
43+
if ($items === null) {
3644
return 0;
3745
}
38-
$setValues = array_intersect($valueSet, (array)$val);
46+
$setValues = array_intersect($valueSet, (array)$items);
3947

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

57+
/**
58+
* @deprecated Use aptly named {@see static::convertToBitmask()}.
59+
*
60+
* @param array<string>|string|null $val
61+
* @param array<int, string> $valueSet
62+
*
63+
* @return int
64+
*/
65+
public static function convertToInt($val, array $valueSet): int
66+
{
67+
return static::convertToBitmask($val, $valueSet);
68+
}
69+
4970
/**
5071
* Converts set column integer value to corresponding array.
5172
*
@@ -56,7 +77,7 @@ public static function convertToInt($val, array $valueSet): int
5677
*
5778
* @return list<string>
5879
*/
59-
public static function convertIntToArray(?int $val, array $valueSet): array
80+
public static function convertBitmaskToArray(?int $val, array $valueSet): array
6081
{
6182
if ($val === null) {
6283
return [];
@@ -69,4 +90,102 @@ public static function convertIntToArray(?int $val, array $valueSet): array
6990

7091
return array_values(array_filter($valueSet, fn ($ix) => (bool)($val & (1 << $ix)), ARRAY_FILTER_USE_KEY));
7192
}
93+
94+
/**
95+
* @deprecated Use aptly named {@see static::convertBitmaskToArray()}
96+
*
97+
* @param int|null $val
98+
* @param array<int, string> $valueSet
99+
*
100+
* @return list<string>
101+
*/
102+
public static function convertIntToArray(?int $val, array $valueSet): array
103+
{
104+
return static::convertBitmaskToArray($val, $valueSet);
105+
}
106+
107+
/**
108+
* @psalm-return ($items is null ? null : array)
109+
*
110+
* @param array|string $items
111+
* @param array $setValues
112+
*
113+
* @return array|null
114+
*/
115+
public static function getItemsInOrder(array|string|null $items, array $setValues): array|null
116+
{
117+
if ($items === null) {
118+
return null;
119+
}
120+
$items = (array)$items;
121+
static::requireValuesInSet($items, $setValues);
122+
123+
return array_values(array_intersect($setValues, $items));
124+
}
125+
126+
/**
127+
* @param array|string $items
128+
* @param array $setValues
129+
* @param string $locationDescription
130+
*
131+
* @throws \Propel\Runtime\Exception\PropelException
132+
*
133+
* @return void
134+
*/
135+
public static function requireValuesInSet(array|string $items, array $setValues, string $locationDescription = ''): void
136+
{
137+
$unknownValues = array_diff((array)$items, $setValues);
138+
if (!$unknownValues) {
139+
return;
140+
}
141+
142+
$unknownValuesCsv = implode(',', $unknownValues);
143+
$allowedValuesCsv = implode(',', $setValues);
144+
145+
throw new PropelException("Illegal value in SET $locationDescription: Set '$allowedValuesCsv' does not contain '$unknownValuesCsv'");
146+
}
147+
148+
/**
149+
* @param string $itemsCsv
150+
*
151+
* @return array<string>
152+
*/
153+
public static function itemsCsvToArray(string $itemsCsv): array
154+
{
155+
if (!trim($itemsCsv)) {
156+
return [];
157+
}
158+
$items = explode(',', $itemsCsv);
159+
160+
return array_filter(array_map('trim', $items));
161+
}
162+
163+
/**
164+
* Tries to resolve set items for unknown input type.
165+
*
166+
* @param array<string>|string|int $value
167+
* @param array<string> $valueSet
168+
*
169+
* @return array<string>
170+
*/
171+
public static function rawInputToSetItems(array|string|int $value, array $valueSet): array
172+
{
173+
$items = match (gettype($value)) {
174+
'string' => self::itemsCsvToArray($value),
175+
'integer' => self::convertBitmaskToArray($value, $valueSet),
176+
default => (array)$value,
177+
};
178+
179+
return self::getItemsInOrder($items, $valueSet);
180+
}
181+
182+
/**
183+
* @param class-string<\UnitEnum> $enumClass
184+
*
185+
* @return array<string>
186+
*/
187+
public static function getItemsFromEnum(string $enumClass): array
188+
{
189+
return array_map(fn (UnitEnum $case) => (string)($case instanceof BackedEnum ? $case->value : $case->name), $enumClass::cases());
190+
}
72191
}

src/Propel/Generator/Behavior/Sortable/SortableBehaviorObjectBuilderModifier.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -372,16 +372,16 @@ public function getScopeValue(\$returnNulls = true)
372372
373373
return \$onlyNulls && \$returnNulls ? null : \$result;
374374
";
375-
} elseif ($this->behavior->getColumnForParameter('scope_column')->isEnumType()) {
375+
} elseif ($this->behavior->getColumnForParameter('scope_column')->isBinaryEnumType()) {
376376
$columnConstant = strtoupper(preg_replace('/[^a-zA-Z0-9_\x7f-\xff]/', '_', $this->getColumnAttribute('scope_column')));
377377
$script .= "
378378
return array_search(\$this->{$this->getColumnGetter('scope_column')}(), {$this->tableMapClassName}::getValueSet({$this->tableMapClassName}::COL_{$columnConstant}));
379379
";
380-
} elseif ($this->behavior->getColumnForParameter('scope_column')->isSetType()) {
380+
} elseif ($this->behavior->getColumnForParameter('scope_column')->isBinarySetType()) {
381381
$columnConstant = strtoupper(preg_replace('/[^a-zA-Z0-9_\x7f-\xff]/', '_', $this->getColumnAttribute('scope_column')));
382382
$script .= "
383383
try {
384-
return SetColumnConverter::convertToInt(\$this->{$this->getColumnGetter('scope_column')}(), {$this->tableMapClassName}::getValueSet({$this->tableMapClassName}::COL_{$columnConstant}));
384+
return SetColumnConverter::convertToBitmask(\$this->{$this->getColumnGetter('scope_column')}(), {$this->tableMapClassName}::getValueSet({$this->tableMapClassName}::COL_{$columnConstant}));
385385
} catch (SetColumnConverterException \$e) {
386386
throw new PropelException(sprintf('Value `%s` is not accepted in this set column', \$e->getValue()), \$e->getCode(), \$e);
387387
}

src/Propel/Generator/Behavior/Util/BehaviorWithParameterAccess.php

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@
44

55
namespace Propel\Generator\Behavior\Util;
66

7+
use Propel\Common\Util\SetColumnConverter;
78
use Propel\Generator\Exception\SchemaException;
89
use Propel\Generator\Model\Behavior;
910
use function array_map;
1011
use function count;
11-
use function explode;
1212
use function in_array;
1313
use function is_array;
1414
use function is_numeric;
@@ -130,9 +130,7 @@ public function getParameterTrueOrCsv(string $parameterName, ?array $defaultValu
130130
*/
131131
protected function explodeCsv(string $stringValue): ?array
132132
{
133-
$stringValue = trim($stringValue);
134-
135-
return trim($stringValue) ? array_map('trim', explode(',', $stringValue)) : null;
133+
return SetColumnConverter::itemsCsvToArray($stringValue) ?: null;
136134
}
137135

138136
/**

src/Propel/Generator/Builder/Om/ObjectBuilder.php

Lines changed: 11 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -732,27 +732,13 @@ public function applyDefaultValues(): void
732732
*/
733733
protected function addApplyDefaultValuesBody(string &$script): void
734734
{
735-
$table = $this->getTable();
736-
// FIXME - Apply support for PHP default expressions here
737-
// see: http://propel.phpdb.org/trac/ticket/378
738-
739-
foreach ($table->getColumns() as $column) {
740-
$def = $column->getDefaultValue();
741-
if ($def === null || $def->isExpression()) {
735+
foreach ($this->columnCodeProducers as $columnCodeProducer) {
736+
$isValueType = $columnCodeProducer->getColumn()->getDefaultValue()?->isValueType();
737+
if (!$isValueType) {
742738
continue;
743739
}
744740

745-
$clo = $column->getLowercasedName();
746-
$defaultValue = ColumnCodeProducerFactory::create($column, $this)->getDefaultValueString();
747-
if ($column->isTemporalType()) {
748-
$dateTimeClass = $this->resolveColumnDateTimeClass($column);
749-
$defaultValue = "PropelDateTime::newInstance($defaultValue, null, '$dateTimeClass')";
750-
} elseif ($column->isPhpObjectType()) {
751-
$assumedClassName = $this->declareClass($column->getPhpType());
752-
$defaultValue = "new $assumedClassName($defaultValue )";
753-
}
754-
$script .= "
755-
\$this->{$clo} = $defaultValue;";
741+
$script .= $columnCodeProducer->getApplyDefaultValueStatement();
756742
}
757743
}
758744

@@ -1027,7 +1013,7 @@ protected function addHydrateBody(string &$script): void
10271013
$script .= "
10281014
\$this->$clo = \$columnValue;
10291015
\$this->$cloUnserialized = null;";
1030-
} elseif ($col->isSetType()) {
1016+
} elseif ($col->isBinarySetType()) {
10311017
$cloConverted = $clo . '_converted';
10321018
$script .= "
10331019
\$this->$clo = \$columnValue;
@@ -3112,7 +3098,7 @@ public function clear()
31123098
$script .= "
31133099
\$this->$cloUnserialized = null;";
31143100
}
3115-
if ($col->isSetType()) {
3101+
if ($col->isBinarySetType()) {
31163102
$cloConverted = $clo . '_converted';
31173103

31183104
$script .= "
@@ -3307,24 +3293,11 @@ public static function createFromFilters(FilterCollector \$filterCollector)
33073293
\$value = \$filter->getValue();
33083294
33093295
match (\$columnIdentifier) {";
3310-
foreach ($this->getTable()->getColumns() as $col) {
3311-
$columnName = $col->getFullyQualifiedName(true);
3312-
$cfc = $col->getPhpName();
3313-
$valueExpression = '$value';
3314-
3315-
if ($col->getType() === PropelTypes::PHP_ARRAY) {
3316-
$this->declareGlobalFunction('is_array');
3317-
$valueExpression = "is_array($valueExpression) ? $valueExpression : static::unserializeArray($valueExpression)";
3318-
} elseif ($col->getType() === PropelTypes::ENUM) {
3319-
$tableMapClassName = $this->getTableMapClassName();
3320-
$columnConstant = $this->getColumnConstant($col);
3321-
$valueExpression = "$tableMapClassName::getValueSet($columnConstant)[$valueExpression] ?? $valueExpression";
3322-
} elseif ($col->isSetType()) {
3323-
$this->declareClasses('Propel\Common\Util\SetColumnConverter');
3324-
$tableMapClassName = $this->getTableMapClassName();
3325-
$columnConstant = $this->getColumnConstant($col);
3326-
$valueExpression = "SetColumnConverter::convertIntToArray($valueExpression, $tableMapClassName::getValueSet($columnConstant))";
3327-
}
3296+
foreach ($this->columnCodeProducers as $columnCodeProducer) {
3297+
$column = $columnCodeProducer->getColumn();
3298+
$columnName = $column->getFullyQualifiedName(true);
3299+
$cfc = $column->getPhpName();
3300+
$valueExpression = $columnCodeProducer->buildCreateFromFilterValueExpression('$value');
33283301

33293302
$script .= "
33303303
'$columnName' => {$objectVar}->set$cfc($valueExpression),";

src/Propel/Generator/Builder/Om/ObjectBuilder/ColumnTypes/ArrayColumnCodeProducer.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,4 +116,19 @@ protected function addMutatorBody(string &$script): void
116116
\$this->modifiedColumns[" . $this->objectBuilder->getColumnConstant($col) . "] = true;
117117
}\n";
118118
}
119+
120+
/**
121+
* @see \Propel\Generator\Builder\Om\ObjectBuilder::addCreateFromFilter()
122+
*
123+
* @param string $valueExpression The variable expression holding the value (i.e. '$value')
124+
*
125+
* @return string
126+
*/
127+
#[\Override]
128+
public function buildCreateFromFilterValueExpression(string $valueExpression): string
129+
{
130+
$this->referencedClasses->registerFunction('is_array');
131+
132+
return "is_array($valueExpression) ? $valueExpression : static::unserializeArray($valueExpression)";
133+
}
119134
}

0 commit comments

Comments
 (0)