| @@ -46,8 +46,14 @@ | ||
| 46 | 46 | if ( ! $tree ) { |
| 47 | 47 | return null; |
| 48 | 48 | } |
| 49 | 49 | |
| 50 | + $order = is_array( $tree['data'] ) ? $tree['data'] : array(); | |
| 51 | + | |
| 52 | + // Saved drag-and-drop order. Sorting the tree's plans here also orders | |
| 53 | + // the duration rows inside every product's price table. | |
| 54 | + $tree['plans'] = self::apply_order( $tree['plans'], $order['term_order'] ?? array(), false ); | |
| 55 | + | |
| 50 | 56 | $type_key = $tree['type_key']; |
| 51 | 57 | $terms = array(); |
| 52 | 58 | |
| 53 | 59 | foreach ( $tree['plans'] as $plan ) { |
| @@ -67,10 +73,47 @@ | ||
| 67 | 73 | 'status' => $tree['status'], |
| 68 | 74 | 'created' => self::ago( $tree['created_at'] ), |
| 69 | 75 | 'edited' => self::ago( $tree['updated_at'] ), |
| 70 | 76 | 'terms' => $terms, |
| 71 | - 'products' => self::products( $tree, $type_key ), | |
| 77 | + 'products' => self::apply_order( self::products( $tree, $type_key ), $order['product_order'] ?? array(), true ), | |
| 72 | 78 | ); |
| 79 | + } | |
| 80 | + | |
| 81 | + /** | |
| 82 | + * Reorder items by a saved list of ids (the detail page's drag-and-drop). | |
| 83 | + * | |
| 84 | + * Items missing from the list keep their default relative order and go | |
| 85 | + * after the listed ones — or before them when $unlisted_first, so a newly | |
| 86 | + * attached product still shows at the top as it did before any reorder. | |
| 87 | + * | |
| 88 | + * @param array $items Items, each with an `id`. | |
| 89 | + * @param array $ids Saved order (ids). | |
| 90 | + * @param bool $unlisted_first Put unlisted items first. | |
| 91 | + * | |
| 92 | + * @return array | |
| 93 | + */ | |
| 94 | + protected static function apply_order( $items, $ids, $unlisted_first ) { | |
| 95 | + if ( empty( $ids ) || ! is_array( $ids ) ) { | |
| 96 | + return $items; | |
| 97 | + } | |
| 98 | + | |
| 99 | + $position = array_flip( array_map( 'intval', array_values( $ids ) ) ); | |
| 100 | + $listed = array(); | |
| 101 | + $unlisted = array(); | |
| 102 | + | |
| 103 | + foreach ( $items as $item ) { | |
| 104 | + $id = (int) $item['id']; | |
| 105 | + if ( isset( $position[ $id ] ) ) { | |
| 106 | + $listed[ $position[ $id ] ] = $item; | |
| 107 | + } else { | |
| 108 | + $unlisted[] = $item; | |
| 109 | + } | |
| 110 | + } | |
| 111 | + | |
| 112 | + ksort( $listed ); | |
| 113 | + $listed = array_values( $listed ); | |
| 114 | + | |
| 115 | + return $unlisted_first ? array_merge( $unlisted, $listed ) : array_merge( $listed, $unlisted ); | |
| 73 | 116 | } |
| 74 | 117 | |
| 75 | 118 | /** |
| 76 | 119 | * Group relations by product into the products[] contract (read-only). |