Μοντέλο

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

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

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

Θα δημιουργήσουμε την κλάση μοντέλου PostFacade στον κατάλογο app/Model/ για να αναλάβει τη φροντίδα των άρθρων μας:

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

Στην κλάση περνάμε την βάση δεδομένων Explorer. Με αυτόν τον τρόπο θα εκμεταλλευτούμε τη δύναμη του DI container.

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

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

Στο τμήμα χρήσης, χρησιμοποιούμε το App\Model\PostFacade, οπότε μπορούμε να συντομεύσουμε τον κώδικα PHP σε PostFacade. Ζητάμε αυτό το αντικείμενο στον κατασκευαστή, το γράφουμε στην ιδιότητα $facade και το χρησιμοποιούμε στη μέθοδο renderDefault.

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

...

services:
	- App\Model\PostFacade

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

Περίληψη

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

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