PluginProbe
Gutenberg / 23.2.1
Gutenberg v23.2.1
23.9.1 23.9.0 23.8.0 23.7.2 23.7.1 23.7.0 23.6.1 23.6.2 23.6.0 23.5.3 23.5.2 23.5.1 23.5.0 23.4.0 23.3.2 23.3.1 23.3.0 23.2.0 23.2.1 23.2.2 23.1.1 23.1.0 23.0.1 12.6.0 7.4.0 All 402 releases
gutenberg / lib / experimental / class-gutenberg-hierarchical-sort.php

class-gutenberg-hierarchical-sort.php in Gutenberg 23.2.1, at lib/experimental/class-gutenberg-hierarchical-sort.php

206 lines 4.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * Modifies the Post controller endpoint to support orderby_hierarchy.
5 *
6 * @package gutenberg
7 * @since 6.8.0
8 */
9
10 class Gutenberg_Hierarchical_Sort {
11 private static $post_ids = array();
12 private static $levels = array();
13 private static $instance;
14
15 public static function get_instance() {
16 if ( null === self::$instance ) {
17 self::$instance = new self();
18 }
19
20 return self::$instance;
21 }
22
23 public function run( $args ) {
24 $new_args = array_merge(
25 $args,
26 array(
27 'fields' => 'id=>parent',
28 'posts_per_page' => -1,
29 )
30 );
31 $query = new WP_Query( $new_args );
32 $posts = $query->posts;
33 $result = self::sort( $posts );
34
35 self::$post_ids = $result['post_ids'];
36 self::$levels = $result['levels'];
37 }
38
39 /**
40 * Check if the request is eligible for hierarchical sorting.
41 *
42 * @param array $request The request data.
43 *
44 * @return bool Return true if the request is eligible for hierarchical sorting.
45 */
46 public static function is_eligible( $request ) {
47 if ( ! isset( $request['orderby_hierarchy'] ) || true !== $request['orderby_hierarchy'] ) {
48 return false;
49 }
50
51 return true;
52 }
53
54 public static function get_ancestor( $post_id ) {
55 return get_post( $post_id )->post_parent ?? 0;
56 }
57
58 /**
59 * Sort posts by hierarchy.
60 *
61 * Takes an array of posts and sorts them based on their parent-child relationships.
62 * It also tracks the level depth of each post in the hierarchy.
63 *
64 * Example input:
65 * ```
66 * [
67 * ['ID' => 4, 'post_parent' => 2],
68 * ['ID' => 2, 'post_parent' => 0],
69 * ['ID' => 3, 'post_parent' => 2],
70 * ]
71 * ```
72 *
73 * Example output:
74 * ```
75 * [
76 * 'post_ids' => [2, 4, 3],
77 * 'levels' => [0, 1, 1]
78 * ]
79 * ```
80 *
81 * @param array $posts Array of post objects containing ID and post_parent properties.
82 *
83 * @return array {
84 * Sorted post IDs and their hierarchical levels
85 *
86 * @type array $post_ids Array of post IDs
87 * @type array $levels Array of levels for the corresponding post ID in the same index
88 * }
89 */
90 public static function sort( $posts ) {
91 /*
92 * Arrange pages in two arrays:
93 *
94 * - $top_level: posts whose parent is 0
95 * - $children: post ID as the key and an array of children post IDs as the value.
96 * Example: $children[10][] contains all sub-pages whose parent is 10.
97 *
98 * Additionally, keep track of the levels of each post in $levels.
99 * Example: $levels[10] = 0 means the post ID is a top-level page.
100 *
101 */
102 $top_level = array();
103 $children = array();
104 foreach ( $posts as $post ) {
105 if ( empty( $post->post_parent ) ) {
106 $top_level[] = $post->ID;
107 } else {
108 $children[ $post->post_parent ][] = $post->ID;
109 }
110 }
111
112 $ids = array();
113 $levels = array();
114 self::add_hierarchical_ids( $ids, $levels, 0, $top_level, $children );
115
116 // Process remaining children.
117 if ( ! empty( $children ) ) {
118 foreach ( $children as $parent_id => $child_ids ) {
119 $level = 0;
120 $ancestor = $parent_id;
121 while ( 0 !== $ancestor ) {
122 ++$level;
123 $ancestor = self::get_ancestor( $ancestor );
124 }
125 self::add_hierarchical_ids( $ids, $levels, $level, $child_ids, $children );
126 }
127 }
128
129 return array(
130 'post_ids' => $ids,
131 'levels' => $levels,
132 );
133 }
134
135 private static function add_hierarchical_ids( &$ids, &$levels, $level, $to_process, $children ) {
136 foreach ( $to_process as $id ) {
137 if ( in_array( $id, $ids, true ) ) {
138 continue;
139 }
140 $ids[] = $id;
141 $levels[ $id ] = $level;
142
143 if ( isset( $children[ $id ] ) ) {
144 self::add_hierarchical_ids( $ids, $levels, $level + 1, $children[ $id ], $children );
145 unset( $children[ $id ] );
146 }
147 }
148 }
149
150 public static function get_post_ids() {
151 return self::$post_ids;
152 }
153
154 public static function get_levels() {
155 return self::$levels;
156 }
157 }
158
159 add_filter(
160 'rest_page_collection_params',
161 function ( $params ) {
162 $params['orderby_hierarchy'] = array(
163 'description' => 'Sort pages by hierarchy.',
164 'type' => 'boolean',
165 'default' => false,
166 );
167 return $params;
168 }
169 );
170
171 add_filter(
172 'rest_page_query',
173 function ( $args, $request ) {
174 if ( ! Gutenberg_Hierarchical_Sort::is_eligible( $request ) ) {
175 return $args;
176 }
177
178 $hs = Gutenberg_Hierarchical_Sort::get_instance();
179 $hs->run( $args );
180
181 // Reconfigure the args to display only the ids in the list.
182 $args['post__in'] = $hs->get_post_ids();
183 $args['orderby'] = 'post__in';
184
185 return $args;
186 },
187 10,
188 2
189 );
190
191 add_filter(
192 'rest_prepare_page',
193 function ( $response, $post, $request ) {
194 if ( ! Gutenberg_Hierarchical_Sort::is_eligible( $request ) ) {
195 return $response;
196 }
197
198 $hs = Gutenberg_Hierarchical_Sort::get_instance();
199 $response->data['level'] = $hs->get_levels()[ $post->ID ];
200
201 return $response;
202 },
203 10,
204 3
205 );
206