v1.0.0
This commit is contained in:
commit
e1f5a47c96
22 changed files with 3155 additions and 0 deletions
211
includes/class-fontdelbosc-combustible-api.php
Normal file
211
includes/class-fontdelbosc-combustible-api.php
Normal file
|
|
@ -0,0 +1,211 @@
|
|||
<?php
|
||||
/**
|
||||
* MITECO / MINETUR ServiciosRestCarburantes API client.
|
||||
*
|
||||
* @package FontdelboscCombustible
|
||||
*/
|
||||
|
||||
declare( strict_types=1 );
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
/**
|
||||
* Fetches and normalizes province station data and the product catalog.
|
||||
*/
|
||||
final class Fontdelbosc_Combustible_Api {
|
||||
|
||||
/**
|
||||
* Hardcoded province ID: Barcelona (08).
|
||||
*/
|
||||
public const PROVINCE_ID = '08';
|
||||
|
||||
/**
|
||||
* Primary API base URL.
|
||||
*/
|
||||
private const BASE_PRIMARY = 'https://energia.serviciosmin.gob.es/ServiciosRestCarburantes/PreciosCarburantes/';
|
||||
|
||||
/**
|
||||
* Fallback API base URL.
|
||||
*/
|
||||
private const BASE_FALLBACK = 'https://sedeaplicaciones.minetur.gob.es/ServiciosRESTCarburantes/PreciosCarburantes/';
|
||||
|
||||
/**
|
||||
* Per-request timeout in seconds. Worst case (primary + fallback) stays bounded.
|
||||
*/
|
||||
private const TIMEOUT = 8;
|
||||
|
||||
/**
|
||||
* Fetches all stations of the province, normalized with distance.
|
||||
*
|
||||
* @return array{fecha:string,stations:array<int,array<string,mixed>>,fetched_at:int,duration:float}|null Null on total failure.
|
||||
*/
|
||||
public static function fetch_province(): ?array {
|
||||
$started = microtime( true );
|
||||
$body = self::get( 'EstacionesTerrestres/FiltroProvincia/' . self::PROVINCE_ID );
|
||||
|
||||
if ( null === $body ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$decoded = json_decode( $body, true );
|
||||
|
||||
if ( ! is_array( $decoded ) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$fecha = Fontdelbosc_Combustible_Payload::str( Fontdelbosc_Combustible_Payload::get( $decoded, 'Fecha' ) ) ?? '';
|
||||
$raw_list = Fontdelbosc_Combustible_Payload::arr( Fontdelbosc_Combustible_Payload::get( $decoded, 'ListaEESSPrecio' ) );
|
||||
$stations = array();
|
||||
|
||||
foreach ( $raw_list as $raw ) {
|
||||
$station = self::normalize_station( $raw );
|
||||
|
||||
if ( null !== $station ) {
|
||||
$stations[] = $station;
|
||||
}
|
||||
}
|
||||
|
||||
return array(
|
||||
'fecha' => $fecha,
|
||||
'stations' => $stations,
|
||||
'fetched_at' => time(),
|
||||
'duration' => microtime( true ) - $started,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the petroleum product catalog.
|
||||
*
|
||||
* @return array<int,array{id:int,name:string,abbr:string}>|null Null on total failure.
|
||||
*/
|
||||
public static function fetch_products(): ?array {
|
||||
$body = self::get( 'Listados/ProductosPetroliferos/' );
|
||||
|
||||
if ( null === $body ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$decoded = json_decode( $body, true );
|
||||
|
||||
if ( ! is_array( $decoded ) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$products = array();
|
||||
|
||||
foreach ( $decoded as $entry ) {
|
||||
$id = Fontdelbosc_Combustible_Payload::int( Fontdelbosc_Combustible_Payload::get( $entry, 'IDProducto' ) );
|
||||
|
||||
if ( null === $id ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$products[] = array(
|
||||
'id' => $id,
|
||||
'name' => Fontdelbosc_Combustible_Payload::str( Fontdelbosc_Combustible_Payload::get( $entry, 'NombreProducto' ) ) ?? (string) $id,
|
||||
'abbr' => Fontdelbosc_Combustible_Payload::str( Fontdelbosc_Combustible_Payload::get( $entry, 'NombreProductoAbreviatura' ) ) ?? '',
|
||||
);
|
||||
}
|
||||
|
||||
return $products;
|
||||
}
|
||||
|
||||
/**
|
||||
* GETs a path against the primary base URL, falling back to the legacy host.
|
||||
*
|
||||
* @param string $path API path relative to the base URL.
|
||||
* @return string|null Response body, or null when both hosts fail.
|
||||
*/
|
||||
private static function get( string $path ): ?string {
|
||||
foreach ( array( self::BASE_PRIMARY, self::BASE_FALLBACK ) as $base ) {
|
||||
$response = wp_remote_get(
|
||||
$base . $path,
|
||||
array(
|
||||
'timeout' => self::TIMEOUT,
|
||||
'headers' => array( 'Accept' => 'application/json' ),
|
||||
)
|
||||
);
|
||||
|
||||
if ( is_wp_error( $response ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( 200 !== wp_remote_retrieve_response_code( $response ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$body = wp_remote_retrieve_body( $response );
|
||||
|
||||
if ( '' !== $body ) {
|
||||
return $body;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes one raw station entry; null when coordinates are unusable.
|
||||
*
|
||||
* @param mixed $raw Raw station.
|
||||
* @return array<string,mixed>|null
|
||||
*/
|
||||
private static function normalize_station( mixed $raw ): ?array {
|
||||
if ( ! is_array( $raw ) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$lat = self::coord( Fontdelbosc_Combustible_Payload::get( $raw, 'Latitud' ) );
|
||||
$lon = self::coord( Fontdelbosc_Combustible_Payload::get( $raw, 'Longitud (WGS84)' ) );
|
||||
|
||||
if ( null === $lat || null === $lon ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$prices = array();
|
||||
|
||||
foreach ( $raw as $key => $value ) {
|
||||
if ( ! is_string( $key ) || ! str_starts_with( $key, 'Precio ' ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$text = Fontdelbosc_Combustible_Payload::str( $value );
|
||||
$price = null === $text ? null : Fontdelbosc_Combustible_Payload::float( str_replace( ',', '.', $text ) );
|
||||
|
||||
if ( null !== $price && $price > 0.0 ) {
|
||||
$prices[ substr( $key, 7 ) ] = $price;
|
||||
}
|
||||
}
|
||||
|
||||
return array(
|
||||
'id' => Fontdelbosc_Combustible_Payload::int( Fontdelbosc_Combustible_Payload::get( $raw, 'IDEESS' ) ) ?? 0,
|
||||
'brand' => Fontdelbosc_Combustible_Payload::str( Fontdelbosc_Combustible_Payload::get( $raw, 'Rótulo' ) ) ?? '',
|
||||
'address' => Fontdelbosc_Combustible_Payload::str( Fontdelbosc_Combustible_Payload::get( $raw, 'Dirección' ) ) ?? '',
|
||||
'locality' => Fontdelbosc_Combustible_Payload::str( Fontdelbosc_Combustible_Payload::get( $raw, 'Localidad' ) ) ?? '',
|
||||
'municipality' => Fontdelbosc_Combustible_Payload::str( Fontdelbosc_Combustible_Payload::get( $raw, 'Municipio' ) ) ?? '',
|
||||
'cp' => Fontdelbosc_Combustible_Payload::str( Fontdelbosc_Combustible_Payload::get( $raw, 'C.P.' ) ) ?? '',
|
||||
'schedule' => Fontdelbosc_Combustible_Payload::str( Fontdelbosc_Combustible_Payload::get( $raw, 'Horario' ) ) ?? '',
|
||||
'tipo_venta' => Fontdelbosc_Combustible_Payload::str( Fontdelbosc_Combustible_Payload::get( $raw, 'Tipo Venta' ) ) ?? '',
|
||||
'lat' => $lat,
|
||||
'lon' => $lon,
|
||||
'distance' => Fontdelbosc_Combustible_Geo::haversine( $lat, $lon ),
|
||||
'prices' => $prices,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a comma-decimal coordinate string.
|
||||
*
|
||||
* @param mixed $value Raw coordinate value.
|
||||
*/
|
||||
private static function coord( mixed $value ): ?float {
|
||||
$text = Fontdelbosc_Combustible_Payload::str( $value );
|
||||
$float = null === $text ? null : Fontdelbosc_Combustible_Payload::float( str_replace( ',', '.', $text ) );
|
||||
|
||||
if ( null === $float || abs( $float ) > 180.0 ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $float;
|
||||
}
|
||||
}
|
||||
407
includes/class-fontdelbosc-combustible-data-manager.php
Normal file
407
includes/class-fontdelbosc-combustible-data-manager.php
Normal file
|
|
@ -0,0 +1,407 @@
|
|||
<?php
|
||||
/**
|
||||
* Orchestration: caching, filtering and sorting of station data.
|
||||
*
|
||||
* @package FontdelboscCombustible
|
||||
*/
|
||||
|
||||
declare( strict_types=1 );
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
/**
|
||||
* Caches API payloads and builds the final station selection.
|
||||
*/
|
||||
final class Fontdelbosc_Combustible_Data_Manager {
|
||||
|
||||
/**
|
||||
* Transient holding the live province payload.
|
||||
*/
|
||||
public const PROVINCE_TRANSIENT = 'fontdelbosc_combustible_province_08';
|
||||
|
||||
/**
|
||||
* Transient holding the stale-fallback copy of the province payload.
|
||||
*/
|
||||
public const STALE_TRANSIENT = 'fontdelbosc_combustible_province_08_stale';
|
||||
|
||||
/**
|
||||
* Transient holding the product catalog.
|
||||
*/
|
||||
public const PRODUCTS_TRANSIENT = 'fontdelbosc_combustible_products';
|
||||
|
||||
/**
|
||||
* Option holding fetch status for the admin screen.
|
||||
*/
|
||||
public const STATUS_OPTION = 'fontdelbosc_combustible_status';
|
||||
|
||||
/**
|
||||
* Option holding plugin settings.
|
||||
*/
|
||||
public const OPTIONS_OPTION = 'fontdelbosc_combustible_options';
|
||||
|
||||
/**
|
||||
* Stale copy lifetime: 25 hours.
|
||||
*/
|
||||
private const STALE_TTL = 90000;
|
||||
|
||||
/**
|
||||
* Product catalog lifetime: 7 days.
|
||||
*/
|
||||
private const PRODUCTS_TTL = 604800;
|
||||
|
||||
/**
|
||||
* Default settings.
|
||||
*
|
||||
* @return array<string,mixed>
|
||||
*/
|
||||
public static function defaults(): array {
|
||||
return array(
|
||||
'search_radius_km' => 15,
|
||||
'max_stations' => 10,
|
||||
'fuel_types' => Fontdelbosc_Combustible_Fuels::DEFAULT_IDS,
|
||||
'show_only_public' => true,
|
||||
'cache_ttl' => 3600,
|
||||
'show_distance' => true,
|
||||
'show_schedule' => true,
|
||||
'sort_by' => 'distance',
|
||||
'delete_data_on_uninstall' => false,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the merged settings.
|
||||
*
|
||||
* @return array<string,mixed>
|
||||
*/
|
||||
public static function options(): array {
|
||||
$clean = array();
|
||||
|
||||
foreach ( Fontdelbosc_Combustible_Payload::arr( get_option( self::OPTIONS_OPTION, array() ) ) as $key => $value ) {
|
||||
if ( is_string( $key ) ) {
|
||||
$clean[ $key ] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
return array_merge( self::defaults(), $clean );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the cached province payload, refreshing it when stale.
|
||||
*
|
||||
* @param bool $force_refresh Skip the live cache.
|
||||
* @return array{fecha:string,stations:array<int,array<mixed>>,fetched_at:int,duration:float,stale?:bool}|null
|
||||
*/
|
||||
public static function get_payload( bool $force_refresh = false ): ?array {
|
||||
if ( ! $force_refresh ) {
|
||||
$cached = get_transient( self::PROVINCE_TRANSIENT );
|
||||
|
||||
if ( is_array( $cached ) ) {
|
||||
return self::shape( $cached );
|
||||
}
|
||||
}
|
||||
|
||||
$payload = Fontdelbosc_Combustible_Api::fetch_province();
|
||||
|
||||
if ( null === $payload ) {
|
||||
$stale = get_transient( self::STALE_TRANSIENT );
|
||||
|
||||
if ( is_array( $stale ) ) {
|
||||
$shaped = self::shape( $stale );
|
||||
$shaped['stale'] = true;
|
||||
|
||||
return $shaped;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
set_transient( self::PROVINCE_TRANSIENT, $payload, max( 60, Fontdelbosc_Combustible_Payload::int( self::options()['cache_ttl'] ?? null ) ?? 3600 ) );
|
||||
set_transient( self::STALE_TRANSIENT, $payload, self::STALE_TTL );
|
||||
self::record_status( $payload );
|
||||
|
||||
return $payload;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the cached product catalog, possibly empty.
|
||||
*
|
||||
* @return array<int,array{id:int,name:string,abbr:string}>
|
||||
*/
|
||||
public static function get_products(): array {
|
||||
$cached = get_transient( self::PRODUCTS_TRANSIENT );
|
||||
|
||||
if ( is_array( $cached ) ) {
|
||||
return self::shape_products( $cached );
|
||||
}
|
||||
|
||||
$products = Fontdelbosc_Combustible_Api::fetch_products();
|
||||
|
||||
if ( null === $products ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
set_transient( self::PRODUCTS_TRANSIENT, $products, self::PRODUCTS_TTL );
|
||||
|
||||
return $products;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the final station selection.
|
||||
*
|
||||
* @param array<string,mixed> $args Overrides: radius, max, fuel, sort, only_public.
|
||||
* @return array{stations:array<int,array<mixed>>,fecha:string,stale:bool,radius:int,fuels:array<int>,fields:array<int,string>}
|
||||
*/
|
||||
public static function get_stations( array $args = array() ): array {
|
||||
$options = self::options();
|
||||
$opt_radius = Fontdelbosc_Combustible_Payload::int( $options['search_radius_km'] ?? null ) ?? 15;
|
||||
$opt_max = Fontdelbosc_Combustible_Payload::int( $options['max_stations'] ?? null ) ?? 10;
|
||||
$opt_sort = Fontdelbosc_Combustible_Payload::str( $options['sort_by'] ?? null ) ?? 'distance';
|
||||
$opt_public = ! empty( $options['show_only_public'] );
|
||||
$only_public = $opt_public && ( $args['only_public'] ?? true );
|
||||
$radius = max( 5, min( 50, Fontdelbosc_Combustible_Payload::int( $args['radius'] ?? null ) ?? $opt_radius ) );
|
||||
$max = max( 1, min( 20, Fontdelbosc_Combustible_Payload::int( $args['max'] ?? null ) ?? $opt_max ) );
|
||||
$fuels = self::fuel_ids( Fontdelbosc_Combustible_Payload::str( $args['fuel'] ?? null ) ?? '', $options );
|
||||
$sort_att = Fontdelbosc_Combustible_Payload::str( $args['sort'] ?? null );
|
||||
$sort = ( $sort_att ?? $opt_sort ) === 'price_asc' ? 'price_asc' : 'distance';
|
||||
|
||||
$stations = array();
|
||||
$fields = array();
|
||||
$stale = false;
|
||||
$fecha = '';
|
||||
$payload = self::get_payload();
|
||||
|
||||
if ( null !== $payload ) {
|
||||
$stale = ! empty( $payload['stale'] );
|
||||
$fecha = $payload['fecha'];
|
||||
$selected = self::select( $payload['stations'], $radius, $fuels, $sort, $only_public );
|
||||
$stations = $selected['stations'];
|
||||
$fields = $selected['fields'];
|
||||
}
|
||||
|
||||
return array(
|
||||
'stations' => array_slice( $stations, 0, $max ),
|
||||
'fecha' => $fecha,
|
||||
'stale' => $stale,
|
||||
'radius' => $radius,
|
||||
'fuels' => $fuels,
|
||||
'fields' => $fields,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes every cache entry.
|
||||
*/
|
||||
public static function flush_cache(): void {
|
||||
delete_transient( self::PROVINCE_TRANSIENT );
|
||||
delete_transient( self::STALE_TRANSIENT );
|
||||
delete_transient( self::PRODUCTS_TRANSIENT );
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters, sorts and dedupes the normalized station list.
|
||||
*
|
||||
* @param array<int,array<mixed>> $stations Stations.
|
||||
* @param int $radius Radius in km.
|
||||
* @param array<int> $fuels Product IDs.
|
||||
* @param string $sort distance|price_asc.
|
||||
* @param bool $only_public Keep Tipo Venta "P" only.
|
||||
* @return array{stations:array<int,array<mixed>>,fields:array<int,string>}
|
||||
*/
|
||||
private static function select( array $stations, int $radius, array $fuels, string $sort, bool $only_public ): array {
|
||||
$labels = array();
|
||||
|
||||
foreach ( $stations as $station ) {
|
||||
foreach ( Fontdelbosc_Combustible_Payload::arr( Fontdelbosc_Combustible_Payload::get( $station, 'prices' ) ) as $label => $_unused ) {
|
||||
if ( is_string( $label ) ) {
|
||||
$labels[ $label ] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$fields = self::fuel_fields( $fuels, array_keys( $labels ) );
|
||||
$keep = array();
|
||||
|
||||
foreach ( $stations as $station ) {
|
||||
$tipo = Fontdelbosc_Combustible_Payload::str( Fontdelbosc_Combustible_Payload::get( $station, 'tipo_venta' ) ) ?? '';
|
||||
|
||||
if ( $only_public && 'P' !== $tipo ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$distance = Fontdelbosc_Combustible_Payload::float( Fontdelbosc_Combustible_Payload::get( $station, 'distance' ) );
|
||||
|
||||
if ( null === $distance || $distance > (float) $radius ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$prices = array();
|
||||
|
||||
foreach ( $fields as $field ) {
|
||||
$price = Fontdelbosc_Combustible_Payload::float(
|
||||
Fontdelbosc_Combustible_Payload::get( Fontdelbosc_Combustible_Payload::arr( Fontdelbosc_Combustible_Payload::get( $station, 'prices' ) ), $field )
|
||||
);
|
||||
|
||||
if ( null !== $price ) {
|
||||
$prices[ $field ] = $price;
|
||||
}
|
||||
}
|
||||
|
||||
if ( array() === $prices ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$station['selected_prices'] = $prices;
|
||||
$station['best_price'] = min( $prices );
|
||||
$keep[] = $station;
|
||||
}
|
||||
|
||||
usort(
|
||||
$keep,
|
||||
static function ( array $a, array $b ) use ( $sort ): int {
|
||||
$a_price = Fontdelbosc_Combustible_Payload::float( Fontdelbosc_Combustible_Payload::get( $a, 'best_price' ) );
|
||||
$b_price = Fontdelbosc_Combustible_Payload::float( Fontdelbosc_Combustible_Payload::get( $b, 'best_price' ) );
|
||||
$a_key = 'price_asc' === $sort ? ( $a_price ?? INF ) : Fontdelbosc_Combustible_Payload::float( Fontdelbosc_Combustible_Payload::get( $a, 'distance' ) ) ?? INF;
|
||||
$b_key = 'price_asc' === $sort ? ( $b_price ?? INF ) : Fontdelbosc_Combustible_Payload::float( Fontdelbosc_Combustible_Payload::get( $b, 'distance' ) ) ?? INF;
|
||||
|
||||
return $a_key <=> $b_key;
|
||||
}
|
||||
);
|
||||
|
||||
return array(
|
||||
'stations' => $keep,
|
||||
'fields' => $fields,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the fuel selection from a shortcode attribute or the options.
|
||||
*
|
||||
* @param string $att Raw "1,4" style attribute.
|
||||
* @param array<string,mixed> $options Settings.
|
||||
* @return array<int>
|
||||
*/
|
||||
private static function fuel_ids( string $att, array $options ): array {
|
||||
if ( '' !== $att ) {
|
||||
$ids = array();
|
||||
|
||||
foreach ( explode( ',', $att ) as $piece ) {
|
||||
$id = Fontdelbosc_Combustible_Payload::int( trim( $piece ) );
|
||||
|
||||
if ( null !== $id && $id > 0 ) {
|
||||
$ids[] = $id;
|
||||
}
|
||||
}
|
||||
|
||||
if ( array() !== $ids ) {
|
||||
return array_values( array_unique( $ids ) );
|
||||
}
|
||||
}
|
||||
|
||||
$saved = array();
|
||||
|
||||
foreach ( Fontdelbosc_Combustible_Payload::arr( $options['fuel_types'] ?? null ) as $id ) {
|
||||
$int = Fontdelbosc_Combustible_Payload::int( $id );
|
||||
|
||||
if ( null !== $int && $int > 0 ) {
|
||||
$saved[] = $int;
|
||||
}
|
||||
}
|
||||
|
||||
return array() === $saved ? Fontdelbosc_Combustible_Fuels::DEFAULT_IDS : array_values( array_unique( $saved ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps product IDs to station price fields using the catalog.
|
||||
*
|
||||
* @param array<int> $ids Product IDs.
|
||||
* @param array<string> $labels Price field labels present in the payload.
|
||||
* @return array<int,string>
|
||||
*/
|
||||
private static function fuel_fields( array $ids, array $labels ): array {
|
||||
$names = array();
|
||||
|
||||
foreach ( self::get_products() as $product ) {
|
||||
$names[ $product['id'] ] = $product['name'];
|
||||
}
|
||||
|
||||
$fields = array();
|
||||
|
||||
foreach ( $ids as $id ) {
|
||||
$name = $names[ (int) $id ] ?? null;
|
||||
$field = null === $name ? null : Fontdelbosc_Combustible_Fuels::station_field( $name, $labels );
|
||||
|
||||
if ( null !== $field ) {
|
||||
$fields[ (int) $id ] = $field;
|
||||
}
|
||||
}
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Coerces a cached payload back into its typed shape.
|
||||
*
|
||||
* @param array<mixed> $raw Cached value.
|
||||
* @return array{fecha:string,stations:array<int,array<mixed>>,fetched_at:int,duration:float,stale?:bool}
|
||||
*/
|
||||
private static function shape( array $raw ): array {
|
||||
$stations = array();
|
||||
|
||||
foreach ( Fontdelbosc_Combustible_Payload::arr( Fontdelbosc_Combustible_Payload::get( $raw, 'stations' ) ) as $station ) {
|
||||
if ( is_array( $station ) ) {
|
||||
$stations[] = $station;
|
||||
}
|
||||
}
|
||||
|
||||
return array(
|
||||
'fecha' => Fontdelbosc_Combustible_Payload::str( Fontdelbosc_Combustible_Payload::get( $raw, 'fecha' ) ) ?? '',
|
||||
'stations' => $stations,
|
||||
'fetched_at' => Fontdelbosc_Combustible_Payload::int( Fontdelbosc_Combustible_Payload::get( $raw, 'fetched_at' ) ) ?? time(),
|
||||
'duration' => Fontdelbosc_Combustible_Payload::float( Fontdelbosc_Combustible_Payload::get( $raw, 'duration' ) ) ?? 0.0,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Coerces a cached product catalog back into its typed shape.
|
||||
*
|
||||
* @param array<mixed> $raw Cached value.
|
||||
* @return array<int,array{id:int,name:string,abbr:string}>
|
||||
*/
|
||||
private static function shape_products( array $raw ): array {
|
||||
$products = array();
|
||||
|
||||
foreach ( $raw as $entry ) {
|
||||
$id = Fontdelbosc_Combustible_Payload::int( Fontdelbosc_Combustible_Payload::get( $entry, 'IDProducto' ) )
|
||||
?? Fontdelbosc_Combustible_Payload::int( Fontdelbosc_Combustible_Payload::get( $entry, 'id' ) );
|
||||
|
||||
if ( null === $id ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$products[] = array(
|
||||
'id' => $id,
|
||||
'name' => Fontdelbosc_Combustible_Payload::str( Fontdelbosc_Combustible_Payload::get( $entry, 'name' ) ) ?? (string) $id,
|
||||
'abbr' => Fontdelbosc_Combustible_Payload::str( Fontdelbosc_Combustible_Payload::get( $entry, 'abbr' ) ) ?? '',
|
||||
);
|
||||
}
|
||||
|
||||
return $products;
|
||||
}
|
||||
|
||||
/**
|
||||
* Persists fetch status for the admin screen.
|
||||
*
|
||||
* @param array<string,mixed> $payload Fresh payload.
|
||||
*/
|
||||
private static function record_status( array $payload ): void {
|
||||
update_option(
|
||||
self::STATUS_OPTION,
|
||||
array(
|
||||
'last_success' => Fontdelbosc_Combustible_Payload::int( Fontdelbosc_Combustible_Payload::get( $payload, 'fetched_at' ) ) ?? time(),
|
||||
'last_duration' => Fontdelbosc_Combustible_Payload::float( Fontdelbosc_Combustible_Payload::get( $payload, 'duration' ) ) ?? 0.0,
|
||||
'last_data_date' => Fontdelbosc_Combustible_Payload::str( Fontdelbosc_Combustible_Payload::get( $payload, 'fecha' ) ) ?? '',
|
||||
),
|
||||
false
|
||||
);
|
||||
}
|
||||
}
|
||||
100
includes/class-fontdelbosc-combustible-fuels.php
Normal file
100
includes/class-fontdelbosc-combustible-fuels.php
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
<?php
|
||||
/**
|
||||
* Fuel catalog bridge between MITECO product IDs and station price fields.
|
||||
*
|
||||
* @package FontdelboscCombustible
|
||||
*/
|
||||
|
||||
declare( strict_types=1 );
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
/**
|
||||
* Resolves product IDs to station price field names and translated labels.
|
||||
*/
|
||||
final class Fontdelbosc_Combustible_Fuels {
|
||||
|
||||
/**
|
||||
* Default products: Gasolina 95 E5 (1), Gasolina 98 E5 (3),
|
||||
* Gasóleo A habitual (4), Gasóleo Premium (5). Verified against the
|
||||
* live catalog; the spec's older ID table is outdated.
|
||||
*/
|
||||
public const DEFAULT_IDS = array( 1, 3, 4, 5 );
|
||||
|
||||
/**
|
||||
* Catalog product names whose station payload key differs from
|
||||
* the "Precio " + name join (accents or wording).
|
||||
*
|
||||
* @var array<string,string>
|
||||
*/
|
||||
private const NAME_ALIASES = array(
|
||||
'Gasóleo A habitual' => 'Gasoleo A',
|
||||
'Gasóleo B' => 'Gasoleo B',
|
||||
'Gasóleo C' => 'Gasoleo C',
|
||||
'Gasóleo Premium' => 'Gasoleo Premium',
|
||||
'Biodiésel' => 'Biodiesel',
|
||||
'Hidrógeno' => 'Hidrogeno',
|
||||
);
|
||||
|
||||
/**
|
||||
* Resolves a catalog product name to the matching station price field.
|
||||
*
|
||||
* @param string $product_name Catalog NombreProducto.
|
||||
* @param array<string> $labels Price field labels present in the payload.
|
||||
*/
|
||||
public static function station_field( string $product_name, array $labels ): ?string {
|
||||
$alias = self::NAME_ALIASES[ $product_name ] ?? $product_name;
|
||||
$want = self::norm( $alias );
|
||||
|
||||
foreach ( $labels as $label ) {
|
||||
if ( self::norm( $label ) === $want ) {
|
||||
return $label;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a translated display label for a product name,
|
||||
* falling back to the raw API name for unknown products.
|
||||
*
|
||||
* @param string $product_name Catalog NombreProducto or station field label.
|
||||
*/
|
||||
public static function label( string $product_name ): string {
|
||||
$name = self::NAME_ALIASES[ $product_name ] ?? $product_name;
|
||||
|
||||
return match ( self::norm( $name ) ) {
|
||||
'gasolina 95 e5' => __( 'Gasoline 95 E5', 'fontdelbosc-combustible' ),
|
||||
'gasolina 95 e5 premium' => __( 'Gasoline 95 E5 Premium', 'fontdelbosc-combustible' ),
|
||||
'gasolina 98 e5' => __( 'Gasoline 98 E5', 'fontdelbosc-combustible' ),
|
||||
'gasoleo a' => __( 'Diesel A', 'fontdelbosc-combustible' ),
|
||||
'gasoleo premium' => __( 'Premium Diesel', 'fontdelbosc-combustible' ),
|
||||
'gasoleo b' => __( 'Diesel B', 'fontdelbosc-combustible' ),
|
||||
'gases licuados del petroleo' => __( 'LPG', 'fontdelbosc-combustible' ),
|
||||
'gas natural comprimido' => __( 'CNG', 'fontdelbosc-combustible' ),
|
||||
'gas natural licuado' => __( 'LNG', 'fontdelbosc-combustible' ),
|
||||
'hidrogeno' => __( 'Hydrogen', 'fontdelbosc-combustible' ),
|
||||
'biodiesel' => __( 'Biodiesel', 'fontdelbosc-combustible' ),
|
||||
'diesel renovable' => __( 'Renewable Diesel', 'fontdelbosc-combustible' ),
|
||||
'gasolina renovable' => __( 'Renewable Gasoline', 'fontdelbosc-combustible' ),
|
||||
'adblue' => __( 'AdBlue', 'fontdelbosc-combustible' ),
|
||||
default => $product_name,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes a name for comparison: lowercase, accents stripped.
|
||||
*
|
||||
* @param string $name Raw name.
|
||||
*/
|
||||
private static function norm( string $name ): string {
|
||||
$ascii = iconv( 'UTF-8', 'ASCII//TRANSLIT//IGNORE', $name );
|
||||
|
||||
if ( false === $ascii || '' === $ascii ) {
|
||||
$ascii = $name;
|
||||
}
|
||||
|
||||
return trim( strtolower( $ascii ) );
|
||||
}
|
||||
}
|
||||
42
includes/class-fontdelbosc-combustible-geo.php
Normal file
42
includes/class-fontdelbosc-combustible-geo.php
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
/**
|
||||
* Haversine distance from Font del Bosc.
|
||||
*
|
||||
* @package FontdelboscCombustible
|
||||
*/
|
||||
|
||||
declare( strict_types=1 );
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
/**
|
||||
* Distance calculations against the fixed origin point.
|
||||
*/
|
||||
final class Fontdelbosc_Combustible_Geo {
|
||||
|
||||
/**
|
||||
* Reference latitude of Font del Bosc.
|
||||
*/
|
||||
public const ORIGIN_LAT = 41.4819;
|
||||
|
||||
/**
|
||||
* Reference longitude of Font del Bosc.
|
||||
*/
|
||||
public const ORIGIN_LON = 1.6483;
|
||||
|
||||
/**
|
||||
* Great-circle distance in km from Font del Bosc to a point.
|
||||
*
|
||||
* @param float $lat2 Target latitude.
|
||||
* @param float $lon2 Target longitude.
|
||||
*/
|
||||
public static function haversine( float $lat2, float $lon2 ): float {
|
||||
$r = 6371;
|
||||
$dlat = deg2rad( $lat2 - self::ORIGIN_LAT );
|
||||
$dlon = deg2rad( $lon2 - self::ORIGIN_LON );
|
||||
$a = ( sin( $dlat / 2 ) ** 2 )
|
||||
+ ( cos( deg2rad( self::ORIGIN_LAT ) ) * cos( deg2rad( $lat2 ) ) * ( sin( $dlon / 2 ) ** 2 ) );
|
||||
|
||||
return 2 * $r * asin( sqrt( $a ) );
|
||||
}
|
||||
}
|
||||
93
includes/class-fontdelbosc-combustible-payload.php
Normal file
93
includes/class-fontdelbosc-combustible-payload.php
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
<?php
|
||||
/**
|
||||
* Typed access helpers for mixed JSON payload values.
|
||||
*
|
||||
* @package FontdelboscCombustible
|
||||
*/
|
||||
|
||||
declare( strict_types=1 );
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
/**
|
||||
* Static helper to read mixed API values with predictable types.
|
||||
*/
|
||||
final class Fontdelbosc_Combustible_Payload {
|
||||
|
||||
/**
|
||||
* Returns an array value for a possibly missing or mixed key.
|
||||
*
|
||||
* @param mixed $value Raw value.
|
||||
* @param array<mixed> $default Fallback.
|
||||
* @return array<mixed>
|
||||
*/
|
||||
public static function arr( mixed $value, array $default = array() ): array {
|
||||
return is_array( $value ) ? $value : $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string value for a possibly missing or mixed key.
|
||||
*
|
||||
* @param mixed $value Raw value.
|
||||
*/
|
||||
public static function str( mixed $value ): ?string {
|
||||
return is_string( $value ) ? trim( $value ) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an int value for a possibly missing or mixed key.
|
||||
*
|
||||
* @param mixed $value Raw value.
|
||||
*/
|
||||
public static function int( mixed $value ): ?int {
|
||||
if ( is_int( $value ) ) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
if ( is_float( $value ) ) {
|
||||
return (int) $value;
|
||||
}
|
||||
|
||||
if ( is_string( $value ) && preg_match( '/^-?\d+$/', trim( $value ) ) ) {
|
||||
return (int) $value;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a float value for a possibly missing or mixed key.
|
||||
*
|
||||
* @param mixed $value Raw value.
|
||||
*/
|
||||
public static function float( mixed $value ): ?float {
|
||||
if ( is_float( $value ) ) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
if ( is_int( $value ) ) {
|
||||
return (float) $value;
|
||||
}
|
||||
|
||||
if ( is_string( $value ) && is_numeric( trim( $value ) ) ) {
|
||||
return (float) trim( $value );
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a key from an array with a default, null-safe.
|
||||
*
|
||||
* @param mixed $value Haystack.
|
||||
* @param string $key Key.
|
||||
* @param mixed $default Fallback.
|
||||
*/
|
||||
public static function get( mixed $value, string $key, mixed $default = null ): mixed {
|
||||
if ( is_array( $value ) && array_key_exists( $key, $value ) ) {
|
||||
return $value[ $key ];
|
||||
}
|
||||
|
||||
return $default;
|
||||
}
|
||||
}
|
||||
76
includes/class-fontdelbosc-combustible-plugin.php
Normal file
76
includes/class-fontdelbosc-combustible-plugin.php
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
<?php
|
||||
/**
|
||||
* Plugin bootstrap singleton.
|
||||
*
|
||||
* @package FontdelboscCombustible
|
||||
*/
|
||||
|
||||
declare( strict_types=1 );
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
/**
|
||||
* Wires hooks and loads the textdomain.
|
||||
*/
|
||||
final class Fontdelbosc_Combustible_Plugin {
|
||||
|
||||
/**
|
||||
* Singleton instance.
|
||||
*
|
||||
* @var self|null
|
||||
*/
|
||||
private static ?self $instance = null;
|
||||
|
||||
/**
|
||||
* Returns the singleton.
|
||||
*/
|
||||
public static function instance(): self {
|
||||
return self::$instance ??= new self();
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor: wires every hook.
|
||||
*/
|
||||
private function __construct() {
|
||||
add_action( 'plugins_loaded', array( self::class, 'load_textdomain' ) );
|
||||
add_action( 'wp_enqueue_scripts', array( self::class, 'register_assets' ) );
|
||||
add_action( 'init', array( 'Fontdelbosc_Combustible_Shortcode', 'init' ) );
|
||||
add_action( 'widgets_init', array( self::class, 'register_widget' ) );
|
||||
|
||||
if ( is_admin() ) {
|
||||
Fontdelbosc_Combustible_Admin::init();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the plugin textdomain.
|
||||
*/
|
||||
public static function load_textdomain(): void {
|
||||
load_plugin_textdomain(
|
||||
'fontdelbosc-combustible',
|
||||
false,
|
||||
dirname( plugin_basename( FONTDELBOSC_COMBUSTIBLE_FILE ) ) . '/languages/'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the public stylesheet.
|
||||
*/
|
||||
public static function register_assets(): void {
|
||||
$css = FONTDELBOSC_COMBUSTIBLE_DIR . 'public/css/fdbc-fuel.css';
|
||||
|
||||
wp_register_style(
|
||||
'fdbc-fuel',
|
||||
plugin_dir_url( FONTDELBOSC_COMBUSTIBLE_FILE ) . 'public/css/fdbc-fuel.css',
|
||||
array(),
|
||||
(string) filemtime( $css )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the widget.
|
||||
*/
|
||||
public static function register_widget(): void {
|
||||
register_widget( 'Fontdelbosc_Combustible_Widget' );
|
||||
}
|
||||
}
|
||||
73
includes/class-fontdelbosc-combustible-shortcode.php
Normal file
73
includes/class-fontdelbosc-combustible-shortcode.php
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
<?php
|
||||
/**
|
||||
* Shortcode [fontdelbosc_combustible] and shared view renderer.
|
||||
*
|
||||
* @package FontdelboscCombustible
|
||||
*/
|
||||
|
||||
declare( strict_types=1 );
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
/**
|
||||
* Renders the station list via the templates.
|
||||
*/
|
||||
final class Fontdelbosc_Combustible_Shortcode {
|
||||
|
||||
/**
|
||||
* Registers the shortcode.
|
||||
*/
|
||||
public static function init(): void {
|
||||
add_shortcode( 'fontdelbosc_combustible', array( self::class, 'render' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the shortcode.
|
||||
*
|
||||
* @param array<string> $atts Shortcode attributes.
|
||||
*/
|
||||
public static function render( $atts = array() ): string {
|
||||
$atts = shortcode_atts(
|
||||
array(
|
||||
'radius' => '',
|
||||
'max' => '',
|
||||
'fuel' => '',
|
||||
'sort' => '',
|
||||
),
|
||||
$atts,
|
||||
'fontdelbosc_combustible'
|
||||
);
|
||||
|
||||
wp_enqueue_style( 'fdbc-fuel' );
|
||||
|
||||
$data = Fontdelbosc_Combustible_Data_Manager::get_stations(
|
||||
array(
|
||||
'radius' => $atts['radius'],
|
||||
'max' => $atts['max'],
|
||||
'fuel' => $atts['fuel'],
|
||||
'sort' => $atts['sort'],
|
||||
)
|
||||
);
|
||||
|
||||
return self::view( 'station-list', $data );
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders a template by name with the given data.
|
||||
*
|
||||
* @param string $name Template name.
|
||||
* @param array<string,mixed> $fontdelbosc_data Template data.
|
||||
*/
|
||||
public static function view( string $name, array $fontdelbosc_data ): string {
|
||||
$file = FONTDELBOSC_COMBUSTIBLE_DIR . 'templates/' . $name . '.php';
|
||||
|
||||
if ( ! is_file( $file ) ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
ob_start();
|
||||
include $file;
|
||||
|
||||
return (string) ob_get_clean();
|
||||
}
|
||||
}
|
||||
156
includes/class-fontdelbosc-combustible-widget.php
Normal file
156
includes/class-fontdelbosc-combustible-widget.php
Normal file
|
|
@ -0,0 +1,156 @@
|
|||
<?php
|
||||
/**
|
||||
* Sidebar widget: nearest stations for one fuel type.
|
||||
*
|
||||
* @package FontdelboscCombustible
|
||||
*/
|
||||
|
||||
declare( strict_types=1 );
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
/**
|
||||
* Compact list of the three nearest stations.
|
||||
*/
|
||||
final class Fontdelbosc_Combustible_Widget extends WP_Widget {
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public function __construct() {
|
||||
parent::__construct(
|
||||
'fontdelbosc_combustible',
|
||||
__( 'Font del Bosc — Fuel Prices', 'fontdelbosc-combustible' ),
|
||||
array(
|
||||
'description' => __( 'Nearest petrol stations with the price of one fuel type.', 'fontdelbosc-combustible' ),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the widget.
|
||||
*
|
||||
* @param array<string,mixed> $args Sidebar args.
|
||||
* @param array<string,mixed> $instance Saved settings.
|
||||
*/
|
||||
public function widget( $args, $instance ): void {
|
||||
$fuel = max( 1, Fontdelbosc_Combustible_Payload::int( Fontdelbosc_Combustible_Payload::get( $instance, 'fuel' ) ) ?? Fontdelbosc_Combustible_Fuels::DEFAULT_IDS[0] );
|
||||
$data = Fontdelbosc_Combustible_Data_Manager::get_stations(
|
||||
array(
|
||||
'fuel' => (string) $fuel,
|
||||
'max' => 3,
|
||||
'sort' => 'distance',
|
||||
)
|
||||
);
|
||||
$field = $data['fields'][ $fuel ] ?? null;
|
||||
|
||||
wp_enqueue_style( 'fdbc-fuel' );
|
||||
|
||||
$before_widget = Fontdelbosc_Combustible_Payload::str( Fontdelbosc_Combustible_Payload::get( $args, 'before_widget' ) ) ?? '';
|
||||
$after_widget = Fontdelbosc_Combustible_Payload::str( Fontdelbosc_Combustible_Payload::get( $args, 'after_widget' ) ) ?? '';
|
||||
$before_title = Fontdelbosc_Combustible_Payload::str( Fontdelbosc_Combustible_Payload::get( $args, 'before_title' ) ) ?? '';
|
||||
$after_title = Fontdelbosc_Combustible_Payload::str( Fontdelbosc_Combustible_Payload::get( $args, 'after_title' ) ) ?? '';
|
||||
$title = Fontdelbosc_Combustible_Payload::str( Fontdelbosc_Combustible_Payload::get( $instance, 'title' ) ) ?? '';
|
||||
|
||||
$title = '' !== $title ? Fontdelbosc_Combustible_Payload::str( apply_filters( 'widget_title', $title, $instance, $this->id_base ) ) ?? $title : '';
|
||||
|
||||
echo wp_kses_post( $before_widget );
|
||||
|
||||
if ( '' !== $title ) {
|
||||
echo wp_kses_post( $before_title . esc_html( $title ) . $after_title );
|
||||
}
|
||||
|
||||
if ( array() === $data['stations'] ) {
|
||||
echo '<p class="fdbc-widget__empty">' . esc_html__( 'Data unavailable.', 'fontdelbosc-combustible' ) . '</p>';
|
||||
} else {
|
||||
echo '<ul class="fdbc-widget__list">';
|
||||
foreach ( $data['stations'] as $station ) {
|
||||
$brand = Fontdelbosc_Combustible_Payload::str( Fontdelbosc_Combustible_Payload::get( $station, 'brand' ) ) ?? '';
|
||||
$muni = Fontdelbosc_Combustible_Payload::str( Fontdelbosc_Combustible_Payload::get( $station, 'municipality' ) ) ?? '';
|
||||
$dist = Fontdelbosc_Combustible_Payload::float( Fontdelbosc_Combustible_Payload::get( $station, 'distance' ) ) ?? 0.0;
|
||||
$price = null === $field ? null : Fontdelbosc_Combustible_Payload::float(
|
||||
Fontdelbosc_Combustible_Payload::get( Fontdelbosc_Combustible_Payload::arr( Fontdelbosc_Combustible_Payload::get( $station, 'prices' ) ), $field )
|
||||
);
|
||||
|
||||
echo '<li class="fdbc-widget__item">';
|
||||
echo '<span class="fdbc-widget__brand">' . esc_html( $brand ) . '</span> ';
|
||||
echo '<span class="fdbc-widget__meta">' . esc_html( $muni ) . '</span>';
|
||||
if ( null !== $price ) {
|
||||
echo ' <span class="fdbc-widget__price">' . esc_html( number_format_i18n( $price, 3 ) ) . '</span>';
|
||||
}
|
||||
/* translators: %s: distance in kilometers. */
|
||||
echo ' <span class="fdbc-widget__dist">' . esc_html( sprintf( __( '%s km away', 'fontdelbosc-combustible' ), number_format_i18n( $dist, 1 ) ) ) . '</span>';
|
||||
echo '</li>';
|
||||
}
|
||||
echo '</ul>';
|
||||
}
|
||||
|
||||
echo wp_kses_post( $after_widget );
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the settings form.
|
||||
*
|
||||
* @param array<string,mixed> $instance Saved settings.
|
||||
*/
|
||||
public function form( $instance ): ?string {
|
||||
$title = Fontdelbosc_Combustible_Payload::str( Fontdelbosc_Combustible_Payload::get( $instance, 'title' ) ) ?? __( 'Fuel prices near Font del Bosc', 'fontdelbosc-combustible' );
|
||||
$fuel = Fontdelbosc_Combustible_Payload::int( Fontdelbosc_Combustible_Payload::get( $instance, 'fuel' ) ) ?? Fontdelbosc_Combustible_Fuels::DEFAULT_IDS[0];
|
||||
?>
|
||||
<p>
|
||||
<label for="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>"><?php esc_html_e( 'Title', 'fontdelbosc-combustible' ); ?></label>
|
||||
<input class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'title' ) ); ?>" type="text" value="<?php echo esc_attr( $title ); ?>" />
|
||||
</p>
|
||||
<p>
|
||||
<label for="<?php echo esc_attr( $this->get_field_id( 'fuel' ) ); ?>"><?php esc_html_e( 'Fuel type', 'fontdelbosc-combustible' ); ?></label>
|
||||
<select class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'fuel' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'fuel' ) ); ?>">
|
||||
<?php foreach ( $this->fuel_choices() as $id => $label ) : ?>
|
||||
<option value="<?php echo esc_attr( (string) $id ); ?>" <?php selected( $id, $fuel ); ?>><?php echo esc_html( $label ); ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</p>
|
||||
<?php
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the settings.
|
||||
*
|
||||
* @param array<string,mixed> $new_instance New settings.
|
||||
* @param array<string,mixed> $old_instance Old settings.
|
||||
* @return array<string,mixed>
|
||||
*/
|
||||
public function update( $new_instance, $old_instance ): array {
|
||||
$instance = $old_instance;
|
||||
|
||||
$instance['title'] = sanitize_text_field( Fontdelbosc_Combustible_Payload::str( Fontdelbosc_Combustible_Payload::get( $new_instance, 'title' ) ) ?? '' );
|
||||
$fuel = Fontdelbosc_Combustible_Payload::int( Fontdelbosc_Combustible_Payload::get( $new_instance, 'fuel' ) );
|
||||
|
||||
if ( null !== $fuel && $fuel > 0 ) {
|
||||
$instance['fuel'] = $fuel;
|
||||
}
|
||||
|
||||
return $instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns fuel choices for the form select.
|
||||
*
|
||||
* @return array<int,string>
|
||||
*/
|
||||
private function fuel_choices(): array {
|
||||
$choices = array();
|
||||
|
||||
foreach ( Fontdelbosc_Combustible_Data_Manager::get_products() as $product ) {
|
||||
$choices[ $product['id'] ] = Fontdelbosc_Combustible_Fuels::label( $product['name'] );
|
||||
}
|
||||
|
||||
if ( array() === $choices ) {
|
||||
foreach ( Fontdelbosc_Combustible_Fuels::DEFAULT_IDS as $id ) {
|
||||
$choices[ $id ] = (string) $id;
|
||||
}
|
||||
}
|
||||
|
||||
return $choices;
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue