"event_id",
self::FLD_START_DATE => "start_date",
self::FLD_END_DATE => "end_date",
self::FLD_TEXT => "text",
self::FLD_RECURRING_TYPE => "rec_type",
self::FLD_PARENT_ID => "event_pid",
self::FLD_LENGTH => "event_length"
);
private $_connect_configs;
private $_fields_values = array();
public $config = array(
"debug" => true,
"server_date" => false,
"start_on_monday" => true,
"occurrence_timestamp_in_utc" => false
);
protected $_mapped_fields = array();
protected $_use_only_mapped_fields = false;
protected function getIdFieldName()
{
return $this->getFieldsNames(self::FLD_ID);
}
protected function getStartDateFieldName()
{
return $this->getFieldsNames(self::FLD_START_DATE);
}
protected function getEndDateFieldName()
{
return $this->getFieldsNames(self::FLD_END_DATE);
}
protected function getTextFieldName()
{
return $this->getFieldsNames(self::FLD_TEXT);
}
protected function getRecurringTypeFieldName()
{
return $this->getFieldsNames(self::FLD_RECURRING_TYPE);
}
protected function getParentIdFieldName()
{
return $this->getFieldsNames(self::FLD_PARENT_ID);
}
protected function getLengthFieldName()
{
return $this->getFieldsNames(self::FLD_LENGTH);
}
protected function getConnectConfigs()
{
return $this->_connect_configs;
}
protected function setConnectConfigs($connectConfigs)
{
$this->_connect_configs = $connectConfigs;
return $this;
}
protected function getFieldsNames($field = NULL)
{
if(is_null($field))
return $this->_fields_names;
if(!isset($this->_fields_names[$field]))
throw new Exception("Field {$field} not found.");
return $this->_fields_names[$field];
}
public function setFieldsNames($fieldsDataArray, $useOnlySetFields = false)
{
if(!is_array($fieldsDataArray))
throw new Exception("Fields data must be array.");
$this->_mapped_fields = $fieldsDataArray;
$this->_use_only_mapped_fields = $useOnlySetFields;
foreach($fieldsDataArray as $fieldKey => $fieldValue) {
//If field name is numeric, then made same field key and field value.
if(is_numeric($fieldKey))
$fieldKey = $fieldValue;
$this->_fields_names[$fieldKey] = $fieldValue;
}
return $this;
}
protected function setFieldsValues($dataArray)
{
foreach($dataArray as $fieldKey => $fieldValue)
$this->_fields_values[$this->_fields_names[$fieldKey]] = $fieldValue;
return $this;
}
protected function getFieldsValues($field = NULL)
{
if(is_null($field))
return $this->_fields_values;
if(isset($this->_fields_values[$this->_fields_names[$field]]))
return $this->_fields_values[$this->_fields_names[$field]];
return NULL;
}
}
interface IHelper
{
public function getData($startDate, $endDate);
public function saveData($dataArray);
public function deleteById($id);
}
class Helper extends DHelper implements IHelper
{
public function __construct($connectConfigs = NULL)
{
$this->setConnectConfigs($connectConfigs);
parent::__construct($connectConfigs);
}
public static function getInstance($connectConfigs = NULL)
{
return new self($connectConfigs);
}
/**
* Gets timestamp from date
* @param $date
* @return int
*/
private function getDateTimestamp($date)
{
return SchedulerHelperDate::getDateTimestamp($date, $this->config["server_date"]);
}
private function getTimestampFromUTCTimestamp($timestamp)
{
return SchedulerHelperDate::getTimestampFromUTCTimestamp($timestamp, $this->config["server_date"]);
}
/**
* Get recurring events data exceptions. And prepare data to format: []
* @return array
*/
private function _getRecurringEventsExceptionsByInterval()
{
$getEventsSql = "
SELECT
*
FROM
".$this->getTableName()."
WHERE
(
".$this->getRecurringTypeFieldName()." = '".RecurringType::IS_RECURRING_EXCEPTION."'
OR ".$this->getRecurringTypeFieldName()." = '".RecurringType::IS_RECURRING_BREAK."'
OR ".$this->getRecurringTypeFieldName()." IS NULL
)
AND ".$this->getLengthFieldName()." > '0'
";
$query = $this->getConnection()->prepare($getEventsSql);
$query->execute();
$events = array();
while($eventData = $query->fetch(PDO::FETCH_ASSOC))
{
$eventParentId = $eventData[$this->getParentIdFieldName()];
if(!isset($events[$eventParentId]))
$events[$eventParentId] = array();
$eventLength = $eventData[$this->getLengthFieldName()];
if($this->config["occurrence_timestamp_in_utc"]) {
$eventLength = $this->getTimestampFromUTCTimestamp($eventLength);
}
$events[$eventParentId][$eventLength] = $eventData;
}
$this->closeConnection();
return $events;
}
/**
* Get simple events by interval.
* @param $startDate
* @param $endDate
* @return array
*/
private function _getSimpleEventsByInterval($startDate, $endDate) {
$getEventsSql = "
SELECT
*
FROM
".$this->getTablename()."
WHERE
(
".$this->getStartDateFieldName()." <= '{$endDate}'
AND ".$this->getEndDateFieldName()." >= '{$startDate}'
)
AND (
".$this->getRecurringTypeFieldName()." = '".RecurringType::IS_RECURRING_EXCEPTION."'
OR ".$this->getRecurringTypeFieldName()." = '".RecurringType::IS_RECURRING_BREAK."'
OR ".$this->getRecurringTypeFieldName()." IS NULL
)
AND (".$this->getLengthFieldName()." = '0' OR ".$this->getLengthFieldName()." is NULL)
";
$query = $this->getConnection()->prepare($getEventsSql);
$query->execute();
$data = $query->fetchAll();
$this->closeConnection();
return $data;
}
/**
* Prepare events data.
* @param $events
* @return array
*/
private function _prepareSimpleEvents($events)
{
$resultData = array();
$evCount = count($events);
for($i = 0; $i < $evCount; $i++)
{
array_push($resultData, $this->_filterEventDataToResponse($events[$i]));
}
return $resultData;
}
/**
* Get recurring events data by interval.
* @param $startDate
* @param $endDate
* @return array
*/
private function _getRecurringEventsByInterval($startDate, $endDate)
{
$getEventsSql = "
SELECT
*
FROM
" . $this->getTablename() . "
WHERE
(
" . $this->getRecurringTypeFieldName() . " != '" . RecurringType::IS_RECURRING_EXCEPTION . "'
AND " . $this->getRecurringTypeFieldName() . " != '" . RecurringType::IS_RECURRING_BREAK . "'
AND " . $this->getRecurringTypeFieldName() . " IS NOT NULL
)
AND " . $this->getLengthFieldName() . " != '0'
";
$query = $this->getConnection()->prepare($getEventsSql);
$query->execute();
$data = $query->fetchAll();
$this->closeConnection();
$startDateFieldName = $this->getStartDateFieldName();
$endDateFieldName = $this->getEndDateFieldName();
$lengthFieldName = $this->getLengthFieldName();
$startStamp = $this->getDateTimestamp($startDate);
$endStamp = $this->getDateTimestamp($endDate);
$data = array_filter($data, function ($el) use ($startDateFieldName, $endDateFieldName, $lengthFieldName, $startStamp, $endStamp) {
$evStart = $this->getDateTimestamp($el[$startDateFieldName]);
$evEnd = $this->getDateTimestamp($el[$endDateFieldName]);
$evLength = $el[$lengthFieldName];
return $evEnd + $evLength > $startStamp && $evStart < $endStamp;
});
return $data;
}
/**
* Exclude event extra data.
* @param $eventDataArray
* @return array
*/
private function _filterEventDataToResponse($eventDataArray)
{
$filteredEventData = array();
$fullEventData = array();
foreach($eventDataArray as $dataKey => $dataValue) {
$fullEventData[$dataKey] = $dataValue;
if (!$this->_use_only_mapped_fields || array_key_exists($dataKey, array_flip($this->_mapped_fields)))
$filteredEventData[$dataKey] = $dataValue;
}
return array("filtered_event_data" => $filteredEventData, "full_event_data" => $fullEventData);
}
/**
* Exclude recurring exceptions from dates and prepare events data.
* @param $recurringDatesStamps
* @param $recurringEventData
* @param array $recurringEventExceptionsData
* @return array
*/
private function _prepareRecurringDataWithoutExceptions($recurringDatesStamps, $recurringEventData, $recurringEventExceptionsData = array())
{
$recurringData = array();
$parentRecurringExceptions = array();
if(isset($recurringEventExceptionsData[$recurringEventData[$this->getIdFieldName()]]))
$parentRecurringExceptions = $recurringEventExceptionsData[$recurringEventData[$this->getIdFieldName()]];
$startField = $this->getStartDateFieldName();
$lengthField = $this->getLengthFieldName();
$endField = $this->getEndDateFieldName();
$stampsCount = count($recurringDatesStamps);
for($i = 0; $i < $stampsCount; $i++) {
$preparedEventData = $recurringEventData;
$eventStartDateStamp = $recurringDatesStamps[$i];
$preparedEventData[$startField] = date(SchedulerHelperDate::FORMAT_DEFAULT, $eventStartDateStamp);
$eventEndDateStamp = $eventStartDateStamp + $recurringEventData[$lengthField];
$preparedEventData[$endField] = date(SchedulerHelperDate::FORMAT_DEFAULT, $eventEndDateStamp);
if(isset($parentRecurringExceptions[$eventStartDateStamp])) {
$eventExceptionData = $parentRecurringExceptions[$eventStartDateStamp];
if($eventExceptionData[$this->getRecurringTypeFieldName()] != RecurringType::IS_RECURRING_BREAK)
$preparedEventData = $eventExceptionData;
else
continue;
}
$preparedEventData = $this->_filterEventDataToResponse($preparedEventData);
array_push($recurringData, $preparedEventData);
}
return $recurringData;
}
/**
* Get recurring events data by interval.
* @param $startDate
* @param $endDate
* @return array
*/
public function getData($startDate, $endDate)
{
$eventsData = array();
$recurringEventsExceptions = $this->_getRecurringEventsExceptionsByInterval();
$recurringEvents = $this->_getRecurringEventsByInterval($startDate, $endDate);
$intervalStartDateStamp = $this->getDateTimestamp($startDate);
$intervalEndDateStamp = $this->getDateTimestamp($endDate);
$recField = $this->getRecurringTypeFieldName();
$startField = $this->getStartDateFieldName();
$endField = $this->getEndDateFieldName();
$lengthField = $this->getLengthFieldName();
$recConfig = array(
"start_on_monday" => $this->config["start_on_monday"]
);
$recCount = count($recurringEvents);
for($i = 0; $i < $recCount; $i++) {
$eventData = $recurringEvents[$i];
//Parse recurring data format.
$recurringTypeData = $eventData[$recField];
$recLength = $eventData[$lengthField];
$recurringStartDateStamp = $this->getDateTimestamp($eventData[$startField]);
$recurringEndDateStamp = $this->getDateTimestamp($eventData[$endField]);
$recurringTypeObj = new RecurringType($recurringTypeData, $recurringStartDateStamp, $recurringEndDateStamp, $recLength, $recConfig);
//Get recurring dates by parsed format.
$recurringDatesStamps = $recurringTypeObj->getRecurringDates($intervalStartDateStamp, $intervalEndDateStamp);
//Exclude recurring exceptions by dates and prepare events data.
$recurringEventData = $this->_prepareRecurringDataWithoutExceptions($recurringDatesStamps, $eventData, $recurringEventsExceptions);
$eventsData = array_merge($eventsData, $recurringEventData);
}
//Add simple events.
$simpleEvents = $this->_getSimpleEventsByInterval($startDate, $endDate);
$simpleEvents = $this->_prepareSimpleEvents($simpleEvents);
$eventsData = array_merge($eventsData, $simpleEvents);
//Leave events that belongs to interval.
$resultData = array();
$evCount = count($eventsData);
for($i = 0; $i < $evCount; $i++) {
$eventData = $eventsData[$i];
$fullEventData = $eventData["full_event_data"];
$recurringStartDateStamp = $this->getDateTimestamp($fullEventData[$startField]);
$recurringEndDateStamp = $this->getDateTimestamp($fullEventData[$endField]);
if($recurringStartDateStamp < $intervalEndDateStamp && $recurringEndDateStamp > $intervalStartDateStamp) {
array_push($resultData, $eventData["filtered_event_data"]);
}
}
return $resultData;
}
/**
* Save recurring events data.
* @param $dataArray
* @throws Exception
*/
public function saveData($dataArray)
{
//If exist recurring type field and this array, then parse this to string.
if((isset($dataArray[self::FLD_RECURRING_TYPE])) && is_array($dataArray[self::FLD_RECURRING_TYPE]))
$dataArray[self::FLD_RECURRING_TYPE] = RecurringType::parseRecurringDataArrayToString($dataArray[self::FLD_RECURRING_TYPE]);
$connection = $this->getConnection();
$connection->beginTransaction();
try {
self::getInstance($this->getConnectConfigs())
->setFieldsNames($this->getFieldsNames())
->setFieldsValues($dataArray)
->save();
$connection->commit();
}
catch(Exception $error) {
$connection->rollBack();
throw new Exception("Data not saved.");
}
$this->closeConnection();
}
/**
* Delete data event by id.
* @param $id
* @throws Exception
*/
public function deleteById($id)
{
$connection = $this->getConnection();
$connection->beginTransaction();
try {
self::getInstance($this->getConnectConfigs())
->setFieldsNames($this->getFieldsNames())
->setFieldsValues(array(self::FLD_ID => $id))
->delete();
$connection->commit();
}
catch(Exception $error) {
$connection->rollBack();
throw new Exception("Data not deleted.");
}
$this->closeConnection();
}
/**
* Get max recurring end date for recurring type.
* @param $recurringType
* @param $startDateStr
* @param $eventLength
* @return int
*/
public function getRecurringEndDateStr($recurringType, $startDateStr, $eventLength) {
$endDateStamp = RecurringType::getRecurringEndDate($recurringType, $this->getDateTimestamp($startDateStr), $eventLength);
return date(SchedulerHelperDate::FORMAT_DEFAULT, $endDateStamp);
}
}