You are here

FeedsExcelParser.inc in Feeds Excel 7.2

Contains FeedsExcelParser.

File

FeedsExcelParser.inc
View source
<?php

/**
 * @file
 * Contains FeedsExcelParser.
 */

/**
 * Provides feeds parser class for Excel files.
 */
class FeedsExcelParser extends FeedsParser {
  private $file_path;
  private $library;
  private $reader;

  /**
   * Init file path.
   */
  private function initFilePath(FeedsFetcherResult $fetcher_result) {
    if (!isset($this->file_path)) {
      $this->file_path = drupal_realpath($fetcher_result
        ->getFilePath());
    }
  }

  /**
   * Init PHPExcel library.
   */
  private function initLibrary() {
    if (!isset($this->library)) {
      $this->library = libraries_load('PHPExcel');
      if (empty($this->library['loaded'])) {
        throw new Exception(t('PHPExcel missed.'));
      }
    }
  }

  /**
   * Init PHPExcel reader.
   */
  private function initReader() {
    if (!isset($this->reader)) {
      $this->reader = PHPExcel_IOFactory::createReaderForFile($this->file_path);
      $this->reader
        ->setReadDataOnly(TRUE);
    }
  }

  /**
   * Get total rows.
   */
  private function getTotal() {
    $this->reader
      ->setReadFilter(new FeedsExcelParserIReadFilter());
    $this->reader
      ->load($this->file_path);
    return drupal_static('feeds_excel_parser_row_counter');
  }

  /**
   * Parse first row as headers.
   */
  private function getHeaders() {
    $headers = array();
    $this->reader
      ->setReadFilter(new FeedsExcelParserIReadFilter(1, 1));
    foreach ($this->reader
      ->load($this->file_path)
      ->getSheet()
      ->getRowIterator() as $row) {
      foreach ($row
        ->getCellIterator() as $cell_index => $cell) {
        $headers[$cell_index] = filter_xss($cell
          ->getValue());
      }
    }
    return $headers;
  }

  /**
   * {@inheritdoc}
   */
  public function parse(FeedsSource $source, FeedsFetcherResult $fetcher_result) {

    // Init vars.
    $this
      ->initFilePath($fetcher_result);
    $this
      ->initLibrary();
    $this
      ->initReader();

    // Init state. Use two pointers: global and by sheet.
    $state = $source
      ->state(FEEDS_PARSE);
    $state->total = !empty($state->total) ? $state->total : $this
      ->getTotal();
    $state->pointer = !empty($state->pointer) ? $state->pointer : array(
      'sheet_pointer' => 1,
      'processed_rows' => array(),
    );

    // Prepare to parsing.
    $items = array();
    $filter = new FeedsExcelParserIReadFilter($state->pointer['sheet_pointer'], $this->config['chunk_size']);
    $this->reader
      ->setReadFilter($filter);
    $excel = $this->reader
      ->load($this->file_path);

    // Setup parse type.
    switch ($this->config['sheets']['parse_type']) {
      case 'first':
        $sheets = array(
          $excel
            ->getSheet(),
        );
        break;
      case 'title':
        $sheets = array(
          $excel
            ->getSheetByName($this->config['sheets']['parse_value']),
        );
        break;
      case 'index':
        $sheets = array(
          $excel
            ->getSheet($this->config['sheets']['parse_value']),
        );
        break;
      default:
        $sheets = $excel
          ->getWorksheetIterator();
    }

    // Check for errors.
    if (is_array($sheets) && in_array(NULL, $sheets)) {
      throw new Exception(t('Wrong sheet !type: !value.', array(
        '!type' => $this->config['sheets']['parse_type'],
        '!value' => $this->config['sheets']['parse_value'],
      )));
    }

    // Parse values.
    foreach ($sheets as $sheet) {
      foreach ($sheet
        ->getRowIterator() as $row_index => $row) {

        // Generate row key.
        $row_key = implode('__', array(
          $sheet
            ->getTitle(),
          $row_index,
        ));

        // Iterate cells.
        foreach ($row
          ->getCellIterator() as $cell_index => $cell) {

          // Detect headers row.
          if (!$this->config['no_headers'] && $row_index === 1 && !is_null($header = $cell
            ->getCalculatedValue())) {
            $state->pointer['headers'][$cell_index] = $header;
            continue;
          }

          // Detect cell index and put values if not empty.
          $cell_key = !$this->config['no_headers'] && !empty($state->pointer['headers'][$cell_index]) ? $state->pointer['headers'][$cell_index] : $cell_index;
          $value = $this->config['calculate'] ? $cell
            ->getCalculatedValue() : $cell
            ->getValue();
          if (!is_null($value)) {
            $items[$row_key][$cell_key] = $value;
          }
        }

        // Add sheet title to values.
        if (isset($items[$row_key]) && !empty($this->config['sheets']['map_title'])) {
          $items[$row_key][$this->config['sheets']['map_title']] = $sheet
            ->getTitle();
        }

        // Increment row pointer.
        $state->pointer['sheet_pointer'] = $row_index + 1;

        // Save row as processed.
        $state->pointer['processed_rows'][$row_key] = $row_key;
      }
    }
    $state
      ->progress($state->total, count($state->pointer['processed_rows']));
    return new FeedsParserResult(array_values($items));
  }

