Error Call to a member function table() on null

RVS
Člen | 8
+
0
-

Poradí někdo jak na tuto chybu: Error Call to a member function table() on null

RVS
Člen | 8
+
0
-

BasePresenter.php

<?php
namespace App\Presenters;

use Nette;
use App\Components\PollControl;
use Nette\Database\Context;

abstract class BasePresenter extends Nette\Application\UI\Presenter
{   /**
     * @var Context
     * @inject
     */
    private $database;

	public function __construct(Nette\Database\Context $database)
    {
        $this->database = $database;
    }


    /**
     * Společná inicializace pro všechny presentery
     */
    protected function startup()
    {
        parent::startup();

        // Zde můžete používat $this->database pro práci s databází

		$knihy = $this->database->table('books')->get(3);
        return $knihy->title;
    }

	  protected function createComponentPoll(): PollControl
	{


		$userId = $this->getUser()->getId(); // Získání ID přihlášeného uživatele
       // $itemList = new ItemListControl($userId);

		$poll = new PollControl();
		$poll->spoj($userId);
		$poll->items = $this->item;
		return $poll;
	}

    public function beforeRender()
    {
        parent::beforeRender();
        $this->setLayout('layout');   // nastavení layoutu @layout.latte pro všechny finální presentery
    }
}
Pavel Kravčík
Člen | 1182
+
0
-

Takhle ne, málo informací. Pravděpodobně máš špatně načtenou DB přes DI.

  1. https://doc.nette.org/…dependencies
  2. https://doc.nette.org/…onfiguration
  3. Cache vymazat
RVS
Člen | 8
+
0
-

když v kódu odstraním :

$knihy = $this->database->table('books')->get(3);
return $knihy->title;

tak vše funguje včetně podpresenteru

Pavel Kravčík
Člen | 1182
+
0
-

Chyba Ti říká, že $this->database je null. Takže to špatně voláš a zajímá můj první bod, pravděpodobně máš model a nemáš správně napsaný constructor.

Pavel Kravčík
Člen | 1182
+
0
-

Aha v mezidobí přibyl příspěvek, který jsem neviděl. Startup by neměl mít ten return. Co chceš s $knihy dělat? Pokud vypsat ve všech šablonách tak to dej do beforeRender() jako $this->template->knihy = ....

