You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PHP类内方法间属性更新传递问题(Laravel Excel场景)

解决Laravel Excel中headings()无法获取array()更新后属性的问题

这个问题我之前也踩过坑!核心原因是Laravel Excel的执行顺序——headings()方法会在array()方法之前被调用,所以你在array()循环里对$a_list_only的更新,在headings()执行的时候根本还没发生,自然拿不到最新值。

解决思路

我们需要把生成表头所需的$a_list_only数据的逻辑,提前到array()执行之前完成,比如放到构造函数里,或者单独写一个初始化方法提前处理好,这样headings()就能直接拿到准备好的数据了。

修改后的代码示例

我调整了你的代码,把生成amenity列表的逻辑抽成了单独的方法,并在构造函数里提前执行:

<?php
namespace App\Exports;
use Maatwebsite\Excel\Concerns\FromArray;
use DB;
use Maatwebsite\Excel\Concerns\WithHeadings;

class MatrixExport implements FromArray, WithHeadings {
    protected $building_ids = [], $amenities_list = [], $property ;
    public $a_list_only;

    public function __construct($building_ids, $amenities_list, $property, $a_list_only = []) {
        $this->building_ids = $building_ids;
        $this->amenities_list = $amenities_list;
        $this->property = $property;
        // 提前初始化表头需要的amenity列表
        $this->initializeAmenityList();
    }

    // 新增方法:提前获取需要的amenity列表
    private function initializeAmenityList() {
        $query = \DB::table('units_amenities_values')
            ->join('units', 'units.id', 'units_amenities_values.unit_id')
            ->join('floors', 'floors.id', 'units.floor_id')
            ->join('buildings', 'buildings.id', 'floors.building_id')
            ->join('properties', 'properties.id', 'buildings.property_id')
            ->leftJoin('amenity_values', 'amenity_values.id', 'units_amenities_values.amenity_value_id')
            ->leftJoin('amenities', 'amenities.id', 'amenity_values.amenity_id')
            ->leftJoin('categories', 'amenities.category_id', 'categories.id')
            ->select('amenities.id as amenity_id', 'amenities.amenity_name', 'categories.category_name')
            ->where('amenities.property_id', $this->property->id)
            ->whereIn('buildings.id', $this->building_ids)
            ->whereNull('buildings.deleted_at');

        if(!in_array('-1',$this->amenities_list)){
            $query->whereIn('amenities.id',$this->amenities_list);
        }

        $res = $query
            ->orderByRaw('LENGTH(amenity_name)', 'ASC')
            ->orderBy('amenity_name')
            ->get();

        $included_amenity_ids = [];
        $selected_data = [];

        foreach($res as $rk => $rv){
            if( (!in_array($rv->amenity_id,$included_amenity_ids) && in_array($rv->amenity_id, $this->amenities_list)) || (!in_array($rv->amenity_id,$included_amenity_ids) && in_array('-1', $this->amenities_list)) ){
                $included_amenity_ids[] = $rv->amenity_id;
                $selected_data[$rv->category_id]['category_name'] = $rv->category_name;
                $selected_data[$rv->category_id]['amenities'][$rv->amenity_id] = $rv->amenity_name;
            }
        }

        ksort($selected_data);

        $am_lists_only = [];
        foreach($selected_data as $sk => $sv){
            $am_lists_only = $am_lists_only + $sv['amenities'];
        }

        // 转换为值数组,对应表头需要的格式
        $this->a_list_only = array_values($am_lists_only);
    }