  /**
   * {@inheritdoc}
   */
  public function clear(FeedsSource $source) {
  }

  /**
   * {@inheritdoc}
   */
  public function getMappingSources() {
    return FALSE;
  }

  /**
   * {@inheritdoc}
   */
  public function configDefaults() {
    return array(
      'chunk_size' => 10,
      'no_headers' => 0,
      'calculate' => 0,
      'sheets' => array(
        'parse_type' => 'all',
        'parse_value' => '',
        'map_title' => '',
      ),
    );
  }

  /**
   * {@inheritdoc}
   */
  public function configForm(&$form_state) {
    $form = array();
    $form['chunk_size'] = array(
      '#type' => 'select',
      '#title' => t('Chunk size'),
      '#options' => drupal_map_assoc(array(
        1,
        5,
        10,
        20,
        50,
        100,
        200,
      )),
      '#description' => t("Select quantity of rows which will be parsed per ounce. <b>Notice: selected quantity will be multiplied on sheets' quantity.</b>"),
      '#default_value' => $this->config['chunk_size'],
    );
    $form['no_headers'] = array(
      '#type' => 'checkbox',
      '#title' => t('No headers'),
      '#description' => t("Check if the imported Excel file does not start with a header row. If checked, mapping sources must be named '0', '1', '2' etc."),
      '#default_value' => $this->config['no_headers'],
    );
    $form['calculate'] = array(
      '#type' => 'checkbox',
      '#title' => t('Calculate'),
      '#description' => t('Check if need to calculate cells data via getCalculatedValue()'),
      '#default_value' => $this->config['calculate'],
    );
    $form['sheets'] = array(
      '#type' => 'fieldset',
      '#title' => t('Sheets settings'),
      '#tree' => TRUE,
    );
    $form['sheets']['parse_type'] = array(
      '#type' => 'select',
      '#title' => t('How to parse?'),
      '#default_value' => $this->config['sheets']['parse_type'],
      '#options' => array(
        'all' => t('All sheets'),
        'first' => t('First sheet'),
        'title' => t('By title'),
        'index' => t('By index'),
      ),
    );
    $form['sheets']['parse_value'] = array(
      '#type' => 'textfield',
      '#default_value' => $this->config['sheets']['parse_value'],
      '#states' => array(
        'visible' => array(
          array(
            array(
              'select[name="sheets[parse_type]"]' => array(
                'value' => 'title',
              ),
            ),
            'or',
            array(
              'select[name="sheets[parse_type]"]' => array(
                'value' => 'index',
              ),
            ),
          ),
        ),
      ),
    );
    $form['sheets']['map_title'] = array(
      '#type' => 'textfield',
      '#title' => t('Source key of sheet title'),
      '#description' => t('If filled, sheet title will be available in mapping.'),
      '#default_value' => $this->config['sheets']['map_title'],
    );
    return $form;
  }

}

Classes

Namesort descending Description
FeedsExcelParser Provides feeds parser class for Excel files.