使用 Laravel 将经过验证的制表符分隔文件导入 MySQL

Import validated tab delimited file to MySQL with Laravel

我正在尝试上传制表符分隔的文本文件。 问题可能是列并不总是以相同的顺序排列。 一旦上传了喜欢的ID,请执行以下步骤:

  1. 验证列名。如果验证通过 -> 2
  2. 逐行读取文件(最好跳过列名)并验证一些值。如果一行通过验证 -> 3.
  3. 创建模型 object 并将其存储在数组中以供以后批量插入。
  4. 对文件中的所有行重复。
  5. 完成所有行并验证所有内容后,批量插入所有 objects。

到目前为止,我已经完成了上传部分并尝试了一些不同的解决方案。但我现在几乎被困住了。 我将从控制器粘贴我的代码(请记住文件中有 40 列我刚刚编写了其中的一些):

public function store()
{

    $file = Input::file('file');

    $rules = array(
        'file' => 'required|mimes:txt'
    );

    $validator = Validator::make(array('file'=> $file), $rules);

    if($validator->passes()){

        foreach(file($file) as $row) {

            $row = explode("\t", $row);

            $validator = Validator::make(array(
                'X'=> $row[0],
                'Y'=> $row[1],
                'year'=> $row[2],
                'provnr'=> $row[3],
                'id'=> $row[4]

            ), Sample::$insertRules);

            if($validator->passes()){

                $sample = New Sample;
                $sample->X                  = $row[0];
                $sample->Y                  = $row[1];
                $sample->year               = $row[2];
                $sample->provnr             = $row[3];
                $sample->costumer_id        = $row[4];

                $sample->save();

            } else {

                Session::flash('notice', 'Something is wrong!');
                Session::flash('sample', $row[3]);

                return Redirect::to('import');
            }

        }

        exit;

        Session::flash('success', 'Upload successfully');
        return Redirect::to('import');
    }
    else {
        // redirect back with errors.
        return Redirect::to('import')->withInput()->withErrors($validator);
    }

}

这很好用(还没有批量插入,稍后我会倾向于),但我不是很喜欢它,我认为有更好的解决方案。 别介意验证,它还没有真正完成。

如果我 print_r 您可以在该文本下方找到的所有行,我最终会得到什么。 现在有点困难,因为列名是第一个数组。沿线的某个地方 id 喜欢将其删除。因为它不会通过其他行验证。

Array ( [0] => x [1] => y [2] => År [3] => Provnr ) 
Array ( [0] => 1315903.24 [1] => 6213877.72 [2] => 2014 [3] => 223 )
Array ( [0] => 1315819.62 [1] => 6213937.42 [2] => 2014 [3] => 224 )

所以,我很想知道你们对此的想法。你有什么想法吗?

我认为以某种方式将数组转换为 objects 会更好。所以我得到了类似数据库结果的东西。我只是不知道怎么办。 我希望能够在我的 foreach 循环中编写 $row['X'] 。我认为那会好得多。 有什么方法可以做到这一点吗?列名将是每个 object 的标识。

非常感谢您的帮助!

编辑:

