Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

UserAction StepExecutor improvement #1687

Open
wants to merge 26 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
185747f
UserAction StepExecutor improvement
ibelar Nov 4, 2021
a2d793b
fix card deck - clone action for setting proper argument as a model
ibelar Nov 4, 2021
4d70967
revert card deck clone action
ibelar Nov 4, 2021
90d123f
clone action at executor - leave original action un touch
ibelar Nov 4, 2021
51d5d0c
cloning UserAction arguments only instead of UserAction
ibelar Nov 4, 2021
734602e
added comment
ibelar Nov 4, 2021
8bbbda8
cs fix comment
ibelar Nov 4, 2021
8564190
added default argument as empty array
ibelar Nov 4, 2021
9c8795c
Merge branch 'develop' into feature/step-executor-enhancement
ibelar Nov 4, 2021
5fec5da
fix phpstan
ibelar Nov 4, 2021
dbd95de
remove array filter for UserAction fields
ibelar Nov 4, 2021
0f4d39c
use hint name for ArgModel and set ArgValue by default
ibelar Nov 5, 2021
0fe881b
cs fix
ibelar Nov 5, 2021
2e8ea00
Add variable type to Country UserActions callback
ibelar Nov 5, 2021
327fb43
fix phpstan - access to undefined property iso3
ibelar Nov 5, 2021
3c37d34
cs fix
ibelar Nov 5, 2021
1e21dd4
refactor getModelForArg to initActionArg
ibelar Nov 5, 2021
569ad99
Merge branch 'develop' into feature/step-executor-enhancement
mvorisek Nov 12, 2021
8722647
no need to reset id field type to string, guessing fixed in data
mvorisek Nov 12, 2021
5f318b0
Merge branch 'develop' into feature/step-executor-enhancement
mvorisek May 22, 2022
fcce80d
adjust to the latest atk4
mvorisek May 22, 2022
5a1a59f
fix cs
mvorisek May 22, 2022
fb8cb1a
update comments before merge
mvorisek May 22, 2022
9af3020
Merge branch 'develop' into feature/step-executor-enhancement
mvorisek May 22, 2022
67fab83
fix merge
mvorisek May 22, 2022
d0ab1c3
Merge branch 'develop'
mvorisek Apr 29, 2023
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
49 changes: 49 additions & 0 deletions demos/_includes/DemoActionsUtil.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,40 @@

use Atk4\Data\Model;
use Atk4\Data\Model\UserAction;
use Atk4\Data\Persistence\Array_;
use Atk4\Ui\Exception;
use Atk4\Ui\Form;

/**
* @property string $age @Atk4\Field()
* @property string $city @Atk4\Field()
* @property string $gender @Atk4\Field()
*/
class ArgModel extends Model
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

our testing/demos Model should be used here to test if field aliasing is used properly

{
protected function init(): void
{
parent::init();
$this->addField($this->fieldName()->age, ['type' => 'integer', 'required' => true, 'caption' => 'Age must be 21 or over:']);
$this->addField($this->fieldName()->city);
$this->addField($this->fieldName()->gender, [
'default' => 'm',
'ui' => ['form' => [Form\Control\Dropdown::class, 'values' => ['m' => 'Male', 'f' => 'Female']],
],
]);
}

public function validate(string $intent = null): array
{
$error = [];
if ($this->get(self::hinting()->fieldName()->age) < 21) {
$error = [self::hinting()->fieldName()->age => 'You must be at least 21 years old.'];
}

return array_merge($error, parent::validate($intent));
}
}

class DemoActionsUtil
{
public static function setupDemoActions(Country $country): void
Expand Down Expand Up @@ -135,5 +166,23 @@ public static function setupDemoActions(Country $country): void
return 'Gender = ' . $gender . ' / Age = ' . $age;
},
]);

$country->addUserAction('arg_using_model', [
'caption' => 'Arg with Model',
'description' => 'Ask for Arguments set via a Data\Model. Allow usage of model validate() for your arguments',
'args' => [
'__atk_model' => new ArgModel(new Array_([])),
'extra' => ['type' => 'string'],
],
'fields' => [$country->fieldName()->iso3],
'callback' => function (Country $model, int $age, string $city, string $gender) {
$n = $gender === 'm' ? 'Mr.' : 'Mrs.';

return 'Thank you ' . $n . ' at age ' . $age;
},
'preview' => function (Country $model, int $age, string $city, string $gender) {
return 'Gender = ' . $gender . ' / Age = ' . $age;
},
]);
}
}
64 changes: 47 additions & 17 deletions src/UserAction/StepExecutorTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use Atk4\Core\Factory;
use Atk4\Data\Model;
use Atk4\Data\Model\UserAction;
use Atk4\Data\Persistence\Array_;
use Atk4\Data\ValidationException;
use Atk4\Ui\Button;
use Atk4\Ui\Form;
Expand Down Expand Up @@ -70,6 +71,9 @@ trait StepExecutorTrait
/** @var string */
public $finalMsg = 'Complete!';

