Skocz do zawartości

Rekomendowane odpowiedzi

Opublikowano

There's a table in the Admin CP of the IPS Social Suite that I really like - the members table. It has some really cool options - you can reorder the data just by clicking on a column head; you can quickly search for a member by typing a name into a search box at the top; there's some filter options to quickly show banned, locked, spam and validating members; and there's an advanced search form to search for members based on practically any criteria.

 

It would be great if these features were available elsewhere. So much like we did for forms, we decided to create a central helper class for building tables.

 

To demonstrate how it works, I'm going to go through, step by step, how I recreated the Admin CP members table in IPS 4.

 

 

It starts with one line to create the table, and another to pass it to the output class:

 

 

With just those two lines, you'll see this:

 

Some things to note:

  • IPSHelpersTableDb - the "Db" part indicates that the source of data for our table is a database table. There are other classes to use, for example, a JSON document as the data source.
  • We pass it the name of our database table (or for the other classes, whatever the data source is) and the query string part of the URL where we're going to be displaying this (which we need to build the links and AJAX calls).
  • I'm passing it to the output through a template called "block" which simply adds the dark-blue bar at the top, which isn't actually part of the table itself, and some padding. The "members" parameter is the key for the langauge string to use in that dark-blue bar.
  • I'm passing $table directly to the template - the helper class has a __toString method which renders the table, so the output class thinks it's been given a normal string.
 

 

 

The first obvious thing is that we're showing all the columns in the database table, which obviously we don't want. So let's add another line to specify which columns we want:

 

 

In this example, I'm giving the helper class a list of columns to include - I could alternatively pass a list of columns to exclude, if that would be more appropriate.

The output is now this:

 

Some things to note:

  • $table->langPrefix = 'members_'; 

And I'll then create some language strings that match that (so "members_name", "members_email", etc.).

 

The output is now this:

 

 

 

 

Next - we need to change how we display some of those values. The joined date and the group are displaying the raw values from the database, but we want something more meaningful than that.

 

To format the values, we simply create an array of lambda functions - one for each we want to format:

 

 

 

The output is now this:

 

Some things to note:

  • IPSDateTime class to format the joined date. The ts method in this is a factory method which takes a UNIX timestamp and returns an object of
IPSDateTime. IPSDateTime extends DateTime, so all the features of that class are available to us. The localeDate method returns a string with the date formatted appropriately according to user's locale.The IPSMemberGroup::load call being executed for each result may look like it might be resource intensive, but it caches objects it creates, so it's only actually "loading" each group once. 

 

 

 

