Nette Documentation Preview

syntax
Model
*****

W miarę rozrastania się naszej aplikacji szybko odkrywamy, że w różnych miejscach i w różnych presenterach musimy wykonywać podobne operacje na bazie danych, na przykład pobierać najnowsze opublikowane wpisy. Jeśli ulepszymy naszą aplikację, na przykład dodając wpisom flagę wskazującą, czy są szkicami, musimy też przejrzeć wszystkie miejsca w aplikacji, w których pobierane są wpisy z bazy, i dodać warunek `where`, żeby wybierane były tylko wpisy niebędące szkicami.

W tym momencie praca bezpośrednio z bazą danych przestaje wystarczać i mądrzej będzie użyć nowej metody zwracającej opublikowane wpisy. A gdy później dodamy kolejny warunek (na przykład żeby nie wyświetlać wpisów z przyszłą datą), zmodyfikujemy nasz kod tylko w jednym miejscu.

Metodę umieścimy w klasie `PostFacade` i nazwiemy ją `getPublicArticles()`.

Naszą klasę modelu `PostFacade` utworzymy w katalogu `app/Model/`, żeby zajęła się naszymi wpisami:

```php .{file:app/Model/PostFacade.php}
<?php
namespace App\Model;

use Nette;

final class PostFacade
{
	public function __construct(
		private Nette\Database\Explorer $database,
	) {
	}

	public function getPublicArticles()
	{
		return $this->database
			->table('posts')
			->where('created_at < ', new \DateTime)
			->order('created_at DESC');
	}
}
```

W klasie prosimy przez konstruktor o bazodanowy [Explorer |api:Nette\Database\Explorer]. Wykorzystuje to moc [kontenera DI |dependency-injection:passing-dependencies].

Przejdziemy do `HomePresenter`, który zmodyfikujemy, pozbywając się zależności od `Nette\Database\Explorer` i zastępując ją nową zależnością od naszej nowej klasy.

```php .{file:app/Presentation/Home/HomePresenter.php}
<?php
namespace App\Presentation\Home;

use App\Model\PostFacade;
use Nette;

final class HomePresenter extends Nette\Application\UI\Presenter
{
	public function __construct(
		private PostFacade $facade,
	) {
	}

	public function renderDefault(): void
	{
		$this->template->posts = $this->facade
			->getPublicArticles()
			->limit(5);
	}
}
```

W sekcji `use` mamy `App\Model\PostFacade`, więc możemy skrócić zapis w kodzie PHP do `PostFacade`. O ten obiekt prosimy w konstruktorze, zapisujemy go do właściwości `$facade` i używamy w metodzie `renderDefault`.

Ostatnim krokiem jest nauczenie kontenera DI, żeby ten obiekt produkował. Robi się to zwykle przez dodanie pozycji do pliku `config/services.neon` w sekcji `services`, podając pełną nazwę klasy i parametry konstruktora. Rejestruje ją to, a obiekt nazywamy potem **usługą**. Dzięki magii [autowiringu |dependency-injection:autowiring] zwykle nie musimy podawać parametrów konstruktora, bo DI rozpozna je i przekaże automatycznie. Wystarczyłoby więc podać tylko nazwę klasy:

```neon .{file:config/services.neon}
...

services:
	- App\Model\PostFacade
```

Tej linii jednak też nie musisz dodawać. W sekcji `search` pliku `services.neon` zdefiniowane jest, że wszystkie klasy kończące się na `-Facade` albo `-Factory` zostaną znalezione przez DI automatycznie, co dotyczy także `PostFacade`.


Podsumowanie
============

Klasa `PostFacade` prosi w swoim konstruktorze o `Nette\Database\Explorer`, a ponieważ klasa ta jest zarejestrowana w kontenerze DI, kontener tworzy tę instancję i ją przekazuje. DI tworzy więc za nas instancję `PostFacade` i przekazuje ją w konstruktorze klasie `HomePresenter`, która o nią poprosiła. To jak matrioszka. :) Każdy tylko mówi, czego chce, i nie obchodzi go, gdzie i jak to powstaje. Tworzeniem zajmuje się kontener DI.

