导入时拆分分组数据

Split grouped data at import

我有一个 excel 电子表格,如下所示:

这将产生具有该属性的九个模型:

我尝试使用 Laravel Excel 找到解决方案,但我被卡住了。问题是一行变成三行,我不知道如何实现,因为我无法覆盖这些行。

<?php

namespace App\Imports;

use App\Models\MyModel;
use Maatwebsite\Excel\Concerns\OnEachRow;
use Maatwebsite\Excel\Concerns\ToModel;
use Maatwebsite\Excel\Row;

//use Maatwebsite\Excel\Concerns\WithMapping;

class ForecastImport implements ToModel, OnEachRow
{
    function headingRow(): int { return 2; }

    /**
    * @param array $row
    *
    * @return \Illuminate\Database\Eloquent\Model|null
    */
    public function model(array $row)
    {
        dd($row);
        return new MyModel([
            'group' => $row[0],
            'column' => $row[1],
            'value' => $row[2]
        ]);
    }

    public function onRow(Row $row)
    {
        $entries = array_map(function($entry) {
            return [
                'group' => $entry[0],
                'column' => $entry[1],
                'value' => $rowentry2]
            ];
        }, array_chunk($row->toArray(), 3));
        /*foreach($entries as $entry) {
            $this->model($entry);
        }*/
        // One row became three
        return $entries;
    }
}

看来您的方向是正确的。正如相关 documentation 中提到的,OnEachRow 问题据说可以让您更好地控制非标准案例场景中发生的事情,其中​​整行不一定是直接模型表示(与ToModel关注)。文档甚至建议不要将两者混用。

就您而言,是什么阻止您按照您希望的方式填充模型?虽然我不太了解你的模型的三字段结构,但我建议你简单地去:

<?php

namespace App\Imports;

use App\Models\MyModel;
use Maatwebsite\Excel\Concerns\OnEachRow;
use Maatwebsite\Excel\Row;

class ForecastImport implements OnEachRow
{
    function headingRow(): int { return 2; }

    public function onRow(Row $row)
    {
        //splitting the 9-column array into chunks of 3
        $entries = array_map(function($entry) {
            return [
                'group' => $entry[0],
                'column' => $entry[1],
                'value' => $entry[2]
            ];
        }, array_chunk($row->toArray(), 3));

        //populate a model with each chunk
        foreach($entries as $entry) {
            MyModel::create($entry);
        }
    }
}

但同样,我看不到与您正在解析的列和组的相关性,因为您将只分配值。无论如何,您可以自己修改它 - 重点是使用 OnEachRow,您可以更自由地自己管理模型创建。在这种情况下,我们每行创建 3 个模型记录,所以我想这就足够了。如果我误解了什么,请纠正我。编码愉快!

如果数据库中有以下列table

group|column|value

那你可以

<?php

namespace App\Imports;

use Maatwebsite\Excel\Concerns\OnEachRow;
use Maatwebsite\Excel\Concerns\ToModel;
use Maatwebsite\Excel\Concerns\WithHeadingRow;
use Maatwebsite\Excel\Imports\HeadingRowFormatter;
use Maatwebsite\Excel\Row;

$tempHeading = null;
HeadingRowFormatter::extend('custom', function ($value, $key) use (&$tempHeading) {

    if (!empty($value)) {

        $tempHeading = $value;
    }

    return $key . "-" . $tempHeading;


});

HeadingRowFormatter::default('custom');


class UsersImport implements ToModel, WithHeadingRow, OnEachRow
{
    /**
     * @param array $row
     *
     * @return \Illuminate\Database\Eloquent\Model|null
     */


    private $firstRow = [];

    public function model(array $row)
    {

        //onrow same logic you can apply here also
        //any one you can use onRow or model

    }

    public function headingRow(): int
    {
        return 1;
    }


    public function onRow(Row $row)
    {

        $rowIndex = $row->getIndex();
        $row = $row->toArray();

        if ($rowIndex == 2) {

            $this->firstRow = $row;

        } else {

            $result = collect($row)->transform(function ($value, $key) {


                $columnName = $this->firstRow[$key];
                $group = explode("-", $key)[1];

                return [
                    'group' => $group,
                    'column' => $columnName,
                    'value' => $value
                ];
            })->values()->toArray();

            echo "<br>****************Start of Row***************<br>";
            dump($result);
            echo "<br>****************End of Row***************<br>";


        }

    }

}

所以 $result 的输出是

所以每一行都会得到多维数组所以我们可以使用

ModelName::insert($result)

或者如果您在数据库 table 中有以下列,如下所示

group|column1|column2|column3

然后

<?php

namespace App\Imports;

use Maatwebsite\Excel\Concerns\OnEachRow;
use Maatwebsite\Excel\Concerns\ToModel;
use Maatwebsite\Excel\Concerns\WithHeadingRow;
use Maatwebsite\Excel\Imports\HeadingRowFormatter;
use Maatwebsite\Excel\Row;

