Model

Καθώς η εφαρμογή μεγαλώνει, σύντομα θα διαπιστώσουμε ότι σε διάφορα σημεία, σε διάφορους presenters, χρειαζόμαστε να εκτελούμε παρόμοιες λειτουργίες με τη βάση δεδομένων. Για παράδειγμα, να λαμβάνουμε τα πιο πρόσφατα δημοσιευμένα άρθρα. Όταν βελτιώνουμε την εφαρμογή, για παράδειγμα προσθέτοντας στα άρθρα μια σημαία που υποδεικνύει αν είναι πρόχειρο, πρέπει στη συνέχεια να περάσουμε από όλα τα σημεία στην εφαρμογή όπου λαμβάνονται άρθρα από τη βάση δεδομένων και να συμπληρώσουμε την συνθήκη where, ώστε να επιλέγονται μόνο τα μη πρόχειρα άρθρα.

Σε εκείνη τη στιγμή, η άμεση εργασία με τη βάση δεδομένων γίνεται ανεπαρκής και θα ήταν πιο έξυπνο να βοηθηθούμε με μια νέα συνάρτηση που θα μας επιστρέφει τα δημοσιευμένα άρθρα. Και όταν αργότερα προσθέσουμε μια άλλη συνθήκη, για παράδειγμα ότι δεν πρέπει να εμφανίζονται άρθρα με μελλοντική ημερομηνία, θα τροποποιήσουμε τον κώδικα μόνο σε ένα σημείο.

Θα τοποθετήσουμε τη συνάρτηση, για παράδειγμα, στην κλάση PostFacade και θα την ονομάσουμε getPublicArticles().

Στον κατάλογο app/Model/ θα δημιουργήσουμε την κλάση model PostFacade, η οποία θα είναι υπεύθυνη για τα άρθρα:

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

Στην κλάση, μέσω του constructor, θα ζητήσουμε να μας περαστεί το Database Explorer. Θα εκμεταλλευτούμε έτσι τη δύναμη του DI container.

Θα μεταβούμε στον HomePresenter, τον οποίο θα τροποποιήσουμε έτσι ώστε να απαλλαγούμε από την εξάρτηση από το Nette\Database\Explorer και να την αντικαταστήσουμε με μια νέα εξάρτηση από τη νέα μας κλάση.

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

Στην ενότητα use έχουμε App\Model\PostFacade, οπότε μπορούμε να συντομεύσουμε τη σύνταξη στον κώδικα PHP σε PostFacade. Θα ζητήσουμε αυτό το αντικείμενο στον constructor, θα το γράψουμε στην ιδιότητα $facade και θα το χρησιμοποιήσουμε στη μέθοδο renderDefault.

Απομένει το τελευταίο βήμα, να διδάξουμε στον DI container πώς να παράγει αυτό το αντικείμενο. Αυτό συνήθως γίνεται προσθέτοντας μια κουκκίδα στο αρχείο config/services.neon στην ενότητα services, αναφέροντας το πλήρες όνομα της κλάσης και τις παραμέτρους του constructor. Με αυτόν τον τρόπο την καταχωρούμε, όπως λέγεται, και το αντικείμενο ονομάζεται τότε service. Χάρη στη μαγεία που ονομάζεται autowiring, συνήθως δεν χρειάζεται να αναφέρουμε τις παραμέτρους του constructor, επειδή το DI τις αναγνωρίζει και τις περνά αυτόματα. Θα αρκούσε λοιπόν να αναφέρουμε μόνο το όνομα της κλάσης:

...

services:
	- App\Model\PostFacade

Ωστόσο, ούτε αυτή τη γραμμή δεν χρειάζεται να προσθέσετε. Στην ενότητα search στην αρχή του services.neon ορίζεται ότι όλες οι κλάσεις που τελειώνουν με τη λέξη -Facade ή -Factory τις βρίσκει το DI από μόνο του, κάτι που ισχύει και για την PostFacade.

Σύνοψη

Η κλάση PostFacade ζητά στον constructor της να της περαστεί το Nette\Database\Explorer και επειδή αυτή η κλάση είναι καταχωρημένη στον DI container, ο container δημιουργεί αυτήν την παρουσία και την περνά. Το DI δημιουργεί έτσι για εμάς την παρουσία PostFacade και την περνά στον constructor της κλάσης HomePresenter, η οποία την ζήτησε. Σαν μια ματριόσκα. :) Όλοι απλά λένε τι θέλουν και δεν ενδιαφέρονται για το πού και πώς δημιουργείται κάτι. Για τη δημιουργία φροντίζει ο DI container.

Εδώ μπορείτε να διαβάσετε περισσότερα για το dependency injection και τη διαμόρφωση.