PluginProbe ʕ •ᴥ•ʔ
AlphaListing / 4.5.0
AlphaListing v4.5.0
4.5.0 trunk 4.3.4 4.3.5 4.3.6 4.3.7 4.4.0
alphalisting / src / Shortcode / QueryParts / GroupBy.php
alphalisting / src / Shortcode / QueryParts Last commit date
Alphabet.php 2 weeks ago BackToTop.php 2 weeks ago ColumnGap.php 2 weeks ago ColumnWidth.php 2 weeks ago Columns.php 2 weeks ago CssLengthCommon.php 2 weeks ago ExcludePosts.php 2 weeks ago ExcludeTerms.php 2 weeks ago GroupBy.php 2 weeks ago HideEmptyTerms.php 2 weeks ago HideEmpty_Deprecated.php 2 weeks ago InstanceId.php 2 weeks ago ParentPost.php 2 weeks ago ParentTermCommon.php 2 weeks ago ParentTermId.php 2 weeks ago ParentTermSlugOrId.php 2 weeks ago PostType.php 2 weeks ago PostsTerms.php 2 weeks ago SymbolsFirst.php 2 weeks ago Taxonomy.php 2 weeks ago TermsCommon.php 2 weeks ago TermsTerms.php 2 weeks ago
GroupBy.php
156 lines
1 <?php
2 /**
3 * Group By Query Part.
4 *
5 * @package alphalisting
6 */
7
8 declare(strict_types=1);
9
10 namespace eslin87\AlphaListing\Shortcode\QueryParts;
11
12 if ( ! defined( 'ABSPATH' ) ) {
13 exit;
14 }
15
16 use \eslin87\AlphaListing\Alphabet;
17 use \eslin87\AlphaListing\Shortcode\Extension;
18 use \eslin87\AlphaListing\Strings;
19
20 /**
21 * Group posts by a selected part of their title.
22 */
23 class GroupBy extends Extension {
24 /**
25 * The attribute for this Query Part.
26 *
27 * @var string
28 */
29 public $attribute_name = 'group-by';
30
31 /**
32 * The listing types supported by this Query Part.
33 *
34 * @var array<string>
35 */
36 public $display_types = array( 'posts' );
37
38 /**
39 * Sanitize the grouping mode.
40 *
41 * @param mixed $value The value of the shortcode attribute.
42 * @param array $attributes The complete set of shortcode attributes.
43 * @return string
44 */
45 public function sanitize_attribute( $value, array $attributes ): string {
46 $value = is_string( $value ) ? strtolower( trim( $value ) ) : '';
47 return 'last-word' === $value ? 'last-word' : '';
48 }
49
50 /**
51 * Enable last-word grouping for post listings.
52 *
53 * @param mixed $query The query.
54 * @param string $display The display/query type.
55 * @param string $key The name of the attribute.
56 * @param mixed $value The shortcode attribute value.
57 * @param array $attributes The complete set of shortcode attributes.
58 * @return mixed
59 */
60 public function shortcode_query_for_display_and_attribute( $query, string $display, string $key, $value, array $attributes ) {
61 if ( 'last-word' !== $value ) {
62 return $query;
63 }
64
65 $this->add_hook( 'filter', 'alphalisting_item_index_letter', array( $this, 'index_by_last_word' ), 10, 4 );
66 $this->add_hook( 'filter', 'alphalisting_item_sorting_comparator', array( $this, 'sort_by_last_word' ), 10, 4 );
67
68 if ( is_array( $query ) ) {
69 // Distinguish this result from the default grouping in external cache implementations.
70 $query['_alphalisting_group_by'] = 'last-word';
71 }
72
73 return $query;
74 }
75
76 /**
77 * Return the index letter from the last word of a post title.
78 *
79 * @param array $letters Existing index letters.
80 * @param mixed $item The post object or ID.
81 * @param string $type The listing type.
82 * @param string $title The filtered title used to index the item.
83 * @return array
84 */
85 public function index_by_last_word( array $letters, $item, string $type, string $title = '' ): array {
86 if ( 'posts' !== $type ) {
87 return $letters;
88 }
89
90 if ( '' === $title ) {
91 return $letters;
92 }
93
94 $word = self::get_last_word( $title );
95
96 if ( '' === $word ) {
97 return $letters;
98 }
99
100 return array( Strings::maybe_mb_substr( $word, 0, 1 ) );
101 }
102
103 /**
104 * Sort titles by their last word, then by their complete title.
105 *
106 * @param int $default_sort The existing comparison result.
107 * @param string $first_title The first title.
108 * @param string $second_title The second title.
109 * @param Alphabet $alphabet The configured listing alphabet.
110 * @return int
111 */
112 public function sort_by_last_word( int $default_sort, string $first_title, string $second_title, Alphabet $alphabet ): int {
113 $first_word = self::get_last_word( $first_title );
114 $second_word = self::get_last_word( $second_title );
115
116 if ( '' === $first_word || '' === $second_word ) {
117 return $default_sort;
118 }
119
120 $comparison = $alphabet->compare_strings( $first_word, $second_word );
121 if ( 0 !== $comparison ) {
122 return $comparison <=> 0;
123 }
124
125 return $default_sort;
126 }
127
128 /**
129 * Extract the final word from a title.
130 *
131 * @param string $title The title to parse.
132 * @return string
133 */
134 public static function get_last_word( string $title ): string {
135 $title = trim( wp_strip_all_tags( $title ) );
136 if ( '' === $title ) {
137 return '';
138 }
139
140 $words = preg_split( '/\s+/u', $title );
141 if ( ! is_array( $words ) || empty( $words ) ) {
142 return '';
143 }
144
145 for ( $index = count( $words ) - 1; $index >= 0; $index-- ) {
146 $word = preg_replace( '/^[\p{P}\p{S}]+|[\p{P}\p{S}]+$/u', '', (string) $words[ $index ] );
147
148 if ( is_string( $word ) && '' !== $word ) {
149 return $word;
150 }
151 }
152
153 return '';
154 }
155 }
156