Importers

Relationships

Link imported rows to related records using relationships and entity links.

Tapix can link imported rows to existing or newly created related records. This enables importing a CSV of contacts and automatically associating each row with a company, assigning tags, or linking to any other related model.

Overview

There are two ways to define relationships:

  1. ImportField shorthand -- use the ->relationship() method directly on a field for simple cases.
  2. EntityLink objects -- override the defineEntityLinks() method on your importer for advanced configuration.

Both approaches produce the same result. Use the field shorthand when defaults are sufficient, and switch to EntityLink objects when you need full control over matching, storage, or morphing behavior.

ImportField Relationship Shorthand

The simplest way to define a relationship is through the ->relationship() method on an ImportField:

app/Importers/ContactImporter.php
use Tapix\Core\Enums\MatchBehavior;
use Tapix\Core\Fields\ImportField;

ImportField::make('company')
    ->relationship(
        name: 'company',
        model: Company::class,
        matchBy: ['name', 'domain'],
        behavior: MatchBehavior::MatchOrCreate,
    )
    ->guess(['company', 'organization', 'org']);

Parameters:

  • name -- The Eloquent relationship method name on the parent model.
  • model -- The related model class.
  • matchBy -- An array of field keys used to look up existing records.
  • behavior -- A MatchBehavior enum value controlling what happens when a match is or is not found.

For advanced relationship configuration, override the defineEntityLinks() method on your importer and return an array of EntityLink objects.

Factory Methods

EntityLink::belongsTo(string $name, string $modelClass)

Creates a belongs-to relationship link. The related model's ID is stored in a foreign key column on the imported model.

app/Importers/ContactImporter.php
use Tapix\Core\EntityLinks\EntityLink;

EntityLink::belongsTo('company', Company::class);

EntityLink::morphToMany(string $name, string $modelClass)

Creates a polymorphic many-to-many relationship link. Records are associated through a pivot table.

app/Importers/ContactImporter.php
EntityLink::morphToMany('tags', Tag::class);

EntityLink::fromCustomField(code, name, lookupType, matchableFields, supportsMultiValue, sortOrder)

Creates an entity link backed by a custom field adapter rather than a standard Eloquent relationship.

app/Importers/ContactImporter.php
EntityLink::fromCustomField(
    code: 'industry',
    name: 'Industry',
    lookupType: 'industry',
    matchableFields: [MatchableField::name()],
    supportsMultiValue: false,
    sortOrder: 5,
);

Builder Methods

->matchableFields(array $fields)

Defines the fields used to look up existing related records.

app/Importers/ContactImporter.php
EntityLink::belongsTo('company', Company::class)
    ->matchableFields([
        MatchableField::email('email'),
        MatchableField::domain('domain'),
        MatchableField::name(),
    ]);

->foreignKey(string $key)

Specifies the foreign key column name on the imported model.

app/Importers/ContactImporter.php
EntityLink::belongsTo('company', Company::class)
    ->foreignKey('company_id');

->morphRelation(string $relation)

Sets the morph relation name for polymorphic relationships.

app/Importers/ContactImporter.php
EntityLink::morphToMany('tags', Tag::class)
    ->morphRelation('taggable');

->guess(array $aliases)

Adds column name aliases for auto-mapping, similar to ImportField::guess().

app/Importers/ContactImporter.php
EntityLink::belongsTo('company', Company::class)
    ->guess(['company', 'organization', 'org']);

->label(string $label)

Sets a custom display label.

app/Importers/ContactImporter.php
EntityLink::belongsTo('company', Company::class)
    ->label('Organization');

->sortOrder(?int $order)

Controls the display ordering.

app/Importers/ContactImporter.php
EntityLink::belongsTo('company', Company::class)
    ->sortOrder(1);

MatchBehavior Enum

The MatchBehavior enum controls what happens when Tapix attempts to match an imported value to an existing related record.

ValueDescription
MatchOnlyOnly link to existing records. Skip the value if no match found.
MatchOrCreateFind an existing record or create a new one if not found.
CreateAlways create a new record. No lookup is performed.
app/Importers/ContactImporter.php
use Tapix\Core\Enums\MatchBehavior;

ImportField::make('company')
    ->relationship(
        name: 'company',
        model: Company::class,
        matchBy: ['name'],
        behavior: MatchBehavior::MatchOrCreate,
    );

MatchableField Presets

MatchableField provides preset configurations for common lookup strategies. Each preset has a priority value that determines the order in which matching is attempted (higher priority first).

PresetPriorityDefault BehaviorMulti-Value
MatchableField::id()100MatchOnlyNo
MatchableField::email($key)90--Yes
MatchableField::domain($key)80--Yes
MatchableField::phone($key)70--Yes
MatchableField::name()10CreateNo
app/Importers/ContactImporter.php
use Tapix\Core\Matching\MatchableField;

EntityLink::belongsTo('company', Company::class)
    ->matchableFields([
        MatchableField::email('email'),
        MatchableField::domain('domain'),
        MatchableField::name(),
    ]);

Tapix supports three storage strategies for entity links, determined by the factory method used:

  • ForeignKey -- Stores the related model's ID in a foreign key column on the imported model. Used by EntityLink::belongsTo().
  • MorphToMany -- Associates records through a polymorphic many-to-many pivot table. Used by EntityLink::morphToMany().
  • CustomFieldValue -- Stores the association via a CustomFieldsAdapter, for non-standard relationship storage. Used by EntityLink::fromCustomField().

Intra-Import Deduplication

When multiple rows in the same CSV reference the same related entity (e.g., several contacts belonging to "Acme Corp"), Tapix creates the related record once and reuses its ID for subsequent rows. This prevents duplicate records and ensures referential consistency within a single import.

Full Example

app/Importers/ContactImporter.php
use Tapix\Core\EntityLinks\EntityLink;
use Tapix\Core\Matching\MatchableField;

protected function defineEntityLinks(): array
{
    return [
        'company' => EntityLink::belongsTo('company', Company::class)
            ->matchableFields([
                MatchableField::email('email'),
                MatchableField::domain('domain'),
                MatchableField::name(),
            ])
            ->guess(['company', 'organization']),

        'tags' => EntityLink::morphToMany('tags', Tag::class)
            ->matchableFields([
                MatchableField::name(),
            ])
            ->guess(['tags', 'labels']),
    ];
}
Copyright © 2026