2014-11-12 3 views
0

Я реализовал следующие решения по @Darragh найти последовательные даты и превращая их в диапазонов дат:Некоторые помогают «синхронизироваться» две петли в PHP

Check for consecutive dates within a set and return as range

Теперь я пытаясь напечатать некоторую дополнительную информацию на выходе этого цикла. Это ошибочный сценарий я создал, на основе ответа @Darragh «s:

<?php 
$output = ''; 
$dates = array(); 
$query = 'SELECT name, company, date FROM project GROUP BY date ORDER BY date'; 
$sth = $dbh->prepare($query); 
$sth->execute(); 
if($sth->rowCount() > 0) { 
    $output .= '<ul>'; 
    while($row = $sth->fetch()) { 
     array_push($dates,new DateTime($row['date'])); 
     $name = $row['name']; 
     $company = $row['company']; 
    } 
    $lastDate = null; 
    $ranges = array(); 
    $currentRange = array(); 
    foreach ($dates as $date) { 
     if (null === $lastDate) { 
      $currentRange[] = $date; 
     } else { 
      $interval = $date->diff($lastDate); 
      if ($interval->days === 1) { 
       $currentRange[] = $date; 
      } else { 
       $ranges[] = $currentRange; 
       $currentRange = array($date); 
      } 
     } 
     $lastDate = $date; 
    } 
    $ranges[] = $currentRange; 
    foreach ($ranges as $range) { 
     $saverange = array(); 
     foreach($range as $entry) { 
      array_push($saverange,$entry->format('Y-m-d')); 
     } 
     $startDate = array_shift($range); 
     $str = sprintf('%s', $startDate->format('d/m/Y')); 
     if (count($range)) { 
      $endDate = array_pop($range); 
      $str .= sprintf(' tot %s', $endDate->format('d/m/Y')); 
     } 
     $output .= '<li>'.$name.', '.$company.' - '.$str.'</li>'; 
    } 
    $output .= '</ul>'; 

    // Show me what you got 
    echo $output; 
} 
?> 

Очевидно, что while цикл, который перебирает выход БД из синхронизации с foreach цикла, который выводит dateranges.

Необработанные выход DB выглядит следующим образом:

+------------------+-------------+------------+ 
|  name  | company | date | 
+------------------+-------------+------------+ 
| EBU    | Belgacom sa | 2014-09-12 | 
| Mosquito Replica | Mosquito nv | 2014-09-17 | 
| Mosquito Replica | Mosquito nv | 2014-09-19 | 
| Mosquito Replica | Mosquito nv | 2014-09-20 | 
+------------------+-------------+------------+ 

Что мой сценарий выхода выглядит следующим образом:

<ul> 
    <li>Mosquito Replica, Mosquito nv - 12/09/2014</li> 
    <li>Mosquito Replica, Mosquito nv - 17/09/2014</li> 
    <li>Mosquito Replica, Mosquito nv - 19/09/2014 tot 20/09/2014</li> 
</ul> 

Что мне нужно для вывода выглядит следующим образом:

<ul> 
    <li>EBU, Belgacom sa - 12/09/2014</li> 
    <li>Mosquito Replica, Mosquito nv - 17/09/2014</li> 
    <li>Mosquito Replica, Mosquito nv - 19/09/2014 tot 20/09/2014</li> 
</ul> 

Это может быть очевидно для вас, ребята, но я не могу, чтобы жизнь меня поняла. Кто покажет мне дорогу? Заранее спасибо!

+0

Если интервал один день, но название/компания отличается, должен ли этот результат в отдельном диапазоне? если нет, какое имя/компания должна отображаться? – khartnett

+0

Привет, нет, только не последовательные даты должны представлять новую строку. Хорошая точка зрения! – maartenmachiels

ответ

2

Причина, почему ваш сценарий не печатает EBU, Belgacum sa ложь в этой части:

while($row = $sth->fetch()) { 
    array_push($dates,new DateTime($row['date'])); 
    $name = $row['name']; 
    $company = $row['company']; 
} 

Хотя значения даты выталкиваются в массив, $name и $company перезаписываются каждой новой строки.

Кроме того, я думаю, что ваш скрипт нарушает эту проблему. Как насчет этого?

$output = ''; 
    $list = array(); 
    $lastitem = $lastdate = null; 
    $query = 'SELECT name, company, date FROM project GROUP BY date ORDER BY date, name, company'; 
    $sth = $dbh->prepare($query); 
    $sth->execute(); 
    if($sth->rowCount() > 0) { 
      $i = 0; 
     while($row = $sth->fetch()) { 
       $date = new DateTime($row['date']); 
       $item = array(
        'date' => array(), 
        'name' => $row['name'], 
        'company' => $row['company'], 
       ); 
       if ($item === $lastitem && $date->diff($lastdate)->days === 1) { 
        $list[$i-1]['date']['end'] = $date; 
       } 
       else { 
        $list[$i] = $item; 
        $list[$i]['date']['start'] = $date; 


        $lastitem = $item; 
        $lastdate = $date; 
        $i++; 
       } 
     } 
    } 
    if (count($list)) { 
      $output .= '<ul>'; 
    } 
    foreach ($list AS $item) { 
      $output .= '<li>' . $item['name'] . ', ' . $item['company'] . ' - ' . $item['date']['start']->format('d/m/Y'); 
      $output .= isset($item['date']['end']) ? ' tot ' . $item['date']['end']->format('d/m/Y') : ''; 
      $output .= '</li>'; 
    } 
    if (count($list)) { 
      $output .= '</ul>'; 
    } 
