Creating, using, and extending the transfer objects
  • 8 Minutes To Read
  • Print
  • Share
  • Dark
    Light

Creating, using, and extending the transfer objects

  • Print
  • Share
  • Dark
    Light

Transfer objects are simple data containers. Their purpose is to retrieve a standardized way to access data and to get more expressive method signatures. Transfer objects are available everywhere in the system.

This article will teach you how to create and use transfer objects.

Creating transfer objects

Transfer objects are defined in XML. The specific classes are generated by an internal script.

XML definition

The following example describes a Customer with email, first name, last name, and the isGuest flag:

<?xml version="1.0"?>
<transfers xmlns="spryker:transfer-01"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="spryker:transfer-01 http://static.spryker.com/transfer-01.xsd">

    <transfer name="Customer">
        <property name="email" type="string"/>
        <property name="firstName" type="string"/>
        <property name="lastName" type="string"/>
        <property name="isGuest" type="bool"/>
    </transfer>

</transfers>

Available types

You can use any name for your transfer objects. But make sure that the names start with a small letter and use the camelCase format.
As for the types, you can use PHP native types: int, string, bool, and array. In case you want to create a nested transfer object, just use the name of the transfer object as the type. You can also define collections of objects with the [] symbols.

<transfer name="MyTransfer">
    <property name="foo"    type="int" />
    <property name="bar"    type="string" />
    <property name="baz"    type="bool" />
    <property name="bat"    type="array" />
    <property name="item"   type="Foo" /> <!-- Foo is the name of another transfer object-->
    <property name="items"  type="Foo[]" />
</transfer>

Transfer object associative property attribute allows working with associative arrays and collections. An associative array is an array with a string index where instead of linear storage, each value can be assigned a specific key.

The associative attribute can be used with all PHP native data types or collections.

Schema generation example:

<?xml version="1.0"?>
<transfers xmlns="spryker:transfer-01"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="spryker:transfer-01 http://static.spryker.com/transfer-01.xsd" >

    <transfer name="Foo">
        <property name="bars" type="Bar[]" singular="bar" associative="true" />
        <property name="bazs" type="array" singular="baz" associative="true" />
    </transfer>

    <transfer name="Bar">
        <property name="test" type="string"/>
    </transfer>

</transfers>

Examples of associative arrays usage:

$fooTransfer = new FooTranfer();

// adding transfers with associated keys
$fooTransfer->addBar('a', new Bar());
$fooTransfer->addBar('b', new Bar());

// setting a transfer object with associated keys
$bars = new \ArrayObject([
    'a' => new Bar(),
    'b' > new Bar(),
]);
$fooTransfer->setBars($bars);

// adding associated array items
$fooTransfer->addBaz('x', 'X');
$fooTransfer->addBaz('y', 'Y');

// setting an associated array
$fooTransfer->setBazs([
    'x' => 'X',
    'y' => 'Y',
]);

Using the transfer objects

The example below is a typical use case for a transfer object. The Customer object is created and the setters are used to set email, first, and last name:

<?php
$customerTransfer = new CustomerTransfer();
$customerTransfer
    ->setEmail('[email protected]')
    ->setFirstName('John')
    ->setLastName('Doe');

echo $customerTransfer->getFirstName(); // echos 'John'

Object nesting

Transfer objects can be nested. For instance, a cart object contains several items like this:

<?php
// first cart item
$cartItem1 = new ItemTransfer();
$cartItem1->setSku('123abc')->setQuantity(1);

// second cart item
$cartItem2 = new ItemTransfer();
$cartItem2->setSku('888abc')->setQuantity(1);

// a cart with two items
$cartTransfer = new CartTransfer();
$cartTransfer->addItem($cartItem1)->addItem($cartItem2);

// returns ItemTransfer[] as ArrayObject
$items = $cartTransfer->getItems();

Checking the required fields

Require methods have been deprecated in version 3.25.0 of the Transfer module and replaced by the get-or-fail methods.

In general, a transfer object must not know which fields are required, as it can be used for different use cases. But when you use a transfer object, you always expect the existence of specific parameters. This can be checked with a special require-method for each property:

<?php
// This throws a RequiredTransferPropertyException if the first name is not set:
$customerTransfer->requireFirstName()->getFirstName();

Using the get-or-fail methods

Starting from version 3.25.0 of the Transfer module, transfers expose the new get-or-fail methods that are meant to replace the deprecated require methods. Here is how it works: for each nullable property, an extra getter method is generated, along with the regular one. This new getter will throw an exception when trying to get the value of a property that was not previously set. The name of the method is composed of the get prefix, the name of the property, and the OrFail suffix.

$fooBarTransfer = new FooBarTransfer();

// happy case
$fooBarTransfer->setFoo('foo');
$fooBarTransfer->getFooOrFail();

// unhappy case
$fooBarTransfer->getBarOrFail(); // exception will be thrown

Keep in mind that these new methods are not generated for the array and collection transfer properties, as these properties cannot be nullable by design.

Property constants

The transfer object exposes all properties as constants which can be used in forms and tables:

<?php
ItemTransfer::SKU; // = 'sku'
CustomerTransfer::FIRST_NAME; // = 'firstName'

File location

Most of the modules define transfer objects in a dedicated XML file: (ModuleNamespace)/Shared/Module/Transfer/module.transfer.xml.

Therefore, you can find the XML definition, for example, for the CustomerGroup module in vendor/spryker/spryker/Bundles/CustomerGroup/src/Spryker/Shared/CustomerGroup/Transfer/customer_group.transfer.xml.

Adding more file locations

If you have third-party modules using our transfer objects, you can easily add additional source directories in your projects. To do so, extend Spryker\Zed\Transfer\TransferConfig and return all additional glob patterns from getAdditionalSourceDirectoryGlobPatterns().

