2016 年 3 月 25 日,星期五

使用 Editor 進行父/子編輯

當處理來自資料庫的資料時,使用父/子資料表示法是一種相當常見的模式。這讓最終使用者可以選取父表格中的列,然後顯示該記錄相關聯的子資料。當使用具有強關聯(例如,聯結表格)的表格時,此介面特別有用,因為它向最終使用者顯示了一個非常簡單但強大且資訊密集的介面。

使用 Editor 設定父/子編輯是一個相當常見的問題,因此在這篇文章中,我將詳細說明如何做到這一點。您將在此處看到可以透過使用 DataTables API事件輕鬆實現。

在這篇文章中,我將使用網站表格作為主表格,並使用使用者表格作為子表格,其中每個使用者都分配有一個網站。當我們在網站表格中選取不同的列時,資料會根據需要載入到使用者表格中。兩個表格都是完全可編輯的,結果示範如下。

名稱 使用者
名字 姓氏 電話號碼 位置

父表格

建立父/子顯示的第一步是建立父表格。這是一個非常簡單的 Editor 和 DataTable 組合,就像您在 Editor 範例中找到的一樣。

Editor Javascript

Editor Javascript 盡可能簡單 - 單一欄位(網站名稱),它會將資料提交到伺服器端腳本

var siteEditor = new DataTable.Editor( {
    ajax: '../php/sites.php',
    table: '#sites',
    fields: [ {
        label: 'Site name:',
        name: 'name'
    } ]
} );

DataTables Javascript

DataTable 初始化也同樣簡單 - 我們有兩個欄位

  • 網站名稱
  • 分配給該網站的使用者數量。為此,使用 columns.render 函數,該函數僅從資料陣列中返回使用者數量。

請注意,使用 select.style 以允許表格中一次僅選取單個項目。完全可以允許多個列被選取,但為了本文中的簡單起見,單個列就足夠了。

var siteTable = $('#sites').DataTable( {
    ajax: '../php/sites.php',
    columns: [
        { data: 'name' },
        { data: 'users', render: function ( data ) {
            return data.length;
        } }
    ],
    select: {
        style: 'single'
    },
    layout: {
        topStart: {
            buttons: [
                { extend: 'create', editor: siteEditor },
                { extend: 'edit',   editor: siteEditor },
                { extend: 'remove', editor: siteEditor }
            ]
        }
    }
} );

伺服器端 (PHP)

最後,對於父表格,PHP 腳本會從網站表格讀取 idname 欄位。當選取列時,需要 id 欄位,以便將資訊提交到子表格伺服器端腳本 - id 實際上沒有顯示在表格中,也沒有顯示在 Editor 表單中(因此,為了安全起見,使用了 set(false))。

這裡值得注意的一點是,使用 Mjoin 實例來取得有關使用每個網站的項目數量的資訊(Mjoin 是「多重聯結」的縮寫)。有關如何使用 Mjoin 的詳細說明,請參閱 Editor 手冊。如果您的父表格中不需要或不想顯示計數欄位,則不需要 Mjoin

Editor::inst( $db, 'sites' )
    ->fields(
        Field::inst( 'id' )->set( false ),
        Field::inst( 'name' )->validator( 'Validate::notEmpty' )
    )
    ->join(
        Mjoin::inst( 'users' )
            ->link( 'sites.id', 'users.site' )
            ->fields(
                Field::inst( 'id' )
            )
    )
    ->process( $_POST )
    ->json();

子表格

子表格(使用者)在結構上與父表格非常相似 - 欄位和名稱不同,但此處也應用與其他 DataTables 和 Editor 表格相同的基本模式。

使整個過程運作的關鍵因素是能夠提交從父表格中選取的列的 id 值。這可以透過使用 {selected:true} selector-modifierrow().data() 方法輕鬆取得 - 例如

table.row( { selected: true } ).data();

有關 Select 如何與 DataTables API 整合的完整詳細資訊,請參閱 Select 手冊

Editor Javascript

使用者 Editor 實例是使用指定為函數的 ajax.data 選項建立的。這表示每當 Editor 向伺服器發出 Ajax 請求時,此函數就會執行並擴充提交到伺服器的資料。在這種情況下,我們想要提交父表格中選取之列的網站 id(如上所述)。因此,我們有以下內容

var usersEditor = new DataTable.Editor( {
    ajax: {
        url: '../php/users.php',
        data: function ( d ) {
            var selected = siteTable.row( { selected: true } );

            if ( selected.any() ) {
                d.site = selected.data().id;
            }
        }
    },
    table: '#users',
    fields: [ ... ]
} );

