mirror of
https://github.com/bdnugget/magewire.git
synced 2026-11-07 08:07:27 +00:00
Improvements/nested input sync methods (#119)
* New nested property input sync lifecycle hooks --------- Co-authored-by: Willem Poortman <git@wpoortman.nl>
This commit is contained in:
1 parent
9f282a3c69
commit
d7269575dd
8 files changed
+203
-49
No files matched your search
@@ -378,6 +378,14 @@ class Explanation extends \Magewirephp\Magewire\Component
|
||||
{
|
||||
public $nested = ['foo' => ['bar' => 'Hello world']];
|
||||
|
||||
// Since v1.10.6: Before nested array properties are updating.
|
||||
public function updatingNested(array $value): array
|
||||
{
|
||||
// Make sure "nested.foo.bar" is always strtolower (helloworld)
|
||||
$value['foo']['bar'] = strtolower($value['foo']['bar']);
|
||||
return $value;
|
||||
}
|
||||
|
||||
// Before it's getting updated.
|
||||
public function updatingNestedFooBar(string $value): string
|
||||
{
|
||||
@@ -391,6 +399,14 @@ class Explanation extends \Magewirephp\Magewire\Component
|
||||
// Returns ['foo' => ['bar' => 'hello world']]
|
||||
return strtolower($value);
|
||||
}
|
||||
|
||||
// Since v1.10.6: After nested array properties are updated.
|
||||
public function updatedNested(array $value): array
|
||||
{
|
||||
// Evantually strtoupper "nested.foo.bar" again (HELLOWORLD).
|
||||
$value['foo']['bar'] = strtoupper($value['foo']['bar']);
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -39,8 +39,8 @@ class Property
|
||||
throw new ComponentException(__('Public property %1 does\'nt exist', [$property]));
|
||||
}
|
||||
|
||||
$path = substr(strstr($path, '.'), 1);
|
||||
$data = $this->arrayManager->set($path, $component->{$property}, $value, '.');
|
||||
$path = substr(strstr($path, '.'), 1);
|
||||
$data = $this->arrayManager->set($path, $component->{$property}, $value, '.');
|
||||
|
||||
return compact('property', 'data', 'realpath', 'path');
|
||||
}
|
||||
@@ -54,15 +54,16 @@ class Property
|
||||
* Use a callback function to assign component property
|
||||
* values except default reserved properties.
|
||||
*/
|
||||
public function assign(callable $callback, Component $component, array $data = null): void
|
||||
public function assign(callable $callback, Component $component, array $data = null, bool $merge = true): void
|
||||
{
|
||||
$publicProperties = $component->getPublicProperties(true);
|
||||
$data = $data === null ? $publicProperties : array_merge($publicProperties, $data);
|
||||
$data = $data === null ? $publicProperties : ($merge ? array_merge($publicProperties, $data) : $data);
|
||||
|
||||
foreach ($data as $property => $value) {
|
||||
if (in_array($property, Component::RESERVED_PROPERTIES, true)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (array_key_exists($property, $publicProperties)) {
|
||||
$callback($component, $property, $value);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php declare(strict_types=1);
|
||||
/**
|
||||
* Copyright © Willem Poortman 2021-present. All rights reserved.
|
||||
*
|
||||
* Please read the README and LICENSE files for more
|
||||
* details on copyrights and license information.
|
||||
*/
|
||||
|
||||
namespace Magewirephp\Magewire\Model;
|
||||
|
||||
use Magento\Framework\ObjectManagerInterface;
|
||||
use Magento\Framework\View\Layout;
|
||||
use Magewirephp\Magewire\Component;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
abstract class Action implements ActionInterface
|
||||
{
|
||||
abstract public function handle(Component $component, array $payload);
|
||||
|
||||
public function inspect(Component $component, array $updates): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function evaluate(Component $component, array $updates)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -11,11 +11,11 @@ namespace Magewirephp\Magewire\Model\Action;
|
||||
use Magento\Framework\Exception\LocalizedException;
|
||||
use Magewirephp\Magewire\Exception\ComponentActionException;
|
||||
use Magewirephp\Magewire\Component;
|
||||
use Magewirephp\Magewire\Model\Action;
|
||||
use Magewirephp\Magewire\Model\Action\Type\Factory as TypeFactory;
|
||||
use Magewirephp\Magewire\Model\Action\Type\Magic;
|
||||
use Magewirephp\Magewire\Model\ActionInterface;
|
||||
|
||||
class CallMethod implements ActionInterface
|
||||
class CallMethod extends Action
|
||||
{
|
||||
public const ACTION = 'callMethod';
|
||||
|
||||
|
||||
@@ -11,10 +11,10 @@ namespace Magewirephp\Magewire\Model\Action;
|
||||
use Magento\Framework\Exception\LocalizedException;
|
||||
use Magewirephp\Magewire\Exception\ComponentActionException;
|
||||
use Magewirephp\Magewire\Component;
|
||||
use Magewirephp\Magewire\Model\ActionInterface;
|
||||
use Magewirephp\Magewire\Model\Action;
|
||||
use Magewirephp\Magewire\Model\Hydrator\Listener as ListenerHydrator;
|
||||
|
||||
class FireEvent implements ActionInterface
|
||||
class FireEvent extends Action
|
||||
{
|
||||
public const ACTION = 'fireEvent';
|
||||
|
||||
|
||||
+102
-29
@@ -10,13 +10,14 @@ namespace Magewirephp\Magewire\Model\Action;
|
||||
|
||||
use Exception;
|
||||
use Magewirephp\Magewire\Exception\ComponentActionException;
|
||||
use Magewirephp\Magewire\Exception\ComponentException;
|
||||
use Magewirephp\Magewire\Component;
|
||||
use Magewirephp\Magewire\Exception\ValidationException;
|
||||
use Magewirephp\Magewire\Helper\Property as PropertyHelper;
|
||||
use Magewirephp\Magewire\Model\ActionInterface;
|
||||
use Magewirephp\Magewire\Model\Action;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
class SyncInput implements ActionInterface
|
||||
class SyncInput extends Action
|
||||
{
|
||||
public const ACTION = 'syncInput';
|
||||
|
||||
@@ -31,56 +32,128 @@ class SyncInput implements ActionInterface
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
public function inspect(Component $component, array $updates): bool
|
||||
{
|
||||
foreach ($this->reduceProperty($component, $updates) as $property => $value) {
|
||||
$this->assign($component, $property, $this->updating($component, $property, $value));
|
||||
};
|
||||
|
||||
return parent::inspect($component, $updates);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws ComponentActionException
|
||||
*/
|
||||
public function handle(Component $component, array $payload)
|
||||
{
|
||||
if (!isset($payload['name'], $payload['value'])) {
|
||||
if (! isset($payload['name'], $payload['value'])) {
|
||||
throw new ComponentActionException(__('Invalid update payload'));
|
||||
}
|
||||
|
||||
// Grep the required type elements from the action payload.
|
||||
$property = $payload['name'];
|
||||
$value = $payload['value'];
|
||||
|
||||
try {
|
||||
$containsDots = $this->propertyHelper->containsDots($property);
|
||||
$nested = $this->propertyHelper->containsDots($property);
|
||||
$transform = [];
|
||||
|
||||
if ($containsDots) {
|
||||
if ($nested) {
|
||||
// Full property value including its new payload value.
|
||||
$transform = $this->propertyHelper->transformDots($property, $value, $component);
|
||||
// Search for the newly set property value by path.
|
||||
$value = $this->propertyHelper->searchViaDots($transform['path'], $transform['data']);
|
||||
}
|
||||
|
||||
// Prepare lifecycle methods.
|
||||
$before = 'updating' . str_replace(' ', '', ucwords(str_replace(['-', '_', '.'], ' ', $property)));
|
||||
$after = str_replace('updating', 'updated', $before);
|
||||
// Assign 'updating' result in the middle and re-assign the final result at the end.
|
||||
$methods = [$before, 'updating', 'assign', 'updated', $after, 'assign'];
|
||||
// Try to run existing pre-assignment methods if they exist.
|
||||
$value = $this->updating($component, $property, $value);
|
||||
|
||||
foreach ($methods as $method) {
|
||||
if ($method === 'assign') {
|
||||
if ($containsDots) {
|
||||
$component->{$transform['property']} = $transform['data'];
|
||||
} else {
|
||||
$component->{$property} = $value;
|
||||
}
|
||||
} elseif (method_exists($component, $method)) {
|
||||
try {
|
||||
$value = $component->{$method}($value, $property);
|
||||
|
||||
if ($containsDots) {
|
||||
// Put the new value in its original nested spot.
|
||||
$transform = $this->propertyHelper->transformDots($property, $value, $component);
|
||||
}
|
||||
} catch (ValidationException $exception) {
|
||||
$this->logger->critical('Magewire:' . $exception->getMessage());
|
||||
}
|
||||
}
|
||||
if ($nested) {
|
||||
$transform = $this->propertyHelper->transformDots($property, $value, $component);
|
||||
}
|
||||
|
||||
// Regular value assignment.
|
||||
$this->assign($component, $transform ? $transform['property'] : $property, $transform ? $transform['data'] : $value);
|
||||
|
||||
// Try to run post-assignment methods if they exist.
|
||||
$value = $this->updated($component, $property, $value);
|
||||
|
||||
if ($nested) {
|
||||
$transform = $this->propertyHelper->transformDots($property, $value, $component);
|
||||
}
|
||||
|
||||
// Re-assign the final value onto the component property.
|
||||
$this->assign($component, $transform ? $transform['property'] : $property, $transform ? $transform['data'] : $value);
|
||||
} catch (Exception $exception) {
|
||||
$this->logger->critical(
|
||||
__sprintf('Magewire: Something went wrong while syncing property "%s" onto component "%s"', [$property, $component->name]),
|
||||
['exception' => $exception]
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
public function evaluate(Component $component, array $updates)
|
||||
{
|
||||
foreach ($this->reduceProperty($component, $updates) as $property => $value) {
|
||||
$this->assign($component, $property, $this->updated($component, $property, $value));
|
||||
};
|
||||
|
||||
return parent::evaluate($component, $updates);
|
||||
}
|
||||
|
||||
private function assign(Component $component, string $property, $value): void
|
||||
{
|
||||
$this->propertyHelper->assign(fn ($component, $property, $value) => $component->{$property} = $value, $component, [$property => $value], false);
|
||||
}
|
||||
|
||||
private function updating(Component $component, string $property, $value)
|
||||
{
|
||||
$methods = ['updating' . str_replace(' ', '', ucwords(str_replace(['-', '_', '.'], ' ', $property))), 'updating'];
|
||||
|
||||
foreach ($methods as $method) {
|
||||
if (method_exists($component, $method)) {
|
||||
$value = $component->{$method}($value, $property);
|
||||
}
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
private function updated(Component $component, string $property, $value)
|
||||
{
|
||||
$methods = ['updated', 'updated' . str_replace(' ', '', ucwords(str_replace(['-', '_', '.'], ' ', $property)))];
|
||||
|
||||
foreach ($methods as $method) {
|
||||
if (method_exists($component, $method)) {
|
||||
$value = $component->{$method}($value, $property);
|
||||
}
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
protected function reduceProperty(Component $component, array $updates): array
|
||||
{
|
||||
$nested = [];
|
||||
|
||||
foreach ($updates as $update) {
|
||||
$property = $update['payload']['name'];
|
||||
$value = $update['payload']['value'];
|
||||
|
||||
if ($this->propertyHelper->containsDots($property)) {
|
||||
try {
|
||||
$transform = $this->propertyHelper->transformDots($property, $value, $component);
|
||||
$nested[$transform['property']] = $component->{$transform['property']};
|
||||
|
||||
/** @todo multidimensional array support still needs to be included here. */
|
||||
} catch (ComponentException $exception) {
|
||||
$this->logger->critical($exception->getMessage(), ['exception' => $exception]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $nested;
|
||||
}
|
||||
}
|
||||
@@ -16,12 +16,23 @@ use Magewirephp\Magewire\Component;
|
||||
interface ActionInterface
|
||||
{
|
||||
/**
|
||||
* Handle update action.
|
||||
* Runs before (planning stage) a individual action update is beind handled. Inspection is responsible
|
||||
* for allowing updates for the current type to proceed or not. In other words, when returning false,
|
||||
* both the handle and evaluate methods won't be able to run.
|
||||
*
|
||||
* @param Component $component
|
||||
* @param array $payload
|
||||
*
|
||||
* @return mixed
|
||||
* @param array $updates // specific updates only for this action type.
|
||||
*/
|
||||
public function inspect(Component $component, array $updates): bool;
|
||||
|
||||
/**
|
||||
* Handle update action (execution stage).
|
||||
*/
|
||||
public function handle(Component $component, array $payload);
|
||||
|
||||
/**
|
||||
* Runs after (evaluation stage) a individual action update is beind handled.
|
||||
*
|
||||
* @param array $updates // specific updates only for this action type.
|
||||
*/
|
||||
public function evaluate(Component $component, array $updates);
|
||||
}
|
||||
@@ -63,13 +63,37 @@ class ComponentManager
|
||||
throw new LocalizedException(__('No request object found'));
|
||||
}
|
||||
|
||||
foreach ($updates as $update) {
|
||||
try {
|
||||
$this->updateActionsPool[$update['type']]->handle($component, $update['payload']);
|
||||
} catch (AcceptableException $exception) {
|
||||
continue;
|
||||
} catch (Exception $exception) {
|
||||
throw new LocalizedException(__($exception->getMessage()));
|
||||
// Temporary typed update basket.
|
||||
$types = [];
|
||||
// Key to handle the update request.
|
||||
$handle = false;
|
||||
|
||||
foreach ($updates as $key => $update) {
|
||||
// Process an inspection before the first update by type runs.
|
||||
if ($key === 0 || (isset($updates[$key]) && $updates[$key]['type'] !== $updates[$key - 1]['type'])) {
|
||||
// Filter out only those who have an simular update type.
|
||||
$types = array_filter($updates, fn ($value) => $value['type'] === $update['type']);
|
||||
// Lock update handling until inspect releases it.
|
||||
$handle = false;
|
||||
|
||||
if (! empty($types)) {
|
||||
$handle = $this->updateActionsPool[$update['type']]->inspect($component, $types);
|
||||
}
|
||||
}
|
||||
|
||||
if ($handle) {
|
||||
try {
|
||||
$this->updateActionsPool[$update['type']]->handle($component, $update['payload']);
|
||||
} catch (AcceptableException $exception) {
|
||||
continue;
|
||||
} catch (Exception $exception) {
|
||||
throw new LocalizedException(__($exception->getMessage()));
|
||||
}
|
||||
|
||||
// Process an evaluation after the last update by type ran.
|
||||
if ((! isset($updates[$key + 1]) || $updates[$key + 1]['type'] !== $update['type'])) {
|
||||
$this->updateActionsPool[$update['type']]->evaluate($component, $types);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user