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);
}
}
}