Cannot save resource that uses Flexible field. (Call to undefined method forceFill)
Blindmikey opened this issue ยท 27 comments
Upon saving I get the error:
Call to undefined method Whitecube\NovaFlexibleContent\Layouts\Layout::forceFill()
I believe this is related to this issue here: outl1ne/nova-settings#166
Followed same steps to reproduce the issue on a clean install with with whitecube/nova-flexible-content and still get this error as well.
Log:
[2023-06-28 06:02:43] local.ERROR: Call to undefined method Whitecube\NovaFlexibleContent\Layouts\Layout::forceFill() {"userId":1,"exception":"[object] (Error(code: 0): Call to undefined method Whitecube\\NovaFlexibleContent\\Layouts\\Layout::forceFill() at /home/vagrant/code/vendor/laravel/nova/src/Fields/Field.php:492)
[stacktrace]
#0 /home/vagrant/code/vendor/laravel/nova/src/Fields/Field.php(475): Laravel\\Nova\\Fields\\Field->fillModelWithData()
#1 /home/vagrant/code/vendor/laravel/framework/src/Illuminate/Support/helpers.php(307): Laravel\\Nova\\Fields\\Field->Laravel\\Nova\\Fields\\{closure}()
#2 /home/vagrant/code/vendor/laravel/nova/src/Fields/Field.php(472): tap()
#3 /home/vagrant/code/vendor/laravel/nova/src/Fields/Field.php(457): Laravel\\Nova\\Fields\\Field->fillAttributeFromRequest()
#4 /home/vagrant/code/vendor/laravel/nova/src/Fields/Field.php(439): Laravel\\Nova\\Fields\\Field->fillAttribute()
#5 /home/vagrant/code/vendor/laravel/nova/src/Fields/Field.php(413): Laravel\\Nova\\Fields\\Field->fillInto()
#6 /home/vagrant/code/vendor/whitecube/nova-flexible-content/src/Layouts/Layout.php(381): Laravel\\Nova\\Fields\\Field->fill()
#7 [internal function]: Whitecube\\NovaFlexibleContent\\Layouts\\Layout->Whitecube\\NovaFlexibleContent\\Layouts\\{closure}()
#8 /home/vagrant/code/vendor/laravel/framework/src/Illuminate/Collections/Arr.php(558): array_map()
#9 /home/vagrant/code/vendor/laravel/framework/src/Illuminate/Collections/Collection.php(771): Illuminate\\Support\\Arr::map()
#10 /home/vagrant/code/vendor/whitecube/nova-flexible-content/src/Layouts/Layout.php(380): Illuminate\\Support\\Collection->map()
#11 /home/vagrant/code/vendor/whitecube/nova-flexible-content/src/Flexible.php(362): Whitecube\\NovaFlexibleContent\\Layouts\\Layout->fill()
#12 [internal function]: Whitecube\\NovaFlexibleContent\\Flexible->Whitecube\\NovaFlexibleContent\\{closure}()
#13 /home/vagrant/code/vendor/laravel/framework/src/Illuminate/Collections/Arr.php(558): array_map()
#14 /home/vagrant/code/vendor/laravel/framework/src/Illuminate/Collections/Collection.php(771): Illuminate\\Support\\Arr::map()
#15 /home/vagrant/code/vendor/whitecube/nova-flexible-content/src/Flexible.php(350): Illuminate\\Support\\Collection->map()
#16 /home/vagrant/code/vendor/whitecube/nova-flexible-content/src/Flexible.php(319): Whitecube\\NovaFlexibleContent\\Flexible->syncAndFillGroups()
#17 /home/vagrant/code/vendor/laravel/nova/src/Fields/Field.php(439): Whitecube\\NovaFlexibleContent\\Flexible->fillAttribute()
#18 /home/vagrant/code/vendor/laravel/nova/src/Fields/Field.php(413): Laravel\\Nova\\Fields\\Field->fillInto()
#19 /home/vagrant/code/vendor/laravel/framework/src/Illuminate/Collections/HigherOrderCollectionProxy.php(60): Laravel\\Nova\\Fields\\Field->fill()
#20 [internal function]: Illuminate\\Support\\HigherOrderCollectionProxy->Illuminate\\Support\\{closure}()
#21 /home/vagrant/code/vendor/laravel/framework/src/Illuminate/Collections/Arr.php(558): array_map()
#22 /home/vagrant/code/vendor/laravel/framework/src/Illuminate/Collections/Collection.php(771): Illuminate\\Support\\Arr::map()
#23 /home/vagrant/code/vendor/laravel/framework/src/Illuminate/Collections/HigherOrderCollectionProxy.php(59): Illuminate\\Support\\Collection->map()
#24 /home/vagrant/code/vendor/laravel/nova/src/FillsFields.php(100): Illuminate\\Support\\HigherOrderCollectionProxy->__call()
#25 /home/vagrant/code/vendor/laravel/nova/src/FillsFields.php(37): Laravel\\Nova\\Resource::fillFields()
#26 /home/vagrant/code/vendor/laravel/nova/src/Http/Controllers/ResourceUpdateController.php(46): Laravel\\Nova\\Resource::fillForUpdate()
#27 /home/vagrant/code/vendor/laravel/framework/src/Illuminate/Database/Concerns/ManagesTransactions.php(30): Laravel\\Nova\\Http\\Controllers\\ResourceUpdateController->Laravel\\Nova\\Http\\Controllers\\{closure}()
#28 /home/vagrant/code/vendor/laravel/nova/src/Http/Controllers/ResourceUpdateController.php(37): Illuminate\\Database\\Connection->transaction()
#29 /home/vagrant/code/vendor/laravel/framework/src/Illuminate/Routing/Controller.php(54): Laravel\\Nova\\Http\\Controllers\\ResourceUpdateController->__invoke()
#30 /home/vagrant/code/vendor/laravel/framework/src/Illuminate/Routing/ControllerDispatcher.php(43): Illuminate\\Routing\\Controller->callAction()
#31 /home/vagrant/code/vendor/laravel/framework/src/Illuminate/Routing/Route.php(259): Illuminate\\Routing\\ControllerDispatcher->dispatch()
#32 /home/vagrant/code/vendor/laravel/framework/src/Illuminate/Routing/Route.php(205): Illuminate\\Routing\\Route->runController()
#33 /home/vagrant/code/vendor/laravel/framework/src/Illuminate/Routing/Router.php(799): Illuminate\\Routing\\Route->run()
#34 /home/vagrant/code/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(141): Illuminate\\Routing\\Router->Illuminate\\Routing\\{closure}()
#35 /home/vagrant/code/vendor/laravel/nova/src/Http/Middleware/Authorize.php(18): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#36 /home/vagrant/code/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Laravel\\Nova\\Http\\Middleware\\Authorize->handle()
#37 /home/vagrant/code/vendor/laravel/framework/src/Illuminate/Auth/Middleware/Authenticate.php(57): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#38 /home/vagrant/code/vendor/laravel/nova/src/Http/Middleware/Authenticate.php(31): Illuminate\\Auth\\Middleware\\Authenticate->handle()
#39 /home/vagrant/code/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Laravel\\Nova\\Http\\Middleware\\Authenticate->handle()
#40 /home/vagrant/code/vendor/whitecube/nova-flexible-content/src/Http/Middleware/InterceptFlexibleAttributes.php(33): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#41 /home/vagrant/code/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Whitecube\\NovaFlexibleContent\\Http\\Middleware\\InterceptFlexibleAttributes->handle()
#42 /home/vagrant/code/vendor/laravel/nova/src/Http/Middleware/BootTools.php(20): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#43 /home/vagrant/code/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Laravel\\Nova\\Http\\Middleware\\BootTools->handle()
#44 /home/vagrant/code/vendor/laravel/nova/src/Http/Middleware/DispatchServingNovaEvent.php(33): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#45 /home/vagrant/code/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Laravel\\Nova\\Http\\Middleware\\DispatchServingNovaEvent->handle()
#46 /home/vagrant/code/vendor/inertiajs/inertia-laravel/src/Middleware.php(87): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#47 /home/vagrant/code/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Inertia\\Middleware->handle()
#48 /home/vagrant/code/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/VerifyCsrfToken.php(78): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#49 /home/vagrant/code/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\\Foundation\\Http\\Middleware\\VerifyCsrfToken->handle()
#50 /home/vagrant/code/vendor/laravel/framework/src/Illuminate/View/Middleware/ShareErrorsFromSession.php(49): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#51 /home/vagrant/code/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\\View\\Middleware\\ShareErrorsFromSession->handle()
#52 /home/vagrant/code/vendor/laravel/framework/src/Illuminate/Session/Middleware/StartSession.php(121): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#53 /home/vagrant/code/vendor/laravel/framework/src/Illuminate/Session/Middleware/StartSession.php(64): Illuminate\\Session\\Middleware\\StartSession->handleStatefulRequest()
#54 /home/vagrant/code/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\\Session\\Middleware\\StartSession->handle()
#55 /home/vagrant/code/vendor/laravel/framework/src/Illuminate/Cookie/Middleware/AddQueuedCookiesToResponse.php(37): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#56 /home/vagrant/code/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\\Cookie\\Middleware\\AddQueuedCookiesToResponse->handle()
#57 /home/vagrant/code/vendor/laravel/framework/src/Illuminate/Cookie/Middleware/EncryptCookies.php(67): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#58 /home/vagrant/code/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\\Cookie\\Middleware\\EncryptCookies->handle()
#59 /home/vagrant/code/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(116): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#60 /home/vagrant/code/vendor/laravel/framework/src/Illuminate/Routing/Router.php(798): Illuminate\\Pipeline\\Pipeline->then()
#61 /home/vagrant/code/vendor/laravel/framework/src/Illuminate/Routing/Router.php(777): Illuminate\\Routing\\Router->runRouteWithinStack()
#62 /home/vagrant/code/vendor/laravel/framework/src/Illuminate/Routing/Router.php(741): Illuminate\\Routing\\Router->runRoute()
#63 /home/vagrant/code/vendor/laravel/framework/src/Illuminate/Routing/Router.php(730): Illuminate\\Routing\\Router->dispatchToRoute()
#64 /home/vagrant/code/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(200): Illuminate\\Routing\\Router->dispatch()
#65 /home/vagrant/code/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(141): Illuminate\\Foundation\\Http\\Kernel->Illuminate\\Foundation\\Http\\{closure}()
#66 /home/vagrant/code/vendor/laravel/nova/src/Http/Middleware/ServeNova.php(23): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#67 /home/vagrant/code/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Laravel\\Nova\\Http\\Middleware\\ServeNova->handle()
#68 /home/vagrant/code/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TransformsRequest.php(21): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#69 /home/vagrant/code/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/ConvertEmptyStringsToNull.php(31): Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest->handle()
#70 /home/vagrant/code/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\\Foundation\\Http\\Middleware\\ConvertEmptyStringsToNull->handle()
#71 /home/vagrant/code/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TransformsRequest.php(21): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#72 /home/vagrant/code/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TrimStrings.php(40): Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest->handle()
#73 /home/vagrant/code/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\\Foundation\\Http\\Middleware\\TrimStrings->handle()
#74 /home/vagrant/code/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/ValidatePostSize.php(27): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#75 /home/vagrant/code/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\\Foundation\\Http\\Middleware\\ValidatePostSize->handle()
#76 /home/vagrant/code/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/PreventRequestsDuringMaintenance.php(86): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#77 /home/vagrant/code/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\\Foundation\\Http\\Middleware\\PreventRequestsDuringMaintenance->handle()
#78 /home/vagrant/code/vendor/laravel/framework/src/Illuminate/Http/Middleware/HandleCors.php(49): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#79 /home/vagrant/code/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\\Http\\Middleware\\HandleCors->handle()
#80 /home/vagrant/code/vendor/laravel/framework/src/Illuminate/Http/Middleware/TrustProxies.php(39): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#81 /home/vagrant/code/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\\Http\\Middleware\\TrustProxies->handle()
#82 /home/vagrant/code/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(116): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#83 /home/vagrant/code/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(175): Illuminate\\Pipeline\\Pipeline->then()
#84 /home/vagrant/code/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(144): Illuminate\\Foundation\\Http\\Kernel->sendRequestThroughRouter()
#85 /home/vagrant/code/public/index.php(51): Illuminate\\Foundation\\Http\\Kernel->handle()
#86 {main}
"}
Found the issue - Laravel Nova 4.26.0 (released today) introduces the error. Rolling back to 4.25.1 (one release prior) resolves the issue.
Temp fix. Not raising PR because I am not sure if its the correct way. I have done only basic testing.
--- a/src/Layouts/Layout.php
+++ b/src/Layouts/Layout.php
@@ -7,6 +7,8 @@ use Illuminate\Contracts\Support\Arrayable;
use Illuminate\Database\Eloquent\Concerns\HasAttributes;
use Illuminate\Database\Eloquent\Concerns\HidesAttributes;
use Illuminate\Database\Eloquent\Model;
+use Illuminate\Support\Arr;
+use Illuminate\Support\Str;
use JsonSerializable;
use Laravel\Nova\Fields\Field;
use Laravel\Nova\Fields\FieldCollection;
@@ -701,4 +703,21 @@ class Layout implements LayoutInterface, JsonSerializable, ArrayAccess, Arrayabl
{
return $this->attributesToArray();
}
+
+ /**
+ * Fill the layout with an array of attributes.
+ *
+ * @param array $attributes
+ * @return $this
+ */
+ public function forceFill(array $attributes)
+ {
+ foreach ($attributes as $key => $value) {
+ $attribute = Str::replace('->', '.', $key);
+
+ Arr::set($this->attributes, $attribute, $value);
+ }
+
+ return $this;
+ }
}
Can more people chime in on this fix? I have no time to investigate and I'm fine merging a PR if enough people can confirm that it solves the problem.
I really wish Nova would be more careful with breaking changes on minor and patch releases.
Investigation:
In Nova's Field
class, method fillAttributeFromRequest()
with object $model
is passed to \Illuminate\Database\Eloquent\Model|\Illuminate\Support\Fluent $model
of fillModelWithData()
.
Layout
object doesn't have method forceFill()
like Model
which Nova team has utilized in the 4.26 release. In earlier realeases, hydration was happening at fillAttributeFromRequest()
itself.
Solution(Hacking) thought process:
Nova team member has suggested to use Fluent
instead of \stdClass
for other package which has forceFill()
. I have copied the same method from Fluent
class. Property $this->attributes
is being used by both Layout
and Field
so function(above patch) is working without any change.
This is affecting me too - hopefully Nova provides a fix in their next update - I can confirm rolling back and updating composer.json to:
"laravel/nova": "4.25.1",
Has fixed the issue for me. Thank you so much to everyone who has inputted.
I really wish Nova would be more careful with breaking changes on minor and patch releases.
Hey folks! Sorry to hear the latest update broke this package. I'm curious, though, what could we have done in this instance? Our codebase has extensive test coverage, and we didn't introduce any backward-incompatible changes according to our tests.
I really wish Nova would be more careful with breaking changes on minor and patch releases.
Hey folks! Sorry to hear the latest update broke this package. I'm curious, though, what could we have done in this instance? Our codebase has extensive test coverage, and we didn't introduce any backward-incompatible changes according to our tests.
I might be wrong as I can't think from Nova maintainence or Flexible maintanence point of view.
According to me forceFill()
should have been avoided and implementation to hydrate attributes should have been kept the same.
Package maintainers already have taken advantage of this object $model
where they had freedom to create object of their any of the classes. And suddenly in 4.26, Nova is expecting them to have forceFill()
.
Is there any ETA on when this package will be working again with newest versions of Nova?
@davidhemphill
There is a package that can find you BC breaks: https://github.com/Roave/BackwardCompatibilityCheck (and CI integration example https://github.com/spatie/calendar-links/blob/master/.github/workflows/backward-compatibility-check.yml) , but it's too strict for Nova project I think. It will encourage you a lot to use final
keyword for classes and methods - there is a room for such improvements on Nova and such changes of course will be introduce BC breaks for A LOT packages, but IMO this is a required step to avoid issues like this in the future.
Perhaps this is already being done, or perhaps it's not feasible - however a pre-release notification to 3rd party package developers with a window to test and communicate breaking changes and collaborate on fixes before public release should alleviate issues like this.
PR submitted contributions welcome.
This should be fixed in v1.0.9 thanks to @LorenzoSapora. Can someone report back? I will reopen if needed.
v.10.9 did not fix the issue. Still getting the error
call to undefined method Whitecube\NovaFlexibleContent\Layouts\Layout::forceFill()
I can confirm, 10.9 did not fix the issue
Okay, apologies for that, we could not test the fix because our license has expired. Still looking for someone who could submit a PR with a fix.
Okay, apologies for that, we could not test the fix because our license has expired. Still looking for someone who could submit a PR with a fix.
You can use Nova 4 locally without License. @voidgraphics
Okay, apologies for that, we could not test the fix because our license has expired. Still looking for someone who could submit a PR with a fix.
You can use Nova 4 locally without License. @voidgraphics
Interesting, that did not work for me earlier, composer refused to download the package. I will try again in a bit. Thanks for the link!
@voidgraphics I proposed a PR #470
This fixes the problem on our version of the package
@voidgraphics
Almost there! ๐
Just need to add missing imports and it will work:
use Illuminate\Support\Arr;
use Illuminate\Support\Str;
PS: hey @davidhemphill
@voidgraphics wrote:
Okay, apologies for that, we could not test the fix because our license has expired. Still looking for someone who could submit a PR with a fix.
Interesting, that did not work for me earlier, composer refused to download the package. I will try again in a bit. Thanks for the link!
I have an idea: provide free licenses to maintainers of some popular nova packages just to keep ecosystem healthy and avoid issues like this
@voidgraphics Almost there! ๐ Just need to add missing imports and it will work:
use Illuminate\Support\Arr; use Illuminate\Support\Str;
PS: hey @davidhemphill
@voidgraphics wrote:
Okay, apologies for that, we could not test the fix because our license has expired. Still looking for someone who could submit a PR with a fix.Interesting, that did not work for me earlier, composer refused to download the package. I will try again in a bit. Thanks for the link!
I have an idea: provide free licenses to maintainers of some popular nova packages just to keep ecosystem healthy and avoid issues like this
@lptn My bad, forgot them.
@voidgraphics I added them in the new pr #471
Totally missed the imports ๐ sorry, this is a bit of a mess. I added the imports and tagged yet another new version.
No tests, no licenses, I feel it's like 200X ๐
Thanks a lot guys, it works now! ๐ช
Working with new version. Thank you.
@voidgraphics
I just created 2 PRs to avoid such issues: one with a test, another one with improved PHPDoc (to help Psalm and PHPStan to find issues like this). Can you please review them?
#472
#473
Thanks!
PS: hey @davidhemphill
I have an idea: provide free licenses to maintainers of some popular nova packages just to keep ecosystem healthy and avoid issues like this
We already do! laravel/nova-issues#4911 (comment).