Циклическая обработка массивов в PHP для обновления записей в MySQL, но не получение всех результатов - PullRequest
0 голосов
/ 18 января 2019

Я пытаюсь перебрать некоторые данные, хранящиеся в массивах, а затем обновлять записи в MySQL в течение всего месяца. Я могу получить правильные данные за день, но не за весь месяц.

У меня есть форма с календарем, где пользователь может выбрать дату. В настоящее время у меня есть его код, так что пользователь может выбрать любую дату (месяц, день, год), и дни в этом месяце определены (я уверен, что есть более эффективный способ сделать это). Например, если выбрано 18 января 2019 года, возвращается 31 день (поскольку в январе 31 день).

Однако происходит то, что данные за выбранную дату вставляются в таблицу только за 1 января (например, если я выберу 18 января 2019 года из календаря, данные за 18 января 2019 года будут вставлены, но на 1 января 2019 г. другие данные не добавляются). Мне нужно, чтобы данные за весь месяц, независимо от того, что существует до текущего дня, были вставлены в правильный день месяца. Если я изменю дату, выбранную из календаря, данные для этой даты будут вставлены, но снова они будут вставлены для 1 января.

Мне нужно иметь возможность циклически проходить по дням месяца, получать все сайты, на которых есть данные (это работает), а затем получать данные для каждого сайта за каждый день месяца (это работает для каждого дня, но я нужен весь месяц сразу).

importDate - дата, выбранная в календаре. Если я повторяю это, он возвращает дату, которую я выбрал.

Вот так я определяю, установлена ​​ли importDate и что делать, если она есть или нет, плюс количество дней для выбранного месяца (например, при выборе 18 января 2019 возвращает 31 день для января).

if(!isset($_GET['importDate'])) {

$importDate = date('Y-m-d', strtotime('yesterday'));

$mthDays = date('t',strtotime($importDate));

}else{

$importDate = $_GET['importDate'];

$dexpl = explode('-',$importDate);

$mthDays = date('t',strtotime($importDate));
}

Затем я перебираю дни месяца (вероятно, не делаю это в нужном месте), затем перебираю свои сайты. Все сайты возвращены. getSites - это функция, которая получает все сайты.

Внутри цикла, чтобы получить сайты, я подключаюсь к отдельной базе данных для программного обеспечения, которое мы используем, получаю данные из этой базы данных для каждого сайта, делаю некоторые вычисления и затем перехожу на следующий сайт.

Затем я вызываю функцию, которая обновит мою таблицу, как указано, со всеми данными за весь месяц. В настоящее время, как описано выше, я получаю данные только для одного дня для вставки.

$y = 0;

while ($y <= $mthDays) {

    // Loop to get all sites
    for($x=0; $x < $getSites; $x++)
    {

// Code here for connecting to database for software we are using, get the data from that db for each site, do some calculations...  

// The variables being passed into the function in the code directly below are the site, the date, plus calculated items not shown here.

 $updateManualStatsDaily = $statsDao->updateStats($getSites[$x]['site_id'],$total_sessions,$count_guests,$count_unique_guests,$total_minutes,$dateImport);

}

$y++;

$dateImport = date('Y-m-d',strtotime($dexpl[0]."-".$dexpl[1]."-".$y));

}

Вот моя функция:

