From 0bfe5360f24bd2565d445281e3196338699699c0 Mon Sep 17 00:00:00 2001 From: ianrobertsFF <91917328+ianrobertsFF@users.noreply.github.com> Date: Thu, 29 Dec 2022 15:37:09 +0000 Subject: [PATCH] feat: include subfield dependson middleware - Includes middleware to allow for dependsOn functionality for any field containing subfields - Will allow different types of nested fields to be resolved universally, so long as they all use this package --- composer.json | 13 +- src/{ => Contracts}/Subfield.php | 2 + .../Middleware/InterceptSubfieldDependsOn.php | 144 ++++++++++++++++++ src/SubfieldServiceProvider.php | 28 ++++ 4 files changed, 184 insertions(+), 3 deletions(-) rename src/{ => Contracts}/Subfield.php (80%) create mode 100644 src/Http/Middleware/InterceptSubfieldDependsOn.php create mode 100644 src/SubfieldServiceProvider.php diff --git a/composer.json b/composer.json index ba04f91..d5f54c0 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,6 @@ { - "name": "formfeed-uk/nova-subfields", - "description": "Subfields interface for Nova Fields", + "name": "formfeed-uk/nova-subfield-dependson", + "description": "Functionality to allow fields which are contained within other fields to use Nova DependsOn", "license": "MIT", "require": { "php": "^8.0", @@ -9,7 +9,7 @@ }, "autoload": { "psr-4": { - "Formfeed\\Subfields\\": "src/" + "Formfeed\\SubfieldDependsOn\\": "src/" } }, "autoload-dev": { @@ -17,6 +17,13 @@ "Tests\\": "tests/" } }, + "extra": { + "laravel": { + "providers": [ + "Formfeed\\SubfieldDependsOn\\SubfieldServiceProvider" + ] + } + }, "repositories": [ { "type": "composer", diff --git a/src/Subfield.php b/src/Contracts/Subfield.php similarity index 80% rename from src/Subfield.php rename to src/Contracts/Subfield.php index 2f0c344..60b55ab 100644 --- a/src/Subfield.php +++ b/src/Contracts/Subfield.php @@ -11,4 +11,6 @@ public function hasSubfields(): bool; public function getSubfields(): FieldCollection; + public function afterDependsOnSync() : self; + } \ No newline at end of file diff --git a/src/Http/Middleware/InterceptSubfieldDependsOn.php b/src/Http/Middleware/InterceptSubfieldDependsOn.php new file mode 100644 index 0000000..eedb7f1 --- /dev/null +++ b/src/Http/Middleware/InterceptSubfieldDependsOn.php @@ -0,0 +1,144 @@ +route()->action) && $request->route()->action['uses'] instanceof Closure) { + return $next($request); + } + + $novaRequest = NovaRequest::createFrom($request); + + if (!$this->isDependentFieldRequest($request)) { + return $next($request); + } + + if (!$this->resourceHasSubfields($novaRequest)) { + return $next($request); + } + + $response = $next($request); + + if ($response instanceof JsonResponse) { + $content = $response->getOriginalContent(); + + $field = []; + + // If the Field is successfully resolved via another middleware or resource method, just use that + if ($content instanceof Field) { + $field = $content; + } + + // If the Field is not resolved, try to find it and sync depends on + if ($content instanceof ArrayObject && $content->count() === 0) { + $field = $this->findField($novaRequest, $request->input("component"), $request->input("_viaField")); + if ($field instanceof Field) { + $field->syncDependsOn($novaRequest); + } + } + + // If there are additional actions needed after dependsOn sync, run them + if (method_exists($field, "afterDependsOnSync")) { + $field = $field->afterDependsOnSync() ?? $field; + } + + $response = response()->json($field); + } + + return $response; + } + + protected function isDependentFieldRequest(Request $request) { + if (!$request->isMethod("PATCH")) { + return false; + } + return (is_null($this->getFieldMethod($request))) ? false : true; + } + + protected function findField(NovaRequest $request, string $componentKey, string $viaField) { + $fields = $this->getResourceFields($request); + return $this->findNestedField($request, $fields, $componentKey, $viaField); + } + + protected function findNestedField(NovaRequest $request, FieldCollection $fields, string $componentKey) { + + if ($fields->count() === 0) { + return []; + } + + return $fields->first(fn ($field) => ($field->dependentComponentKey() === $componentKey)) ?? $this->findNestedField($request, $this->getSubfields($request, $fields), $componentKey); + } + + protected function getSubfields(NovaRequest $request, FieldCollection $fields) { + return rescue(function () use ($fields, $request) { + return $fields->filter(function ($field) use ($request) { + return $this->fieldHasSubfields($request, $field); + })->map(function ($field) use ($request) { + return $field->getSubfields($request); + })->flatten(); + }, FieldCollection::make([]), false); + } + + protected function fieldHasSubfields(NovaRequest $request, Field $field): bool { + return method_exists($field, "getSubfields") && method_exists($field, "hasSubfields") && $field->hasSubfields($request); + } + + protected function getResourceFields(NovaRequest $request): FieldCollection { + $fieldMethod = $this->getFieldMethod($request); + return $request->newResource() + ->$fieldMethod($request); + } + + protected function resourceHasSubfields(NovaRequest $request): bool { + return $this->getResourceFields($request)->contains(function ($field) { + return method_exists($field, "getSubfields") && method_exists($field, "hasSubfields"); + }); + } + + protected function getFieldMethod(Request $request) { + $routeController = $request->route()->getController(); + switch (get_class($routeController)) { + case UpdateFieldController::class: + return "updateFieldsWithinPanels"; + case UpdatePivotFieldController::class: + return "updatePivotFields"; + case CreationFieldController::class: + case CreationFieldSyncController::class: + return "creationFieldsWithinPanels"; + case CreationPivotFieldController::class: + return "creationPivotFields"; + } + } +} diff --git a/src/SubfieldServiceProvider.php b/src/SubfieldServiceProvider.php new file mode 100644 index 0000000..56ae28b --- /dev/null +++ b/src/SubfieldServiceProvider.php @@ -0,0 +1,28 @@ +app['router']; + + if ($router->hasMiddlewareGroup('nova')) { + $router->pushMiddlewareToGroup('nova', InterceptSubfieldDependsOn::class); + return; + } + + if (!$this->app->configurationIsCached()) { + config()->set('nova.middleware', array_merge( + config('nova.middleware', []), + [InterceptSubfieldDependsOn::class] + )); + } + } + +}