web-dev-qa-db-fra.com

Lister le tableau qui ne rend pas quand $ this-> items est rempli

J'essaie d'ajouter un list table à une page d'administrateur personnalisée. J'ai suivi ce guide et cette implémentation de référence .

Malheureusement, lorsque je définis $this->items pour ajouter les données, rien s'affiche dans la page d'administration (pas même le code HTML autour de la liste) et il n'y a pas de message d'erreur. Si je commente cette ligne (ligne 135), alors cela fonctionne sauf pour le manque évident de données.

Mon code:

<?php

add_action('admin_menu', 'add_example_menues');

function add_example_menues() {
    add_menu_page('test', 'test', 'administrator', 'test-top', 'build_test_page');
    add_submenu_page('test-top', 'All tests', 'All tests', 'administrator', 'test-top');
}

if(!class_exists('WP_List_Table')){
    require_once( ABSPATH . 'wp-admin/includes/class-wp-list-table.php' );
}

class test_List_Table extends WP_List_Table {
    function __construct() {
        parent::__construct( array(
            'singular' => 'test',
            'plural' => 'tests',
            'ajax' => false
        ));
    }


    function extra_tablenav ($which) {
        if ($which == "top") {
            echo "Top";
        }
        if ($which == "bottom") {
            echo "Bottom";
        }
    }

    function get_columns() {
        $columns = array(
            'id'    => 'ID',
            'title'     => 'Title',
            'user_id'   => 'User ID',
            'description'   => 'Description'
        );
        return $columns;
    }

    function get_sortable_columns() {
        return $sortable = array(
            'id'        => array('id',false),
            'title'         => array('title',false),
            'user_id'       => array('user_id',false)
        );
    }

    function prepare_items() {
        global $wpdb;

        //data normally gotten from non-wp database. Wordpress db user has access, as per this SE post:
        //http://wordpress.stackexchange.com/questions/1604/using-wpdb-to-connect-to-a-separate-database
        $query = "SELECT `id`, `title`, `user_id`, `description` FROM otherdb.example_table";

        //pagination stuff
        $orderby = !empty($_GET["orderby"]) ? mysql_real_escape_string($_GET["orderby"]) : 'ASC';
        $order = !empty($_GET["order"]) ? mysql_real_escape_string($_GET["order"]) : '';
        if(!empty($orderby) & !empty($order)){ $query.=' ORDER BY '.$orderby.' '.$order; }
        $totalitems = $wpdb->query($query);
        echo "$totalitems";
        $per_page = 5;
        $paged = !empty($_GET["paged"]) ? mysql_real_escape_string($_GET["paged"]) : '';
        if(empty($paged) || !is_numeric($paged) || $paged<=0 ){ $paged=1; }
        $totalpages = ceil($totalitems/$perpage);
        if(!empty($paged) && !empty($perpage)){
            $offset=($paged-1)*$perpage;
            $query.=' LIMIT '.(int)$offset.','.(int)$perpage;
        }
        $this->set_pagination_args( array(
            "total_items" => 4,
            "total_pages" => 1,
            "per_page" => 5,
        ) );


        $columns = $this->get_columns();
        $hidden = array();
        $sortable = $this->get_sortable_columns();

        $this->_column_headers = array($columns, $hidden, $sortable);

        //actual data gotten from database, but for SE use hardcoded array
        //$data = $wpdb->get_results($query, ARRAY_N);

        $example_data = array(
                array(
                        'id'        => 1,
                        'title'     => 'nonsense',
                        'user_id'    => 1,
                        'description'  => 'asdf'
                ),
                array(
                        'id'        => 2,
                        'title'     => 'notanumber',
                        'user_id'    => 2,
                        'description'  => '404'
                ),
                array(
                        'id'        => 3,
                        'title'     => 'I Am A Title',
                        'user_id'    => 3,
                        'description'  => 'desc'
                ),
                array(
                        'id'        => 4,
                        'title'     => 'Example',
                        'user_id'    => 4,
                        'description'  => 'useless'
                ),
                array(
                        'id'        => 5,
                        'title'     => 'aeou',
                        'user_id'    => 5,
                        'description'  => 'keyboard layouts'
                ),
                array(
                        'id'        => 6,
                        'title'     => 'example data',
                        'user_id'    => 6,
                        'description'  => 'data example'
                ),
                array(
                        'id'        => 7,
                        'title'     => 'last one',
                        'user_id'       => 7,
                        'description'  => 'done'
                )
            );


        //This is the line:
        $this->items = $example_data;
        //When the above line is commented, it works as expected (except for the lack of data, of course)
    }
}


function build_test_page() {

    $testListTable = new test_List_Table();
    $testListTable->prepare_items();

?>
<div class="wrap">

    <div id="icon-users" class="icon32"><br/></div>
    <h2>List Table Test</h2>

    <?php $testListTable->display() ?>

</div>
<?php
}
?>

Le code ci-dessus est dans un fichier séparé, inclus dans functions.php avec un require_once().

J'utilise wordpress 4.1.1

Qu'est-ce qui se passe ici? Pourquoi tout disparaîtrait-il et aucune erreur ne serait commise?

5
gandalf3

J'ai fait la même erreur la première fois que j'ai implémenté WP_List_Table.

