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\UI\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 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.