CSV import many_many

I struggled to find any posts on how to implement a csv import to a many_many relationship. I’m posting my solution to it here, so hopefully it is helpful to someone else.

Building on SS5 documentation example for importing CSV data:

Sample CSV content:

"Number","Name","Birthday","CoachIDs"
11,"John Doe","2,5"
12,"Jane Johnson", "2,3"
13,"Jimmy Dole","3,5"

Data model for Player:

namespace App\Model;

use SilverStripe\ORM\DataObject;

class Player extends DataObject
{
    private static $db = [
      'PlayerNumber' => 'Int',
      'FirstName' => 'Text',
      'LastName' => 'Text'
    ];

    private static $many_many = [
      'Coaches' => Coach::class,
    ];
}

Data model for Coach:

namespace App\Model;

use SilverStripe\ORM\DataObject;

class Coach extends DataObject
{
    private static $db = [
      'Name' => 'Text',
    ];

    private static $belongs_many_many = [
      'Players' => Player::class,
    ];
}

Custom bulk loader:

namespace App\Admin;

use App\Model\Coach;
use SilverStripe\Dev\CsvBulkLoader;

class PlayerCsvBulkLoader extends CsvBulkLoader
{
    public function __construct($objectClass)
    {
        $this->columnMap = [
            'Number' => 'PlayerNumber',
            'Name' => '->importFirstAndLastName',
            'CoachIDs' => '->importCoaches'    // eg. "1,5,6"
        ];

        $this->duplicateChecks = [
            'Number' => 'PlayerNumber',
        ];

        parent::__construct($objectClass);
    }

    public static function importFirstAndLastName(&$obj, $val, $record)
    {
        $parts = explode(' ', $val);
        if (count($parts) != 2) {
            return false;
        }
        $obj->FirstName = $parts[0];
        $obj->LastName = $parts[1];
    }

    public static function importCoaches(&$obj, $val, $record)
    {
        $ids = explode(',', $val);
	foreach ($ids as $id) {
           // check if coach exists
	   if ($id) {
                if ($coach = Coach::get_by_id($id)) {
                    $obj->Coaches()->add($coach);
               }
	   } else {
             // add coach
             $coach = Coach::create();
             $coach->ID = $id;
             $coach->write();
             $obj->Coaches()->add($coach);
	}
    }
}