我希望扩展Silverstripe的CSVBulkLoader类,以便在导入之前/导入时进行一些业务逻辑。
在 WineAdmin类(扩展了ModelAdmin)中,我定义了一个自定义加载器,该加载器定义了 $ model_importers 属性:
//WineAdmin.php
private static $model_importers = [
'Wine' => 'WineCsvBulkLoader'
];
在 WineCsvBulkLoader 类中, $ columnMap 属性将CSV列映射到SS DataObject列:
//WineCsvBulkLoader.php
use SilverStripe\Dev\CsvBulkLoader;
class WineCsvBulkLoader extends CsvBulkLoader
{
public $columnMap = [
// csv columns // SS DO columns
'Item Number' => 'ItemNumber',
'COUNTRY' => 'Country',
'Producer' => 'Producer',
'BrandName' => 'BrandName',
// etc
];
此外, $ duplicateChecks 属性设置为查找重复项。
public $duplicateChecks = [
'ItemNumber' => 'ItemNumber'
];
}
在docs中,我找到了一个示例方法的一些代码,该示例方法将列中的数据分为两部分,并将这些部分映射到类的单独列中:
public static function importFirstAndLastName(&$obj, $val, $record)
{
$parts = explode(' ', $val);
if(count($parts) != 2) return false;
$obj->FirstName = $parts[0];
$obj->LastName = $parts[1];
}
以下是我希望进行的其他一些改进:
我不是在寻找完整的答案,而是对任何见解表示赞赏。
答案 0 :(得分:2)
我将尝试根据SilverStripe 4.2.0回答您的一些问题:
根据CsvBulkLoader::findExistingObject
中的逻辑,重复检查属性用于帮助查找现有记录以对其进行更新(而不是创建)。它将使用数组中定义的值,以便找到与给定值匹配的 first 记录并返回它。
当重复项时,
$duplicateChecks
属性实际上是做什么的?它会跳过记录吗?
没什么,它只会返回找到的第一条记录。
我可以在这里使用回调吗?
种类。您可以在CsvBulkLoader实例上使用一种方法,但不能直接将其传递给回调(例如,从_config.php等)。示例:
public $duplicateChecks = [
'YourFieldName' => [
'callback' => 'loadRecordByMyFieldName'
]
];
/**
* Take responsibility for loading a record based on "MyFieldName" property
* given the CSV value for "MyFieldName" and the original array record for the row
*
* @return DataObject|false
*/
public function loadRecordByMyFieldName($inputFieldName, array $record)
{
// ....
注意:单元测试当前不覆盖重复检查回调。 CsvBulkLoaderTest中有一个待办事项可以添加它们。
$obj
是最终的导入对象吗?如何处理?
您可以看到在CsvBulkLoader::processRecord
中调用这些魔术方法的地方:
if ($mapped && strpos($this->columnMap[$fieldName], '->') === 0) {
$funcName = substr($this->columnMap[$fieldName], 2);
$this->$funcName($obj, $val, $record); // <-------- here: option 1
} elseif ($obj->hasMethod("import{$fieldName}")) {
$obj->{"import{$fieldName}"}($val, $record); // <----- here: option 2
} else {
$obj->update(array($fieldName => $val));
}
这实际上有点误导,特别是因为该方法的PHPDoc说“请注意,未使用columnMap”。但是,将优先考虑columnMap
属性中的值为->myMethodName
的值。在您链接到的文档以及框架的单元测试中的CustomLoader
测试实现中,他们都使用以下语法专门针对该列的处理程序:
$loader->columnMap = array(
'FirstName' => '->importFirstName',
在这种情况下,$obj
是您要更新的DataObject(例如Member
)。
如果不这样做,则可以在要导入的DataObject上定义importFirstName
,然后上面代码中的elseif
将调用该函数。在这种情况下,不提供$obj
,因为您可以改用$this
。
“是否是最终的导入对象”-是的。它在代码所在的循环之后被写入:
// write record
if (!$preview) {
$obj->write();
}
需要使用您的自定义函数将数据设置为$obj
(如果使用$this
样式,则将数据设置为importFieldName
)而不是将其写入。
$val
似乎是要导入的csv中的列的值。正确吗?
是的,在应用任何格式之后。
$record
中包含什么?
这是在其上执行格式化回调后提供给CSV的记录的源行(为上下文提供)。
我希望这对您有所帮助,并且您可以实现想要实现的目标!框架的这一部分最近可能没有受到太多关注,所以请随时提出请求以任何方式进行改进,即使只是文档更新!祝你好运。