/** @var array An extended copy of UserAction arguments. It contains original action arguments and arguments set by '__atk_model'. */
private $cloneArgs;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should add native Model args support to atk4/data model. It is also not completely clear to me, why there are args & fields. In atk4/data, the UserAction::$fields property is described to be used for dirty detection only.


/**
* Utility for setting Title for each step.
*/
Expand Down Expand Up @@ -107,12 +111,47 @@ protected function setFormField(Form $form, array $fields, string $step): Form
return $form;
}

/**
* Set model for userAction arguments.
* Override existing argument with model definition.
*/
protected function initActionArguments(): Model
{
$args = $this->getAction()->args;
if (array_key_exists('__atk_model', $args)) {
/** @var Model $argsModel */
$argsModel = Factory::factory($args['__atk_model']);
// if seed is supplied, we need to initialize
if (!$argsModel->isInitialized()) {
$argsModel->invokeInit();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Model::init is invoked when persistence is set

when not initialized, I belive we should set new Array_([]) persistence instead

side question, when custom model for args does bring any advantage?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • argument can be easily validate using a Validator;
  • argument can be easily define within a model;

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

when not initialized, I belive we should set new Array_([]) persistence instead

If using seed then developper should supply proper property within the seed array, including Persistence.

}

unset($args['__atk_model']);
} else {
$argsModel = new Model(new Array_([]));
}

foreach ($args as $key => $val) {
$argsModel->addField($key, $val);
mvorisek marked this conversation as resolved.
Show resolved Hide resolved
}

$this->cloneArgs = [];
// set userAction args using model field
foreach ($argsModel->getFields('editable') as $k => $field) {
mvorisek marked this conversation as resolved.
Show resolved Hide resolved
$this->cloneArgs[$k] = $field->shortName;
}

return $argsModel;
}

protected function runSteps(): void
{
$this->loader->set(function (Loader $p) {
$argModel = $this->initActionArguments();

$this->loader->set(function (Loader $p) use ($argModel) {
switch ($this->step) {
case 'args':
$this->doArgs($p);
$this->doArgs($p, $argModel);

break;
case 'fields':
Expand All @@ -131,23 +170,13 @@ protected function runSteps(): void
});
}

protected function doArgs(View $page): void
protected function doArgs(View $page, Model $model): void
{
$this->addStepTitle($page, $this->step);

$form = $this->addFormTo($page);
foreach ($this->action->args as $key => $val) {
if ($val instanceof Model) {
$val = ['model' => $val];
}

if (isset($val['model'])) {
$val['model'] = Factory::factory($val['model']);
$form->addControl($key, [Form\Control\Lookup::class])->setModel($val['model']);
} else {
$form->addControl($key, [], $val);
}
}
$form->setModel($model->createEntity());

// set args value if available
$this->setFormField($form, $this->getActionData('args'), $this->step);
Expand All @@ -157,8 +186,9 @@ protected function doArgs(View $page): void
$this->jsSetPrevHandler($page, $this->step);

$form->onSubmit(function (Form $form) {
$form->model->save();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is now save needed?

// collect arguments
$this->setActionDataFromModel('args', $form->model, array_keys($form->model->getFields()));
$this->setActionDataFromModel('args', $form->model, array_keys($form->model->getFields('editable')));

return $this->jsStepSubmit($this->step);
});
Expand Down Expand Up @@ -491,7 +521,7 @@ protected function getActionPreview()
{
$args = [];

foreach ($this->action->args as $key => $val) {
foreach ($this->cloneArgs as $key => $val) {
$args[] = $this->getActionData('args')[$key];
}

Expand All @@ -505,7 +535,7 @@ protected function getActionArgs(array $data): array
{
$args = [];

foreach ($this->action->args as $key => $val) {
foreach ($this->cloneArgs as $key => $val) {
$args[] = $data[$key];
}

Expand Down