App-Development broken: «Could Not Resolve»-Error - Variable name mixed with class?

I follow the tutorial to write my own nextcloud app. First of all, everything used to work once in owncloud, before I tried to migrate to nextcloud. What I see is, that the tutorial is completely broken and not even the tutorial app does work!

Here is my current problem:

<?php
namespace OCA\GgrWinti\Controller;

use Exception;

use OCP\IRequest;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\DataResponse;

use OCA\GgrWinti\Db\Geschaeft;
use OCA\GgrWinti\Db\GeschaeftMapper;

class GeschaeftController extends Controller {

  private $mapper;
  private $userId;
  
  public function __construct($AppName, IRequest $request, GeschaeftMapper $hereismapper, $UserId){
    parent::__construct($AppName, $request);
    $this->mapper = $hereismapper;
    $this->userId = $UserId;
  }
  [...]

Results in:


Interner Serverfehler

Der Server hat einen internen Fehler und konnte Ihre Anfrage nicht vervollständigen.

Bitte wende dich an den Serveradministrator, falls dieser Fehler mehrfach auftritt, und füge deiner Anfrage die untenstehenden technischen Details bei.

Weitere Details können im Serverprotokoll gefunden werden.

Technische Details

    Entfernte Adresse: 172.21.42.1
    Anforderungskennung: EMaOtyA3EVgcq8ODr9oq
    Typ: OCP\AppFramework\QueryException
    Code: 0
    Nachricht: Could not resolve hereismapper! Class hereismapper does not exist
    Datei: /var/www/nextcloud/lib/private/AppFramework/Utility/SimpleContainer.php
    Zeile: 102


Spur

#0 /var/www/nextcloud/lib/private/AppFramework/Utility/SimpleContainer.php(117): OC\AppFramework\Utility\SimpleContainer->resolve('hereismapper')
#1 /var/www/nextcloud/lib/private/AppFramework/DependencyInjection/DIContainer.php(544): OC\AppFramework\Utility\SimpleContainer->query('hereismapper')
#2 /var/www/nextcloud/lib/private/AppFramework/Utility/SimpleContainer.php(73): OC\AppFramework\DependencyInjection\DIContainer->query('hereismapper')
#3 /var/www/nextcloud/lib/private/AppFramework/Utility/SimpleContainer.php(96): OC\AppFramework\Utility\SimpleContainer->buildClass(Object(ReflectionClass))
#4 /var/www/nextcloud/lib/private/AppFramework/Utility/SimpleContainer.php(117): OC\AppFramework\Utility\SimpleContainer->resolve('OCA\\GgrWinti\\Co...')
#5 /var/www/nextcloud/lib/private/AppFramework/DependencyInjection/DIContainer.php(544): OC\AppFramework\Utility\SimpleContainer->query('OCA\\GgrWinti\\Co...')
#6 /var/www/nextcloud/lib/private/AppFramework/App.php(101): OC\AppFramework\DependencyInjection\DIContainer->query('OCA\\GgrWinti\\Co...')
#7 /var/www/nextcloud/lib/private/AppFramework/Routing/RouteActionHandler.php(47): OC\AppFramework\App::main('OCA\\GgrWinti\\Co...', 'index', Object(OC\AppFramework\DependencyInjection\DIContainer), Array)
#8 [internal function]: OC\AppFramework\Routing\RouteActionHandler->__invoke(Array)
#9 /var/www/nextcloud/lib/private/Route/Router.php(299): call_user_func(Object(OC\AppFramework\Routing\RouteActionHandler), Array)
#10 /var/www/nextcloud/lib/base.php(1010): OC\Route\Router->match('/apps/ggrwinti/...')
#11 /var/www/nextcloud/index.php(40): OC::handleRequest()
#12 {main}

It wants to resolve the Variable-Name! That’s crazy.

I even downgraded from NC12 to NC11 because of known bugs in NC12.

Source code is at: https://github.com/mwaeckerlin/ggrwinti
Execute ./bootstrap.sh -a to build, then copy directory html to apps/ggrwinti.

I have docker container, so you can easily test my app:

docker run -d --name ggrwinti-mysql -e MYSQL_DATABASE=nextcloud -e MYSQL_USER=nextcloud -e MYSQL_PASSWORD=ert456 -e MYSQL_RANDOM_ROOT_PASSWORD=1 mysql
docker run -d --name ggrwinti -p 9000:80 -e ADMIN_PWD=ert456 --link ggrwinti-mysql:mysql mwaeckerlin/ggrwinti
docker logs -f ggrwinti

As soon as apache started, head your browser to http://localhost:9000 and login with admin and ert456. Click on one of the two (why two?) GGR-Winti apps, then click on «Geschäfte» in the navigation.

→ You see the problem.

Until new docker build is ready (with changed navigation), you have do enter the URL directly into the browser to test:

http://localhost:9000/index.php/apps/ggrwinti/geschaefte

Is it possible to test your app without the whole docker thing ? like if I just copy the content of the html/ folder in my own nextcloud installation ?

Yes, if you call ./bootstrap.sh -a or use this tar:
https://dev.marc.waeckerlin.org/owncloud/s/FHD4MaQx84XGOCI

(All that ./bootstrap.sh -a does, is to crate html/appinfo/info.xml from html/appinfo/info.xml.in using autoconf)

@Cult, did you succeed or do you need something more?

You could move or link the html subdir to /var/www/nextcloud/apps/ggrwinti and add the following simplified appinfo/info.xml:

<?xml version="1.0"?>
<info xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance"
      xsi:noNamespaceSchemaLocation="https://apps.nextcloud.com/schema/apps/info.xsd">
    <id>ggrwinti</id>
    <name>GGR-Winti</name>
    <summary>NextCloud App für die Fraktionsarbeit im Winterthurer Gemeinderat</summary>
    <description><![CDATA[... some text ...]]></description>
    <licence>agpl</licence>
    <author mail="fisrtname@lastname.org" homepage="https://marc.wäckerlin.ch">Marc Wäckerlin</author>
    <version>0.1.38</version>
    <namespace>GgrWinti</namespace>
    <category>organization</category>
    <bugs>https://github.com/mwaeckerlin/ggrwinti</bugs>
    <dependencies>
        <nextcloud min-version="9"/>
    </dependencies>
    <navigations>
        <navigation>
            <name>GGR-Winti</name>
            <route>ggrwinti.page.index</route>
        </navigation>
    </navigations>
</info>

Yes, I also managed to avoid the double navigation icon :slight_smile:

@mrw: I am not a huge fan of automatic DI, do you see any issue if I am defining your Containers manually ?

@Cult I don’t understand. What is the problem / question? What is «automated DI»?

If you mean to build the docker containers on your own? Yes, they’re all in my docker hub and github account:
https://hub.docker.com/u/mwaeckerlin/


But they are built on top of other of my containers: mwaeckerlin/ggrwinti inherits from mwaeckerlin/nextcloud inherits from mwaeckerlin/ubuntu-base inherits from ubnuntu.

But you can simply copy the files from html to ggrwinti if you add the appinfo/info.xml above.

No, I am talking about the app itself.

This is how I define my Services, Controllers, etc manually:

But, I know you can make this automatically:

I can help you with the first setup, but I never user the second one.

@Cult I am sorry, I have absolutely no idea about this topic, I am a noop, just following the tutorial and changing it, so that it fulfills my needs.

As you see, I have no Application.php file at all and I have no idea, what this file should do. The file is not even mentioned in the tutorial.

Could you help me?

As far as I see, the tutorial is completely broken. It fails at least when you add a Mapper class to the Controller’s constructor — even if you do it exactly according to the tutorial.

If I change the lines from:

