## The fetch_data() Method

The `fetch_data()` method is the core of your custom data source. It fetches your data and returns it in a format that Alpha Insights can understand and display.

### Method Signature

    public function fetch_data( WPDAI_Data_Warehouse $data_warehouse )

There is a single required parameter: the data warehouse instance. Filters (including date range) are not passed as a separate argument; use the data warehouse methods below to get them.

### Parameters

#### $data_warehouse (WPDAI_Data_Warehouse)

The data warehouse instance. Use it to:

- Get the date range: `$data_warehouse->get_date_from()`, `$data_warehouse->get_date_to()`
- Get any filter: `$data_warehouse->get_filter( 'date_format_display' )`, `$data_warehouse->get_filter()` for all filters
- Get the date range container for alignment: `$data_warehouse->get_data_by_date_range_container()`
- Access other entity data: `$data_warehouse->get_data( 'orders', 'totals' )`

See Data Warehouse API Reference for all available methods.

### Return Value

Must return an array with the following optional keys (all keys are optional, but must match the structure if provided):

    array(
        'totals'              => array(...),      // Aggregated totals/metrics
        'categorized_data'    => array(...),      // Data grouped by categories
        'data_table'          => array(...),      // Tabular data for data table widgets
        'data_by_date'        => array(...),      // Time-series data keyed by date
        'total_db_records'    => 0,               // Number of records fetched
    )
    // execution_time and memory_usage are added automatically by the warehouse

### Data Structure Details

#### totals (array)

Aggregated totals/metrics. Keys are metric names, values are numeric values.

    'totals' => array(
        'total_custom_metric' => 1250.50,
        'total_custom_count' => 42,
        'average_custom_value' => 29.77,
        'custom_percentage' => 15.5,
    )

Important: Metric keys here must match keys defined in `get_data_mapping()` 'totals' section (if you provide mapping).

#### categorized_data (array)

Data grouped by categories for pie charts, doughnut charts, bar charts, etc. Structure:

    'categorized_data' => array(
        'category_key' => array(
            'label' => 'Category Display Name',    // Optional: display name
            'total_custom_metric' => 500.25,       // Metric values
            'total_custom_count' => 15,
            // Additional metrics...
        ),
        'another_category' => array(
            'label' => 'Another Category',
            'total_custom_metric' => 750.75,
            'total_custom_count' => 20,
        ),
    )

Notes:

- Category keys (e.g., 'category_key') are used in widget configurations
- The 'label' key is optional but recommended for better display
- Metric keys inside each category must match 'value' keys in `get_data_mapping()` 'categorized_data' 'metric_fields'

#### data_table (array)

Tabular data for data table widgets. Structure:

    'data_table' => array(
        'table_key' => array(
            array(
                'id' => 1,
                'name' => 'Item 1',
                'value' => 125.50,
                'category' => 'category_a',
                'date' => '2024-01-01',
            ),
            array(
                'id' => 2,
                'name' => 'Item 2',
                'value' => 250.75,
                'category' => 'category_b',
                'date' => '2024-01-02',
            ),
            // More rows...
        ),
    )

Notes:

- Table keys (e.g., 'table_key') must match keys in `get_data_mapping()` 'data_table' section
- Each array element is a table row
- Column keys should match column definitions in the mapping
- Use data table limits for performance (see Data Warehouse API Reference)

#### data_by_date (array)

Time-series data keyed by date. This is used for line charts, area charts, and time-based bar charts.

CRITICAL: Always initialize metrics using the date range container from the data warehouse to ensure proper date alignment:

    $date_range_container = $data_warehouse->get_data_by_date_range_container();

    'data_by_date' => array(
        'metric_key' => $date_range_container,  // Initialize with container
        // Then populate with your data
    )

Complete example:

    $date_range_container = $data_warehouse->get_data_by_date_range_container();
    $data_by_date = array(
        'total_custom_metric_by_date' => $date_range_container,
        'total_custom_count_by_date' => $date_range_container,
    );

    // Populate with actual data
    foreach ( array_keys( $date_range_container ) as $date_key ) {
        // Fetch or calculate your data for this date
        $data_by_date['total_custom_metric_by_date'][ $date_key ] = /* your value */;
        $data_by_date['total_custom_count_by_date'][ $date_key ] = /* your value */;
    }

Why use the date container?

- Ensures all dates are present (even if you have no data for a date)
- Matches date format and range used by other data sources
- Prevents chart rendering issues with missing dates
- Respects date grouping (day, month, quarter, year) from filters

Important: Metric keys must match keys defined in `get_data_mapping()` 'data_by_date' section (if you provide mapping).

#### total_db_records (int)

The number of database records processed. Used for performance tracking and debugging.

    'total_db_records' => 150

