PHPackages                             dynamic/silverstripe-salsify - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. dynamic/silverstripe-salsify

ActiveSilverstripe-vendormodule

dynamic/silverstripe-salsify
============================

Salsify integration for SilverStripe websites.

3.0.0(1y ago)21.6k↓100%4[8 issues](https://github.com/dynamic/silverstripe-salsify/issues)BSD-3-ClausePHPCI failing

Since Oct 21Pushed 4mo ago5 watchersCompare

[ Source](https://github.com/dynamic/silverstripe-salsify)[ Packagist](https://packagist.org/packages/dynamic/silverstripe-salsify)[ RSS](/packages/dynamic-silverstripe-salsify/feed)WikiDiscussions 3 Synced today

READMEChangelog (6)Dependencies (3)Versions (11)Used By (0)

SilverStripe Salsify
====================

[](#silverstripe-salsify)

[![CI](https://github.com/dynamic/silverstripe-salsify/workflows/CI/badge.svg)](https://github.com/dynamic/silverstripe-salsify/workflows/CI/badge.svg)[![Build Status](https://camo.githubusercontent.com/d1c8b8a950574c3442e939326ce3711ed222934690ffe3f65cd3fe78124fb049/68747470733a2f2f7472617669732d63692e636f6d2f64796e616d69632f73696c7665727374726970652d73616c736966792e7376673f6272616e63683d6d6173746572)](https://travis-ci.com/dynamic/silverstripe-salsify)[![Scrutinizer Code Quality](https://camo.githubusercontent.com/f10f1a41d710f89f7da6fe447763beefa69f3368d3470463ed5debc694e81fb0/68747470733a2f2f7363727574696e697a65722d63692e636f6d2f672f64796e616d69632f73696c7665727374726970652d73616c736966792f6261646765732f7175616c6974792d73636f72652e706e673f623d6d6173746572)](https://scrutinizer-ci.com/g/dynamic/silverstripe-salsify/?branch=master)[![codecov](https://camo.githubusercontent.com/c664b95a5b2208f3638faf49dbfc111a6cb87ac9d98bc704f471a1d89e26d707/68747470733a2f2f636f6465636f762e696f2f67682f64796e616d69632f73696c7665727374726970652d73616c736966792f6272616e63682f6d61737465722f67726170682f62616467652e737667)](https://codecov.io/gh/dynamic/silverstripe-salsify)

[![Latest Stable Version](https://camo.githubusercontent.com/320fa344a012961cad18c8599f95fda6d0c958c0e42004046551fb3b76ec4126/68747470733a2f2f706f7365722e707567782e6f72672f64796e616d69632f73696c7665727374726970652d73686f706966792f762f737461626c65)](https://packagist.org/packages/dynamic/silverstripe-shopify)[![Total Downloads](https://camo.githubusercontent.com/79f69db64b2e00ff6a8d94f3e11d724cfd4bcba02dd148e07526b56921dac61c/68747470733a2f2f706f7365722e707567782e6f72672f64796e616d69632f73696c7665727374726970652d73686f706966792f646f776e6c6f616473)](https://packagist.org/packages/dynamic/silverstripe-shopify)[![Latest Unstable Version](https://camo.githubusercontent.com/cc8e0b1177619c68b6637f678a45198d10e07809d03684fa5eea6107b4ef5764/68747470733a2f2f706f7365722e707567782e6f72672f64796e616d69632f73696c7665727374726970652d73686f706966792f762f756e737461626c65)](https://packagist.org/packages/dynamic/silverstripe-shopify)[![License](https://camo.githubusercontent.com/6129b21c3f239d681c8971fce93afe022d37e7e239af574c16389bbf3e35e302/68747470733a2f2f706f7365722e707567782e6f72672f64796e616d69632f73696c7665727374726970652d73686f706966792f6c6963656e7365)](https://packagist.org/packages/dynamic/silverstripe-shopify)

Salsify integration for SilverStripe websites.

Requirements
------------

[](#requirements)

- SilverStripe ^4.0

Installation
------------

[](#installation)

```
composer require dynamic/silverstripe-salsify

```

License
-------

[](#license)

See [License](license.md)

Table of Contents
-----------------

[](#table-of-contents)

- [Running the task](#running-the-task)
- [Example configuration](#example-configuration)
    - [Extensions](#extensions)
        - [SalsifyIDExtension](#salsifyidextension)
        - [SalsifyFetchExtension](#salsifyfetchextension)
    - [Importer](#importer)
    - [Fetcher](#fetcher)
    - [Mapper](#mapper)
        - [Unique Fields](#unique-fields)
        - [Field Types](#field-types)
            - [Raw](#raw)
            - [SalsifyID, SalsifyUpdatedAt, and SalsifyRelationsUpdatedAt](#salsifyid-salsifyupdatedat-and-salsifyrelationsupdatedat)
            - [Boolean](#boolean)
                - [isTrue](#isTrue)
            - [Literal](#literal)
            - [Files and Images](#files-and-images)
                - [Image Transformation](#image-transformation)
            - [HasOne and HasMany](#hasone-and-hasmany)
                - [HasOne Example](#hasone-example)
                - [ManyRelation Example](#manyrelation-example)
            - [Salsify Relations](#salsify-relations)
        - [Field Fallback](#field-fallback)
        - [Keeping Field Values Without a Salsify Field](#keeping-field-values-without-a-salsify-field)
        - [Extending onBeforeMap](#extending-onbeforemap)
        - [Extending onAfterMap](#extending-onaftermap)
        - [Extending beforeObjectWrite](#extending-beforeobjectwrite)
        - [Extending afterObjectWrite](#extending-afterobjectwrite)
        - [Advanced](#advanced)
            - [Custom Field Types](#custom-field-types)
            - [Skipping Objects](#skipping-objects)
            - [Modify Field Data](#modify-field-data)
            - [Using a Property To Assign a Parent](#using-a-property-to-assign-a-parent)
            - [Creating Virtual Pages with Pages](#creating-virtual-pages-with-pages)
            - [Redirects](#redirects)
            - [Attributes in Group as DataObjects](#attributes-in-group-as-dataobjects)
    - [Single Object Import](#single-object-import)
- [Troubleshooting](#troubleshooting)
- [Maintainers](#maintainers)
- [Bugtracker](#bugtracker)
- [Development and contribution](#development-and-contribution)

Running the task
----------------

[](#running-the-task)

The task can be run from a browser window or the command line. To run the task in the browser go to `dev/tasks` and find `Import products from salsify` or visit `dev/tasks/SalsifyImportTask`.

It is recommended to use the command line, because the task can easily time out in a browser. To run the task in the command line sake must be installed and the command `sake dev/tasks/SalsifyImportTask` must be run.

Example configuration
---------------------

[](#example-configuration)

### Extensions

[](#extensions)

#### SalsifyIDExtension

[](#salsifyidextension)

It is recommended to add `Dynamic\Salsify\ORM\SalsifyIDExtension` as an extension of any object being mapped to. It will add a `SalsifyID` and `SalsifyUpdatedAt` field that can be mapped to. The `SalsifyID` field is used in single object updates.

```
MyObject:
  extensions:
    - Dynamic\Salsify\ORM\SalsifyIDExtension
```

The `SalsifyID` and `SalsifyUpdatedAt` fields will still need to be explicitly mapped in the mapper config.

```
Dynamic\Salsify\Model\Mapper.example:
  mapping:
    \Page:
      SalsifyID:
        salsifyField: 'salsify:id'
        unique: true
      SalsifyUpdatedAt: 'salsify:updated_at'
```

#### SalsifyFetchExtension

[](#salsifyfetchextension)

The `SalsifyFetchExtension` is automatically added to left and main. It will provide a button on data objects that have a salsify mapping to refetch.

To have the button force update `refetch_force_update` and `refetch_force_update_relations` can be set to true on the data object being mapped to. When set to true `refetch_force_update` will force update any non-relation field in salsify. When set to true `refetch_force_update_relations` will force update any relation field in salsify.

```
\Page:
  refetch_force_update: true
  refetch_force_update_relations: true
```

See the [Single Object Import](#single-object-import) section for more setup information.

### Importer

[](#importer)

Importers will run fetchers and mappers. Each importer needs to be passed an importerKey to its constructor. For the rest of the readme `example` will be used for the services.

```
SilverStripe\Core\Injector\Injector:
  Dynamic\Salsify\Model\Importer.example:
    class: Dynamic\Salsify\Model\Importer
    constructor:
      importerKey: example
```

### Fetcher

[](#fetcher)

To set up a fetcher an api key and a channel id need to be provided. The `apiKey` can be in the root Fetcher config, but can also be overridden in a specific service config. The channel id can be found by visiting the channel in Salsify and copying the last section of the url. `https://app.salsify.com/app/orgs//channels/`To find the api key follow [this](https://developers.salsify.com/reference#token-based-authentication)

```
Dynamic\Salsify\Model\Fetcher:
  apiKey: 'api key here'

Dynamic\Salsify\Model\Fetcher.example:
    channel: 'channel id'
```

or

```
Dynamic\Salsify\Model\Fetcher.example:
  apiKey: 'api key here'
  channel: 'channel id'
```

An organization ID can also be included to avoid an account having access to multiple organizations.

```
Dynamic\Salsify\Model\Fetcher:
  organizationID: 'org id'
```

or

```
Dynamic\Salsify\Model\Fetcher.example:
  organizationID: 'org id'
```

The fetcher can also have the timeout changed for http requests. This is not a timeout for Salsify to generate an export. Timeout is in milliseconds and defaults to 2000 ms or 2 seconds. Like an `apiKey` the timeout can be set in the root fetcher config and be overridden by a service config.

```
Dynamic\Salsify\Model\Fetcher:
  timeout: 4000
```

or

```
Dynamic\Salsify\Model\Fetcher.example:
  timeout: 4000
```

### Mapper

[](#mapper)

To set up a mapper, which will map fields from Salsify to SilverStripe, some configuration is needed.

```
Dynamic\Salsify\Model\Mapper.example:
  mapping:
    \Page:
       SKU:
        salsifyField: SKU
        unique: true
       Title: Product Title
```

Each mapper instance will need a service config. Under the `mapping` config one or more classes can be specified for import. Each class can have one or more fields to map, and must have at least one that is unique. All fields have the key of the SilverStripe field to map to. `Title: Product Title` will map `Product Title` from Salsify to `Title` in SilverStripe.

#### Unique Fields

[](#unique-fields)

Like non-unique fields, the key is the SilverStripe field to map to. `salsifyField` is the field from Salsify to map. `unique` is either true or false and will be used as a filter to check against existing records.

```
Dynamic\Salsify\Model\Mapper.example:
  mapping:
    \Page:
      SKU:
        salsifyField: SKU
        unique: true
```

The unique fields will be added to an array, with the values for each product and will be used as a filter to find existing. This allows for multiple compound unique fields.

#### Field Types

[](#field-types)

The built in field types are `Raw`, `Literal`, `File`, `Image`, `HasOne`, `HasMany`. There are some specialized field types that are `SalsifyID`, `SalsifyUpdatedAt`, `SalsifyRelationsUpdatedAt` that are meant for mapping to specific fields without modifications. More types can also be added.

##### Raw

[](#raw)

By default the `Raw` type is used. It will write the salsify property value into the object field.

```
Dynamic\Salsify\Model\Mapper.example:
  example:
    mapping:
      \Page:
        Title: 'Web Title'
```

#### SalsifyID, SalsifyUpdatedAt, and SalsifyRelationsUpdatedAt

[](#salsifyid-salsifyupdatedat-and-salsifyrelationsupdatedat)

Fields that have these types cannot be modified, but will be handled like a raw type.

```
Dynamic\Salsify\Model\Mapper.example:
  example:
    mapping:
      \Page:
        SalsifyID:
          salsifyField: 'salsify:id'
          type: SalsifyID
        SalsifyUpdatedAt:
          salsifyField: 'salsify:updated_at'
          type: SalsifyUpdatedAt
        SalsifyRelationsUpdatedAt:
          salsifyField: 'salsify:relations_updated_at'
          type: SalsifyRelationsUpdatedAt
```

##### Boolean

[](#boolean)

Useful for mapping Yes/No value fields from salsify to the boolean database type.

```
Dynamic\Salsify\Model\Mapper.example:
  example:
    mapping:
      \Page:
        Obsolete:
          salsifyField: 'obsolete'
          type: Boolean
```

###### isTrue

[](#istrue)

The boolean handler also comes with a handy `isTrue` helper method. This is helpful when modifications or skipping has to be done on a Yes/No formatted property.

##### Literal

[](#literal)

To set a field that doesn't have a salsify field a literal field can be used.

```
Dynamic\Salsify\Model\Mapper.example:
  example:
    mapping:
      \Page:
        Author:
          value: 'Chris P. Bacon'
          type: Literal
```

The above example will set the Author field to `Chris P. Bacon` for all mapped pages.

##### Files and Images

[](#files-and-images)

To get an image or file from salsify to map to an object a type needs to be specified.

```
Dynamic\Salsify\Model\Mapper.example:
  example:
    mapping:
      \Page:
        FrontImage:
          salsifyField: Front Image
          type: Image
```

Images and files can also be mapped by ID.

```
Dynamic\Salsify\Model\Mapper.example:
  mapping:
    \Page:
      FrontImageID:
        salsifyField: Front Image
        type: Image
```

If the mapping is specified as an image and it is not a valid image extension, salsify will be used to try and convert the file into a png.

###### Image Transformation

[](#image-transformation)

To cut down on 500 errors caused by trying to resize images when visiting a page images can be transformed by salsify. When an image transformation is updated in the config it will also re-download the image with the new transformations.

```
Dynamic\Salsify\Model\Mapper.example:
  mapping:
    \Page:
      FrontImageID:
        salsifyField: Front Image
        type: Image
        transform:
          - 'c_fit'
          - 'w_1000'
          - 'h_1000'
          - 'dn_300'
          - 'cs_srgb'
```

The above will download `http://a1.images.salsify.com/image/upload/c_fit,w_1000,h_1000,dn_300,cs_srgb/sample.jpg` instead of `http://a1.images.salsify.com/image/upload/sample.jpg`.

To see what transformations salsify supports please visit .

It is recommended to do this to all large files.

##### HasOne and HasMany

[](#hasone-and-hasmany)

has\_one and has\_many relations can be done just about the same. The `HasOne`'s `salsifyField` doesn't matter. The `ManyRelation` type requires a salsify field that is an array. `ManyRelation` can also have a sort column specified. All modifications to the data will be passed through to the mapping relation.

###### HasOne example:

[](#hasone-example)

```
Dynamic\Salsify\Model\Mapper.example:
  mapping:
    \Page:
      Document:
        salsifyField: 'salsify:id'
        type: 'HasOne'
        relation:
          \Documentobject:
            Title: Document Title
```

###### ManyRelation example:

[](#manyrelation-example)

```
namespace {
    use SilverStripe\CMS\Model\SiteTree;

    class Page extends SiteTree
    {

        /**
         * @var array
         */
        private static $many_many = [
            'Features' => Feature::class,
        ];

        private static $many_many_extraFields = [
            'Features' => [
                'SortOrder'=> 'Int',
            ],
        ];
    }
}
```

```