+0

Спасибо, @Paul. Это работает как шарм. Надеюсь, он будет работать для всех сценариев в моей БД :-) – maartenmachiels

+0

вы не знаете, почему эта функция ломается, если я изменяю свой запрос, чтобы выбрать идентификатор таблицы? В этом случае диапазоны больше не распознаются ... – maartenmachiels

+0

Вероятно, вам следует открыть новый вопрос для него, включая обновленную схему db (в этом вопросе нет идентификатора). – Paul

1

клиентский код должен выглядеть следующим образом:

<?php 

$output = ''; 
$query = 'SELECT name, company, date FROM project GROUP BY date ORDER BY date'; 
$sth = $dbh->prepare($query); 
$sth->execute(); 
if ($sth->rowCount() > 0) { 
    $rows = array(); 
    $output .= '<ul>'; 

    //object responsible for holidng all the ranges 
    //and returning the reference to them based on date 
    $dateRange = new TimeRange(); 

    while ($row = $sth->fetch()) { 
     //getting the period id for this row's time 
     $row['range_id'] = $dateRange->getRangeId(new DateTime($row['date'])); 
     //indexing by name, company and range id 
     $rows[$row['name'] . $row['company'] . $row['range_id']] = $row; 
    } 

    //each row has it's range id, now we just loop through them and render 
    //Time Period object is rendering itself 
    foreach ($rows as $row) { 
     $output .= '<li>' . $row['name'] . ', ' . $row['company'] 
       . ' - ' . $dateRange->render($row['range_id']) . '</li>'; 
    } 

    $output .= '</ul>'; 

// Show me what you got 
    echo $output; 
} 

И классы, чтобы сделать клиентский код запустить (один из возможных вариантов реализации):

class TimeRange { 

    /** 
    * @var TimePeriod[] 
    */ 
    private $periods = array(); 

    /** 
    * @var TimePeriod 
    */ 
    private $currentPeriod; 

    private function newPeriod(DateTime $time) { 
     $this->currentPeriod = new TimePeriod($time); 
     $this->periods[] = $this->currentPeriod; 
    } 

    /** 
    * Returns the period id for the given date 
    * @param DateTime $time 
    * @return string 
    */ 
    public function getRangeId(DateTime $time) { 
     if ($this->currentPeriod === null) { 
      $this->newPeriod($time); 
     } 

     if (!$this->currentPeriod->setNewEndTime($time)) { 
      $this->newPeriod($time); 
     } 

     //returning period id 
     end($this->periods); 
     return key($this->periods); 
    } 

    /** 
    * Renders the time period based on provided id 
    * @param string $periodId 
    * @return string 
    */ 
    public function render($periodId) { 
     return $this->periods[$periodId]->render(); 
    } 

} 

class TimePeriod { 

    /** 
    * @var DateTime 
    */ 
    private $startTime; 

    /** 
    * @var DateTime 
    */ 
    private $endTime; 

    public function __construct(DateTime $startTime) { 
     $this->startTime = $startTime; 
    } 

    /** 
    * Attepmts to set the end time for the period 
    * if the given time is more than 1 day apart from the current end time 
    * the period is ended 
    * @param DateTime $time 
    * @return boolean 
    */ 
    public function setNewEndTime(DateTime $time) { 
     $compareTime = $this->endTime !== null ? $this->endTime : $this->startTime; 
     $interval = $compareTime->diff($time); 
     $dateSet = $interval->days <= 1; 

     if ($dateSet) { 
      $this->endTime = $time; 
     } else { 
      $this->endTime = $this->startTime; 
     } 

     return $dateSet; 
    } 

    /** 
    * Renders the time period 
    * if the end time is less than a day apart only start time is rendered 
    * @return string 
    */ 
    public function render() { 
     $render = $this->startTime->format('d/m/Y'); 

     if ($this->startTime->diff($this->endTime, true)->days >= 1) { 
      $render .= ' tot ' . $this->endTime->format('d/m/Y'); 
     } 

     return $render; 
    } 

} 
+0

Спасибо, Никола, за ваш обширный ответ. Я пошел с ответом @Paul, потому что я не очень хорошо знаком с программированием OO, и большая часть моего кода структурирована ... Я уверен, что это сделало бы прекрасное решение! – maartenmachiels

Смежные вопросы