Model

As our application grows, we soon find out that we need to perform similar database operations in various locations and in various presenters, for example acquiring the newest published articles. If we improve our application by adding a flag to articles to indicate a work-in-progress state, we must also go through all locations in our application and add a where clause to make sure only finished articles are selected.

At this point, direct work with the database becomes insufficient and it will be smarter to help ourselves with a new function that returns published articles. And when we add another clause later (for example not to display articles with a future date), we only edit our code in one place.

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

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

<?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 pass the database Explorer. This will take advantage of the power of DI container.

We will switch to HomePresenter which we will edit so that we get rid of the dependency on Nette\Database\Explorer replacing that with a new dependency on our new class.

<?php
namespace App\Presenters;

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 are using App\Model\PostFacade, so we can shorten 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 left is to teach the DI container to produce this object. This is usually done by adding a bullet point to the config/services.neon file in the services section, giving the full class name and constructor parameters. This registers it, so to speak, and the object is then called service. Thanks to some magic called autowiring, we usually don't need to specify the constructor parameters because DI will recognize them and pass them automatically. Thus, it would be sufficient to just provide the name of the class:

...

services:
	- App\Model\PostFacade

However, you do not 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 looked up by DI automatically, which is also the case for PostFacade.

Summary

The PostFacade class asks for Nette\Database\Explorer in a constructor and because this class is registered in the DI container, the container creates this instance and passes it. DI this way creates an PostFacade instance for us and passes it in a constructor to the HomePresenter class which asked for it. Sort of a Matryoshka doll of code. :) All the components only request what they need and they don't care where and how it gets created. The creation is handled by DI container.

Here you can read more about dependency injection, and about configuration.