$tempHeading = null;
HeadingRowFormatter::extend('custom', function ($value, $key) use (&$tempHeading) {

    if (!empty($value)) {

        $tempHeading = $value;
    }

    return $key . "-" . $tempHeading;


});

HeadingRowFormatter::default('custom');


class UsersImport implements ToModel, WithHeadingRow, OnEachRow
{
    /**
     * @param array $row
     *
     * @return \Illuminate\Database\Eloquent\Model|null
     */


    private $firstRow = [];

    public function model(array $row)
    {

        //onrow same logic you can apply here also
        //any one you can use onRow or model

    }

    public function headingRow(): int
    {
        return 1;
    }


    public function onRow(Row $row)
    {


        $rowIndex = $row->getIndex();
        $row = $row->toArray();

        if ($rowIndex == 2) {

            $this->firstRow = $row;

        } else {
            $rows=[];
             foreach ($row as $key=>$value){
                 $columnName = $this->firstRow[$key];
                 $group = explode("-", $key)[1];
                 $rows[$group]['group']=$group;
                 $rows[$group][$columnName]=$value;
             }
             
            echo "<br>****************Start of Row***************<br>";
            dump(array_values($rows));

            echo "<br>****************End of Row***************<br>";


        }

    }

}

dump(array_values($rows));

的输出

我想,如果 $rows 是可以事先修改的实例属性,那么 onRow()model() 和类似方法将获得真正的“行”。其他解决方案对我来说看起来很老套,因为我只需要修改原始输入并希望在其他解决方案无法完成之后照常继续。由于 $rows 不是实例 属性,我需要进行一些更改:

app/Extends/Maatwebsite/Excel/TransformRows.php

namespace App\Extends\Maatwebsite\Excel;

interface TransformRows
{
    public function transformRows(array $rows): array;
}

app/Extends/Maatwebsite/Excel/Sheet.php

namespace App\Extends\Maatwebsite\Excel;

use Maatwebsite\Excel\Sheet as ExcelSheet;

class Sheet extends ExcelSheet
{
    public $test = 'test';
    private array $rows = [];
    /**
     * @param object   $import
     * @param int|null $startRow
     * @param null     $nullValue
     * @param bool     $calculateFormulas
     * @param bool     $formatData
     *
     * @return array
     */
    public function toArray($import, int $startRow = null, $nullValue = null, $calculateFormulas = false, $formatData = false)
    {
        $rows = parent::toArray($import, $startRow, $nullValue, $calculateFormulas, $formatData);
        if ($import instanceof TransformRows) {
            $rows = $import->transformRows($rows);
        }
        $this->rows = $rows;
        return $rows;
    }
}

app/Extends/Maatwebsite/Excel/Reader.php

namespace App\Extends\Maatwebsite\Excel;

use Maatwebsite\Excel\Reader as BaseReader;
use App\Extends\Maatwebsite\Excel\Sheet;
use Maatwebsite\Excel\Concerns\SkipsUnknownSheets;
use Maatwebsite\Excel\Exceptions\SheetNotFoundException;

class Reader extends BaseReader
{

    /**
     * @param $import
     * @param $sheetImport
     * @param $index
     *
     * @return Sheet|null
     * @throws \PhpOffice\PhpSpreadsheet\Exception
     * @throws SheetNotFoundException
     */
    protected function getSheet($import, $sheetImport, $index)
    {
        try {
            return Sheet::make($this->spreadsheet, $index);
        } catch (SheetNotFoundException $e) {
            if ($import instanceof SkipsUnknownSheets) {
                $import->onUnknownSheet($index);

                return null;
            }

            if ($sheetImport instanceof SkipsUnknownSheets) {
                $sheetImport->onUnknownSheet($index);

                return null;
            }

            throw $e;
        }
    }
}

app/Providers/AppServiceProvider.php

namespace App\Providers;

use App\Extends\Sheet as ExtendsSheet;
use App\Extends\Maatwebsite\Excel\Reader as ExtendsReader;
use Illuminate\Support\ServiceProvider;
use Maatwebsite\Excel\Excel;
use Maatwebsite\Excel\Files\Filesystem;
use Maatwebsite\Excel\QueuedWriter;
use Maatwebsite\Excel\Sheet;
use Maatwebsite\Excel\Writer;

class AppServiceProvider extends ServiceProvider
{
    /**
     * Register any application services.
     *
     * @return void
     */
    public function register()
    {
        $this->app->bind('excel', function ($app) {
            return new Excel(
                $app->make(Writer::class),
                $app->make(QueuedWriter::class),
                $app->make(ExtendsReader::class),
                $app->make(Filesystem::class)
            );
        });
    }
}