Le problème est que lorsque vous appelez WP_List_Table::display(), WordPress appelle à son tour:

La dernière fonction est appelée pour chaque ligne. Si vous regardez son code (voir source ), il a:

if ( 'cb' == $column_name ) {
   // you don't have 'cb' column, code remove because not relevant
} elseif ( method_exists( $this, 'column_' . $column_name ) ) {
   // you don't have method named 'column_{$column_name}',
   // code remove because not relevant 
} else {
   // not relevant line
   echo $this->column_default( $item, $column_name );
}

Donc, WordPress appelle WP_List_Table::column_default() pour chaque ligne, mais ... cette méthode n'existe pas .

Ajouter à votre classe:

public function column_default($item, $column_name) {
    return $item[$column_name];
}

Et vos données seront correctement affichées.

Lorsque vous n’ajoutez pas de données, l’erreur ne s’affiche pas car, en l’absence de données, WordPress n’appelle jamais display_rows(), ni d’autres méthodes par la suite.


Notes de bonus:

  1. Dans votre méthode prepare_items(), vous utilisez la variable $perpage mais vous la définissez comme suit: $per_page (notez le trait de soulignement), ce qui provoque un avertissement division par zéro (et la pagination ne fonctionne pas).

  2. le code mysql_real_escape_string($_GET["orderby"]) est not safe, car pour 'orderby' la clause SQL mysql_real_escape_string() n'est pas suffisante. (voir récent bug d'injection Yoast SQL ).

    Faites quelque chose comme:

    $orderby = 'id'; // default
    $by = strtolower(filter_input(INPUT_GET, 'orderby', FILTER_SANITIZE_STRING));
    if (in_array($by, array('title', 'description', 'user_id'), true) {
        $orderby = $by;
    }
    

    et faire quelque chose de similaire à la clause 'order' également: très probablement, il ne peut s'agir que de ASC ou de DESC: n'autorise rien d'autre.

9
gmazzap

Maintenant à la maison, j'ai en fait couru votre code. J'ai eu ceci:

Erreur fatale: appel de la fonction non définie convert_to_screen () dans .../wp-admin/includes/class-wp-list-table.php à la ligne 88

Note:

Veuillez activer Debugging_in_WordPress lors du développement.

Quoi qu'il en soit, cela m'amène à la question suivante sur Stackoverflow:

La réponse de @ brasofilo a beaucoup aidé, ce qui n’est pas surprenant, car il est l’un des protagonistes de WordPress Development.

Quoi qu'il en soit, à la suite de sa réponse, il vous manque la méthode column_default, de plus la méthode _construct est différente. Mais vous pouvez le lire vous-même, voici un code de travail - dépouillé -:

add_action( 'admin_menu', 'add_test_list_table_menues' );
function add_test_list_table_menues() {
    add_menu_page(
        'test', 
        'test', 
        'manage_options', 
        'test-top', 
        'test_list_table_output'
    );
}

function test_list_table_output() { 
    echo '<div class="wrap">';
    echo '<h2>Test List Table</h2>';
    new Test_List_Table(); 
    echo '</div>';
}

if( ! class_exists( 'WP_List_Table' ) ) {
    require_once( ABSPATH . 'wp-admin/includes/class-wp-list-table.php' );
}

class Test_List_Table extends WP_List_Table {
    public function __construct() {
        parent::__construct( array(
            'singular' => 'test',
            'plural' => 'tests',
            'ajax' => false
        ));
        $this->prepare_items();
        $this->display();
    }

    function get_columns() {
        $columns = array(
            'tid'    => 'ID',
            'title'     => 'Title',
            'user_id'   => 'User ID',
            'description'   => 'Description'
        );
        return $columns;
    }

    function column_default( $item, $column_name ) {
        switch( $column_name ) {
            case 'tid':
            case 'title':
            case 'user_id':
            case 'description':
                return $item[ $column_name ];
            default:
                return print_r( $item, true ) ;
        }
    }

    function prepare_items() {
        $example_data = array(
                array(
                        'tid'        => 1,
                        'title'     => 'nonsense',
                        'user_id'    => 1,
                        'description'  => 'asdf'
                ),
                array(
                        'tid'        => 2,
                        'title'     => 'notanumber',
                        'user_id'    => 2,
                        'description'  => '404'
                ),
                array(
                        'tid'        => 3,
                        'title'     => 'I Am A Title',
                        'user_id'    => 3,
                        'description'  => 'desc'
                ),
                array(
                        'tid'        => 4,
                        'title'     => 'Example',
                        'user_id'    => 4,
                        'description'  => 'useless'
                ),
                array(
                        'tid'        => 5,
                        'title'     => 'aeou',
                        'user_id'    => 5,
                        'description'  => 'keyboard layouts'
                ),
                array(
                        'tid'        => 6,
                        'title'     => 'example data',
                        'user_id'    => 6,
                        'description'  => 'data example'
                ),
                array(
                        'tid'        => 7,
                        'title'     => 'last one',
                        'user_id'       => 7,
                        'description'  => 'done'
                )
            );

        $columns = $this->get_columns();
        $hidden = array();
        $sortable = $this->get_sortable_columns();
        $this->_column_headers = array($columns, $hidden, $sortable);
        $this->items = $example_data;
    }
}
2
Nicolai