Nette Documentation Preview

syntax
Φόρμα δημιουργίας και επεξεργασίας εγγραφής
*******************************************

.[perex]
Πώς να υλοποιήσετε σωστά την προσθήκη και την επεξεργασία μιας εγγραφής στη Nette, χρησιμοποιώντας την ίδια φόρμα και για τα δύο;

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


Προσθήκη εγγραφής .[#toc-adding-a-record]
-----------------------------------------

Ένα παράδειγμα παρουσιαστή που χρησιμοποιείται για την προσθήκη μιας εγγραφής. Θα αφήσουμε την πραγματική εργασία της βάσης δεδομένων στην κλάση `Facade`, της οποίας ο κώδικας δεν είναι σχετικός με το παράδειγμα.


```php
use Nette\Application\UI\Form;

class RecordPresenter extends Nette\Application\UI\Presenter
{
	public function __construct(
		private Facade $facade,
	) {
	}

	protected function createComponentRecordForm(): Form
	{
		$form = new Form;

		// ... προσθέστε πεδία φόρμας ...

		$form->onSuccess[] = [$this, 'recordFormSucceeded'];
		return $form;
	}

	public function recordFormSucceeded(Form $form, array $data): void
	{
		$this->facade->add($data); // προσθήκη εγγραφής στη βάση δεδομένων
		$this->flashMessage('Successfully added');
		$this->redirect('...');
	}

	public function renderAdd(): void
	{
		// ...
	}
}
```


Επεξεργασία εγγραφής .[#toc-editing-a-record]
---------------------------------------------

Τώρα ας δούμε πώς θα έμοιαζε ένας παρουσιαστής που χρησιμοποιείται για την επεξεργασία μιας εγγραφής:


```php
use Nette\Application\UI\Form;

class RecordPresenter extends Nette\Application\UI\Presenter
{
	private $record;

	public function __construct(
		private Facade $facade,
	) {
	}

	public function actionEdit(int $id): void
	{
		$record = $this->facade->get($id);
		if (
			!$record // επαληθεύει την ύπαρξη του αρχείου
			|| !$this->facade->isEditAllowed(/*...*/) // έλεγχος δικαιωμάτων
		) {
			$this->error(); // σφάλμα 404
		}

		$this->record = $record;
	}

	protected function createComponentRecordForm(): Form
	{
		// επαληθεύστε ότι η ενέργεια είναι 'edit'
		if ($this->getAction() !== 'edit') {
			$this->error();
		}

		$form = new Form;

		// ... προσθέστε πεδία φόρμας ...

		$form->setDefaults($this->record); // Ορισμός προεπιλεγμένων τιμών
		$form->onSuccess[] = [$this, 'recordFormSucceeded'];
		return $form;
	}

	public function recordFormSucceeded(Form $form, array $data): void
	{
		$this->facade->update($this->record->id, $data); // ενημέρωση εγγραφής
		$this->flashMessage('Successfully updated');
		$this->redirect('...');
	}
}
```

Στη μέθοδο *action*, η οποία καλείται ακριβώς στην αρχή του [κύκλου ζωής του παρουσιαστή |application:presenters#Life Cycle of Presenter], επαληθεύουμε την ύπαρξη της εγγραφής και την άδεια του χρήστη να την επεξεργαστεί.

Αποθηκεύουμε την εγγραφή στην ιδιότητα `$record`, ώστε να είναι διαθέσιμη στη μέθοδο `createComponentRecordForm()` για τον καθορισμό των προεπιλογών και `recordFormSucceeded()` για το αναγνωριστικό. Μια εναλλακτική λύση θα ήταν να ορίσουμε τις προεπιλεγμένες τιμές απευθείας στη μέθοδο `actionEdit()` και η τιμή του ID, η οποία αποτελεί μέρος της διεύθυνσης URL, ανακτάται με τη χρήση της μεθόδου `getParameter('id')`:


```php
	public function actionEdit(int $id): void
	{
		$record = $this->facade->get($id);
		if (
			// επαληθεύει την ύπαρξη και ελέγχει τα δικαιώματα
		) {
			$this->error();
		}

		// ορισμός προεπιλεγμένων τιμών φόρμας
		$this->getComponent('recordForm')
			->setDefaults($record);
	}

	public function recordFormSucceeded(Form $form, array $data): void
	{
		$id = (int) $this->getParameter('id');
		$this->facade->update($id, $data);
		// ...
	}
}
```

Ωστόσο, και αυτό θα πρέπει να είναι **το πιο σημαντικό συμπέρασμα από όλο τον κώδικα**, πρέπει να βεβαιωθούμε ότι η ενέργεια είναι όντως `edit` όταν δημιουργούμε τη φόρμα. Διότι διαφορετικά η επικύρωση στη μέθοδο `actionEdit()` δεν θα γινόταν καθόλου!


Ίδια φόρμα για προσθήκη και επεξεργασία .[#toc-same-form-for-adding-and-editing]
--------------------------------------------------------------------------------

Και τώρα θα συνδυάσουμε και τους δύο παρουσιαστές σε έναν. Είτε θα μπορούσαμε να διακρίνουμε ποια ενέργεια εμπλέκεται στη μέθοδο `createComponentRecordForm()` και να διαμορφώσουμε τη φόρμα ανάλογα, είτε μπορούμε να το αφήσουμε απευθείας στις μεθόδους ενέργειας και να απαλλαγούμε από τη συνθήκη:


```php
class RecordPresenter extends Nette\Application\UI\Presenter
{
	public function __construct(
		private Facade $facade,
	) {
	}

	public function actionAdd(): void
	{
		$form = $this->getComponent('recordForm');
		$form->onSuccess[] = [$this, 'addingFormSucceeded'];
	}

	public function actionEdit(int $id): void
	{
		$record = $this->facade->get($id);
		if (
			!$record // επαληθεύει την ύπαρξη του αρχείου
			|| !$this->facade->isEditAllowed(/*...*/) // έλεγχος δικαιωμάτων
		) {
			$this->error(); // σφάλμα 404
		}

		$form = $this->getComponent('recordForm');
		$form->setDefaults($record); // Ορισμός προεπιλογών
		$form->onSuccess[] = [$this, 'editingFormSucceeded'];
	}

	protected function createComponentRecordForm(): Form
	{
		// επαληθεύστε ότι η ενέργεια είναι 'add' ή 'edit'.
		if (!in_array($this->getAction(), ['add', 'edit'])) {
			$this->error();
		}

		$form = new Form;

		// ... προσθέστε πεδία φόρμας ...

		return $form;
	}

	public function addingFormSucceeded(Form $form, array $data): void
	{
		$this->facade->add($data); // προσθήκη εγγραφής στη βάση δεδομένων
		$this->flashMessage('Successfully added');
		$this->redirect('...');
	}

	public function editingFormSucceeded(Form $form, array $data): void
	{
		$id = (int) $this->getParameter('id');
		$this->facade->update($id, $data); // ενημέρωση εγγραφής
		$this->flashMessage('Successfully updated');
		$this->redirect('...');
	}
}
```

{{priority: -1}}
{{sitename: Best Practices}}

Φόρμα δημιουργίας και επεξεργασίας εγγραφής

Πώς να υλοποιήσετε σωστά την προσθήκη και την επεξεργασία μιας εγγραφής στη Nette, χρησιμοποιώντας την ίδια φόρμα και για τα δύο;

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

Προσθήκη εγγραφής

Ένα παράδειγμα παρουσιαστή που χρησιμοποιείται για την προσθήκη μιας εγγραφής. Θα αφήσουμε την πραγματική εργασία της βάσης δεδομένων στην κλάση Facade, της οποίας ο κώδικας δεν είναι σχετικός με το παράδειγμα.

use Nette\Application\UI\Form;

class RecordPresenter extends Nette\Application\UI\Presenter
{
	public function __construct(
		private Facade $facade,
	) {
	}

	protected function createComponentRecordForm(): Form
	{
		$form = new Form;

		// ... προσθέστε πεδία φόρμας ...

		$form->onSuccess[] = [$this, 'recordFormSucceeded'];
		return $form;
	}

	public function recordFormSucceeded(Form $form, array $data): void
	{
		$this->facade->add($data); // προσθήκη εγγραφής στη βάση δεδομένων
		$this->flashMessage('Successfully added');
		$this->redirect('...');
	}

	public function renderAdd(): void
	{
		// ...
	}
}

Επεξεργασία εγγραφής

Τώρα ας δούμε πώς θα έμοιαζε ένας παρουσιαστής που χρησιμοποιείται για την επεξεργασία μιας εγγραφής:

use Nette\Application\UI\Form;

class RecordPresenter extends Nette\Application\UI\Presenter
{
	private $record;

	public function __construct(
		private Facade $facade,
	) {
	}

	public function actionEdit(int $id): void
	{
		$record = $this->facade->get($id);
		if (
			!$record // επαληθεύει την ύπαρξη του αρχείου
			|| !$this->facade->isEditAllowed(/*...*/) // έλεγχος δικαιωμάτων
		) {
			$this->error(); // σφάλμα 404
		}

		$this->record = $record;
	}

	protected function createComponentRecordForm(): Form
	{
		// επαληθεύστε ότι η ενέργεια είναι 'edit'
		if ($this->getAction() !== 'edit') {
			$this->error();
		}

		$form = new Form;

		// ... προσθέστε πεδία φόρμας ...

		$form->setDefaults($this->record); // Ορισμός προεπιλεγμένων τιμών
		$form->onSuccess[] = [$this, 'recordFormSucceeded'];
		return $form;
	}

	public function recordFormSucceeded(Form $form, array $data): void
	{
		$this->facade->update($this->record->id, $data); // ενημέρωση εγγραφής
		$this->flashMessage('Successfully updated');
		$this->redirect('...');
	}
}

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

Αποθηκεύουμε την εγγραφή στην ιδιότητα $record, ώστε να είναι διαθέσιμη στη μέθοδο createComponentRecordForm() για τον καθορισμό των προεπιλογών και recordFormSucceeded() για το αναγνωριστικό. Μια εναλλακτική λύση θα ήταν να ορίσουμε τις προεπιλεγμένες τιμές απευθείας στη μέθοδο actionEdit() και η τιμή του ID, η οποία αποτελεί μέρος της διεύθυνσης URL, ανακτάται με τη χρήση της μεθόδου getParameter('id'):

	public function actionEdit(int $id): void
	{
		$record = $this->facade->get($id);
		if (
			// επαληθεύει την ύπαρξη και ελέγχει τα δικαιώματα
		) {
			$this->error();
		}

		// ορισμός προεπιλεγμένων τιμών φόρμας
		$this->getComponent('recordForm')
			->setDefaults($record);
	}

	public function recordFormSucceeded(Form $form, array $data): void
	{
		$id = (int) $this->getParameter('id');
		$this->facade->update($id, $data);
		// ...
	}
}

Ωστόσο, και αυτό θα πρέπει να είναι το πιο σημαντικό συμπέρασμα από όλο τον κώδικα, πρέπει να βεβαιωθούμε ότι η ενέργεια είναι όντως edit όταν δημιουργούμε τη φόρμα. Διότι διαφορετικά η επικύρωση στη μέθοδο actionEdit() δεν θα γινόταν καθόλου!

Ίδια φόρμα για προσθήκη και επεξεργασία

Και τώρα θα συνδυάσουμε και τους δύο παρουσιαστές σε έναν. Είτε θα μπορούσαμε να διακρίνουμε ποια ενέργεια εμπλέκεται στη μέθοδο createComponentRecordForm() και να διαμορφώσουμε τη φόρμα ανάλογα, είτε μπορούμε να το αφήσουμε απευθείας στις μεθόδους ενέργειας και να απαλλαγούμε από τη συνθήκη:

class RecordPresenter extends Nette\Application\UI\Presenter
{
	public function __construct(
		private Facade $facade,
	) {
	}

	public function actionAdd(): void
	{
		$form = $this->getComponent('recordForm');
		$form->onSuccess[] = [$this, 'addingFormSucceeded'];
	}

	public function actionEdit(int $id): void
	{
		$record = $this->facade->get($id);
		if (
			!$record // επαληθεύει την ύπαρξη του αρχείου
			|| !$this->facade->isEditAllowed(/*...*/) // έλεγχος δικαιωμάτων
		) {
			$this->error(); // σφάλμα 404
		}

		$form = $this->getComponent('recordForm');
		$form->setDefaults($record); // Ορισμός προεπιλογών
		$form->onSuccess[] = [$this, 'editingFormSucceeded'];
	}

	protected function createComponentRecordForm(): Form
	{
		// επαληθεύστε ότι η ενέργεια είναι 'add' ή 'edit'.
		if (!in_array($this->getAction(), ['add', 'edit'])) {
			$this->error();
		}

		$form = new Form;

		// ... προσθέστε πεδία φόρμας ...

		return $form;
	}

	public function addingFormSucceeded(Form $form, array $data): void
	{
		$this->facade->add($data); // προσθήκη εγγραφής στη βάση δεδομένων
		$this->flashMessage('Successfully added');
		$this->redirect('...');
	}

	public function editingFormSucceeded(Form $form, array $data): void
	{
		$id = (int) $this->getParameter('id');
		$this->facade->update($id, $data); // ενημέρωση εγγραφής
		$this->flashMessage('Successfully updated');
		$this->redirect('...');
	}
}