Генерация iCalendar(.ics) из чего-угодно на примере выборки данных из MySQL.

У нас на корпоративном портале есть календарь:

Он работает с MySQL, таблица представляет из себя:

CREATE TABLE `inventory` (
  `id` int(6) NOT NULL AUTO_INCREMENT,
  `name` varchar(50) DEFAULT NULL,
  `opisanie` varchar(100) DEFAULT NULL,
  `login` varchar(20) DEFAULT NULL,
  `loginapt` varchar(20) DEFAULT NULL,
  `sotrudniki` varchar(50) DEFAULT NULL,
  `complect` varchar(40) DEFAULT NULL,
  `start` datetime DEFAULT NULL,
  `end` datetime DEFAULT NULL,
  `users` varchar(150) DEFAULT NULL,
  `buh` varchar(60) DEFAULT NULL,
  `colcomp` varchar(1) DEFAULT NULL,
  `close` varchar(5) DEFAULT '0',
  `dateclose` datetime DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `id` (`id`),
  KEY `login` (`login`)
) ENGINE=InnoDB AUTO_INCREMENT=295 DEFAULT CHARSET=utf8;

Её состав:

Задача — организовать выгрузку этого календаря в файл, для возможности импорта в другие календари(например google).

Наиболее распространенным форматом является iCalendar(.ics), его и будем использовать. Как это не смешно, но самое адекватное описание формата можно получить на википедии, а также экспортировав календарь из того же google(или microsoft outlook) и открыв блокнотом.

Для начала отобьём все запросы вне сессии(хотя секретной информации нет, но пусть будет небольшая безопасность):

if(!isset($_SESSION['login'])){ //если обращение вне сессии завершаем скрипт
	exit;
}

Следующим шагом подключимся к БД:

$mysqli = new mysqli("****", "*****", "*******", "*******");
if ($mysqli->connect_errno) {
    echo "Не удалось подключиться к MySQL:".$mysqli->connect_error; 
	exit;
	}

Отберем последние 100 записей инвентаризации(т.к. торговых объектов ~60, то больше нам не нужно) в массив $arraydb:

$mysqli->query("SET NAMES utf8");
$result = $mysqli->query("SELECT * FROM inventory ORDER BY id DESC LIMIT 100"); //для экономии памяти отрываем 100 последних записей
$i=0;
while( $row = $result->fetch_assoc() ){ 
	if(strtotime($row['start']) >= time()) { //выбираем в массив актуальные даты начала
		$arraydb[$i]= $row;
		$i++;
	}
}
$result->close();

Т.к. сотрудники у нас в отдельной таблице:

CREATE TABLE `apt_users` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(160) DEFAULT NULL,
  `fio` varchar(80) DEFAULT NULL,
  `dol` varchar(110) DEFAULT NULL,
  `date` datetime DEFAULT NULL,
  `active` int(1) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1772 DEFAULT CHARSET=utf8;

Ее вид:

Соответственно нам понадобится выборка сотрудников, вытянем ее в массив $arraysotr:

$result = $mysqli->query("SELECT id, fio FROM apt_users"); //вытягиваем список сотрудников
while( $row = $result->fetch_assoc() ){ 
	$arraysotr[$row['id']]= $row['fio'];
}
$result->close(); 
$mysqli->close();

Т.к. нам еще пригодится адрес и время работы, а они в другой базе , то подключемся к ней, вытянем данные в массив $arrayadr, и закроем соединение:

$mysqli = new mysqli("**********", "********", "********", "********");
if ($mysqli->connect_errno) {
    echo "Не удалось подключиться к MySQL:".$mysqli->connect_error; 
	exit;
	}
$mysqli->query("SET NAMES utf8");
$result = $mysqli->query("SELECT LOGIN, ADR, TIME FROM `search.apt`"); //вытягиваем адреса объектов
while( $row = $result->fetch_assoc() ){ 
	$arrayadr[$row['LOGIN']]['ADR'] = $row['ADR'];
	$arrayadr[$row['LOGIN']]['TIME'] = $row['TIME'];
}
$result->close(); 
$mysqli->close();

Т.к. генерация файла будет осуществляться сразу в скрипте, то добавим хидеры для выгрузки файла в браузер:

header("Content-type: application/ics");
header('Content-Disposition: attachment; filename="pereuchet.ics"');

В качестве отладки можно выводить текст генерируемого календаря в браузер, для чего закомментируем хидеры выше и добавим новый:

header("Content-type: text/text; charset=UTF-8"); //вывод в браузер

Соответственно, чтобы выгрузить файл, закомментируем этот и раскомментируем предыдущие. Далее выведем текст, который меняться не будет(заголовок календаря):

echo 'BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//by name dudko
X-WR-CALNAME:Переучет на аптеках
CALSCALE:GREGORIAN
BEGIN:VTIMEZONE
TZID:Europe/Minsk
TZURL:http://tzurl.org/zoneinfo-outlook/Europe/Minsk
X-LIC-LOCATION:Europe/Minsk
BEGIN:STANDARD
TZOFFSETFROM:+0300
TZOFFSETTO:+0300
TZNAME:MSK
DTSTART:19700101T000000
END:STANDARD
END:VTIMEZONE';

Далее в цикле создадим ивенты:

for($i=count($arraydb)-1;$i>=0;$i--){
	$sotrudniki = explode(';',$arraydb[$i]['sotrudniki']);
 
echo '
BEGIN:VEVENT
DTSTAMP:'.date('Ymd').'T'.date('His').'Z
UID:'.date('Ymd').'T'.date('His').'Z-'.md5($arraydb[$i]['start'].$arraydb[$i]['end'].$arraydb[$i]['name']).'@sergdudko.tk
DTSTART;VALUE=DATE:'.date_format(date_create($arraydb[$i]['start']), 'Ymd').'
DTEND;VALUE=DATE:';
$dateend = date_create($arraydb[$i]['end']);
$dateend->modify('+1 day');
echo date_format($dateend, 'Ymd');
echo '
SUMMARY:Переучет:'.$arraydb[$i]['name'].'
DESCRIPTION:Бухгалтер: '.explode(';', $arraydb[$i]['buh'])[0].'\n\n Участники переучета: ';
for($j=0;$j < count($sotrudniki);$j++){
	echo $arraysotr[$sotrudniki[$j]];
	if($j+2 < count($sotrudniki)){
		echo ', ';
	}
}
echo '\n\n Комплекты: '.$arraydb[$i]['complect'].'\n\n Описание: '.$arraydb[$i]['opisanie'].'\n\n Рабочее время: '.$arrayadr[$arraydb[$i]['loginapt']]['TIME'];
echo '
LOCATION:'.$arrayadr[$arraydb[$i]['loginapt']]['ADR'].'
END:VEVENT';
 
}

По генерации в цикле:
UID — должен быть уникальным, иначе он просто перезапишет события одно на другое.
DTSTART и DTEND — время начала и окончания события, соответственно. Не забываем, что если событие весь день, то DTEND — это не 23:59:59указанного числа, а 00:00:00(т.е. +1 день).
SUMMARY — наименование события.
DESCRIPTION — описание события.
LOCATION — адрес для google maps.

Далее закроем календарь:

echo '
END:VCALENDAR';
 
exit;
?>

В качестве отладки, открываем блокнотом(я использую notepad++) сгенерированный календарь и проверяем его соответствия с тем, который экспортировали из google(у меня, например, были проблемы с переносами строк). Проверяем его импорт в гугл и microsoft outlook.

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

<a href="php/saveicall.php" type="application/file">Скачать iCalendar файл</a>