all() as $material ) { if ( ( $material['id'] ?? '' ) === $id ) { return $material; } } return null; } /** * Create a material, or update it when $id is given. Names are unique, ignoring case. * * @param mixed $input Decoded JSON. * @param string|null $id Id of the material to update. * @return array Validator-style result; 'value' is the saved material. */ public function save( $input, ?string $id = null ): array { $result = Validator::material( $input ); if ( ! $result['ok'] ) { return $result; } $value = $result['value']; $list = $this->all(); if ( null !== $id && null === $this->get( $id ) ) { return array( 'ok' => false, 'errors' => array( 'form' => __( 'This material no longer exists.', 'madecost' ) ), 'status' => 404, ); } foreach ( $list as $other ) { if ( ( $other['id'] ?? '' ) !== $id && self::fold( (string) $other['name'] ) === self::fold( $value['name'] ) ) { return array( 'ok' => false, 'errors' => array( 'name' => __( 'A material with this name already exists.', 'madecost' ) ), ); } } $record = $value + array( 'id' => $id ?? wp_generate_uuid4(), 'updatedAt' => gmdate( 'c' ), ); $found = false; foreach ( $list as $index => $existing ) { if ( ( $existing['id'] ?? '' ) === $record['id'] ) { $list[ $index ] = $record; $found = true; } } if ( ! $found ) { $list[] = $record; } update_option( self::OPTION, array_values( $list ), false ); return array( 'ok' => true, 'value' => $record, ); } /** * Delete a material that no recipe uses. * * @param string $id Material id. * @param int $used In how many recipes the material is used. * @return array array( 'ok' => true ) or array( 'ok' => false, 'reason' => 'not_found'|'in_use', 'recipes' => int ). */ public function delete( string $id, int $used ): array { if ( null === $this->get( $id ) ) { return array( 'ok' => false, 'reason' => 'not_found', ); } if ( $used > 0 ) { return array( 'ok' => false, 'reason' => 'in_use', 'recipes' => $used, ); } $list = array_values( array_filter( $this->all(), static function ( $material ) use ( $id ) { return ( $material['id'] ?? '' ) !== $id; } ) ); update_option( self::OPTION, $list, false ); return array( 'ok' => true ); } /** * Lower-case form used to compare names. * * @param string $text Text. */ private static function fold( string $text ): string { return function_exists( 'mb_strtolower' ) ? mb_strtolower( $text ) : strtolower( $text ); } }