柔晶美网络工作室

柔晶美网络工作室,倾心于web技术的博客站点

关注我 微信公众号

您现在的位置是: 首页 > 博客日记

Laravel使用excel3.1导出时,防止长数字变科学计数法的方法

2019-10-08 admin laravel  1539

因项目中使用了最新的excel3.1插件来导出为excel表格,所以相关的资料非常缺乏,网上大多是老版本的,无法使用。经反复测试,最为简单的方法是,将所有导出的内容设置为文本。

首先,修改默认绑定器,打开config/excel.php文件,修改 'value_binder' 这一段,修改为:

 'default' => PhpOffice\PhpSpreadsheet\Cell\StringValueBinder::class,//单元格输出为字符串,防止科学计数法

然后,在导出模型中,也要use这个绑定器:

namespace App\Admin\Controllers;
use Encore\Admin\Grid\Exporters\ExcelExporter; 
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
use PhpOffice\PhpSpreadsheet\Cell\Cell;
use PhpOffice\PhpSpreadsheet\Cell\DataType;
use PhpOffice\PhpSpreadsheet\Cell\StringValueBinder;//防止科学计数法
class PostsExporter extends ExcelExporter
{
      protected $fileName;  //导出的文件名
      protected $headings;  //导出所有字段
      public function __construct($bm,$zwbm)
    {
        $zdsz = Schema::getColumnListing($bm);//获取所有字段
        //$szdx = json_encode($zdsz,JSON_UNESCAPED_UNICODE);//转为对象
        $zwbm = $zwbm.'.xls';
        $this->fileName = $zwbm;  //将控制器传来的导出表名赋值给fileName
        $this->headings = $zdsz;  //将所有字段赋值给headings
    }
}

这样,导出的默认就是文本了。相关知识可以了解一下,绑定器的目录是:

vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/

里面有许多绑定器,其中默认的是DefaultValueBinder.php,它会根据规则进行格式转换:

class DefaultValueBinder implements IValueBinder
{
    /**
     * Bind value to a cell.
     *
     * @param Cell $cell Cell to bind value to
     * @param mixed $value Value to bind in cell
     *
     * @throws \PhpOffice\PhpSpreadsheet\Exception
     *
     * @return bool
     */
    public function bindValue(Cell $cell, $value)
    {
        // sanitize UTF-8 strings
        if (is_string($value)) {
            $value = StringHelper::sanitizeUTF8($value);
        } elseif (is_object($value)) {
            // Handle any objects that might be injected
            if ($value instanceof DateTimeInterface) {
                $value = $value->format('Y-m-d H:i:s');
            } elseif (!($value instanceof RichText)) {
                $value = (string) $value;
            }
        }
        // Set value explicit
        $cell->setValueExplicit($value, static::dataTypeForValue($value));
        // Done!
        return true;
    }
    /**
     * DataType for value.
     *
     * @param mixed $pValue
     *
     * @return string
     */
    public static function dataTypeForValue($pValue)
    {
        // Match the value against a few data types
        if ($pValue === null) {
            return DataType::TYPE_NULL;
        } elseif ($pValue === '') {
            return DataType::TYPE_STRING;
        } elseif ($pValue instanceof RichText) {
            return DataType::TYPE_INLINE;
        } elseif ($pValue[0] === '=' && strlen($pValue) > 1) {
            return DataType::TYPE_FORMULA;
        } elseif (is_bool($pValue)) {
            return DataType::TYPE_BOOL;
        } elseif (is_float($pValue) || is_int($pValue)) {
            return DataType::TYPE_NUMERIC;
        } elseif (preg_match('/^[\+\-]?(\d+\\.?\d*|\d*\\.?\d+)([Ee][\-\+]?[0-2]?\d{1,3})?$/', $pValue)) {
            $tValue = ltrim($pValue, '+-');
            if (is_string($pValue) && $tValue[0] === '0' && strlen($tValue) > 1 && $tValue[1] !== '.') {
                return DataType::TYPE_STRING;
            } elseif ((strpos($pValue, '.') === false) && ($pValue > PHP_INT_MAX)) {
                return DataType::TYPE_STRING;
            }
            return DataType::TYPE_NUMERIC;
        } elseif (is_string($pValue)) {
            $errorCodes = DataType::getErrorCodes();
            if (isset($errorCodes[$pValue])) {
                return DataType::TYPE_ERROR;
            }
        }
        return DataType::TYPE_STRING;
    }
}

