Relationships
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:
- ImportField shorthand -- use the
->relationship()method directly on a field for simple cases. - 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:
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-- AMatchBehaviorenum value controlling what happens when a match is or is not found.
EntityLink Objects
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.
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.
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.
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.
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.
EntityLink::belongsTo('company', Company::class)
->foreignKey('company_id');
->morphRelation(string $relation)
Sets the morph relation name for polymorphic relationships.
EntityLink::morphToMany('tags', Tag::class)
->morphRelation('taggable');
->guess(array $aliases)
Adds column name aliases for auto-mapping, similar to ImportField::guess().
EntityLink::belongsTo('company', Company::class)
->guess(['company', 'organization', 'org']);
->label(string $label)
Sets a custom display label.
EntityLink::belongsTo('company', Company::class)
->label('Organization');
->sortOrder(?int $order)
Controls the display ordering.
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.
| Value | Description |
|---|---|
MatchOnly | Only link to existing records. Skip the value if no match found. |
MatchOrCreate | Find an existing record or create a new one if not found. |
Create | Always create a new record. No lookup is performed. |
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).
| Preset | Priority | Default Behavior | Multi-Value |
|---|---|---|---|
MatchableField::id() | 100 | MatchOnly | No |
MatchableField::email($key) | 90 | -- | Yes |
MatchableField::domain($key) | 80 | -- | Yes |
MatchableField::phone($key) | 70 | -- | Yes |
MatchableField::name() | 10 | Create | No |
use Tapix\Core\Matching\MatchableField;
EntityLink::belongsTo('company', Company::class)
->matchableFields([
MatchableField::email('email'),
MatchableField::domain('domain'),
MatchableField::name(),
]);
EntityLink Storage Types
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 byEntityLink::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
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']),
];
}