.[note]
Tutaj możesz przeczytać więcej o [dependency injection |dependency-injection:introduction] i [konfiguracji |nette:configuring].

{{priority: -1}}

Model

W miarę rozrastania się naszej aplikacji szybko odkrywamy, że w różnych miejscach i w różnych presenterach musimy wykonywać podobne operacje na bazie danych, na przykład pobierać najnowsze opublikowane wpisy. Jeśli ulepszymy naszą aplikację, na przykład dodając wpisom flagę wskazującą, czy są szkicami, musimy też przejrzeć wszystkie miejsca w aplikacji, w których pobierane są wpisy z bazy, i dodać warunek where, żeby wybierane były tylko wpisy niebędące szkicami.

W tym momencie praca bezpośrednio z bazą danych przestaje wystarczać i mądrzej będzie użyć nowej metody zwracającej opublikowane wpisy. A gdy później dodamy kolejny warunek (na przykład żeby nie wyświetlać wpisów z przyszłą datą), zmodyfikujemy nasz kod tylko w jednym miejscu.

Metodę umieścimy w klasie PostFacade i nazwiemy ją getPublicArticles().

Naszą klasę modelu PostFacade utworzymy w katalogu app/Model/, żeby zajęła się naszymi wpisami:

<?php
namespace App\Model;

use Nette;

final class PostFacade
{
	public function __construct(
		private Nette\Database\Explorer $database,
	) {
	}

	public function getPublicArticles()
	{
		return $this->database
			->table('posts')
			->where('created_at < ', new \DateTime)
			->order('created_at DESC');
	}
}

W klasie prosimy przez konstruktor o bazodanowy Explorer. Wykorzystuje to moc kontenera DI.

Przejdziemy do HomePresenter, który zmodyfikujemy, pozbywając się zależności od Nette\Database\Explorer i zastępując ją nową zależnością od naszej nowej klasy.

<?php
namespace App\Presentation\Home;

use App\Model\PostFacade;
use Nette;

final class HomePresenter extends Nette\Application\UI\Presenter
{
	public function __construct(
		private PostFacade $facade,
	) {
	}

	public function renderDefault(): void
	{
		$this->template->posts = $this->facade
			->getPublicArticles()
			->limit(5);
	}
}

W sekcji use mamy App\Model\PostFacade, więc możemy skrócić zapis w kodzie PHP do PostFacade. O ten obiekt prosimy w konstruktorze, zapisujemy go do właściwości $facade i używamy w metodzie renderDefault.

Ostatnim krokiem jest nauczenie kontenera DI, żeby ten obiekt produkował. Robi się to zwykle przez dodanie pozycji do pliku config/services.neon w sekcji services, podając pełną nazwę klasy i parametry konstruktora. Rejestruje ją to, a obiekt nazywamy potem usługą. Dzięki magii autowiringu zwykle nie musimy podawać parametrów konstruktora, bo DI rozpozna je i przekaże automatycznie. Wystarczyłoby więc podać tylko nazwę klasy:

...

services:
	- App\Model\PostFacade

Tej linii jednak też nie musisz dodawać. W sekcji search pliku services.neon zdefiniowane jest, że wszystkie klasy kończące się na -Facade albo -Factory zostaną znalezione przez DI automatycznie, co dotyczy także PostFacade.

Podsumowanie

Klasa PostFacade prosi w swoim konstruktorze o Nette\Database\Explorer, a ponieważ klasa ta jest zarejestrowana w kontenerze DI, kontener tworzy tę instancję i ją przekazuje. DI tworzy więc za nas instancję PostFacade i przekazuje ją w konstruktorze klasie HomePresenter, która o nią poprosiła. To jak matrioszka. :) Każdy tylko mówi, czego chce, i nie obchodzi go, gdzie i jak to powstaje. Tworzeniem zajmuje się kontener DI.

Tutaj możesz przeczytać więcej o dependency injectionkonfiguracji.