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
40 changes: 28 additions & 12 deletions src/Dispatch/Dispatcher.php
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
*/
class Dispatcher {
private const LOGIC_EXECUTION_HEADER = "X-Logic-Execution";
private const COMPONENT_INPUT_NAME = "__component";

private Config $config;
private Request $request;
Expand Down Expand Up @@ -381,20 +382,13 @@ private function handleLogicExecution(
$this->recordLogicExecution($functionReference);
}

// TODO: No need to have the whole Input class. Just pass a nullable string in called $doMethod, from $input->getString("do")
$input->when("do")->call(
function(InputData $data)use($logicAssembly, $extraArgs) {
$doName = "do_" . str_replace(
"-",
"_",
$data->getString("do"),
);
if($doAction = $this->getDoActionForLogic($input, $component)) {
$doName = "do_" . str_replace("-", "_", $doAction);

foreach($this->logicExecutor->invoke($logicAssembly, $doName, $extraArgs) as $functionReference) {
$this->recordLogicExecution($functionReference);
}
foreach($this->logicExecutor->invoke($logicAssembly, $doName, $extraArgs) as $functionReference) {
$this->recordLogicExecution($functionReference);
}
);
}

foreach($this->logicExecutor->invoke($logicAssembly, "go", $extraArgs) as $functionReference) {
$this->recordLogicExecution($functionReference);
Expand All @@ -405,6 +399,28 @@ function(InputData $data)use($logicAssembly, $extraArgs) {
}
}

private function getDoActionForLogic(Input $input, ?Element $component):?string {
$doAction = $input->getString("do");
if($doAction === null || $doAction === "") {
return null;
}

$submittedComponent = $input->getString(self::COMPONENT_INPUT_NAME);
if($submittedComponent === null || $submittedComponent === "") {
return $doAction;
}

if(!$component) {
return null;
}

if(strtolower($component->tagName) !== strtolower($submittedComponent)) {
return null;
}

return $doAction;
}

/**
* @return void
*/
Expand Down
64 changes: 64 additions & 0 deletions test/phpunit/Dispatch/DispatcherTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,70 @@ public function testGenerateResponse_executesComponentAndPageLogicAndAppliesHead
self::assertSame([], $invocations[4]["extraArgs"]);
}

public function testGenerateResponse_componentFormDoActionDoesNotRunPageDoAction():void {
$html = <<<HTML
<!doctype html>
<body>
<form method="post"><button name="do" value="save-item">Save</button></form>
<widget-one>
<form method="post">
<input type="hidden" name="__component" value="widget-one">
<button name="do" value="save-item">Save</button>
</form>
</widget-one>
</body>
HTML;

$viewModel = new HTMLDocument($html);
$component = $viewModel->querySelector("widget-one");
self::assertInstanceOf(Element::class, $component);

$componentAssembly = $this->createAssembly("/tmp/component.php");
$componentList = new LogicAssemblyComponentList();
$componentList->addAssemblyComponent($componentAssembly, $component);

$processor = $this->createMock(ViewModelProcessor::class);
$processor->method("processDynamicPath");
$processor->method("processPartialContent")
->willReturn($componentList);

$invocations = [];
$logicAssembly = $this->createAssembly("/tmp/page.php");
$logicExecutor = $this->createMock(LogicExecutor::class);
$logicExecutor->method("invoke")
->willReturnCallback(function(Assembly $assembly, string $name)use(&$invocations, $componentAssembly):\Generator {
$invocations[] = [
"assembly" => $assembly === $componentAssembly ? "component" : "page",
"name" => $name,
];
yield ($assembly === $componentAssembly ? "/tmp/component.php" : "/tmp/page.php") . "::$name()";
});

$sut = $this->createDispatcher(
input: new Input([], [
"__component" => "widget-one",
"do" => "save-item",
]),
viewModel: $viewModel,
logicAssembly: $logicAssembly,
viewModelInit: $this->createViewModelInit($processor, true),
logicExecutor: $logicExecutor,
);

$response = $sut->generateResponse();

self::assertSame(StatusCode::OK, $response->getStatusCode());
self::assertSame([
["assembly" => "component", "name" => "go_before"],
["assembly" => "component", "name" => "do_save_item"],
["assembly" => "component", "name" => "go"],
["assembly" => "component", "name" => "go_after"],
["assembly" => "page", "name" => "go_before"],
["assembly" => "page", "name" => "go"],
["assembly" => "page", "name" => "go_after"],
], $invocations);
}

public function testProcessResponse_rethrowsComponentThrowableOutsideErrorMode():void {
$viewModel = new HTMLDocument('<!doctype html><body><widget-one data-element="widget-one"></widget-one></body>');
$component = $viewModel->querySelector("widget-one");
Expand Down
Loading