V presenteru constructor nepoužívej, měla by stačit ta inject anotace (v novnějším nette #[Inject]).

RVS
Člen | 8
+
0
-

To jen tak testuju. Původní záměr je že componenta Pollcontrol vypisuje text do hlavní šablony (postranní panel)což je ok. Chtěl jsem ještě aby se načítala nějaká data s DB podle ID přihlášeného uživatele.

nightfish
Člen | 474
+
+1
-

@RVS Má některý z presenterů, které dědí od BasePresenteru, vlastní metodu __construct(), která však nevolá parent::__construct()? Pokud ano, tak to je příčina chyby.
Jak už psal Pavel, BasePresentery obvykle mívají závislosti předávané ne konstruktorem, nýbrž přes atribut #[\Nette\DI\Attributes\Inject] nebo anotaci @inject (property musí být public). Tím se vyhneš nutnosti v potomcích, kteří přes konstruktor získávají své závislosti, předávat závislosti i do konstruktoru BasePresenteru.

m.brecher
Generous Backer | 763
+
+2
-

@RVS

V dobře architektonicky postaveném kódu se snadno najdou jakékoliv chyby. Podobných chybám předejdeš, když budeš přehledně a logicky strukturovat kód. Nepoužívej starou syntaxi jak php tak i Nette, nastartuj na tom nejnovějším co je nyní k dispozici – tj. PHP 8.2 a poslední verze Nette. Na internetu je bohužel spousta starých příkladů Nette zdrojových kódů, které jsou už zastaralé – i když je to třeba pár let. Co vidím, že není v Tvém kódu „ideálně“:

  • Nette aplikace by měly používat MVC členění a $database by se do presenteru neměla předávat, ale modelová třída, která zapouzdří sql dotazy související s aktuálně obsluhovanou entitou – zde ‚books‘ např. BookModel
  • tabulka ‚books‘ by měla být řízena finálním presenterem BookPresenter, BasePresenter by přímo žádnou entitu řídit neměl
  • BookModel by se měl předat přímo do finálního presenteru BookPresenter
  • do BasePresenteru je vhodné předávat závislosti, které se využijí ve všech odvozených finálních presenterech, typicky nějaké komponenty pro layout – třeba menu, které sosá data z databáze. Do BasePresenteru se závislosti NIKDY nepředávají konstruktorem (i když to složitě jde), ale moderním způsobem pomocí atributu #[Inject], anotace @inject je zastaralá, metoda inject*() zbytečně komplikovaná.
  • ve finálních presenterech je naopak MAXIMÁLNĚ VHODNÉ předávat závislosti konstruktorem, aniž by se volal parent konstruktor (to je zbytečné) a doporučuji používat jednoduchou moderní syntaxi PHP 8.0 pro předání parametrů konstruktorem.

Doporučuji rozdělit Tvůj kód do dvou presenterů – abstraktního a finálního a sql dotazy zapouzdřit nekompromisně všechny do modelových tříd. Zde je příklad logicky strukturovaného kódu (samozřejmě jsou i jiné cesty):

abstract class BasePresenter extends UI\Presenter
{
    #[Inject]						  // syntaxe PHP 8.0
    public App\MenuModel $menuModel;  // musí být public !!

    public function beforeRender()
    {
        parent::beforeRender();
        $this->setLayout('layout');   // layoutu @layout.latte
        $this->template->menuItems = $this->menuModel->getItems()   // model pro menu předá data pro menu do layoutu
    }
}
final class BookPresenter extends BasePresenter
{
    public function __construct(private App\BookModel $bookModel)  // moderní syntaxe PHP 8.0
    {}

    public function renderDefault()   // konkrétní entitu řídíme v akci finálního presenteru
    {
        $this->template->books = $this->bookModel->getAll()  // modelová třída předá data pro vykreslení knih
    }
}

Nástin modelových tříd:

final class BookModel
{
    public function __construct(private \Nette\Database\Explorer $database)  // poslední verze Nette databázové knihovny
    {}

	public function getAll(): Selection
    {
        return $this->database->table('books')->where(.......)->order(.......);
    }

    // .....

}
final class MenuModel
{
    public function __construct(private \Nette\Database\Explorer $database)  // poslední verze Nette databázové knihovny
    {}

	public function getItems(): Selection
    {
        return $this->database->table('main_menu')->order('sort');
    }
}

šablona akce:

    {foreach $books as $book}
      <tr>
          <td>{$book->title}</td>
          {* ..... *}
      </tr>
    {/foreach}

Závěr:

Výchozí předání závislostí je konstruktorem s využitím moderní syntaxe – finální presentery, modelové třídy, komponenty. Výjimkou jsou abstraktní presentery, kde je ideální předávat závislosti pomocí atributu #[Inject].

Databázi používej prostřednictvím modelových tříd, Nepoužívej starou verzi databáze Nette\Database\Context, ale moderní Nette\Database\Explorer.

Konkrétní entitu (tabulku) řiď z odpovídajícího finálního presenteru v konkrétní akci. Abstraktní presentery naopak akce nemají, což je v pořádku, protože řídí data a komponenty pro „všechny akce všech finálních presenterů“, tam se používá startup() nebo beforeRender().

Sice se Ti teď může zdát strukturovaný postup komplikovanější, ale časem uvidíš, že se to vyplatí.

Editoval m.brecher (20. 12. 2023 4:06)

RVS
Člen | 8
+
0
-

@mbrecher

Díky za pomoc a vysvětlení. Dost mě to pomohlo.

Editoval RVS (21. 12. 2023 23:59)