請注意,為簡潔起見,欄位已省略 - 如果您想閱讀完整的欄位清單,請參閱 Editor 聯結範例

DataTables Javascript

DataTables 也有一個 ajax.data 選項,每當 DataTables 請求要顯示的資料時都會執行。它的運作方式與 Editor 同名的選項完全相同

var usersTable = $('#users').DataTable( {
    ajax: {
        url: '../php/users.php',
        type: 'post',
        data: function ( d ) {
            var selected = siteTable.row( { selected: true } );

            if ( selected.any() ) {
                d.site = selected.data().id;
            }
        }
    },
    columns: [
        { data: 'users.first_name' },
        { data: 'users.last_name' },
        { data: 'users.phone' },
        { data: 'sites.name' }
    ],
    select: true,
    layout: {
        topStart: {
            buttons: [
                { extend: 'create', editor: usersEditor },
                { extend: 'edit',   editor: usersEditor },
                { extend: 'remove', editor: usersEditor }
            ]
        }
    }
} );

伺服器端 (PHP)

子表格的 PHP 有兩個重要的考量

  1. 如果選取的網站 id(簡稱為 site)未作為請求的一部分提交,則應在用戶端顯示一個空的資料陣列。
  2. 當提交選取的網站資訊時,應將其用作 WHERE 條件,以便子表格僅顯示與該網站相符的資料。這是使用 Editor->where() 方法完成的,如下所示
if ( ! isset($_POST['site']) || ! is_numeric($_POST['site']) ) {
    echo json_encode( [ "data" => [] ] );
}
else {
    Editor::inst( $db, 'users' )
        ->field( 
            Field::inst( 'users.first_name' ),
            Field::inst( 'users.last_name' ),
            Field::inst( 'users.phone' ),
            Field::inst( 'users.site' )
                ->options( 'sites', 'id', 'name' )
                ->validator( 'Validate::dbValues' ),
            Field::inst( 'sites.name' )
        )
        ->leftJoin( 'sites', 'sites.id', '=', 'users.site' )
        ->where( 'site', $_POST['site'] )
        ->process($_POST)
        ->json();
}

將其連接在一起

我們有兩個表格,每個表格都可透過 Editor 編輯,因此我們現在需要做的就是將它們連接在一起,以便父表格中的選取將載入子表格的新資料。此外,任一表格中資料的變更都會反映在另一個表格中。

選取列

當選取列時,Select 擴充功能會觸發 select 事件,而當取消選取列時,則會觸發 deselect 事件。因此,我們需要做的就是監聽這些事件並呼叫 ajax.reload() 方法,以便在任一事件發生時將新資料載入到子表格中(回想一下,ajax.data 函數將在該點執行,取得新選取的列 id)。

siteTable.on( 'select', function () {
    usersTable.ajax.reload();

    usersEditor
        .field( 'users.site' )
        .def( siteTable.row( { selected: true } ).data().id );
} );

siteTable.on( 'deselect', function () {
    usersTable.ajax.reload();
} );

在上面的程式碼中,您也會注意到有呼叫 field().def() - 這用於設定欄位預設值。雖然不是必須的,但如果「網站」欄位的預設值與父列中選取的預設值相符,則確實可以讓最終使用者更輕鬆。

更新的資料

當子表格中的資料更新時(網站變更、新增項目等),需要更新父表格以反映變更。同樣地,當父表格更新時(例如,修改網站的名稱),也應該更新子表格。對於這兩者,我們可以使用的 Editor 的 submitSuccess 事件。與列選取類似,我們只需呼叫 ajax.reload() 方法來更新各自的表格

siteEditor.on( 'submitSuccess', function () {
    usersTable.ajax.reload();
} );

usersEditor.on( 'submitSuccess', function () {
    siteTable.ajax.reload();
} );

從這裡開始

本文的重點應該是如何使用在一個表格中選取的資料來影響在另一個表格中載入的資料。此範例中顯示的表格刻意非常簡單,明顯的延伸是使用更多欄位和其他欄位類型來增加其複雜性。

除此之外,您可能希望考慮以下事項

  • 當父表格中未選取列時,隱藏子表格
  • 可以顯示有關從父表格中刪除子表格中引用的列的警告
  • 在父表格中多列選取,允許在子表格中顯示多個網站(這將需要伺服器端 OR 運算式)。

還有其他建議嗎?!請隨時在此處或在 論壇中發布。