Now  I want to add a column with the user's photo. There isn't a single "photo" column in the database we can use for this (since the photo could be one they uploaded, a photo from their Facebook account if they're using Facebook Connect, a Gravatar image, or some other things), we need to use a method in the IPSMember class.

 

This isn't a problem. I can simply add an element to our list of fields to include and add that into the parsers.


$table->noSort	= array( 'photo' );
 

 

The output is now this:

 

 

Some things to note:

  • $val in the lambda function will be
NULL, however, $row has all the data for that record.We're not using IPSMember::load to get the member object, since that would execute an additional query for every result, which would be resource intensive, and unnecessary since we already have that data. Instead, we use the constructFromData method and pass it the row from the database. 

 

 

Next, I want to specify the default sorting. This is done with just two lines of code:

 

 

The output is now this:

 

 

 

 

Now, I want to add a quick search box. All we need to do is specify which column the quick search should look at:

As you type, results are obtained with AJAX.You can page through your results (the number of pages will update automatically) and reorder your results by clicking the headers without loosing your search. 

 

 

I also want to allow more advanced search options - like to search by email address, or joined date. To do this, I create a new array:

 

 

To explain what's going on here:

  • $groups in this snippet, but it'll be at the end of this blog entry), and, because we need to search both primary and secondary groups based on the value, there's a lambda function to get the proper WHERE clause for the query.
 

Now, next to the quick search box there's a button which will bring up a modal popup (or just take you to a new page if JavaScript is disabled) which looks like this:

 

Some things to note:

  • The date entry boxes use the HTML5 date input type:

    If your browser doesn't support that, there's a JavaScript fallback:

    And if you're really awkward and are using a browser that doesn't support the HTML5 date input type and have JavaScript disabled, you'll see a regular text box where you can enter a date in practically any format, and it'll work it out.

  • After performing the search, you can reorder your results by clicking the headers without loosing your search.
 

 

 

 

Now, I want to add some filters so you can quickly see banned, spam, locked and validating members. To do this, you create an array simply specifying the WHERE clause to use in the query for each filter:

 

For this though, I'll also need to join the core_validating database table, so we add one more line for that:

The helper class will add the "All" filter automatically.It's getting the word to use for the filter by looking for a language string with the same key as the key in the array passed.Like everything else, clicking a filter updates the results with AJAX and the filter is retained in searches. 

 

 

 

Finally, the last thing I need to do is add a column with some buttons. You can specify a normal array for buttons to show in the header, and a lambda functions to return an array for buttons to show for each row:

 

 

 

 

Our finished table looks like this:

 

And behaves like this:

/* Create the table */ $table = new IPSHelpersTableDb( 'core_members', 'app=core&module=members&section=members' ); $table->langPrefix = 'members_'; /* Columns we need */ $table->include = array( 'photo', 'name', 'email', 'joined', 'member_group_id', 'ip_address' ); $table->mainColumn = 'name'; $table->noSort = array( 'photo' ); /* Default sort options */ $table->sortBy = $table->sortBy ?: 'joined'; $table->sortDirection = $table->sortDirection ?: 'desc'; /* Filters */ $table->joins = array( array( 'from' => array( 'core_validating' => 'v' ), 'where' => 'v.member_id=_0.member_id' ) ); $table->filters = array( 'members_filter_banned' => 'member_banned=1', 'members_filter_locked' => 'failed_login_count>=' . (int) IPSSettings::i()->ipb_bruteforce_attempts, /*@todo*/ 'members_filter_spam' => '(members_bitoptions & ' . IPSMember::$bitOptions['bw_is_spammer'] . ') != 0', 'members_filter_validating' => 'v.lost_pass=0 AND v.vid IS NOT NULL' ); /* Groups for advanced filter (need to do it this way because array_merge renumbers the result */ $groups = array( '' => 'any_group' ); foreach ( IPSMemberGroup::groups() as $k => $v ) { $groups[ $k ] = $v; } /* Search */ $table->quickSearch = 'name'; $table->advancedSearch = array( 'member_id' => IPSHelpersTableSEARCH_CONTAINS_TEXT, 'email' => IPSHelpersTableSEARCH_CONTAINS_TEXT, 'ip_address' => IPSHelpersTableSEARCH_CONTAINS_TEXT, 'member_group_id' => array( IPSHelpersTableSEARCH_SELECT, array( 'options' => $groups ), function( $val ) { return array( 'member_group_id=? OR ? IN(mgroup_others)', $val, $val ); } ), 'joined' => IPSHelpersTableSEARCH_DATE_RANGE, ); /* Custom parsers */ $table->parsers = array( 'photo' => function( $val, $row ) { return IPSMember::constructFromData( $row )->photo('mini'); }, 'joined' => function( $val, $row ) { return IPSDateTime::ts( $val )->localeDate(); }, 'member_group_id' => function( $val, $row ) { return IPSMemberGroup::load( $val )->formattedName(); } ); /* Specify the buttons */ $table->rootButtons = array( 'add' => array( 'icon' => array( 'icons/add.png', 'core' ), 'title' => 'members_add', 'link' => 'app=members&module=members&section=members&do=add', ) ); $table->rowButtons = function( $row ) { return array( 'edit' => array( 'icon' => array( 'icons/edit.png', 'core' ), 'title' => 'edit', 'link' => 'app=members&module=members&section=members&do=edit&id=' . $row['member_id'], ), 'delete' => array( 'icon' => array( 'icons/delete.png', 'core' ), 'title' => 'delete', 'link' => 'app=members&module=members&section=members&do=delete&id=' . $row['member_id'], 'class' => 'delete', ), ); }; /* Display */ IPSOutput::i()->output = IPSOutput::i()->getTemplate( 'global' )->block( 'members', $table );

 

 

Zobacz cały wpis

Jeśli chcesz dodać odpowiedź, zaloguj się lub zarejestruj nowe konto

Jedynie zarejestrowani użytkownicy mogą komentować zawartość tej strony.

Zarejestruj nowe konto

Załóż nowe konto. To bardzo proste!

Zarejestruj się

Zaloguj się

Posiadasz już konto? Zaloguj się poniżej.

Zaloguj się
  • Ostatnio przeglądający   0 użytkowników

    • Brak zarejestrowanych użytkowników przeglądających tę stronę.
×
×
  • Dodaj nową pozycję...

Powiadomienie o plikach cookie

Umieściliśmy na Twoim urządzeniu pliki cookie, aby pomóc Ci usprawnić przeglądanie strony. Możesz dostosować ustawienia plików cookie, w przeciwnym wypadku zakładamy, że wyrażasz na to zgodę.