For my user backend app I am trying to use dependency injection. I registered all of its dependencies with registerService()
but I need to create the main class itself so I can register it in app.php, with:
$userBackend = <what do I do here?>;
\OC::$server->getUserManager()->registerBackend($userBackend);
How can I create an instance of this class without first instantiating its parameters which would negate dependency injection?
\OC::$server->query(\OCA\YourApp\YourUserBackend::class)
and replace \OCA\YourApp\YourUserBackend
with the actual full class path
1 Like
This works, thanks! Is there documentation for this, I havenât found?
I found that @nickvergessenâs solution does not always work. I changed the class being instantiated to have an argument that is an abstract class and then Nextcloudâs dependency injection did not work anymore. After 2 hours of looking at the code with a debugger I realized that you should actually start it like that:
\OC::$server->getUserManager()->registerBackend(\OC::$server->query(âUserBackendâ));
So instead of passing the class you pass the name (string) of the service you registered. Then NC actually looks at the registered service and recurses down to its arguments and there you can properly handle the instantiation of the concrete classes.
That sounds like s solvable issue, can you give a link to your repo?
The DI changes are still in a local dev branch, but the current master branch contains everything but the updated Application.php, which looks like this:
<?php
namespace OCA\UserBackendSqlRaw\AppInfo;
use OCA\UserBackendSqlRaw\Config;
use \OCP\AppFramework\App;
use \OCA\UserBackendSqlRaw\UserBackend;
use \OCA\UserBackendSqlRaw\Dbs\Mariadb;
use \OCA\UserBackendSqlRaw\Dbs\Postgresql;
class Application extends App {
public function __construct(array $urlParams = array()) {
parent::__construct('user_backend_sql_raw', $urlParams);
$container = $this->getContainer();
$container->registerService('Logger', function ($c) {
return $c->query('ServerContainer')->getLogger();
});
$container->registerService('NextcloudConfig', function ($c) {
return $c->query('ServerContainer')->getConfig();
});
$container->registerService('AppConfig', function ($c) {
return new Config(
$c->query('Logger'),
$c->query('NextcloudConfig')
);
});
$container->registerService('Database', function ($c) {
if ($c->query('appConfig')->getDbType() === 'mariadb') {
return new Mariadb($c->query('AppConfig'));
} else {
// PostgreSQL is default
return new Postgresql($c->query('AppConfig'));
}
});
$container->registerService('UserBackend', function ($c) {
return new UserBackend(
$c->query('Logger'),
$c->query('AppConfig'),
$c->query('Database')
);
});
\OC::$server->getUserManager()->registerBackend(\OC::$server->query('UserBackend'));
}
}
As you can see I changed it to call the class by its name, i.e. using a string parameter. Db
became an abstract class and the DI code could not instantiate it when I used
\OC::$server->query(\OCA\UserBackendSqlRaw\UserBackend::class)
Replace:
$container->registerService('Database', function ($c) {
With
$container->registerService(\OCA\UserBackendSqlRaw\Db::class, function ($c) {
Then it shouldâą work
Would this be better than my current solution?
Then you should be able to automatically inject everything again and instead of a magic string, you can use your class name again
Hm, can you help me understand why specifying âDatabaseâ as the service name does not work? Shouldnât Container::query('someStringIChose')
just run the closure I registered with registerService('someStringIChose', $closure)
.
Is this a bug or am I missing something?
Something else that might be a bug, but I canât tell because I donât completely understand the code is that when I use âDatabaseâ as the name instead of the specifying the class, as you suggested, the class that needs to instantiated is recognized correctly at first, instantiation fails because it is an abstract class, but then another query method is run against âdbâ (instead of âDbâ). This fails and the exception error message also complains that the class âdbâ is missing, which is of course true, because it should have looked for Db
.
Maybe a screenshot of this state in debug mode helps:
You can see in line 61 getName()
returns db in lowercase.