Confusion in DI variables
- caught-in-a-nette
- Member | 12
I have been building this app for several weeks. It has been functioning
well. I have been testing it on my iPad frequently.
Recently it stopped working on the iPad – Server Error 500.
Log/exception.log says: TypeError:
App\UI\Dashboard\DashboardPresenter::__construct(): Argument #2
($settingsFacade) must be of type App\Model\SettingsFacade,
App\Forms\AddWorkoutForm given, called in
/Users/.me./Projects/nette/workout3/temp/cache/nette.configurator/Container_bf083af21a.php
on line 276 in
/Users/.me./Projects/nette/workout3/app/UI/Dashboard/DashboardPresenter.php:30
I have a few presenters (like DashboardPresenter) that extend my
BasePresenter. Each has: use App\Model\SettingsFacade; then in
__construct I have a private variable to reference the facade
(private readonly SettingsFacade $settingsFacade,
). (and some other
references.). Under that reference I have a separate reference to
AddWorkoutForm (private AddWorkoutForm $addWorkoutForm,
).
(BTW in services.neon I have added *Form in search > classes (along with
*Factory & *Facade.))
I tried renaming the references, recording them and adding readonly; also within the AddWorkoutForm class I renamed its methods to not contain ‘Form’. No difference – same error! the only thing that removed the error was to comment-out the reference, but it is needed.
(Also I get a similar error for other presenters…)
Why are these references suddenly being confused? Am I missing a vital thing?
- Marek Bartoš
- Nette Blogger | 1261
My guess is your app is in production mode (no Tracy bar visible), you've added new dependency to the DashboardPresenter constructor and didn't delete cache. Cached DI container is not automatically reloaded in production mode and you have to delete it when deploying your app to generate a new one.
Also when you forget to delete cache, while the error will show up in production mode, testing in debug mode will not cause the same error at all because the DIC for both modes is generated separately.
Nette DI resolves all references when DIC is compiled and therefore most
errors will occur immediatelly in debug mode and right after deleting the cache
folder (/temp/cache/nette.configurator
) in production mode.
Last edited by Marek Bartoš (2024-10-25 21:27)
- caught-in-a-nette
- Member | 12
Hi
I forgot to mention the app works fine in Safari (and Chrome) on my desktop and only fails on iPad. (The desktop shows tracy bar and shows a red banner with all other errors. So it must be in Dev. mode; Yet my iPad never does and thus Prod. mode. Side question do you know how to specify whole network [ie. 192.168.1.0/24] in Bootstrap Configurator setDebugMode? ATM I have array of different hostnames.)
This is explained “… the DIC for both modes is generated separately.”. But I knew that, but it not at the front-of-my-brain.
Anyway I frequently do rm -rf temp/cache
and yet the error
persists.
Moreover I put those DI references in ages ago…
So do you think there could be an (non explicit) error in the AddWorkoutForm class that could be causing the (above) exception that I see?
- Marek Bartoš
- Nette Blogger | 1261
Perhaps caching in the browser might be a problem?
Or opcode cache? You can flush it via http call to a script calling
opcache_reset();
. Console usually has separate (and disabled)
opcache
There is no reason why different dependencies would be pased by Nette while you are on another device. You can check variables that are used to generate unique DIC. And even if it was the case, (flushing opcache and) deleting generated DIC would help.
Side question do you know how to specify whole network [ie. 192.168.1.0/24] in Bootstrap Configurator setDebugMode?
Current implementation of IP address detection does not support that. But
setDebugMode() accepts boolean, so you can use practically anything for
detection and just pass true
to enable the debug mode (of false to
disable). I've implemented various methods for that (here),
but the simplest one would be to set environment variable in your local
webserver and console configuration and check if the variable exists and is set
to 1/true/whatever
So do you think there could be an (non explicit) error in the AddWorkoutForm class that could be causing the (above) exception that I see?
Nothing else that I am aware of. If the previous steps don't help, perhaps sharing bootstrap file and neon configuration would
Last edited by Marek Bartoš (2024-10-25 23:23)
- caught-in-a-nette
- Member | 12
OK Thanks I (first got server error when I un-commented that error-causing
reference, then) added `
php opcache_reset();`
inside
DashboardPresenter::__construct. Refreshed the page and it
showed OK.
Also thanks for the explanation of setDebugMode(). I will experiment with
similar options.
So in summary you have been very helpful!
- caught-in-a-nette
- Member | 12
Actually can I ask a related question?
I want BasePresenter to get the settings via SettingsFacade, like how DashboardPresenter does. But when I try and set $this->template… (along with use and DI ref.) it won't show. Why?
- caught-in-a-nette
- Member | 12
More information:
BasePresenter.php:
<?php
use App\Model\SettingsFacade;
abstract class BasePresenter extends Presenter
{
public function __construct(
private readonly SettingsFacade $settingsFacade,
)
{
}
public function renderDefault(): void
{
$this->template->settings = $this->settingsFacade->getAll();
}
}
?>
gives error:
Typed property App\BasePresenter::$settingsFacade must not be accessed
before initialization
- Marek Bartoš
- Nette Blogger | 1261
added
php opcache_reset();
inside DashboardPresenter::__construct. Refreshed the page and it showed OK.
Don't do that on a regular page. It will reset cache of all php files available (that is all php files on the server, in the simplest setup) and slow down application significantly.
Instead configure the opcache on your local environment to refresh automatically. And call it on a separate page during deploy to server. If you already have deploy automated, this is my automated version (syntax is specific to Makefile):
@echo "Reset OPCache"
@RAND_STR=$$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 16 | head -n 1); \
echo "<?php opcache_reset();" > "opcache-reset-$${RAND_STR}.php"; \
wget --no-cache --spider https://$(PROJECT_URL)/opcache-reset-$${RAND_STR}.php; \
rm -f opcache-reset-$${RAND_STR}.php
I want BasePresenter to get the settings via SettingsFacade, like how DashboardPresenter does. But when I try and set $this->template… (along with use and DI ref.) it won't show. Why?
Hard to say, I don't have what “does not show” means. Do you have the dependency injected? In which BasePresenter method do you access $this->template?
Last edited by Marek Bartoš (2024-10-26 00:22)
- Marek Bartoš
- Nette Blogger | 1261
Typed property App\BasePresenter::$settingsFacade must not be accessed before initialization
That's PHP behavior. You've overriden constructor of BasePresenter in DashboardPresenter. That means you have to call parent::__construct() in constructor of DashboardPresenter. Otherwise, constructor of BasePresenter is never called and variable is therefore never initialized.
In Nette, base presenters can easily get to have quite a lot of dependencies. Instead of constantly modifying child constructors, you can use inject feature in the BasePresenter and keep the children simpler.
- caught-in-a-nette
- Member | 12
I am a dummy – I should've done that!
Thanks all good now.
Just gotta pass $settingsFacade from DashboardPresenter back
to parent constructor