Nette Documentation Preview

syntax
Model
*****

W miarę jak aplikacja rośnie, szybko odkryjemy, że w różnych miejscach, w różnych presenterach, potrzebujemy wykonywać podobne operacje na bazie danych. Na przykład pobierać najnowsze opublikowane posty. Kiedy ulepszymy aplikację, na przykład dodając do postów flagę oznaczającą, czy są w trakcie pisania, musimy przejrzeć wszystkie miejsca w aplikacji, gdzie posty są pobierane z bazy danych i dodać warunek where, aby wybierać tylko posty niebędące w trakcie pisania.

W tym momencie bezpośrednia praca z bazą danych staje się niewystarczająca i wygodniej będzie wspomóc się nową funkcją, która będzie nam zwracać opublikowane posty. A kiedy później dodamy kolejny warunek, na przykład że nie mają być wyświetlane posty z przyszłą datą, zmodyfikujemy kod tylko w jednym miejscu.

Funkcję umieścimy na przykład w klasie `PostFacade` i nazwiemy ją `getPublicArticles()`.

W katalogu `app/Model/` stworzymy naszą klasę modelową `PostFacade`, która będzie zajmować się postami:

```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 za pomocą konstruktora poprosimy o przekazanie [Nette\Database\Explorer|api:Nette\Database\Explorer]. Wykorzystamy w ten sposób siłę [kontenera DI|dependency-injection:passing-dependencies].

Przełączymy się na `HomePresenter`, który zmodyfikujemy tak, że pozbędziemy się zależności od `Nette\Database\Explorer` i zastąpimy 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 poprosimy w konstruktorze, zapiszemy go we właściwości `$facade` i użyjemy w metodzie renderDefault.

Pozostaje ostatni krok, czyli nauczenie kontenera DI tworzenia tego obiektu. Zwykle robi się to tak, że do pliku `config/services.neon` w sekcji `services` dodajemy myślnik, podajemy pełną nazwę klasy i parametry konstruktora. W ten sposób tak zwaną ją zarejestrujemy, a obiekt nazywa się wtedy **usługą**. Dzięki magii zwanej [autowiring |dependency-injection:autowiring] zazwyczaj nie musimy podawać parametrów konstruktora, ponieważ DI samo je rozpozna i przekaże. Wystarczyłoby więc podać tylko nazwę klasy:

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

services:
	- App\Model\PostFacade
```

Jednak nawet tej linii nie musisz dodawać. W sekcji `search` na początku `services.neon` zdefiniowano, że wszystkie klasy kończące się słowem `-Facade` lub `-Factory` DI wyszuka samo, co jest również przypadkiem `PostFacade`.


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

Klasa `PostFacade` w konstruktorze prosi o przekazanie `Nette\Database\Explorer` i ponieważ ta klasa jest zarejestrowana w kontenerze DI, kontener tworzy tę instancję i przekazuje ją. DI w ten sposób tworzy dla nas instancję `PostFacade` i przekazuje ją w konstruktorze klasie HomePresenter, która o nią poprosiła. Taka matrioszka. :) Wszyscy tylko mówią, czego chcą i nie interesują się tym, gdzie co i jak się tworzy. O tworzenie dba kontener DI.

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

{{priority: -1}}
{{sitename: Nette Quickstart}}

Model

W miarę jak aplikacja rośnie, szybko odkryjemy, że w różnych miejscach, w różnych presenterach, potrzebujemy wykonywać podobne operacje na bazie danych. Na przykład pobierać najnowsze opublikowane posty. Kiedy ulepszymy aplikację, na przykład dodając do postów flagę oznaczającą, czy są w trakcie pisania, musimy przejrzeć wszystkie miejsca w aplikacji, gdzie posty są pobierane z bazy danych i dodać warunek where, aby wybierać tylko posty niebędące w trakcie pisania.

W tym momencie bezpośrednia praca z bazą danych staje się niewystarczająca i wygodniej będzie wspomóc się nową funkcją, która będzie nam zwracać opublikowane posty. A kiedy później dodamy kolejny warunek, na przykład że nie mają być wyświetlane posty z przyszłą datą, zmodyfikujemy kod tylko w jednym miejscu.

Funkcję umieścimy na przykład w klasie PostFacade i nazwiemy ją getPublicArticles().

W katalogu app/Model/ stworzymy naszą klasę modelową PostFacade, która będzie zajmować się postami:

<?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 za pomocą konstruktora poprosimy o przekazanie Nette\Database\Explorer. Wykorzystamy w ten sposób siłę kontenera DI.

Przełączymy się na HomePresenter, który zmodyfikujemy tak, że pozbędziemy się zależności od Nette\Database\Explorer i zastąpimy 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 poprosimy w konstruktorze, zapiszemy go we właściwości $facade i użyjemy w metodzie renderDefault.

Pozostaje ostatni krok, czyli nauczenie kontenera DI tworzenia tego obiektu. Zwykle robi się to tak, że do pliku config/services.neon w sekcji services dodajemy myślnik, podajemy pełną nazwę klasy i parametry konstruktora. W ten sposób tak zwaną ją zarejestrujemy, a obiekt nazywa się wtedy usługą. Dzięki magii zwanej autowiring zazwyczaj nie musimy podawać parametrów konstruktora, ponieważ DI samo je rozpozna i przekaże. Wystarczyłoby więc podać tylko nazwę klasy:

...

services:
	- App\Model\PostFacade

Jednak nawet tej linii nie musisz dodawać. W sekcji search na początku services.neon zdefiniowano, że wszystkie klasy kończące się słowem -Facade lub -Factory DI wyszuka samo, co jest również przypadkiem PostFacade.

Podsumowanie

Klasa PostFacade w konstruktorze prosi o przekazanie Nette\Database\Explorer i ponieważ ta klasa jest zarejestrowana w kontenerze DI, kontener tworzy tę instancję i przekazuje ją. DI w ten sposób tworzy dla nas instancję PostFacade i przekazuje ją w konstruktorze klasie HomePresenter, która o nią poprosiła. Taka matrioszka. :) Wszyscy tylko mówią, czego chcą i nie interesują się tym, gdzie co i jak się tworzy. O tworzenie dba kontener DI.

Tutaj możesz przeczytać więcej o wstrzykiwaniu zależnościkonfiguracji.