所以现在问题出在我的文件的特殊字符上。 我根据 Bogdan 的评论进行了一些编辑。 我已经包含了完整的 $columnMap 代码现在看起来像这样:

    $file = Input::file('file');


    $rules = array(
        'file' => 'required|mimes:txt'
    );

    $validator = Validator::make(array('file'=> $file), $rules);

    if($validator->passes()){

        $columns = [];

        $columnMap = [
            'x'                         =>      'X',
            'y'                         =>      'Y',
            'Ar'                        =>      'Year',
            'Provnr'                    =>      'Provnr',
            'Markning'                  =>      'costumer_id',
            'pH'                        =>      'pH',
            'P_AL'                      =>      'P_AL',
            'P_HCl'                     =>      'P_HCl',
            'K_AL'                      =>      'K_AL',
            'K_HCl'                     =>      'K_HCl',
            'Mg_AL'                     =>      'Mg_AL',
            'Cu_HCl'                    =>      'Cu_HCl',
            'K_Mg_kvot'                 =>      'K_Mg_kvot',
            'Bor'                       =>      'Bor',
            'Ca_AL'                     =>      'Ca_AL',
            'fe'                        =>      'fe',
            'al'                        =>      'al',
            'Mullhalt'                  =>      'Mullhalt',
            'Total_lerhalt'             =>      'Total_lerhalt',
            'Sand_grovmo'               =>      'Sand_grovmo',
            'Volymvikt'                 =>      'Volymvikt',
            'T_varde'                   =>      'T_värde',
            'S_varde'                   =>      'S_värde',
            'Basmattnadsgrad'           =>      'Basmättnadsgrad',
            'Cd_HNO3'                   =>      'Cd_HNO3',
            'Kalkbehov'                 =>      'Kalkbehov',
            'Jordart'                   =>      'Jordart',
            'Fin_lerhalt'               =>      'Fin_lerhalt',
            'Zn'                        =>      'Zn',
            'Cu'                        =>      'Cu',
            'Cr'                        =>      'Cr',
            'Ni'                        =>      'Ni',
            'Pb'                        =>      'Pb',
            'Hg'                        =>      'Hg',
            'Mineralkvave_Kg_N_ha'      =>      'Mineralkväve_Kg_N_ha',
            'Mineralkvave_NO3_N'        =>      'Mineralkväve_NO3_N',
            'Mineralkvave_NH4_N'        =>      'Mineralkväve_NH4_N',
            'Mineralkvave_djup'         =>      'Mineralkväve_djup',
            'Cystnematoder_potatis'     =>      'Cystnematoder_potatis',
            'Cystnematoder_betor'       =>      'Cystnematoder_betor',
            'Cystnematoder_spannmal'    =>      'Cystnematoder_spannmål',
            'Ovrigt'                    =>      'Övrigt'
        ];

        foreach(file($file) as $i => $row)
        {
            $row = explode("\t", $row);

            if($i == 0)
            {
                $columns = $row;

                array_walk($columns, function (&$item)
                {
                    $item = str_replace(
                        ['ä', 'å', 'ö', 'Ä', 'Å', 'Ö'],
                        ['a', 'a', 'o', 'A', 'A', 'O'],
                        utf8_encode($item)
                    );
                });

                continue;
            }

            $_row = array();
            array_walk($row, function ($value, $index) use (&$_row, $columns, $columnMap)
            {
                $_row[$columnMap[$columns[$index]]] = $value; //*The issue is here.*
            });
            $row = $_row;

            $validator = Validator::make($row, Sample::$insertRules);

            if($validator->passes()){

                $sample = New Sample;

                foreach ($row as $property => $value)
                    $sample->{$property} = $value;

                $sample->save();
            } else
            {
                Session::flash('notice', 'Something is wrong!');

                return Redirect::to('import');
            }
        }

我必须 utf8_encode 文件的 header ,否则它不会工作。这可能是文本文件的问题吗? 在需要对具有值的行执行 array_walk 之前,它似乎一直在工作。 然后我得到这个错误:Undefined index: Ovrigt

如果我 var_dump $columns 它看起来像这样:

array(42) { [0]=> 字符串(1) "x" [1]=> 字符串(1) "y" [2]=> 字符串( 2) "Ar" [3]=> 字符串(6) "Provnr" [4]=> 字符串(8) "Markning" [5]=> 字符串(2) "pH" [ 6]=> 字符串(4) "P_AL" [7]=> 字符串(5) "P_HCl" [8]=> 字符串(4) "K_AL" [9]=> 字符串(5) "K_HCl" [10]=> 字符串(5) "Mg_AL" [11]=> 字符串(6) "Cu_HCl" [12]=> 字符串(9) "K_Mg_kvot" [13] => 字符串(3) "Bor" [14]=> 字符串(5) "Ca_AL" [15]=> 字符串(2) "fe" [16]=> 字符串(2) "al" [17]=> 字符串(8) "Mullhalt" [18]=> 字符串(13) "Total_lerhalt" [19]=> 字符串(11) "Sand_grovmo" [20]=>字符串(9) "Volymvikt" [21]=> 字符串(7) "T_varde" [22]=> 字符串(7) "S_varde" [23]=> 字符串(15) "Basmattnadsgrad" [24]=> 字符串(7) "Cd_HNO3" [25]=> 字符串(9) "Kalkbehov" [26]=> 字符串(7) "Jordart" [27]=> 字符串( 11) "Fin_lerhalt" [28]=> 字符串(2) "Zn" [29]=> 字符串(2) "Cu" [30]=> 字符串(2) "Cr" [ 31]=> 字符串(2) "Ni" [32]=> 字符串(2) "Pb" [33]=> 字符串(2) "Hg" [34]=> 字符串(20) "Mineralkvave_Kg_N_ha" [35]=> 字符串(18) "Mineralkvave_NO3_N" [36]=> 字符串(18) "Mineralkvave_NH4_N" [37]=> 字符串(17) "Mineralkvave_djup" [38] => 字符串(21) "Cystnematoder_potatis" [39]=> 字符串(19) "Cystnematoder_betor" [40]=> 字符串(22) "Cystnematoder_spannmal" [41]=> 字符串(8) "Ovrigt " }

如果var_dump $columnMap它看起来像这样:

array(42) { ["x"]=> 字符串(1) "X" ["y"]=> 字符串(1) "Y" ["Ar"]=> 字符串(4) "Year" ["Provnr"]=> 字符串(6) "Provnr" ["Markning"]=> 字符串(11) "costumer_id" ["pH"]=> 字符串(2) "pH" ["P_AL"]=> 字符串(4) "P_AL" ["P_HCl"]=>字符串(5) "P_HCl" ["K_AL"]=> 字符串(4) "K_AL" ["K_HCl"]=> 字符串(5) "K_HCl" ["Mg_AL"]=> 字符串(5) "Mg_AL" ["Cu_HCl"]=> 字符串(6) "Cu_HCl" ["K_Mg_kvot"]=> 字符串(9) "K_Mg_kvot" [ "Bor"]=> 字符串(3) "Bor" ["Ca_AL"]=> 字符串(5) "Ca_AL" ["fe"]=> 字符串(2) "fe" ["al"]=> 字符串(2) "al" ["Mullhalt"]=> 字符串(8) "Mullhalt" ["Total_lerhalt"]=> 字符串( 13) "Total_lerhalt" ["Sand_grovmo"]=> 字符串(11) "Sand_grovmo" ["Volymvikt"]=> 字符串(9) "Volymvikt" ["T_varde"] => 字符串(8) "T_värde" ["S_varde"]=> 字符串(8) "S_värde" ["Basmattnadsgrad"]=> 字符串(16) "Basmättnadsgrad" ["Cd_HNO3"]=> 字符串(7) "Cd_HNO3" ["Kalkbehov"]=> 字符串(9) "Kalkbehov" ["Jordart"]=> 字符串(7) "Jordart" ["Fin_lerhalt"]=> 字符串(11) "Fin_lerhalt" ["Zn"]=> 字符串(2) "Zn" ["Cu"]=> 字符串(2) "Cu" ["Cr"]=> 字符串(2) "Cr" ["Ni"]=> 字符串(2) "Ni" ["Pb"]=>字符串(2) "Pb" ["Hg"]=> 字符串( 2) "Hg" ["Mineralkvave_Kg_N_ha"]=> 字符串(21) "Mineralkväve_Kg_N_ha" ["Mineralkvave_NO3_N"]=> 字符串(19) "Mineralkväve_NO3_N" ["Mineralkvave_NH4_N"] => 字符串(19) "Mineralkväve_NH4_N" ["Mineralkvave_djup"]=> 字符串(18) "Mineralkväve_djup" ["Cystnematoder_potatis"]=> 字符串(21) "Cystnematoder_potatis" ["Cystnematoder_betor"]=> 字符串(19) "Cystnematoder_betor" ["Cystnematoder_spannmal"]=> 字符串(23) "Cystnematoder_spannmÃ¥l" ["Ovrigt"]=> 字符串(7) "Övrigt " }

如果我 var_dump 第一个 $ 行具有 它看起来像这样:

array(42) { [0]=> 字符串(10) "1315903.24" [1]=> 字符串(10) "6213877.72"[2]=> 字符串(4) "2014" [3]=> 字符串(3) "223" [4]=> 字符串(4) "6510" [5]=> 字符串(3) "6.8" [6 ]=> string(4) "10.0" [7]=> string(0) "" [8]=> string(3) "9.5" [9]=> string(0) "" [10]=> string (4) "12.0" [11]=> 字符串(0) "" [12]=> 字符串(3) "0.8" [13]=> 字符串(0) "" [14]=> 字符串(5) " 150.0" [15]=> 字符串(0) "" [16]=> 字符串(0) "" [17]=> 字符串(0) "" [18]=> 字符串(0) "" [19]= > 字符串(0) "" [20]=> 字符串(0) "" [21]=> 字符串(0) "" [22]=> 字符串(0) "" [23]=> 字符串(0) " “ [24]=> 字符串(0) “” [25]=> 字符串(0) “” [26]=> 字符串(0) “” [27]=> 字符串(0) “” [28]=>字符串(0) "" [29]=> 字符串(0) "" [30]=> 字符串(0) "" [31]=> 字符串(0) "" [32]=> 字符串(0) "" [33]=> 字符串(0) "" [34]=> 字符串(0) "" [35]=> 字符串(0) "" [36]=> 字符串(0) "" [37]=> 字符串(0) "" [38]=> 字符串(0) "" [39]=> 字符串(0) "" [40]=> 字符串(0) "" [41]=> 字符串(12) "J038790-14 " }

现在我不知道可能是什么问题。还有什么我可以 post 让你们更好地理解的吗? 由于 "Ovrigt" 是最后一个,它似乎在那个之前工作正常。 奇怪的是,当我 var_dump $columns.. 时,"Ovrigt " 中似乎有一个空白 space。可能是这样吗?

假设文件的第一行总是包含列名,这里有一个快速实现的方法:

// Define the array that will contain the columns names
$columns = [];

foreach($rows as $i => $row)
{
    $row = explode("\t", $row);

    // For the first row get the get the column names
    if ($i == 0)
    {
        $columns = $row;
        continue; // Skip this iteration
    }

    // Map each column name to every item in the row
    $_row = array();
    array_walk($row, function ($value, $index) use (&$_row, $columns)
    {
        $_row[$columns[$index]] = $value;
    });
    $row = $_row;

    // You can now access row items with
    // $row['X'], $row['Y'], etc.
    ...
}

如果您确切知道所有列都将出现在文件中(无论顺序如何),您可以更进一步,将文件中的列名称映射到对应的模型 属性 名称。这将使验证和填充每个模型更加无缝:

// Define the array that will contain the columns names
$columns = [];

// Define the column mapping
// 'column name' => 'model property name'
$columnMap = [
    'X'      => 'X',
    'Y'      => 'Y',
    'Ar'     => 'year',
    'Provnr' => 'provnr',
    'id'     => 'customer_id'
];

foreach($rows as $i => $row)
{
    // Assuming the column names are always on the first row
    if ($i == 0)
    {
        $columns = $row;

        // Replace Swedish special chars with simple Latin chars
        array_walk($columns, function (&$item)
        {
            $item = str_replace(
                ['ä', 'å', 'ö', 'Ä', 'Å', 'Ö'],
                ['a', 'a', 'o', 'A', 'A', 'O'],
                trim($item)
            );
        });
        continue; // Skip this iteration
    }

    // Map each column name to every item in the row
    $_row = array();
    array_walk($row, function ($value, $index) use (&$_row, $columns, $columnMap)
    {
        $_row[$columnMap[$columns[$index]]] = $value;
    });
    $row = $_row;

    // Pass the $row directly to the validator because the mapped keys will work
    $validator = Validator::make($row, Sample::$insertRules);

    if($validator->passes()){

        $sample = New Sample;

        // You can iterate over the row and assign each value automatically
        // because each item key is mapped to a property name
        foreach ($row as $property => $value)
            $sample->{$property} = $value;

        $sample->save();
    }
    else
    {
        Session::flash('notice', 'Something is wrong!');
        Session::flash('sample', $row[3]);

        return Redirect::to('import');
    }
}