failure, P2P_Directed_Connection_Type instance or direction on success. */ public function find_direction( $arg, $instantiate = true, $object_type = null ) { if ( $object_type ) { $direction = $this->direction_from_object_type( $object_type ); if ( !$direction ) return false; if ( in_array( $direction, array( 'from', 'to' ) ) ) return $this->set_direction( $direction, $instantiate ); } $r = $this->direction_from_item( $arg ); if ( !$r ) return false; list( $direction, $item ) = $r; return $this->set_direction( $direction, $instantiate ); } protected function direction_from_item( $arg ) { if ( is_array( $arg ) ) $arg = reset( $arg ); foreach ( array( 'from', 'to' ) as $direction ) { $item = $this->side[ $direction ]->item_recognize( $arg ); if ( !$item ) continue; return array( $this->strategy->choose_direction( $direction ), $item ); } return false; } protected function direction_from_object_type( $current ) { $from = $this->side['from']->get_object_type(); $to = $this->side['to']->get_object_type(); if ( $from == $to && $current == $from ) return 'any'; if ( $current == $from ) return 'to'; if ( $current == $to ) return 'from'; return false; } public function direction_from_types( $object_type, $post_types = null ) { foreach ( array( 'from', 'to' ) as $direction ) { if ( !$this->_type_check( $direction, $object_type, $post_types ) ) continue; return $this->strategy->choose_direction( $direction ); } return false; } private function _type_check( $direction, $object_type, $post_types ) { if ( $object_type != $this->side[ $direction ]->get_object_type() ) return false; $side = $this->side[ $direction ]; if ( !method_exists( $side, 'recognize_post_type' ) ) return true; foreach ( (array) $post_types as $post_type ) { if ( $side->recognize_post_type( $post_type ) ) { return true; } } return false; } /** Alias for get_prev() */ public function get_previous( $from, $to ) { return $this->get_prev( $from, $to ); } /** * Get the previous post in an ordered connection. * * @param int The first end of the connection. * @param int The second end of the connection. * * @return bool|object False on failure, post object on success */ public function get_prev( $from, $to ) { return $this->get_adjacent( $from, $to, -1 ); } /** * Get the next post in an ordered connection. * * @param int The first end of the connection. * @param int The second end of the connection. * * @return bool|object False on failure, post object on success */ public function get_next( $from, $to ) { return $this->get_adjacent( $from, $to, +1 ); } /** * Get another post in an ordered connection. * * @param int The first end of the connection. * @param int The second end of the connection. * @param int The position relative to the first parameter * * @return bool|object False on failure, post object on success */ public function get_adjacent( $from, $to, $which ) { // The direction needs to be based on the second parameter, // so that it's consistent with $this->connect( $from, $to ) etc. $r = $this->direction_from_item( $to ); if ( !$r ) return false; list( $direction, $to ) = $r; $directed = $this->set_direction( $direction ); $key = $directed->get_orderby_key(); if ( !$key ) return false; $p2p_id = $directed->get_p2p_id( $to, $from ); if ( !$p2p_id ) return false; $order = (int) p2p_get_meta( $p2p_id, $key, true ); $adjacent = $directed->get_connected( $to, array( 'connected_meta' => array( array( 'key' => $key, 'value' => $order + $which ) ) ), 'abstract' ); if ( empty( $adjacent->items ) ) return false; $item = reset( $adjacent->items ); return $item->get_object(); } /** * Get the previous, next and parent items, in an ordered connection type. * * @param mixed The current item * * @return bool|array False if the connections aren't sortable, * associative array otherwise: * array( * 'parent' => bool|object * 'previous' => bool|object * 'next' => bool|object * ) */ public function get_adjacent_items( $item ) { $result = array( 'parent' => false, 'previous' => false, 'next' => false, ); $r = $this->direction_from_item( $item ); if ( !$r ) return false; list( $direction, $item ) = $r; $connected_series = $this->set_direction( $direction )->get_connected( $item, array(), 'abstract' )->items; if ( empty( $connected_series ) ) return $r; if ( count( $connected_series ) > 1 ) { trigger_error( 'More than one connected parents found.', E_USER_WARNING ); } $parent = $connected_series[0]; $result['parent'] = $parent->get_object(); $result['previous'] = $this->get_previous( $item->ID, $parent->ID ); $result['next'] = $this->get_next( $item, $parent ); return $result; } /** * Optimized inner query, after the outer query was executed. * * Populates each of the outer querie's $post objects with a 'connected' property, containing a list of connected posts * * @param object|array $items WP_Query instance or list of post objects * @param string|array $extra_qv Additional query vars for the inner query. * @param string $prop_name The name of the property used to store the list of connected items on each post object. */ public function each_connected( $items, $extra_qv = array(), $prop_name = 'connected' ) { if ( is_a( $items, 'WP_Query' ) ) $items =& $items->posts; if ( empty( $items ) || !is_object( $items[0] ) ) return; $post_types = array_unique( wp_list_pluck( $items, 'post_type' ) ); if ( count( $post_types ) > 1 ) { $extra_qv['post_type'] = 'any'; } $possible_directions = array(); foreach ( array( 'from', 'to' ) as $direction ) { $side = $this->side[ $direction ]; if ( 'post' == $side->get_object_type() ) { foreach ( $post_types as $post_type ) { if ( $side->recognize_post_type( $post_type ) ) { $possible_directions[] = $direction; } } } } $direction = _p2p_compress_direction( $possible_directions ); if ( !$direction ) return false; $directed = $this->set_direction( $direction ); // ignore pagination foreach ( array( 'showposts', 'posts_per_page', 'posts_per_archive_page' ) as $disabled_qv ) { if ( isset( $extra_qv[ $disabled_qv ] ) ) { trigger_error( "Can't use '$disabled_qv' in an inner query", E_USER_WARNING ); } } $extra_qv['nopaging'] = true; $q = $directed->get_connected( $items, $extra_qv, 'abstract' ); $raw_connected = array(); foreach ( $q->items as $item ) $raw_connected[] = $item->get_object(); p2p_distribute_connected( $items, $raw_connected, $prop_name ); } public function get_desc() { $desc = array(); foreach ( array( 'from', 'to' ) as $key ) { $desc[ $key ] = $this->side[ $key ]->get_desc(); } $label = sprintf( '%s %s %s', $desc['from'], $this->strategy->get_arrow(), $desc['to'] ); $title = $this->get_field( 'title', 'from' ); if ( $title ) $label .= " ($title)"; return $label; } }