1. 程式人生 > >laravel-admin grid中使用switch操作一對一關聯屬性

laravel-admin grid中使用switch操作一對一關聯屬性

專案中有一個商品表(production) ,有一個庫存表(repertory),兩者一對一關係,production有釋出欄位(release),需求是在repertory的grid中,有一個switch開關,用於釋出production。
如果直接使用

grid>column(production.release)>switch(states);

該列只會顯示一個複選框,樣式崩了。
經過除錯,發現:
vendor/encore/laravel-admin/src/Grid/Displayers/SwitchDisplay.php中的


    public function display($states = [])
    {
        $this->updateStates($states);

        $name = $this->column->getName();

        $class = "grid-switch-{$name}";
        //這裡直接拼接的類名中會有dot導致樣式失效

        .........
    }

所以將列名改成release:

gr

ip>column(release)>switch(states);

然後在repertory模型中設定該值:

    protected $appends = ['release'];
    ......
    public function getReleaseAttribute()
    {
        ......
        return $release;
    }

此時grid中的release開關能正確顯示,但是無法正確執行操作,因為repertory模型中沒有release欄位,需要在更新的時候將release改成關聯production的release:。

檢視vendor/encore/laravel-admin/src/Form.php中的update函式中獲取資料的程式碼如下:

$data = ($data) ?: Input::all();

說明update函式直接使用request的資料,那麼可以重寫update函式,修改request的資料之後再傳入:


  public function update()
    {
        if(!is_null(request()->get('release'))){
             $status = request()->get('release')==='on'?1:2;
             //獲取到switch開關傳的值了,需要將值設定到一個新的資料上
        }
        $data = request()->all();
        $id_arr = request()->route()->parameters();
        return $this->form()->update($id_arr['repertory'],$data);
    }

要設定一個新的資料作為關聯關係去更新,應該有什麼樣的格式呢?帶著這個疑問,先來走一下update的流程:

public function update($id, $data = null)
    {
        //在這裡獲取到了資料
        $data = ($data) ?: Input::all();
        //是否可編輯
        $isEditable = $this->isEditable($data);
        //處理可編輯的資料
        $data = $this->handleEditable($data);
        //這裡不太清楚,大概是處理檔案刪除,沒驗證過
        $data = $this->handleFileDelete($data);
        //如果是排序操作的話,執行到這,獲取排序後的資料就結束了
        if ($this->handleOrderable($id, $data)) {
            return response([
                'status'  => true,
                'message' => trans('admin.update_succeeded'),
            ]);
        }


        /* @var Model $this->model */
        //獲取當前模型
        $this->model = $this->model->with($this->getRelations())->findOrFail($id);
        //設定欄位原始值
        $this->setFieldOriginalValue();

        // Handle validation errors.
        //處理表單驗證
        if ($validationMessages = $this->validationMessages($data)) {
            if (!$isEditable) {
                return back()->withInput()->withErrors($validationMessages);
            } else {
                return response()->json(['errors' => array_dot($validationMessages->getMessages())], 422);
            }
        }
        //預處理,得到$this->updates和$this->relations
        if (($response = $this->prepare($data)) instanceof Response) {
            return $response;
        }


        DB::transaction(function () {
            //預更新,更新該模型欄位
            $updates = $this->prepareUpdate($this->updates);

            foreach ($updates as $column => $value) {
                /* @var Model $this->model */
                $this->model->setAttribute($column, $value);
            }

            $this->model->save();
            //更新關聯模型欄位
            $this->updateRelation($this->relations);
        });

        if (($result = $this->complete($this->saved)) instanceof Response) {
            return $result;
        }

        if ($response = $this->ajaxResponse(trans('admin.update_succeeded'))) {
            return $response;
        }

        return $this->redirectAfterUpdate();
    }

