WCF
WCF copied to clipboard
Replace I18nHandler with new API
In many cases, working with the I18nHandler generates duplicate and unnecessary code. A new API can reduce this and prevent unnecessary errors.
Current using of I18nHandler
class FooAddForm extends AbstractForm
{
#[\Override]
public function validate()
{
parent::validate();
if (!I18nHandler::getInstance()->validateValue('title')) {
if (I18nHandler::getInstance()->isPlainValue('title')) {
throw new UserInputException('title');
} else {
throw new UserInputException('title', 'multilingual');
}
}
}
#[\Override]
public function save()
{
parent::save();
$data = [
'data' => \array_merge($this->additionalFields, [
'title' => $this->title,
…
]),
];
$this->objectAction = new FooAction([], 'create', $data);
$this->objectAction->executeAction();
$returnValues = $this->objectAction->getReturnValues();
$fooID = $returnValues['returnValues']->fooID;
if (!I18nHandler::getInstance()->isPlainValue('title')) {
I18nHandler::getInstance()->save(
'title',
'com.woltlab.foo.title' . $fooID,
'com.woltlab.foo',
1
);
$fooEditor = new FooEditor($returnValues['returnValues']);
$fooEditor->update([
'title' => 'com.woltlab.foo.title' . $fooID,
]);
}
$this->saved();
$this->title = '';
I18nHandler::getInstance()->reset();
}
}
The developer has to check if the entered values are plain or multilingual. There is no real reason why the developer needs to check this, and it is not easy to safe the string in the database.
Possible new API class
class I18nField
{
public function __construct(
public readonly string $fieldName,
public readonly string $pattern,
string|int $languageCategory,
string|int $package = PACKAGE_ID,
public readonly bool $requireI18n = false,
public readonly bool $permitEmptyValue = false,
);
/**
* Initializes the values for this field.
* If the given value is a string and matches the pattern, the values are loaded from the database,
* otherwise, the given value is used as the non-multilingual value.
*/
public function initValues(string|array $value);
/**
* Stores the values of this field in the database if the values are multilingual.
* If the values are multilingual, the given objectID is replaced in the pattern and returned,
* otherwise the plain value is returned.
*
* When the values are non-multilingual, the old language items are removed from the database.
*/
public function save(int $objectID): string;
/**
* Removes language items from the database, matching the given objectIDs in the pattern.
*/
public static function remove(
int|array $objectIDs,
string $pattern,
string|int $languageCategory,
string|int $package
): void;
/**
* Returns true if the values are valid, otherwise a string with the error type is returned.
* `empty` is returned if the field is required and the value is empty.
* `multilingual` is returned if the field requires multilingual values and one or more values are missing.
*/
public function validate(): string|bool;
/**
* Reads the values from the given request data.
* If the request data is `null`, the values are read from the `$_POST` array.
*/
public function readValues(?array $requestData = null): void;
/**
* Resets the values of this field.
*/
public function reset(): void;
public function getValues(): array;
/**
* If the values are multilingual, the value for the default language is returned,
* otherwise the plain value is returned.
*/
public function __toString(): string;
}
This also allows bulk deleting and deleting only language items according to the given parameters. https://github.com/WoltLab/WCF/issues/4472
Possible implementation
class FooAddForm extends AbstractForm
{
public I18nField $title;
#[\Override]
public function readParameters()
{
parent::readParameters();
$this->title = new I18nField('title', 'com.woltlab.foo.title\d+', 'com.woltlab.foo', 'com.woltlab.wcf');
}
#[\Override]
public function readFormParameters()
{
parent::readFormParameters();
$this->title->readValues();
}
#[\Override]
public function validate()
{
parent::validate();
if (($errorType = $this->title->validate()) !== true) {
throw new UserInputException('title', $errorType);
}
}
#[\Override]
public function save()
{
parent::save();
(new FooAction([], 'create', [
'data' => \array_merge($this->additionalFields, [
…
]),
'title' => $this->title,
]))->executeAction();
$this->saved();
$this->title->reset();
}
#[\Override]
public function assignVariables()
{
parent::assignVariables();
WCF::getTPL()->assign([
'title' => $this->title,
…
]);
}
}
class FooAction extends AbstractDatabaseObjectAction
{
#[\Override]
public function create()
{
$foo = parent::create();
if (isset($this->parameters['title'])) {
$fooEditor = new FooEditor($foo);
\assert($this->parameters['title'] instanceof I18nField);
$groupEditor->update([
'title' => $this->parameters['title']->save($foo->fooID),
]);
}
return $foo;
}
#[\Override]
public function delete()
{
if (empty($this->objects)) {
$this->readObjects();
}
$objectIDs = [];
foreach ($this->getObjects() as $object) {
$objectIDs[] = $object->getObjectID();
}
I18nField::remove($objectIDs, 'com.woltlab.foo.title\d+', 'com.woltlab.foo', 'com.woltlab.wcf');
return parent::delete();
}
}