#### execution_time (float) - AUTOMATICALLY TRACKED

DO NOT include this in your return array. Execution time is automatically measured and added by the data warehouse.

The data warehouse automatically tracks execution time by:

1. Recording `microtime(true)` before calling your `fetch_data()` method
2. Recording `microtime(true)` after your method returns
3. Calculating the difference and storing it in the data structure

You do not need to manually track or return execution time. If you include it in your return array, it will be ignored and replaced with the automatically calculated value.

#### memory_usage (int) - AUTOMATICALLY TRACKED

DO NOT include this in your return array. Memory usage is automatically measured and added by the data warehouse (in bytes).

The data warehouse automatically tracks memory usage by:

1. Recording `memory_get_usage(true)` before calling your `fetch_data()` method
2. Recording `memory_get_usage(true)` after your method returns
3. Calculating the difference (memory used during execution) and storing it in the data structure

This provides accurate memory consumption metrics for performance monitoring and debugging.

### Performance Metrics (Automatic)

Important: You do NOT need to manually track performance metrics. The data warehouse automatically tracks:

- execution_time - Execution time in seconds (float)
- memory_usage - Memory consumed during execution in bytes (int)

These metrics are automatically measured before and after your `fetch_data()` method is called and are automatically added to the stored data structure. Do not include them in your return array.

### Best Practices

#### 1. Use the Data Warehouse for Dates and Filters

    $date_from = $data_warehouse->get_date_from();
    $date_to = $data_warehouse->get_date_to();
    $date_format_display = $data_warehouse->get_filter( 'date_format_display', 'day' );

#### 2. Use Date Range Container for data_by_date

Always initialize time-series metrics with the date range container:

    $date_range_container = $data_warehouse->get_data_by_date_range_container();
    $data_by_date['my_metric'] = $date_range_container;
    // Then populate with data

#### 3. Don't Track Performance Metrics

Execution time and memory usage are automatically tracked. Do not include them in your return array:

    // DON'T do this:
    return array(
        'totals' => $totals,
        'execution_time' => microtime( true ) - $start_time, // ❌ Don't include
        'memory_usage' => $memory_used, // ❌ Don't include
    );

    // DO this instead:
    return array(
        'totals' => $totals,
        // execution_time and memory_usage are automatically added
    );

#### 4. Use Prepared Statements for Database Queries

    global $wpdb;
    $date_from = $data_warehouse->get_date_from();
    $date_to = $data_warehouse->get_date_to();
    $results = $wpdb->get_results( $wpdb->prepare(
        "SELECT * FROM {$wpdb->prefix}my_table WHERE date >= %s AND date <= %s",
        $date_from,
        $date_to
    ) );

#### 5. Handle Errors Gracefully

    try {
        // Fetch data
        $data = fetch_from_api();
    } catch ( Exception $e ) {
        // Log error and return empty structure
        error_log( 'Custom data source error: ' . $e->getMessage() );
        return array(
            'totals' => array(),
            // ... other empty arrays
        );
    }

### Complete Example

    public function fetch_data( WPDAI_Data_Warehouse $data_warehouse ) {
        // Get date range from warehouse
        $date_from = $data_warehouse->get_date_from();
        $date_to = $data_warehouse->get_date_to();

        // Get date range container
        $date_range_container = $data_warehouse->get_data_by_date_range_container();

        // Fetch data from database
        global $wpdb;
        $results = $wpdb->get_results( $wpdb->prepare(
            "SELECT * FROM {$wpdb->prefix}my_table WHERE date >= %s AND date <= %s",
            $date_from,
            $date_to
        ) );

        // Build totals
        $totals = array(
            'total_metric' => 0,
            'total_count' => 0,
        );

        // Build data_by_date
        $data_by_date = array(
            'metric_by_date' => $date_range_container,
            'count_by_date' => $date_range_container,
        );

        // Process results
        foreach ( $results as $row ) {
            // Update totals
            $totals['total_metric'] += floatval( $row->metric_value );
            $totals['total_count']++;

            // Update data_by_date
            $date_key = date( 'Y-m-d', strtotime( $row->date ) );
            if ( isset( $data_by_date['metric_by_date'][ $date_key ] ) ) {
                $data_by_date['metric_by_date'][ $date_key ] += floatval( $row->metric_value );
                $data_by_date['count_by_date'][ $date_key ]++;
            }
        }

        // Return data structure (execution_time and memory_usage are added by the warehouse)
        return array(
            'totals' => $totals,
            'data_by_date' => $data_by_date,
            'total_db_records' => count( $results ),
        );
    }

### Related Documentation

- The get_data_mapping() Method - Map your data for React display
- Data Warehouse API Reference - Available helper methods
- Examples - Real-world implementation examples