## Terminology
- Source Instance: The instance of PM4 where you run the export.
- Target Instance: The instance of PM4 where you run the import.
- Manifest: An object that contains all the exported assets and information on how to link them when importing. The json encoded manifest is the export file the users downloads.
- Asset: Any model instance that has a UUID and can be exported. The model class has the trait `Exportable`
- Dependent: A child asset required by a parent asset to work correctly. For example, a Process asset will have a dependent User asset for the process manager if the process manager is set.
  
## Exporters
Exporter classes handle both the importing and exporting of an asset.

See the files in ProcessMaker/ImportExport/Exporters for examples.

In core they should be saved in the ProcessMaker/ImportExport/Exporters folder. In packages, they should be saved in the src/ImportExport folder.

They use a top-down approach to finding dependencies and adding them to the manifest.

A model asset is any model that has a UUID

### Methods
- `export()`
  - Logic to run when exporting the model. This is where you find all dependencies for a model that need to be exported and add them to the manifest using `addDependent()` and `addReference()`
- `import()`
  - Logic to run when importing the model. This is where you re-associate the dependencies to the model. Since IDs change across instances, this is where you will handle that.
- `handleDuplicateAttributes()` (optional)
  - Logic to run if a column must be unique and already exists on the target instance

See Exporting Assets and Importing Assets below for more information

## Exporter Extensions
Extensions are used in packages to extend an Exporter class defined in core
or in another package.

See the files in https://github.com/ProcessMaker/package-data-sources/tree/feature/FOUR-6769/src/ImportExport for examples.

These should be saved in the same folder as the Exporters and should have the same name as the Exporter it's extending but with the `Extension` suffix.

For example, if you are extending the ProcessExporter in core, you should
name the file and class `ProcessExporterExtension`

Note that a package can have it's own Exporter classes if the models are in the package.

### Methods
- `preExport()`
  - Returns a function to run before the export method of the Exporter
- `postExport()`
  - Returns a function to run after the export method of the Exporter
- `preImport()`
  - Returns a function to run before the import method of the Exporter
- `postImport()`
  - Returns a function to run after the import method of the Exporter

For example, the data sources package has:
```
use ProcessMaker\Packages\Connectors\DataSources\ImportExport\DataSourceExporter;
...
class ScreenExportExtension() {

    public function preExport() {
        $this->addDependent('data-source-watcher', $dataSource, DataSourceExporter::class);
    }

}
```
And PM4 Core has:
```
class ScreenExporter extends ExporterBase
{
    public function export()
    {
        $this->addDependent(DependentType::SCREENS, $nestedScreen, self::class);
    }
}
```
When the screen is exported, the screen exporter will run as if it was:
```
public function export()
{
    $this->addDependent('data-source-watcher', $dataSource, DataSourceExporter::class);

    $this->addDependent(DependentType::SCREENS, $nestedScreen, self::class);
}
```

In most cases, you can use the `pre` methods, but in some cases
you might want to do the logic after the Exporter's methods are run. In that cause, you can use `post` methods.

### Registering Extensions
In the `boot` method of the package service provider, register the extension like this:
```
use ProcessMaker\Packages\Connectors\DataSources\ImportExport\ScreenExporterExtension;
...
ScreenExporter::registerExtension(ScreenExporterExtension::class);
```

Note: You don't need to register Exporters. Exporters are imported by the ExporterExtension classes.

## Adding Dependencies
### Exporting Assets

Exporting is handled in a top-down manner by getting all an assets dependencies and running their respective exporters.

For example:
- A Process with a task will need to export the task Screen. The screen is a dependent of the process.
- If the screen has a script watcher, the script will need to be exported as a dependent of the screen.
- The script runs as a user, so that user will need to be exported as a dependent of the script.

- `addDependent('name', $modelInstance, ExporterClass::class, $metadata=null)`
  - When a model depends on another model that has a UUID, you use addDependent.
    For example, a Screen reference a Script (in watchers) and another Screen
  - Params:
    - name: Any string to reference the dependency. You will need to use the same string when importing. Unlike `addReference` below, you can use the same string and the dependencies will be pushed into the dependency stack.
    - $modelInstance: Any model with a UUID, for example a $screen or a $process
    - ExporterClass: The class name of the Exporter for the dependency
    - $metadata: Any data you would like to associate with the dependency to make importing easier. For example, the XPath to the element in a Process's bpmn.
  - Example in ScreenExporter:
    ```
    $this->addDependent('watcher-script', $watcherScript, ScriptExporter::class);
    $this->addDependent('nested-screen', $nestedScreen, self::class);
    ```
- `addReference('name', $data)`
  - When you need to add a dependency that does not have a UUID, use add reference.
    For example, a User has permissions that need to be exported.
  - Params:
    - name: Any string. You will need to use the same string when importing. Unlike `addDependency` above, the name should be unique in the context of the exporter. If the same exporter reuses a name, it will overwrite the previous set name.
    - Any variable you want to export.
  - Example in UserExporter
    ```
    $permissions = $this->model->permissions()->pluck('name')->toArray();
    $this->addReference('permissions', $permissions);
    ```

### Importing

Importing is done in the following order for each asset:
- Find an existing model (based on UUID) or create a new one.
- Set the attributes on the model from the imported manifest.
- Save the model.
- Run any preImport() methods from extensions
- Run the import() method in the Exporter
- Run any postImport() methods from extensions

The import() methods is where you link assets to their dependencies. Because IDs are often different 

- `$this->getDependents('name')`
  - Get the dependencies that were exported
    - Params:
      - name: The name given when exporting
    - Returns: An array of `Dependent` Objects (see below)
  - Example in UserExporter
    ```
    ```
- `$this->getReference('name')`
  - Get a reference that was exported
    - Params:
      - name: The name given when exporting
    - Returns: The data variable exported

Note: You will see `$this->model->save()` some places in the code. This is from a previous version and is not necessary any more. These will be cleaned up later. The model is automatically saved before getting to the `import()` method.

You only need to save the model again if you made changes to it.

### The `Dependent` Object
The dependent object contains the reference model as it exists on the target instance.

Properties:
- model: The dependent model on the target instance. The model has already been saved when you access it here.
- meta: The metadata that was exported with the dependent object.
- originalId: The original ID as it was on the source instance. To get the ID on the target instance, use `$dependent->model->id`


## Options

When importing, you can pass an `Options` object to determine
how to handle each asset.

Pass an array to the constructor in the format of `uuid => ['mode' => mode]]` to
set the import mode for each asset.

### Modes
- `update` (default)
  - Update the existing asset if it exists on the target instance
- `discard`
  - Ignore this asset when importing. Do not modify the asset on the target instance.
- `copy`
  - Create a new copy of the asset with a new UUID. Link all other assets in the
    import to the copy. Do not modify the matching asset on the target instance.
- `new`
  - Not user selectable. Create the asset when it does not exist on the target instance.

Exporting also accepts an `Options` object however the only mode supported is `discard`. When set for an asset, the asset will not be exported.

### Example