db->prepare( 'SELECT * FROM %i WHERE `id` = %d LIMIT 1', $this->table( 'service_extras' ), $id ); $row = $this->db->get_row( $sql, ARRAY_A ); return is_array( $row ) ? $this->hydrate( $row ) : null; } /** * Find by uuid. * * {@inheritDoc} * * @param Uuid $uuid Parameter. * @since 0.1.0 */ public function find_by_uuid( Uuid $uuid ): ?ServiceExtra { $sql = $this->db->prepare( 'SELECT * FROM %i WHERE `uuid` = %s LIMIT 1', $this->table( 'service_extras' ), $uuid->to_string() ); $row = $this->db->get_row( $sql, ARRAY_A ); return is_array( $row ) ? $this->hydrate( $row ) : null; } /** * Save. * * {@inheritDoc} * * @param ServiceExtra $extra Parameter. * @since 0.1.0 */ public function save( ServiceExtra $extra ): void { $data = [ 'uuid' => $extra->uuid()->to_string(), 'name' => $extra->name(), 'description' => $extra->description(), 'price_minor' => $extra->price()->amount_minor(), 'currency' => $extra->price()->currency(), 'duration_min' => $extra->duration_min(), 'max_quantity' => $extra->max_quantity(), 'image_id' => $extra->image_id(), 'status' => $extra->status()->value, 'sort_order' => $extra->sort_order(), 'created_at' => $this->format_datetime( $extra->created_at() ), 'updated_at' => $this->format_datetime( $extra->updated_at() ), ]; if ( null === $extra->id() ) { $this->db->insert( $this->table( 'service_extras' ), $data ); $extra->assign_id( $this->last_insert_id() ); return; } $this->db->update( $this->table( 'service_extras' ), $data, [ 'id' => $extra->id() ] ); } /** * {@inheritDoc} * * @param ServiceExtra $extra Extra. */ public function delete( ServiceExtra $extra ): void { if ( null === $extra->id() ) { return; } $id = (int) $extra->id(); $this->db->delete( $this->table( 'service_extra_links' ), [ 'extra_id' => $id ] ); $this->db->delete( $this->table( 'service_extras' ), [ 'id' => $id ] ); } /** * {@inheritDoc} * * @return list */ public function all(): array { $sql = $this->db->prepare( 'SELECT * FROM %i ORDER BY `sort_order` ASC, `id` ASC', $this->table( 'service_extras' ) ); return $this->hydrate_rows( $this->db->get_results( $sql, ARRAY_A ) ); } /** * {@inheritDoc} * * @param int $service_id Service id. * @return list */ public function find_for_service( int $service_id ): array { return $this->linked_to_service( $service_id, false ); } /** * Active extras linked to a service. * * @param int $service_id Service id. * @return list */ public function find_active_for_service( int $service_id ): array { return $this->linked_to_service( $service_id, true ); } /** * {@inheritDoc} * * @param int $extra_id Extra id. * @return list */ public function service_ids_for( int $extra_id ): array { if ( $extra_id < 1 ) { return []; } $sql = $this->db->prepare( 'SELECT `service_id` FROM %i WHERE `extra_id` = %d ORDER BY `sort_order` ASC, `service_id` ASC', $this->table( 'service_extra_links' ), $extra_id ); $rows = $this->db->get_results( $sql, ARRAY_A ); if ( ! is_array( $rows ) ) { return []; } $ids = []; foreach ( $rows as $row ) { if ( is_array( $row ) ) { $id = (int) ( $row['service_id'] ?? 0 ); if ( $id > 0 ) { $ids[] = $id; } } } return $ids; } /** * {@inheritDoc} * * @param int $extra_id Extra id. * @param list $service_ids Service ids. */ public function replace_links( int $extra_id, array $service_ids ): void { if ( $extra_id < 1 ) { return; } $this->db->delete( $this->table( 'service_extra_links' ), [ 'extra_id' => $extra_id ] ); $order = 0; foreach ( array_values( array_unique( $service_ids ) ) as $service_id ) { $sid = (int) $service_id; if ( $sid < 1 ) { continue; } $this->db->insert( $this->table( 'service_extra_links' ), [ 'service_id' => $sid, 'extra_id' => $extra_id, 'is_required' => 0, 'sort_order' => $order, ] ); ++$order; } } /** * Linked extras, optionally active only. * * @param int $service_id Service id. * @param bool $active_only Active extras only. * @return list */ private function linked_to_service( int $service_id, bool $active_only ): array { if ( $service_id < 1 ) { return []; } if ( $active_only ) { $sql = $this->db->prepare( 'SELECT e.* FROM %i e INNER JOIN %i l ON e.`id` = l.`extra_id` WHERE l.`service_id` = %d AND e.`status` = %s ORDER BY l.`sort_order` ASC, e.`sort_order` ASC, e.`id` ASC', $this->table( 'service_extras' ), $this->table( 'service_extra_links' ), $service_id, ServiceExtraStatus::Active->value ); } else { $sql = $this->db->prepare( 'SELECT e.* FROM %i e INNER JOIN %i l ON e.`id` = l.`extra_id` WHERE l.`service_id` = %d ORDER BY l.`sort_order` ASC, e.`sort_order` ASC, e.`id` ASC', $this->table( 'service_extras' ), $this->table( 'service_extra_links' ), $service_id ); } return $this->hydrate_rows( $this->db->get_results( $sql, ARRAY_A ) ); } /** * Hydrate a result set. * * @param mixed $rows Rows. * @return list */ private function hydrate_rows( mixed $rows ): array { if ( ! is_array( $rows ) ) { return []; } $out = []; foreach ( $rows as $row ) { if ( is_array( $row ) ) { $out[] = $this->hydrate( $row ); } } return $out; } /** * Map a database row to an entity. * * @param array $row Database row. */ private function hydrate( array $row ): ServiceExtra { return new ServiceExtra( (int) $row['id'], new Uuid( (string) $row['uuid'] ), (string) $row['name'], null !== $row['description'] ? (string) $row['description'] : null, new Money( (int) $row['price_minor'], (string) $row['currency'] ), (int) $row['duration_min'], (int) $row['max_quantity'], null !== $row['image_id'] ? (int) $row['image_id'] : null, ServiceExtraStatus::from( (string) $row['status'] ), (int) $row['sort_order'], $this->parse_datetime( (string) $row['created_at'] ), $this->parse_datetime( (string) $row['updated_at'] ), ); } }