# CSV importers [CSV](https://en.wikipedia.org/wiki/Comma-separated_values) files are an easy way to import data into farmOS. The farmOS Import CSV module (`farm_import_csv`) provides a framework for building CSV importers using Drupal's [Migrate API](https://www.drupal.org/docs/drupal-apis/migrate-api). The module uses this framework to provide "default" CSV importers for each asset, log, and taxonomy term type. These are useful if you can fit your data into them, but in some cases a more customized CSV template and/or import logic might be necessary. ## Migration YML Modules can provide their own CSV importers by adding a single YML file to their `config/install` directory, which will add the importer when the module is installed. The YML file defines all the configuration necessary for the importer, using the Drupal [Migrate Plus](https://drupal.org/project/migrate_plus) module's `migration` configuration entity type. The basic template for a CSV importer is as follows (replace all `{{ VARIABLE }}` sections with your specific configuration): ```yaml langcode: en status: true dependencies: enforced: module: - {{ MODULE_NAME }} id: {{ UNIQUE_ID }} label: '{{ LABEL }}' migration_group: farm_import_csv migration_tags: [] source: plugin: csv_file destination: plugin: 'entity:{{ ENTITY_TYPE }}' process: {{ MAPPING_CONFIG }} migration_dependencies: { } third_party_settings: farm_import_csv: access: permissions: - {{ PERMISSION_STRING }} columns: {{ COLUMN_DESCRIPTIONS }} ``` - `{{ MODULE_NAME }}` is the machine-name of the contrib module. Self-dependency is recommended so that configuration objects are removed upon module uninstall. If the module is not enforced in `dependencies: { }`, then `drush config:delete migrate_plus.migration.{{ UNIQUE_ID }}` will need to be run manually before reinstall. - `{{ UNIQUE_ID }}` must be a unique machine-name for the importer, consisting of only alphanumeric characters and underscores. - `{{ LABEL }}` will be the name of the importer shown in the farmOS UI. - `{{ ENTITY_TYPE }}` should be `asset`, `log`, or `taxonomy_term`. - `{{ MAPPING_CONFIG }}` is where all the Drupal Migrate API's `process` pipeline configuration is defined. This is responsible for mapping CSV column names to entity fields (or additional processing). See [Process pipeline](#process-pipeline) below for more information. - `{{ PERMISSION_STRING }}` should be a Drupal permission that the user must have in order to use the importer. Multiple permissions can be included on separate lines. - `{{ COLUMN_DESCRIPTIONS }}` should be an array of items with `name` and `description` keys to describe each CSV column. ### Example Here is an example of an importer for "egg harvests", which will import a CSV with columns named `Date` and `Eggs`. It will create a harvest log named "Collected [num] egg(s)" for each row with the number of eggs saved in a standard `count` quantity: `egg-harvests.csv` ```csv Date,Eggs 2023-09-15,12 2023-09-16,14 2023-09-17,9 ``` `config/install/migrate_plus.migration.egg_harvest.yml` ```yaml langcode: en status: true dependencies: { } id: egg_harvest label: 'Egg harvest importer' migration_group: farm_import_csv migration_tags: [] source: plugin: csv_file constants: UNIT: egg(s) LOG_NAME_PREFIX: Collected destination: plugin: 'entity:log' process: # Hard-code the bundle. type: plugin: default_value default_value: harvest # Parse the log timestamp with strtotime() from Date column. timestamp: plugin: callback callable: strtotime source: Date # Create or load "egg(s)" unit term. _unit: plugin: entity_generate entity_type: taxonomy_term value_key: name bundle_key: vid bundle: unit source: constants/UNIT # Create a quantity from the Eggs column. quantity: - plugin: skip_on_empty source: Eggs method: process - plugin: static_map map: { } default_value: [ [ ] ] - plugin: create_quantity default_values: type: standard measure: count values: value: Eggs units: '@_unit' # Auto-generate the log name. name: plugin: concat source: - constants/LOG_NAME_PREFIX - Eggs - constants/UNIT delimiter: ' ' # Mark the log as done. status: plugin: default_value default_value: done migration_dependencies: { } third_party_settings: farm_import_csv: access: permissions: - create harvest log columns: - name: Date description: Date of egg harvest. - name: Eggs description: Number of eggs harvested. ``` ### Process pipeline The `process` section of the YML is used to define a "process pipeline" for mapping source data from CSV columns into properties of the destination entity. Each `process` item declares one or more "process plugins" that can be chained together to transform data before it is saved to the destination entity. The simplest example is the `get` process plugin, which copies values from the source to the destination without any modification. For example, the following process pipeline will populate the log name from a CSV column called `Log name`: ```yaml ... process: name: - plugin: get source: Log name ... ``` There is also a shorthand syntax for the `get` plugin, which is even simpler: ```yaml ... process: name: Log name ... ``` Chaining plugins together provides more advanced capabilities. For following process pipeline will populate the log categories from a CSV column called `Log categories`, using the `explode` plugin to split a comma-separated list of categories into separate items, and the `term_lookup` plugin to look up existing terms from the `log_category` vocabulary to reference: ```yaml ... process: category: - plugin: explode delimiter: , source: Log categories - plugin: term_lookup bundle: log_category ... ``` Only the first plugin in a process pipeline needs to define the `source` CSV column name. See [Resources](#resources) below for lists of available process plugins. #### farmOS process plugins In addition to the process plugins provided by Drupal core and the [Migrate Plus](https://drupal.org/project/migrate_plus) module, farmOS also provides some process plugins of its own. ##### asset_lookup The `asset_lookup` plugin extends the `entity_lookup` plugin to make it easier to populate asset reference fields on entities. It will attempt to look up an asset using multiple properties, in the following order of precedence: - UUID - ID tag - Name - ID (primary key) ```yaml ... process: equipment: - plugin: asset_lookup bundle: equipment source: Equipment used ... ``` The `bundle` property is optional, and will limit the allowed asset types. It can be a single asset type, or an array of multiple types. If omitted, then all asset types will be allowed. This plugin assumes a single asset is being looked up. If a source CSV column may have multiple comma-separate values, use an `explode` plugin before the `asset_lookup`, and move the `source: Equipment used` to it, as demonstrated in the example in [Process pipeline](#process-pipeline) above. If the `source` CSV column contains any values, and any of the asset lookups fail, the plugin will cause the whole row import to fail and an error will be shown to the user. The plugin will ignore case sensitivity, and will automatically trim whitespace from the start and end of CSV source values. ##### term_lookup The `term_lookup` plugin extends the `entity_lookup` plugin to make it easier to populate taxonomy term reference fields on entities. Example: ```yaml process: animal_type: - plugin: term_lookup bundle: animal_type source: Animal type ``` The `bundle` property is required. If the `source` CSV column contains any values, and any of the term lookups fail, the plugin will cause the whole row import to fail and an error will be shown to the user. The plugin will ignore case sensitivity, and will automatically trim whitespace from the start and end of CSV source values. ## Resources A complete overview of all the options available with Drupal's Migrate API is outside the scope of this documentation, but the following links are a good place to learn more. Also note that CSVs are just one type of data source for migrations. These resources are not specific to CSV imports, but the same principles apply generally. - [Drupal Migrate API documentation](https://www.drupal.org/docs/drupal-apis/migrate-api) - [Migrate API overview](https://www.drupal.org/docs/drupal-apis/migrate-api/migrate-api-overview) - [Migrate process plugins overview](https://www.drupal.org/docs/8/api/migrate-api/migrate-process-plugins/migrate-process-overview). - [31 days of Drupal migrations](https://understanddrupal.com/courses/31-days-of-migrations/) - [List of core Migrate process plugins](https://www.drupal.org/docs/8/api/migrate-api/migrate-process-plugins/list-of-core-migrate-process-plugins) - [List of process plugins provided by Migrate Plus](https://www.drupal.org/docs/8/api/migrate-api/migrate-process-plugins/list-of-process-plugins-provided-by-migrate-plus)