public function updateStats($hz_site_id,$total_sessions,$count_guests,$count_unique_guests,$total_minutes,$updateDate){
    try {

        $stm = $this->dbSt->prepare("UPDATE table set sessions_wifi = :sessions, wifi_users = :wifiUsers, wifi_unique_users = :wifiUniqueUsers, wifi_time = :wifiTime
where site_id = :siteId and circ_date = :statsDate ");

        $stm->bindParam(':siteId',$hz_site_id);
        $stm->bindParam(':sessions',$total_sessions);
        $stm->bindParam(':wifiUsers',$count_guests);
        $stm->bindParam(':wifiUniqueUsers',$count_unique_guests);
        $stm->bindParam(':wifiTime',$total_minutes);
        $stm->bindParam(':statsDate',$updateDate);

        if($stm->execute()){

            return true;

        }else{

            echo $stm->errorInfo();
        }

    }catch (PDOException $e) {

        echo $e->getMessage();
    }

    return false;
}

Примечание: у меня также есть этот код для работы в течение одного дня (выбранный из календаря), который работает. Он получает дни месяца и пытается добавить данные для каждого дня, который доставляет мне неприятности.

Если кто-то может помочь, я был бы очень признателен! Спасибо.

1 Ответ

0 голосов
/ 21 января 2019

Предполагая, что записи для каждого $updateDate уже существуют в базе данных, в противном случае вам нужно будет выполнить запрос INSERT вместо UPDATE.

Основная проблема, которую я вижу с вашим кодом, заключается в цикле for на $getSites.

for ($x=0; $x < $getSites; $x++) {
    $getSites[$x]['site_id'];
}

Что означает, что $getSites является массивом, а не целым числом. Пример: https://3v4l.org/AK8ka

Вы хотели бы изменить его на Пример: https://3v4l.org/0qbWf

$l = count($getSites);
for ($x=0; $x < $l; $x++) {
    $getSites[$x]['site_id'];
}

или, что еще лучше, использовать foreach, пример: https://3v4l.org/FTOCI.

foreach ($getSites as $site) {
    $site['site_id'];
}

Есть также несколько проблем с порядком операций, которые должны быть улучшены.

Первый $dateImport не определен до тех пор, пока после начального цикла это не приведет к отправке нулевого значения в базу данных на первой итерации.

Кроме того, поскольку $y начинается с 0, это создаст дату, подобную 2019-01-0, которая будет интерпретироваться PHP как последний день предыдущего месяца; 2018-12-31 и может привести к неожиданным результатам. Пример: https://3v4l.org/TiHtu.

Кроме того, кажется, $dexpl может не быть определено, если не установлено $_GET['importDate'], что приведет к созданию даты, такой как --0, которая будет интерпретироваться PHP как 1970-01-01

//consolidated conditional logic
$importTimestamp = strtotime(isset($_GET['importDate']) ? $_GET['importDate'] : 'yesterday');
$importDate = date('Y-m-d', $importTimestamp);

//declare dexpl and mthDays
$dexpl = explode('-', $importDate);
$mthDays = date('t', $importTimestamp);

//start at day 1, since we know mthDays will always be 28 to 31
$y = 1;    
while ($y <= $mthDays) {
    //null would be sent to the database initially otherwise
    $dateImport = date('Y-m-d', strtotime($dexpl[0]."-".$dexpl[1]."-".$y));
    $l = count($getSites);
    for ($x=0; $x < $l; $x++) {
        $updateManualStatsDaily = $statsDao->updateStats($getSites[$x]['site_id'],$total_sessions,$count_guests,$count_unique_guests,$total_minutes,$dateImport);
    }
    $y++;
}

Во-вторых, вам нужно всего лишь один раз вызвать prepare и bindParam для выполнения определенной операции запроса. Я предлагаю объявить подготовленный оператор свойству объекта и переключиться на использование PDOStatement::bindValue(). Это связано с тем, что PDOStatement::bindParam() будет использовать ссылку на связанную переменную для получения значения, которое вы не используете в данном контексте updateStats().

Например, обратите внимание на порядок примененных операций, при котором bindParam вызывается до изменения значения $v.

$v = 1;
$stmt->bindParam(':v', $v);
$v = 2;
$stmt->execute(); //results in sending 2 to the DB as the `:v` placeholder value

Тогда вам просто нужно разделить проблемы в вашем updateStats методе, чтобы подготовить заявление, только если оно еще не было.

protected function prepareStats()
{
    if (!isset($this-statsStmt)) {
        $this-statsStmt = $this->dbSt->prepare('UPDATE table SET sessions_wifi = :sessions, wifi_users = :wifiUsers, wifi_unique_users = :wifiUniqueUsers, wifi_time = :wifiTime
WHERE site_id = :siteId and circ_date = :statsDate');
    }

    return $this->statsStmt;
}

public function updateStats($hz_site_id,$total_sessions,$count_guests,$count_unique_guests,$total_minutes,$updateDate)
{
    try {
        $stm = $this->prepareStats();
        $stm->bindValue(':sessions', $total_sessions);
        $stm->bindValue(':wifiUsers', $count_guests);
        $stm->bindValue(':wifiUniqueUsers', $count_unique_guests);
        $stm->bindValue(':wifiTime', $total_minutes);
        $stm->bindValue(':siteId', $hz_site_id);
        $stm->bindValue(':statsDate', $updateDate);    
        if ($stm->execute()) {
            return true;
        }
        //no need for else, since we return on success already
        echo $stm->errorInfo();
    }catch (PDOException $e) {
        echo $e->getMessage();
    }

    return false;
}

Наконец, я настоятельно рекомендую вместо date('t') использовать DatePeriod(). Что даст вам полное представление о днях, которые повторяются. В отличие от итерации по количеству дней указанного $importDate, которое будет включать дни до указанного $importDate и дни в будущем.

Редактировать: После прочтения вашего вопроса я понял, что вы хотите импортировать весь данный месяц. Не только с указанной даты до текущей даты. Но также остановиться на максимуме текущей даты. Поэтому я обновил логику, чтобы отразить желаемое поведение.

Пример: https://3v4l.org/S0H6s

//use !empty to prevent conflict of from today to today
if (!$start = date_create(!empty($_GET['importDate']) ? $_GET['importDate'] : 'yesterday')) {
    die('Invalid Date Supplied');
}
$today = date_create();
if ($start > $today) {
    die('Dates set in the future are not permitted');
}
//start at the first day of the month
$start->modify('first day of this month')->setTime(0,0,0);
//validate the current year and month to prevent exceeding current date
if ($start->format('Y-m') === $today->format('Y-m')) {
    $end = date_create('yesterday')->setTime(23,59,59);
    if ($end < $start) {
        //yesterday was previous month - use current start date
        $end = clone $start;
    }
} else {
    $end = clone $start;
    $end->modify('last day of this month');
}
//always end at end of day
$end->setTime(23,59,59);
$importDates = new \DatePeriod($start, new \DateInterval('P1D'), $end);

//we can use foreach on $getSites which is faster
//switched order of operations, since if there are no sites, we don't need to continue.
foreach ($getSites as $site) {
    foreach ($importDates as $importDate) {
        //logic to calculate site data to UPDATE...
        //...
        $statsDao->updateStats( 
            $site['site_id'],
            $total_sessions,
            $count_guests,
            $count_unique_guests,
            $total_minutes,
            $importDate->format('Y-m-d')
        );
    }
}

Я хотел бы отметить, что понятия не имею, где $total_sessions,
$count_guests, $count_unique_guests, $total_minutes приходят, так что это может представлять дополнительные проблемы, которые, возможно, необходимо решить.

Результаты текущей даты «2019-01-01»: https://3v4l.org/ddQG0

Import Dates: "2019-01-18"
Dates set in the future are not permitted


Import Dates: "2019-01-01" (current day)
2019-01-01


Import Dates: "2018-12-01", "2018-12-20", ""
2018-12-01
2018-12-02
2018-12-03
2018-12-04
2018-12-05
2018-12-06
2018-12-07
2018-12-08
2018-12-09
2018-12-10
2018-12-11
2018-12-12
2018-12-13
2018-12-14
2018-12-15
2018-12-16
2018-12-17
2018-12-18
2018-12-19
2018-12-20
2018-12-21
2018-12-22
2018-12-23
2018-12-24
2018-12-25
2018-12-26
2018-12-27
2018-12-28
2018-12-29
2018-12-30
2018-12-31
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...