Glob patterns
The Transfer module uses PHP's glob() function to resolve paths.
For more information, see PHP documentation.

Let’s say you have a custom extension package called my-vendor/my-package that uses transfer objects. Using Composer, by default, this package will be installed under vendor/my-vendor/my-package.
If you want your transfer objects to be created from definitions stored under vendor/my-vendor/my-package/src/Transfer, provide the necessary glob pattern like this:

<?php

namespace Pyz\Zed\Transfer;

use Spryker\Shared\Application\ApplicationConstants;
use Spryker\Shared\Config\Config;
use Spryker\Zed\Transfer\TransferConfig as SprykerTransferConfig;

class TransferConfig extends SprykerTransferConfig
{

    /**
     * @return string[]
     */
    protected function getAdditionalSourceDirectoryGlobPatterns()
    {
        return [
            APPLICARION_ROOT_DIR . '/vendor/my-vendor/my-package/src/Transfer/',
        ];
    }

}
Naming
Make sure your transfer object definition files end with .transfer.xml (even for your custom packages).

Transfer object generation

To generate the objects, run:

vendor/bin/console transfer:generate

This command retrieves all *.transfer.xml files from the project- and core-level, merges them, and generates PHP classes: src/Generated/Shared/Transfer/(Name)Transfer.php.

Transfer file expansion

Transfer objects can be expanded from different bundles. Any other module can add properties to the existing transfer objects. For instance, the Tax module may expect a customer tax-id. So in the tax.transfer.xml, you can add the required properties to the customer. But keep in mind that it is not possible to remove existing properties or to change their type.

<?xml version="1.0"?>
<transfers xmlns="spryker:transfer-01"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="spryker:transfer-01 http://static.spryker.com/transfer-01.xsd">

    <transfer name="Customer">
        <property name="taxId" type="int"/>
    </transfer>

</transfers>

After the transfers have been generated, you can set and get the customer’s taxId like this:

<?php
use \Generated\Shared\Transfer\CustomerTransfer;

$customerTransfer = new CustomerTransfer();
$customerTransfer->setTaxId(54321);
$taxId = $customerTransfer->getTaxId();

Transfer strict types

Starting from version 3.27.0 of the?Transfer?module, transfers support the so-called strict mode. In the strict mode, all the relevant get-, set- and add- methods are generated with PHP data types in place of their arguments and return values. The strict mode is disabled by default for backward compatibility reasons and can be enabled for specific transfer properties or for an entire transfer. To enable the strict mode, you need to use the new strict XML attribute while defining a transfer. If the strict attribute is applied to a property, then only that property will be considered as a strict property while applying the attribute to a <transfer/> element means that the whole transfer is supposed to be strict:

<?xml version="1.0"?>
<transfers xmlns="spryker:transfer-01"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="spryker:transfer-01 http://static.spryker.com/transfer-01.xsd">

    <transfer name=?Foo? strict=?true?>
        ?
    </transfer>
	
    <transfer name=?Bar?>
        ?
        <property name=?test? type="string" strict=?true?/>
        ?
    </transfer>
	
</transfers>

In the example above, Foo transfer will be fully strict, meaning that all of its getters, setters, and adders will have data types where possible. The only acceptable value of this new attribute is true.

There is a limitation to keep in mind here: the usage of the strict attribute must be consistent for the same transfer/property across different definitions. In other words, if some transfer or property is defined as strict in one module, you cannot have it defined as non-strict in another module or at the project level. Otherwise, an exception will be thrown when merging the transfer definition.

Nullable arrays vs. collections

In the strict mode, there is a concept of nullable arrays. Each strict transfer property of the array type without the singular or associative attribute can accept null as its value. This differs from the non-strict mode, where array transfer properties cannot be nullable by design. All the nullable arrays have the get-or-fail method in place, same as with all the other non-array nullable transfer properties.

Singular name for associative array properties

Associative array properties in the strict mode must have the singular attribute defined, and the value of this attribute must not match the value of the name attribute, otherwise, an exception is thrown during the transfer generation.

Get-collection-item methods

Transfers expose the new get-collection-item methods for all the associative collection transfer properties. For example, for this property definition

…
<property name=“items” type=“string[]” singular=“item” associative=“true”/>

a new method ::getItem($key): string will be available.

If you extend the existing transfer object, don't copy over all fields, but only the fields you want to add on top.

Transfer definition validation

Starting from the version 3.28.0 of the Transfer module, you can validate transfer XML definition files against a configurable XSD schema. This validation is ran as part of the general transfer validation process triggered by the transfer:validate command. You can enable or disable the validation by overriding the \Spryker\Zed\Transfer\TransferConfig::isTransferXmlValidationEnabled() method. Spyker provides default XSD schema for validation that can be found in vendor/transfer/data/definition/transfer-01.xsd. You can change this schema by overriding \Spryker\Zed\Transfer\TransferConfig::getXsdSchemaFilePath().

Under the hood, the validation tool uses regular \DOMDocument::schemaValidate() to validate the transfer definition file against the provided schema, so see the documentation for this method to find out more about possible validation errors in case there are any.

Attribute value of the root element

The only valid attribute value of the root <transfers></transfers> element is spryker:transfer-01 http://static.spryker.com/transfer-01.xsd, even though now the validation also allows spryker:transfer-01 https://static.spryker.com/transfer-01.xsd (https vs http). This is done for backward compatibility reasons. Pay attention to this when creating transfer definition files and always use the valid value.

Related Spryks

You can use the following definitions to generate the related code:

  • Add Shared Transfer Schema

See the Spryk documentation for details.

Was This Article Helpful?