我们改后的绑定器为StringValueBinder.php,它把全部单元格作为文本处理,内容如下:

class StringValueBinder implements IValueBinder
{
    /**
     * Bind value to a cell.
     *
     * @param Cell $cell Cell to bind value to
     * @param mixed $value Value to bind in cell
     *
     * @throws \PhpOffice\PhpSpreadsheet\Exception
     *
     * @return bool
     */
    public function bindValue(Cell $cell, $value)
    {
        // sanitize UTF-8 strings
        if (is_string($value)) {
            $value = StringHelper::sanitizeUTF8($value);
        }
        $cell->setValueExplicit((string) $value, DataType::TYPE_STRING);
        // Done!
        return true;
    }
}

其实,官方文档有描述:如果要禁用值的智能格式,则可以使用扩展导出类 \PhpOffice\PhpSpreadsheet\Cell\StringValueBinder。在这种情况下,所有值都作为字符串传递。可用的数据类型

PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_STRING
PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_FORMULA
PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_NUMERIC
PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_BOOL
PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_NULL
PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_INLINE
PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_ERROR

只是没有中文文档,所以理解有些差错,加上百度等搜索上的文章大多错误,所以浪费我一晚上时间找方法。

以上方法发现有个遗留问题,如果导出的是json数组类型的单元格数据,将报错,不过这个功能暂时用不上,需要使用时,只需要在config/excel.php中,把配置改回默认的即可:

还有一个更好的方法是:

打开默认配置文件vendor/maatwebsite/excel/src/DefaultValueBinder.php,添加

use PhpOffice\PhpSpreadsheet\Cell\DataType;

函数内部添加:

//超过10位的数字转文本格式,防止科学计数法
             if (strlen($value) > 10) {
           $cell->setValueExplicit($value, DataType::TYPE_STRING);
           return true;
       }

文件代码最终如下:

namespace Maatwebsite\Excel;
use PhpOffice\PhpSpreadsheet\Cell\Cell;
use PhpOffice\PhpSpreadsheet\Cell\DefaultValueBinder as PhpSpreadsheetDefaultValueBinder;
use PhpOffice\PhpSpreadsheet\Cell\DataType;
class DefaultValueBinder extends PhpSpreadsheetDefaultValueBinder
{
    /**
     * @param Cell $cell Cell to bind value to
     * @param mixed $value Value to bind in cell
     *
     * @return bool
     */
    public function bindValue(Cell $cell, $value)
    {
        if (is_array($value)) {
            $value = \json_encode($value);
        }
      //超过10位的数字转文本格式,防止科学计数法
             if (strlen($value) > 10) {
           $cell->setValueExplicit($value, DataType::TYPE_STRING);
           return true;
       }
        return parent::bindValue($cell, $value);
    }
}

这个方法可以导出json字段,而且不影响其它类型的数据。最后附上导出类文件代码Encore\Admin\Grid\Exporters\ExcelExporter:

namespace Encore\Admin\Grid\Exporters;
use Illuminate\Support\Str;
use Maatwebsite\Excel\Concerns\Exportable;
use Maatwebsite\Excel\Concerns\FromQuery;
use Maatwebsite\Excel\Concerns\WithHeadings;
abstract class ExcelExporter extends AbstractExporter implements FromQuery, WithHeadings
{
    use Exportable;
    /**
     * @var string
     */
    protected $fileName;
    /**
     * @var array
     */
    protected $headings = [];
    /**
     * @var array
     */
    protected $columns = [];
    /**
     * @return array
     */
    public function headings(): array
    {
        if (!empty($this->columns)) {
            return array_values($this->columns);
        }
        return $this->headings;
    }
    /**
     * @return \Illuminate\Database\Eloquent\Builder|\Illuminate\Database\Eloquent\Model
     */
    public function query()
    {
        if (!empty($this->columns)) {
            $columns = array_keys($this->columns);
            $eagerLoads = array_keys($this->getQuery()->getEagerLoads());
            $columns = collect($columns)->reject(function ($column) use ($eagerLoads) {
                return Str::contains($column, '.') || in_array($column, $eagerLoads);
            });
            return $this->getQuery()->select($columns->toArray());
        }
        return $this->getQuery();
    }
    /**
     * {@inheritdoc}
     */
    public function export()
    {
        $this->download($this->fileName)->prepare(request())->send();
        exit;
    }
}

可参数这个自定义修改。

文章评论


需要 登录 才能发表评论
热门评论
0条评论

暂时没有评论!