Model

As our application grows, we soon find out that we need to perform similar database operations in various places and in various presenters, for example, retrieving the latest published posts. If we improve our application, for instance, by adding a flag to posts indicating whether they are drafts, we must also review all places in our application where posts are retrieved from the database and add a where condition to ensure only non-draft posts are selected.

At this point, working directly with the database becomes insufficient, and it will be smarter to use a new method that returns the published posts. And when we later add another condition (for example, not to display posts with a future date), we only edit our code in one place.

We'll place the method into the PostFacade class and call it getPublicArticles().

We'll create our model class PostFacade in the app/Model/ directory to take care of our posts:

<?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');
	}
}

In the class, we request the database Explorer via the constructor. This utilizes the power of the DI container.

We will switch to HomePresenter, which we will modify by getting rid of the dependency on Nette\Database\Explorer and replacing it with a new dependency on our new class.

<?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);
	}
}

In the use section, we have App\Model\PostFacade, so we can shorten the notation in the PHP code to PostFacade. We request this object in the constructor, write it to the $facade property, and use it in the renderDefault method.

The last step is to teach the DI container to produce this object. This is usually done by adding an item to the config/services.neon file in the services section, specifying the full class name and constructor parameters. This registers it, and the object is then called a service. Thanks to the magic of autowiring, we usually don't need to specify the constructor parameters because DI will recognize and pass them automatically. Thus, it would be sufficient to just provide the class name:

...

services:
	- App\Model\PostFacade

However, you don't need to add this line either. In the search section at the beginning of services.neon, it is defined that all classes ending with -Facade or -Factory will be found by DI automatically, which is also the case for PostFacade.

Summary

The PostFacade class asks for Nette\Database\Explorer in its constructor, and since this class is registered in the DI container, the container creates this instance and passes it. DI thus creates an instance of PostFacade for us and passes it in the constructor to the HomePresenter class, which requested it. It's like a Matryoshka doll. :) Everyone just states what they want, and they don't care where or how it gets created. The creation is handled by the DI container.

Here you can read more about dependency injection and configuration.