DB connection v modelu – jak na to?

Mára98
Člen | 47
+
0
-

Dobrý den,

vytvořil jsem si v modelu třídu Book, která ve svém konstruktoru bere ID, na základě kterého se inicializuje vnitřní stav instance této knihy daty z databáze:

class Book
{

	...

	public function __construct($id)
    {
        // Získá knihu na základě ID
		$book = $this->database->table('BOOKS')
							   ->where('id = ?', $id)
							   ->fetch();

        // Inicializuje vnitřní stav objektu
        $this->id = $id;
        $this->name = $book->name;
        $this->description = $book->description;
    }

	...

}

Problém je v tom, že netuším, jak si vyžádat databázové spojení v modelu. V presenteru pak třídu používám nějak takto:

final class BooksPresenter extends BasePresenter
{
	public function renderDefault($id)
	{
		// Získá knihu na základě předaného ID
		$book = new Book($id);

		// Předá hodnoty do view
		$this->template->book = $book;
	}
}

Dočetl jsem se, že svou třídu musím do configu uvést jako službu a pak si jí vyžádat. Absolutně ale nepobírám, jak na to. Předem děkuji za jakoukoliv pomoc.

nightfish
Člen | 472
+
+9
-

Ve třídě Book mícháš dohromady dvě věci – samotná data a jejich získávání z databáze. Lepší je tuto funkčnost rozdělit do více tříd.

Třída Book bude jen přepravka na data.

namespace App\Model;

final class Book {
  private $id;
  private $name;
  private $description;

  public function __construct(int $id, string $name, string $description) {
    $this->id = $id;
    $this->name = $name;
    $this->description = $description;
  }
  // + gettery, aby ses k datum dostal v šabloně
}

Dále pak vytvoříš třídu, která má na starosti načtení knihy z databáze podle ID.

namespace App\Model;

final class BookRepository {
  private $database;

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

  public function get(int $id): Book {
    // Získá knihu na základě ID
    $row = $this->database->table('BOOKS')
                               ->where('id = ?', $id)
                               ->fetch();
    return new Book($row->id, $row->name, $row->description);
  }
}

Tuto třídu zaregistruješ do config.neon jako službu:

services:
  bookRepository:
    class: App\Model\BookRepository

Následně si v presenteru vyžádáš instanci BookRepository:

final class BooksPresenter extends BasePresenter
{
    /** @var \App\Model\BookDepository */
    private $bookRepository;

    public function __construct(\App\Model\BookRepository $bookRepository) {
        $this->bookRepository = $bookRepository;
    }

    public function renderDefault(int $id)
    {
        // Získá knihu na základě předaného ID
        $book = $this->bookRepository->get($id);

        // Předá hodnoty do view
        $this->template->book = $book;
    }
}
Mára98
Člen | 47
+
+1
-

Děkuji mnohokrát za odpověď a ukázky kódu, hodně mi to pomohlo! :)

Mára98
Člen | 47
+
0
-

Pokusil jsem se to naprogramovat, ale hlásí mi to Call to undefined method Nette\Database\Connection::table(). Netušíte, co je špatně? Jsem už opravdu zoufalý.

config.neon:

#
# WARNING: it is CRITICAL that this file & directory are NOT accessible directly via a web browser!
# https://nette.org/en/security-warning
#
parameters:


application:
	errorPresenter: Error
	mapping:
		*: App\*Module\Presenters\*Presenter


session:
	expiration: 14 days


database:
	dsn: '---'
	user: ---
	password: ---
	options:
		lazy: yes


services:
	- App\Model\UserManager
	router: App\RouterFactory::createRouter
	bookRepository:
		class: App\Repository\BookRepository

Book.php:

<?php

// Namespace
namespace App\Model;

/**
* Book je třída sloužící k ukládání informací o
* učebnicích.
*/
class Book
{
    // ID učebnice
    public $id;

    // Název učebnice
    public $name;

    // Popis učebnice
    public $description;

    /**
    * Konstruktor učebnice.
    */
    public function __construct($id, $name, $description)
    {
        // Inicializuje vnitřní stav objektu
        $this->id = $id;
        $this->name = $name;
        $this->description = $description;
    }
}

BookRepository.php:

<?php

// Namespace
namespace App\Repository;

// Usingy
use App\Model;

/**
* BookRepository slouží k načtení knih z databáze
*/
class BookRepository
{
    // Databázové spojení
    private $database;

    /**
    * Konstruktor repositáře.
    */
    public function __construct(\Nette\Database\Connection $database)
    {
        $this->database = $database;
    }

    /**
    * Získá učebnici s předaným ID
    * @param int $id ID učebnice
    * @return Book Kniha s daným ID
    */
    public function get(int $id) : Book
    {
        // Získá knihu na základě ID
        $row = $this->database->table('BOOKS')
                              ->where('id = ?', $id)
                              ->fetch();

        // Vrátíme novou knihu
        return new Book($row->id, $row->name, $row->description);
    }
}

BooksPresenter.php:

<?php

// Namespace
namespace App\Presenters;

// Usingy
use App\Model\Book;

/**
* BooksPresenter je třída zajišťující správné zobrazení
* obsahu knih
*/
final class BooksPresenter extends BasePresenter
{
    private $bookRepository;

   	public function __construct(\App\Repository\BookRepository $bookRepository) {
		$this->bookRepository = $bookRepository;
   	}

	/**
	* Defaultní render metoda.
	* @param int $id ID knihy, kterou budeme zobrazovat
	*/
	public function renderDefault($id)
	{
		// Získá knihu na základě předaného ID
		$book = $this->bookRepository->get($id);

		// Předá hodnoty do view
		$this->template->book = $book;
	}
}

Editoval Mára98 (19. 7. 2019 12:52)

Šaman
Člen | 2635
+
+3
-

Ty si v konstruktoru žádáš Nette\Database\Connection, ta opravdu žádnou table nezná. Nad connection se použivá prosté ->query(). Ty potřebuješ Nette\Database\Context, což je takové rozšíření.

Dokumentace:
Connection: https://doc.nette.org/…atabase/core
Context: https://doc.nette.org/…ase/explorer

Editoval Šaman (19. 7. 2019 13:23)

Mára98
Člen | 47
+
0
-

Moc Vám oboum děkuji, už to běží jak má!