db->prepare( 'SELECT * FROM %i WHERE `id` = %d LIMIT 1', $this->table( 'cart_items' ), $id ); $row = $this->db->get_row( $sql, ARRAY_A ); return is_array( $row ) ? $this->hydrate( $row ) : null; } /** * {@inheritDoc} * * @param int $cart_id Parameter. */ public function find_by_cart_id( int $cart_id ): array { $sql = $this->db->prepare( 'SELECT * FROM %i WHERE `cart_id` = %d ORDER BY `sort_order` ASC, `id` ASC', $this->table( 'cart_items' ), $cart_id ); $rows = $this->db->get_results( $sql, ARRAY_A ); if ( ! is_array( $rows ) ) { return []; } $out = []; foreach ( $rows as $row ) { if ( is_array( $row ) ) { $out[] = $this->hydrate( $row ); } } return $out; } /** * {@inheritDoc} * * @param CartItem $item Parameter. */ public function save( CartItem $item ): void { $data = $this->to_row( $item ); if ( null === $item->id() ) { $this->db->insert( $this->table( 'cart_items' ), $data ); $item->assign_id( $this->last_insert_id() ); return; } $this->db->update( $this->table( 'cart_items' ), $data, [ 'id' => $item->id() ] ); } /** * {@inheritDoc} * * @param int $cart_id Parameter. * @param list $items Parameter. */ public function replace_for_cart( int $cart_id, array $items ): array { $this->delete_for_cart( $cart_id ); $saved = []; foreach ( $items as $index => $item ) { $fresh = new CartItem( null, $cart_id, $item->service_id(), $item->duration_id(), $item->agent_id(), $item->location_id(), $item->start_utc(), $item->end_utc(), $item->buffer_start_utc(), $item->buffer_end_utc(), $item->timezone(), $item->attendees(), $item->extras(), $item->custom_fields(), $item->price(), $item->hold_id(), $item->sort_order() > 0 ? $item->sort_order() : $index, ); $this->save( $fresh ); $saved[] = $fresh; } return $saved; } /** * {@inheritDoc} * * @param int $cart_id Parameter. */ public function delete_for_cart( int $cart_id ): void { $sql = $this->db->prepare( 'DELETE FROM %i WHERE `cart_id` = %d', $this->table( 'cart_items' ), $cart_id ); $this->query_prepared( $sql ); } /** * {@inheritDoc} * * @param int $cart_id Parameter. */ public function hold_ids_for_cart( int $cart_id ): array { $sql = $this->db->prepare( 'SELECT DISTINCT `hold_id` FROM %i WHERE `cart_id` = %d AND `hold_id` IS NOT NULL', $this->table( 'cart_items' ), $cart_id ); $rows = $this->db->get_col( $sql ); if ( ! is_array( $rows ) ) { return []; } $out = []; foreach ( $rows as $row ) { $id = (int) $row; if ( $id > 0 ) { $out[] = $id; } } return $out; } /** * {@inheritDoc} * * @param list $candidate_hold_ids Parameter. */ public function filter_unreferenced_hold_ids( array $candidate_hold_ids ): array { $ids = array_values( array_unique( array_filter( array_map( 'intval', $candidate_hold_ids ), static fn( int $id ): bool => $id > 0 ) ) ); if ( [] === $ids ) { return []; } $placeholders = implode( ',', array_fill( 0, count( $ids ), '%d' ) ); $sql = $this->db->prepare( "SELECT DISTINCT `hold_id` FROM %i WHERE `hold_id` IN ({$placeholders})", $this->table( 'cart_items' ), ...$ids ); $referenced = $this->db->get_col( $sql ); $ref_set = []; if ( is_array( $referenced ) ) { foreach ( $referenced as $row ) { $ref_set[ (int) $row ] = true; } } $orphans = []; foreach ( $ids as $id ) { if ( ! isset( $ref_set[ $id ] ) ) { $orphans[] = $id; } } return $orphans; } /** * Row payload for insert/update. * * @param CartItem $item Entity. * @return array */ private function to_row( CartItem $item ): array { return [ 'cart_id' => $item->cart_id(), 'service_id' => $item->service_id(), 'duration_id' => $item->duration_id(), 'agent_id' => $item->agent_id(), 'location_id' => $item->location_id(), 'start_utc' => null === $item->start_utc() ? null : $this->format_datetime( $item->start_utc() ), 'end_utc' => null === $item->end_utc() ? null : $this->format_datetime( $item->end_utc() ), 'buffer_start_utc' => null === $item->buffer_start_utc() ? null : $this->format_datetime( $item->buffer_start_utc() ), 'buffer_end_utc' => null === $item->buffer_end_utc() ? null : $this->format_datetime( $item->buffer_end_utc() ), 'timezone' => $item->timezone(), 'attendees' => $item->attendees(), 'extras' => $this->encode_meta( $item->extras() ), 'custom_fields' => $this->encode_meta( $item->custom_fields() ), 'price_minor' => $item->price()->amount_minor(), 'currency' => $item->price()->currency(), 'hold_id' => $item->hold_id(), 'sort_order' => $item->sort_order(), ]; } /** * Map a database row to an entity. * * @param array $row Database row. */ private function hydrate( array $row ): CartItem { return new CartItem( (int) $row['id'], (int) $row['cart_id'], (int) $row['service_id'], null !== $row['duration_id'] ? (int) $row['duration_id'] : null, null !== $row['agent_id'] ? (int) $row['agent_id'] : null, null !== $row['location_id'] ? (int) $row['location_id'] : null, null !== $row['start_utc'] && '' !== $row['start_utc'] ? $this->parse_datetime( (string) $row['start_utc'] ) : null, null !== $row['end_utc'] && '' !== $row['end_utc'] ? $this->parse_datetime( (string) $row['end_utc'] ) : null, null !== $row['buffer_start_utc'] && '' !== $row['buffer_start_utc'] ? $this->parse_datetime( (string) $row['buffer_start_utc'] ) : null, null !== $row['buffer_end_utc'] && '' !== $row['buffer_end_utc'] ? $this->parse_datetime( (string) $row['buffer_end_utc'] ) : null, null !== $row['timezone'] ? (string) $row['timezone'] : null, (int) $row['attendees'], $this->decode_meta( isset( $row['extras'] ) ? (string) $row['extras'] : null ), $this->decode_meta( isset( $row['custom_fields'] ) ? (string) $row['custom_fields'] : null ), new Money( (int) $row['price_minor'], (string) $row['currency'] ), null !== $row['hold_id'] ? (int) $row['hold_id'] : null, (int) $row['sort_order'], ); } }