    public function array(): array {
        $non_unit = DB::table('non_units as nu')
            ->join('buildings as b','b.id','=','nu.building_id')
            ->where('b.property_id',$this->property->id)
            ->pluck('nu.id');

        $query = \DB::table('units_amenities_values')
            ->join('units', 'units.id', 'units_amenities_values.unit_id')
            ->join('floors', 'floors.id', 'units.floor_id')
            ->join('buildings', 'buildings.id', 'floors.building_id')
            ->join('properties', 'properties.id', 'buildings.property_id')
            ->leftJoin('amenity_values', 'amenity_values.id', 'units_amenities_values.amenity_value_id')
            ->leftJoin('amenities', 'amenities.id', 'amenity_values.amenity_id')
            ->leftJoin('categories', 'amenities.category_id', 'categories.id');

        if ($this->property->availability_file == 1 || $this->property->availability_file == 3) {
            $query->leftJoin('availabilities', 'availabilities.unit_id', 'units.id');
            $query->select('units_amenities_values.id as uav_id', 'units_amenities_values.uav_status', 'units_amenities_values.deleted_at as uav_deleted_at', 'amenities.id as amenity_id', 'amenities.amenity_name', 'amenity_values.initial_amenity_value', 'amenity_values.amenity_value', 'amenity_values.status as av_status', 'floors.floor', 'units.id as unit_id', 'units.unit_number', 'units.unit_rent', 'units.stack', 'units.unit_note', 'amenities.category_id', 'buildings.id as building_id', 'buildings.building_number', 'availabilities.status as avail_status','categories.category_name');
        } else {
            $query->select('units_amenities_values.id as uav_id', 'units_amenities_values.uav_status', 'units_amenities_values.deleted_at as uav_deleted_at', 'amenities.id as amenity_id', 'amenities.amenity_name', 'amenity_values.initial_amenity_value', 'amenity_values.amenity_value', 'amenity_values.status as av_status', 'floors.floor', 'units.id as unit_id', 'units.unit_number', 'units.unit_rent', 'units.stack', 'units.unit_note', 'amenities.category_id', 'buildings.id as building_id', 'buildings.building_number','categories.category_name');
        }

        $query->where('amenities.property_id', $this->property->id)
            ->whereIn('buildings.id', $this->building_ids)
            ->whereNull('buildings.deleted_at');

        if(!in_array('-1',$this->amenities_list)){
            $query->whereIn('amenities.id',$this->amenities_list);
        }

        if(!empty($this->building_ids)){
            $query->whereIn('buildings.id', $this->building_ids);
        }else{
            $query->where('buildings.property_id', $this->property->id);
        }

        $res = $query
            ->orderByRaw('LENGTH(amenity_name)', 'ASC')
            ->orderBy('amenity_name')
            ->get();

        $data = $res->groupBy('unit_id');

        $ex_data = $item = [];
        foreach($data as $k => $v){
            $item = [
                'Bldg' => $v[0]->building_number,
                'Unit' => (is_numeric($v[0]->unit_number))?(int)$v[0]->unit_number:$v[0]->unit_number,
                'Floor' =>(is_numeric($v[0]->floor))?(int)$v[0]->floor:$v[0]->floor,
                'Stack' => (is_numeric($v[0]->stack))?(int)$v[0]->stack:$v[0]->stack
            ];

            // 直接使用提前初始化好的amenity列表
            foreach ($this->a_list_only as $av) {
                $current_val = "";
                foreach ($v as $vk => $vv) {
                    if ($vv->amenity_name == $av) {
                        $current_val = $vv->amenity_value;
                    }
                }
                $item[$av] = $current_val;
            }
            $ex_data[] = $item;
        }
        return $ex_data;
    }

    public function headings(): array {
        $common_header_arr = ['Bldg', 'Unit', 'Floor', 'Stack'];
        return array_merge($common_header_arr, $this->a_list_only);
    }
}

关键修改点说明

  1. 提前初始化数据:新增initializeAmenityList()方法,把原来在array()里生成amenity列表的逻辑移到这里,在构造函数里调用,确保$a_list_only在headings()执行前就已经准备完毕。
  2. 避免重复逻辑:array()方法里直接使用已经初始化好的$a_list_only,不需要再往里面追加数据,减少冗余操作。
  3. 优化查询:initializeAmenityList()里的查询只选择需要的字段,减轻数据库查询的负担。

另外,如果你担心重复查询数据库,可以考虑把查询结果缓存起来,或者用->distinct()来避免重复数据,进一步优化性能。

内容的提问来源于stack exchange,提问作者Aayush Dahal

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.11 08:32:53