THINKPHP_(8)_修改TP原始碼,支援基於多層關聯的任一欄位進行排序

秦皇漢武發表於2021-06-08

之前博文

前述博文THINKPHP_(1)_修改TP原始碼,支援對中文字串按拼音進行排序,其解決的主要問題是,對於查詢出的think\collection資料,按指定欄位對資料進行排序,從而在頁面上進行重排。

基本原理

(1)前端使用layui框架進行欄位顯示,比如:

options.cols=[[ //表頭
{field: "canxunDanweiSchool", title: '單位名稱', sort: true, minWidth:150, templet:function(d){
                var str='';
                if(d.canxunDanweiSchool)
        {
          if(d.canxunDanweiSchool.title)
          {
            str = d.canxunDanweiSchool.title;
          }
        }
                return str;
            }},
...

詳細內容查閱layui的options設定表格顯示,其中sort設定為true

(2)後端查詢的think\collection資料按指定欄位進行重排。當點選對應的上下三角排序角標的時候,會返回到thinkphp的後端controller中。

(3)後端從request中取出對應的field值和order值。

 

 (4)對model返回的think\collection資料,按頁面點選的排序要求(field和order,即按什麼欄位,按正序還是倒序進行排序)進行重排。執行的是如下程式碼:

    public function reSetObject($obj, $srcfrom)
    {
        // 整理變數
        $src = [
            'field' => ''
            ,'order' => 'asc'
            ,'page' => 1
            ,'limit' => 10
        ];
        $src = array_cover($srcfrom, $src) ;

        $data = [
            'code' => 0  // ajax請求次數,作為識別符號
            ,'msg' => ""  // 獲取到的結果數(每頁顯示數量)
            ,'count' => 0 // 符合條件的總資料量
            ,'data' => '' //獲取到的資料結果
        ];

        // 整理資料
        $cnt = $obj->count();
        if($cnt > 0)
        {
            if($src['field'] != '') # 排序
            {
                $obj = $obj->order($src['field'], $src['order']);
            }
            $limit_start = $src['page'] * $src['limit'] - $src['limit'];
            $limit_length = $src['limit'] * 1;
            $obj = $obj->slice($limit_start, $limit_length);
            $data = [
                'code' => 0  // ajax請求次數,作為識別符號
                ,'msg' => ""  // 獲取到的結果數(每頁顯示數量)
                ,'count' => $cnt // 符合條件的總資料量
                ,'data' => $obj //獲取到的資料結果
            ];
        }
        return $data;
    }

上述的核心程式碼是

$obj->order

即,對think\collection呼叫order方法。order執行的具體內容位於thinkphp的原始碼Collection.php檔案中。

具體可以見我之前的博文:THINKPHP_(1)_修改TP原始碼,支援對中文字串按拼音進行排序

新的問題

對於多層關聯的欄位(位於多個不同的表中),如何進一步修正TP原始碼,以支援排序。

表的關聯關係是:

(1)本表,其中school_id和xueqi_id兩個欄位均關聯另外一個表.

 

(2)第一層關聯的兩個表:school表和xueqi表。

school表

 

 xueqi表

 

 (3)關聯第一層表後,再關聯第二層的表,即school表的parent_id關聯school表自身,表示上級單位。然後xueqi表的peiyang_category_id和xuenian等均關聯category表。

之前博文中:

TP模型的多表關聯查詢和多表欄位的關鍵字搜尋

TP6中實現多層關聯,第一個表關聯第二個表查詢出的資料,再關聯第三個表

THINKPHP_(4)_TP模型中with、withJoin和多層關聯的深入分析

中能夠查詢多層關聯資料。通過將位於本表,多層關聯的表的欄位取出來顯示在layui的同一個表格中去後。

 

 那麼問題來了,

如何實現,按位於多個表的欄位進行排序呢?

解決方案:(再次修改TP原始碼)

首先,在layui的options表頭配置中,field欄位,填入如下內容:

canxunDanweiSchool.xiaojieShangJiDanwei.jiancheng

對應的就是表中的“上級單位”那一列。當點選文字旁邊的上下三角角標進行排序,就能將這個field內容傳回後臺。

然後我們進一步修改order函式,改為:

    public function order(string $field, string $order = 'asc')
    {
        return $this->sort(function ($a, $b) use ($field, $order) {
            //新增對中文的支援。
            //xiaojie add程式碼,新增對中文的支援。
            //檢測field是否為.區分開的多層。
            //$pos = strpos($field, '.');
//            $field='canxunDanweiSchool.xiaojieShangJiDanwei.jiancheng';
            $pos = strpos($field, '.');
            if($pos!=false){//存在點號
                $k=explode('.',$field);
                $fieldA=$a;
                $fieldB=$b;
                foreach($k as $value){
                    $fieldA=$fieldA[$value];
                    $fieldB=$fieldB[$value];
                }
                $fieldA = $fieldA ?? null;
                $fieldB = $fieldB ?? null;
            }
            else{
                $fieldA = $a[$field] ?? null;
                $fieldB = $b[$field] ?? null;
            }

            //注意,如果取出的$fieldA是數字,就不能用preg_match。所以,應該加上一個字串判斷型別。因為有些時候會對數字進行排序。

            /*新增對多層關聯的支援。*///如果是多層關聯資料,比如field傳入的資料是canxunDanweiSchool.xiaojieShangJiDanwei.jiancheng
            //我們就可以用類似下述的程式碼進行強制關聯。


            if (isset($fieldA) && isset($fieldB) && is_string($fieldA) &&is_string($fieldB) && preg_match("/[\x7f-\xff]/", $fieldA)){ //如果欄位內容中有中文。
                $coll = collator_create( 'zh-CN' );
                $res  = collator_compare( $coll, $fieldA, $fieldB );
                return 'desc' == strtolower($order) ? $res<0 : $res>0;
            }
            else{
//                $fieldA = $a[$field] ?? null;
//                $fieldB = $b[$field] ?? null;

                return 'desc' == strtolower($order) ? $fieldB > $fieldA : $fieldA > $fieldB;
            }
        });
    }

上述程式碼,相較於之前博文THINKPHP_(1)_修改TP原始碼,支援對中文字串按拼音進行排序中的內容,又主要新增了如下程式碼:

            $pos = strpos($field, '.');
            if($pos!=false){//存在點號
                $k=explode('.',$field);
                $fieldA=$a;
                $fieldB=$b;
                foreach($k as $value){
                    $fieldA=$fieldA[$value];
                    $fieldB=$fieldB[$value];
                }
                $fieldA = $fieldA ?? null;
                $fieldB = $fieldB ?? null;
            }

即對多層關聯的欄位進行識別。如果是多層關聯的資料,我們就一直定位到最後的那個表的那個欄位,然後按那個欄位進行排序。最後的排序結果如下:

 

 

 可以看到,實現了按兩層關聯的那個表的jiancheng欄位進行了中文拼音的排序。

相關文章