-
Notifications
You must be signed in to change notification settings - Fork 106
/
JsCallbackExecutor.php
122 lines (105 loc) · 4.22 KB
/
JsCallbackExecutor.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
<?php
declare(strict_types=1);
namespace Atk4\Ui\UserAction;
use Atk4\Core\HookTrait;
use Atk4\Data\Model;
use Atk4\Ui\Js\Jquery;
use Atk4\Ui\Js\JsBlock;
use Atk4\Ui\Js\JsExpressionable;
use Atk4\Ui\Js\JsToast;
use Atk4\Ui\JsCallback;
use Atk4\Ui\View;
/**
* Javascript Action executor.
*
* Will execute a model action using a JS Event.
*
* Usage:
* When use with View::on method, then JsCallbackExecutor executor is automatically create.
* $button->on('click', $model->getUserAction('delete'), [4, 'confirm' => 'This will delete record with ID 4. Are you sure?']);
*
* Manual setup.
* $action = $model->getUserAction('delete')
* $ex = JsCallbackExecutor::addTo($app)->setAction($action, [4])
* $button->on('click', $ex, ['confirm' => 'This will delete record with id 4. Are you sure?']);
*/
class JsCallbackExecutor extends JsCallback implements ExecutorInterface
{
use HookTrait;
/** @var Model\UserAction The model user action */
public $action;
/** @var JsExpressionable|\Closure JS expression to return if action was successful, e.g "new JsToast('Thank you')" */
public $jsSuccess;
public function getAction(): Model\UserAction
{
return $this->action;
}
public function setAction(Model\UserAction $action)
{
$this->action = $action;
if (!$this->action->enabled && $this->getOwner() instanceof View) { // @phpstan-ignore-line
$this->getOwner()->addClass('disabled');
}
return $this;
}
public function jsExecute(array $urlArgs = []): JsBlock
{
// TODO hack to parametrize parent::jsExecute() like JsExecutorInterface::jsExecute($urlArgs)
$argsOrig = $this->args;
$this->args = array_merge($this->args, $urlArgs);
try {
return parent::jsExecute();
} finally {
$this->args = $argsOrig;
}
}
public function executeModelAction(array $args = []): void
{
$this->set(function (Jquery $j) {
$id = $this->getApp()->uiPersistence->typecastLoadField(
$this->action->getModel()->getField($this->action->getModel()->idField),
$_POST['c0'] ?? $_POST[$this->name] ?? null
);
if ($id && $this->action->appliesTo === Model\UserAction::APPLIES_TO_SINGLE_RECORD) {
if ($this->action->isOwnerEntity() && $this->action->getEntity()->getId()) {
$this->action->getEntity()->setId($id); // assert ID is the same
} else {
$this->action = $this->action->getActionForEntity($this->action->getModel()->load($id));
}
} elseif (!$this->action->isOwnerEntity()
&& in_array($this->action->appliesTo, [Model\UserAction::APPLIES_TO_NO_RECORDS, Model\UserAction::APPLIES_TO_SINGLE_RECORD], true)
) {
$this->action = $this->action->getActionForEntity($this->action->getModel()->createEntity());
}
$errors = $this->_hasAllArguments();
if ($errors) {
$js = new JsToast(['title' => 'Error', 'message' => 'Missing Arguments: ' . implode(', ', $errors), 'class' => 'error']);
} else {
$args = [];
foreach ($this->action->args as $key => $val) {
$args[] = $_POST[$key] ?? $this->args[$key];
}
$return = $this->action->execute(...$args);
$success = $this->jsSuccess instanceof \Closure
? ($this->jsSuccess)($this, $this->action->getModel(), $id, $return)
: $this->jsSuccess;
$js = JsBlock::fromHookResult($this->hook(BasicExecutor::HOOK_AFTER_EXECUTE, [$return, $id]) // @phpstan-ignore-line
?: ($success ?? new JsToast('Success' . (is_string($return) ? (': ' . $return) : ''))));
}
return $js;
}, $args);
}
/**
* Check if all argument values have been provided.
*/
private function _hasAllArguments(): array
{
$errors = [];
foreach ($this->action->args as $key => $val) {
if (!isset($this->args[$key])) {
$errors[] = $key;
}
}
return $errors;
}
}