  public function __construct($AppName, IRequest $request, GeschaeftMapper $hereismapper, $UserId){
    parent::__construct($AppName, $request);
    $this->mapper = $hereismapper;
    $this->userId = $UserId;
  }

to:

  public function __construct($AppName, IRequest $request, $UserId){
    parent::__construct($AppName, $request);
    $this->mapper = null;
    $this->userId = $UserId;
  }

Then, well it does not work, but it gives me a different error message:

Zugriff verboten
CSRF check failed

So it seems, it’s all about adding a Mapper to a Controller, which does not work in Nextcloud (and used to work in Owncloud).

Can you give my github account (daita) the possibility to create a branch on your github ?


you need to add this:

* @NoCSRFRequired

before the index() method in the GeschaeftController class. Because you open a new page (you do not call the method from a script, it is a link you open

)

1 Like

That could solve the last problem, but not the initial problem. I invited you to collaborate in git.

thanks,

just created a new branch with few edit on your code, tell me if it does what you were expecting and if so, compare with your master to understand what my changes.

(i will afk from now on ;p)

1 Like

The issue is that you forgot to add the mapper type hint

This is interesting and will help me switch to automatic call on the services

The container tries to resolve things using strings (e.g. registerService(‘thisString’, …). If it encounters a type hint though it tries to resolve things by looking for the fully qualified class string (e.g. registerService(My::class, …). Because it’s easy to take care of things for classes recursively it can be done automatically for you.

Thank you, @cult, so far it seems to work, but I get the data as json; what do I need to do to get it in the html-table from html/templates/content/index.php?

→ I got it:

	public function index() {
		return new TemplateResponse('ggrwinti', 'index', array('data' => $this->geschaeftMapper->findAll($this->userId)));
	}

Btw, to elaborate: In the beginning I’ve built everything on top of pure Pimple (https://pimple.symfony.com/) which requires you to add everything manually. This is easier to understand but in the long run it was too brittle: it was too easy to make a type and not discover it or change a class but forget to adjust the container.

Since PHP isn’t statically typed (runtime type checking: the worst of both worlds) it’s kinda hard to solve this issue so we just went with: the less code you write, the less mistakes you make.

TL;DR: automatic injection was built to make your app more robust