# Saját háttérfeldolgozó készítése

> Hogyan szervezheted ki az időigényes beküldés utáni feladatokat a háttérbe a GF_Background_Process osztály kiterjesztésével.

Ha egy beküldés után külső szolgáltatásnak kell adatot küldeni, jelentést kell készíteni vagy bonyolult számítást kell végezni, az lassíthatja az űrlap beküldését. Ilyenkor a feladatot a háttérbe tudod tenni: a látogató nem vár rá, az oldal gyors marad.

## Mi az a háttérfeldolgozó

A háttérfeldolgozó az időigényes műveleteket a beküldéstől függetlenül, aszinkron módon futtatja. Egy sorba kerülnek a feladatok, és a feldolgozó egyesével végzi el őket. Így a beküldés gyors és válaszkész marad, a beküldés utáni teendők pedig mégis lefutnak.

## Példa megvalósítás

A példa a `GF_Background_Process` osztályt terjeszti ki. Ezt tudja:

- Automatikusan sorba állítja a beküldések által kiváltott feladatokat.
- A sorba állított feladatokat aszinkron módon, saját logikával dolgozza fel.
- A bővítmény eltávolításakor kezeli az ütemezett eseményeket és a sort.
- Kezeli a szélsőséges eseteket, például a kihagyott vagy érvénytelen bejegyzéseket.

Az osztály nevét és az `$action` értékét cseréld egyedire, a saját célodnak megfelelőre.

```php
if ( ! class_exists( 'Gravity_Forms\Gravity_Forms\Async\GF_Background_Process' ) ) {
	require_once GF_PLUGIN_DIR_PATH . 'includes/async/class-gf-background-process.php';
}

use Gravity_Forms\Gravity_Forms\Async\GF_Background_Process;

// Nevezd át az osztályt valami egyedire.
class My_Custom_GF_Background_Processor extends GF_Background_Process {

	// Nevezd át a műveletet valami egyedire.
	protected $action = 'my_custom_gf_background_processor';

	/**
	 * Null vagy az osztály aktuális példánya.
	 *
	 * @var null|self
	 */
	private static $_instance;

	/**
	 * Visszaadja az osztály aktuális példányát.
	 */
	public static function get_instance() {
		if ( null === self::$_instance ) {
			self::$_instance = new self;
		}

		return self::$_instance;
	}

	/**
	 * Előkészíti a háttérfeldolgozót.
	 *
	 * @param bool|array $allowed_batch_data_classes Opcionális. A visszafejthető osztályok nevei. Alapérték: true (bármelyik).
	 */
	public function __construct( $allowed_batch_data_classes = true ) {
		parent::__construct( $allowed_batch_data_classes );
		add_action( 'init', array( $this, 'init' ), 11 );
	}

	/**
	 * Hozzáadja a horgokat.
	 *
	 * @return void
	 */
	public function init() {
		add_action( 'gform_after_submission', array( $this, 'after_submission' ), 10, 2 );
		add_action( 'gform_uninstalling', array( $this, 'uninstalling' ) );
	}

	/**
	 * A bejegyzést a háttérfeldolgozási sorba teszi.
	 *
	 * @param array $entry A beküldés által létrehozott bejegyzés.
	 * @param array $form  A beküldött űrlap.
	 *
	 * @return void
	 */
	public function after_submission( $entry, $form ) {
		if ( rgar( $entry, 'status' ) === 'spam' ) {
			return;
		}

		$entry_id = absint( rgar( $entry, 'id' ) );
		GFCommon::log_debug( __METHOD__ . sprintf( '(): Adding entry #%d to the async processing queue.', $entry_id ) );

		$this->push_to_queue( array(
			'entry_id' => $entry_id,
			'form_id'  => absint( rgar( $form, 'id' ) ),
		) );
		$this->save()->dispatch();
	}

	/**
	 * Takarítás a bővítmény eltávolításakor.
	 *
	 * @return void
	 */
	public function uninstalling() {
		$this->clear_scheduled_events();
		$this->clear_queue( true );
		$this->unlock_process();
	}

	/**
	 * A háttérfeladat feldolgozása.
	 *
	 * @param array $item A feladat adatai.
	 *
	 * @return bool|array
	 */
	protected function task( $item ) {
		$entry = GFAPI::get_entry( rgar( $item, 'entry_id' ) );
		if ( is_wp_error( $entry ) ) {
			GFCommon::log_debug( __METHOD__ . sprintf( '(): Aborting; Entry #%d not found.', rgar( $item, 'entry_id' ) ) );

			return false;
		}

		$form = GFAPI::get_form( rgar( $item, 'form_id' ) );
		if ( empty( $form ) ) {
			GFCommon::log_debug( __METHOD__ . sprintf( '(): Aborting; Form #%d not found.', rgar( $item, 'form_id' ) ) );

			return false;
		}

		// A kiegészítők kitölthetik az űrlap adatait, például a mezők bemeneteit és választásait.
		$form = $this->filter_form( $form, $entry );

		GFCommon::log_debug( __METHOD__ . '(): Processing => ' . print_r( $item, true ) );

		// Itt dolgozd fel a bejegyzést ($entry).
		// false visszaadása: a tétel kikerül a sorból.
		// $item visszaadása: a tétel a sorban marad, és később újra próbálkozik.

		return false;
	}

}

// Az osztály nevét igazítsd a fent megadotthoz.
My_Custom_GF_Background_Processor::get_instance();
```

## Hogyan működik

1. A `gform_after_submission` horog minden beküldés után meghívja az `after_submission` metódust.
2. A kihagyott (spam) bejegyzéseket a metódus nem teszi a sorba.
3. A többi bejegyzés azonosítója és az űrlap azonosítója a `push_to_queue` hívással a sorba kerül, a `save()->dispatch()` pedig elindítja a feldolgozást.
4. A `task` metódus minden tételnél betölti a bejegyzést és az űrlapot. Ha valamelyik nem található, naplóz és `false`-t ad vissza, így a tétel kikerül a sorból.
5. A saját logikádat a `task` metódus végén, a megjegyzésnél írd meg. Ha `false`-t adsz vissza, a tétel kész és kikerül a sorból. Ha magát a tételt adod vissza, a feldolgozó később újra megpróbálja.
6. Az `uninstalling` metódus a Gravity Forms eltávolításakor kiüríti a sort, törli az ütemezett eseményeket és feloldja a folyamatot.

A naplózás (`GFCommon::log_debug`) a Gravity Forms naplózása bekapcsolt állapotában ír a naplóba. A kódot az aktív téma `functions.php` fájljába, saját funkciók bővítményébe vagy egy kódrészlet-kezelő bővítménybe.