進入this>prepare(data):

    /**
     * Prepare input data for insert or update.
     *
     * @param array $data
     *
     * @return mixed
     */
    protected function prepare($data = [])
    {

        if (($response = $this->callSubmitted()) instanceof Response) {
            return $response;
        }

        $this->inputs = $this->removeIgnoredFields($data);

        if (($response = $this->callSaving()) instanceof Response) {
            return $response;
        }
        //在這裡得到關聯關係
        $this->relations = $this->getRelationInputs($this->inputs);
        //在這裡得到去除關聯關係的本模型資料
        $this->updates = array_except($this->inputs, array_keys($this->relations));
    }

進入$this->getRelationInputs:

    /**
     * Get inputs for relations.
     *
     * @param array $inputs
     *
     * @return array
     */
    protected function getRelationInputs($inputs = [])
    {
        $relations = [];

        foreach ($inputs as $column => $value) {
            //判斷函式是否存在
            if (method_exists($this->model, $column)) {
                //說明正確的關聯關係資料是一個數組並且key為關聯關係函式名
                $relation = call_user_func([$this->model, $column]);

                if ($relation instanceof Relation) {
                    //在此設定關聯關係的欄位和值,
                    $relations[$column] = $value;
                }
            }
        }

        return $relations;
    }

在上一個步驟找出了關聯關係資料的大略格式:以關聯函式為鍵的陣列。
再回到update函式,進入this>updateRelation(this->relations):

protected function updateRelation($relationsData)
    {
        foreach ($relationsData as $name => $values) {
            ;
            if (!method_exists($this->model, $name)) {
                continue;
            }

            $relation = $this->model->$name();

            $oneToOneRelation = $relation instanceof \Illuminate\Database\Eloquent\Relations\HasOne
                || $relation instanceof \Illuminate\Database\Eloquent\Relations\MorphOne;
//            echo "start\r\n";
            //上面只檢測了$name,如果關聯關係資料的鍵為關聯函式名的話,可以順利到這一步,而且因為當前要操作的資料的關聯關係是1對1,所有$oneToOneRelation為true
            $prepared = $this->prepareUpdate([$name => $values], $oneToOneRelation);

            if (empty($prepared)) {
                continue;
            }

            switch (get_class($relation)) {
                ..........................
            }
        ................
        }

進入:prepared=this->prepareUpdate([name=>values], $oneToOneRelation);:


    protected function prepareUpdate(array $updates, $oneToOneRelation = false)
    {
        $prepared = [];
//        print_r($updates);
        foreach ($this->builder->fields() as $field) {
            $columns = $field->column();
            //獲取該模型對應的form的所有列名,我們關注的release在form中應為repertory.release

            // If column not in input array data, then continue.
            //這個函式很重要,決定了流程是否可以往下走,進去看一看
            if (!array_has($updates, $columns)) {
                continue;
            }
            ........
    }

進入:array_has(updates,columns):

/**
     * Check if an item or items exist in an array using "dot" notation.
     *
     * @param  \ArrayAccess|array  $array
     * @param  string|array  $keys
     * @return bool
     */
    function array_has($array, $keys)
    {
        return Arr::has($array, $keys);
    }

看註釋!!!,該函式用於檢查第二個引數根據dot處理成陣列後,是否存在於第一個引數的陣列中,由於在form中該欄位是repertory.release,即columnsrepertory.release使updates必為[‘repertory’=>[‘release’=>?]],到此,可以確定關聯關係資料必是

['repertory'=>['release'=>1]]

於是,在控制器中重寫的update中重寫一個關聯關係資料如下:


    public function update()
    {
        $release = request()->get('release');
        if(!is_null($release)){
            $status = $release === 'on' ? 1 : 2;
            //新增一個request欄位
            request()->offsetSet('repertory',['release'=>$release]);
        }
        $data = request()->all();
        $id_arr = request()->route()->parameters();
        return $this->form()->update($id_arr['repertory'],$data);
    }

這樣就修改了request提交的資料,現在request中有一個數組:

['repertory'=>['release'=>1]]

去除除錯列印,在表格中操作開關,結果正確。