work_time-1.0.x-dev/src/Plugin/rest/resource/ApiWorkTimeListResource.php
src/Plugin/rest/resource/ApiWorkTimeListResource.php
<?php
namespace Drupal\work_time\Plugin\rest\resource;
use Drupal\Component\Plugin\DependentPluginInterface;
use Drupal\Core\Database\Connection;
use Drupal\Core\Link;
use Drupal\datetime\Plugin\Field\FieldType\DateTimeItemInterface;
use Drupal\rest\ModifiedResourceResponse;
use Drupal\rest\Plugin\ResourceBase;
use Drupal\user\Entity\User;
use Psr\Log\LoggerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/**
* Represents Api Work Time records as resources.
*
* @RestResource (
* id = "api_work_time_list",
* label = @Translation("Api Work Time list"),
* uri_paths = {
* "canonical" = "/api/work-time-list/{limit}",
* "create" = "/api/work-time-list"
* }
* )
*
* @DCG
* This plugin exposes database records as REST resources. In order to enable it
* import the resource configuration into active configuration storage. You may
* find an example of such configuration in the following file:
* core/modules/rest/config/optional/rest.resource.entity.node.yml.
* Alternatively you can make use of REST UI module.
* @see https://www.drupal.org/project/restui
* For accessing Drupal entities through REST interface use
* \Drupal\rest\Plugin\rest\resource\EntityResource plugin.
*/
class ApiWorkTimeListResource extends ResourceBase implements DependentPluginInterface {
/**
* The database connection.
*
* @var \Drupal\Core\Database\Connection
*/
protected $dbConnection;
/**
* Constructs a Drupal\rest\Plugin\rest\resource\EntityResource object.
*
* @param array $configuration
* A configuration array containing information about the plugin instance.
* @param string $plugin_id
* The plugin_id for the plugin instance.
* @param mixed $plugin_definition
* The plugin implementation definition.
* @param array $serializer_formats
* The available serialization formats.
* @param \Psr\Log\LoggerInterface $logger
* A logger instance.
* @param \Drupal\Core\Database\Connection $db_connection
* The database connection.
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, array $serializer_formats, LoggerInterface $logger, Connection $db_connection) {
parent::__construct($configuration, $plugin_id, $plugin_definition, $serializer_formats, $logger);
$this->dbConnection = $db_connection;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->getParameter('serializer.formats'),
$container->get('logger.factory')->get('rest'),
$container->get('database')
);
}
/**
* Responds to GET requests.
*
* @param string $limit
* The filter of worktime in week or in month.
*
* @return \Drupal\rest\ResourceResponse
* The response containing the record.
*
* @throws \Symfony\Component\HttpKernel\Exception\HttpException
*/
public function get($limit = 'week') {
$workTimes = FALSE;
$currentWorkTimes = [];
$request = \Drupal::request();
$entityManager = \Drupal::entityTypeManager();
$currentUser = \Drupal::currentUser();
$uid = $currentUser->id();
// Get work times in week.
$sql = [
'week' => "SELECT * FROM {work_time}
WHERE
(
(FROM_UNIXTIME(created) BETWEEN DATE_SUB(CURDATE(), INTERVAL WEEKDAY(CURDATE()) DAY) AND DATE_ADD(CURDATE(), INTERVAL 6 - WEEKDAY(CURDATE()) DAY)) OR
(FROM_UNIXTIME(stopped) BETWEEN DATE_SUB(CURDATE(), INTERVAL WEEKDAY(CURDATE()) DAY) AND DATE_ADD(CURDATE(), INTERVAL 6 - WEEKDAY(CURDATE()) DAY))
) AND uid =:uid ORDER BY created DESC",
'month' => "SELECT * FROM {work_time}
WHERE
(
(FROM_UNIXTIME(created) BETWEEN DATE_FORMAT(NOW(),'%Y-%m-01') AND LAST_DAY(NOW()))
OR
(FROM_UNIXTIME(stopped) BETWEEN DATE_FORMAT(NOW(),'%Y-%m-01') AND LAST_DAY(NOW()))
) AND uid = :uid ORDER BY created DESC",
];
if (!empty($sql[$limit])) {
$workTimes = $this->dbConnection->query($sql[$limit], [':uid' => $uid])
->fetchAll(\PDO::FETCH_ASSOC);
}
// Get current playing.
$query = $entityManager->getStorage('work_time')->getQuery()->accessCheck(TRUE);
$query->condition('uid', $uid);
$query->notExists('stopped');
$wids = $query->execute();
if (!empty($wids)) {
$nonStopWorkTimes = $entityManager->getStorage('work_time')
->loadMultiple($wids);
// Get last one.
$workTime = end($nonStopWorkTimes);
$currentWorkTimes = [
'id' => $workTime->id(),
'uuid' => $workTime->uuid(),
'label' => $workTime->label(),
'entity_id' => $workTime->get('entity_id')->getString(),
'entity_type' => $workTime->get('entity_type')->getString(),
'reference_id' => $workTime->get('reference_id')->getString(),
'reference_field' => $workTime->get('reference_field')->getString(),
'reference_type' => $workTime->get('reference_type')->getString(),
'created' => $workTime->get('created')->getString(),
'time' => strtotime("now") - (int) $workTime->get('created')->getString(),
];
}
// Get list projects.
$projects = [];
$referenceField = $request->get('referenceField');
if (!empty($referenceField)) {
$entity_type = $request->get('entityType');
$bundle = $request->get('entityBundle');
$fieldConfig = $entityManager->getStorage('field_config')
->load($entity_type . '.' . $bundle . '.' . $referenceField);
if ($fieldConfig) {
$settings = $fieldConfig->getSetting('handler_settings');
$handler = explode(':', $fieldConfig->getSetting('handler'));
$handler = end($handler);
if (!empty($settings["target_bundles"])) {
$ids = $entityManager->getStorage($handler)->getQuery()->accessCheck(TRUE)
->condition('type', array_values($settings["target_bundles"]), 'IN')
->condition('status', 1)
->execute();
$entities = $entityManager->getStorage($handler)->loadMultiple($ids);
if (!empty($entities)) {
foreach ($entities as $project) {
$projects[] = [
'id' => $project->id(),
'label' => $project->label(),
];
}
}
}
}
}
return new ModifiedResourceResponse([
'worktime' => $workTimes,
'playing' => $currentWorkTimes,
'projects' => $projects,
], 200);
}
/**
* Responds to POST requests and saves the new record.
*
* @param mixed $data
* Data to write into the database.
*
* @return \Drupal\rest\ModifiedResourceResponse
* The HTTP response object.
*/
public function post($data) {
$workTimeStorage = \Drupal::entityTypeManager()->getStorage('work_time');
$user = \Drupal::currentUser();
$currentTimestamp = strtotime('now');
$query = $workTimeStorage->getQuery()
->condition('uid', $user->id())
->condition('stopped', NULL, 'IS NULL');
$workTimes = $query->execute();
if (!empty($data['entity_type']) && !empty($data['entity_id'])) {
$taskManager = \Drupal::entityTypeManager()
->getStorage($type = $data['entity_type']);
$task = $taskManager->load($idTask = $data['entity_id']);
$taskManager->resetCache([$data['entity_id']]);
}
// State is stopped.
if (!empty($workTimes)) {
foreach ($workTimes as $workTimeId) {
$workTime = $workTimeStorage->load($workTimeId);
$start = $workTime->get('created')->value;
$workTime->set('stopped', $currentTimestamp);
$workTime->set('time_total', $currentTimestamp - $start);
$workTime->save();
// Save value to field.
if (!empty($task) && !empty($data['entity_field'])) {
$fieldTimeValue = [
'value' => gmdate(DateTimeItemInterface::DATETIME_STORAGE_FORMAT, $start),
'end_value' => gmdate(DateTimeItemInterface::DATETIME_STORAGE_FORMAT, $currentTimestamp),
];
$task->get($data['entity_field'])->appendItem($fieldTimeValue);
$task->save();
}
}
}
$label = '';
if (!empty($task)) {
if (in_array($type, ['user'])) {
$label = $task->getDisplayName();
}
elseif ($type == 'taxonomy_term') {
$label = $task->getName();
}
elseif (method_exists($task, 'getTitle')) {
$label = $task->getTitle();
}
}
$created_record = ['data' => 'Worktime Save'];
if ($data['play']) {
$workTime = $workTimeStorage
->create([
'label' => $label,
'uid' => $user->id(),
'created' => $currentTimestamp,
'entity_id' => $idTask ?? '',
'entity_type' => $type ?? '',
'entity_field' => $data['entity_field'] ?? '',
'reference_id' => $data['reference_id'] ?? '',
'reference_field' => $data['reference_field'] ?? '',
'reference_type' => $data['reference_type'] ?? '',
'stopped' => NULL,
'type' => 0,
]);
$workTime->save();
$id = $workTime->id();
$created_record = $this->loadRecord($id);
}
// Return the newly created record in the response body.
return new ModifiedResourceResponse($created_record, 200);
}
/**
* Responds to entity PATCH requests.
*
* @param int $id
* The ID of the record.
*
* @return \Drupal\rest\ModifiedResourceResponse
* The HTTP response object.
*/
public function patch(int $id = 0) {
$out = [];
$entity_ids = json_decode(\Drupal::request()->getContent(), TRUE);
$database = \Drupal::database();
$query = $database->select('work_time', 'wt');
$query->addExpression("CONCAT(wt.entity_type, '-', wt.entity_id)", 'entity');
$query->addExpression('SUM(wt.time_total)', 'total');
$query->where("CONCAT(wt.entity_type, '-', wt.entity_id) IN (:entity_ids[])", [':entity_ids[]' => $entity_ids]);
$query->condition('wt.stopped', NULL, 'IS NOT');
$query->groupBy('entity');
$results = $query->execute();
foreach ($results as $result) {
$entity = $result->entity;
$total = $result->total;
$out[$entity] = $total;
}
return new ModifiedResourceResponse($out, 200);
}
/**
* Responds to entity DELETE requests.
*
* @param int $entity_id
* Entity id.
* @param string $entity_field
* Entity field.
*
* @return \Drupal\rest\ModifiedResourceResponse
* The HTTP response object.
*/
public function delete($entity_id, $entity_field) {
// Make sure the record still exists.
$checkExist = $this->loadRecord($entity_id);
if (!empty($checkExist)) {
$this->dbConnection->delete('work_time')
->condition('id', $entity_id)
->execute();
$this->logger->notice('Api Work Time record @id has been deleted.', ['@id' => $entity_id]);
}
// Deleted responses have an empty body.
return new ModifiedResourceResponse(NULL, 204);
}
/**
* Validates incoming record.
*
* @param mixed $record
* Data to validate.
*
* @throws \Symfony\Component\HttpKernel\Exception\BadRequestHttpException
*/
protected function validate($record) {
if (!is_array($record) || count($record) == 0) {
throw new BadRequestHttpException('No record content received.');
}
$allowed_fields = [
'title',
'description',
];
if (count(array_diff(array_keys($record), $allowed_fields)) > 0) {
throw new BadRequestHttpException('Record structure is not correct.');
}
if (empty($record['title'])) {
throw new BadRequestHttpException('Title is required.');
}
elseif (isset($record['title']) && strlen($record['title']) > 255) {
throw new BadRequestHttpException('Title is too big.');
}
// @DCG Add more validation rules here.
}
/**
* Loads record from database.
*
* @param int $id
* The ID of the record.
*
* @return array
* The database record.
*
* @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
*/
protected function loadRecord($id) {
$record = $this->dbConnection->query(
'SELECT * FROM {work_time} WHERE id = :id', [':id' => $id])->fetchAssoc();
if (!$record) {
throw new NotFoundHttpException('The record was not found.');
}
return $record;
}
/**
* Updates record.
*
* @param int $id
* The ID of the record.
* @param array $record
* The record to validate.
*
* @return \Drupal\rest\ModifiedResourceResponse
* The HTTP response object.
*/
protected function updateRecord($id, array $record) {
// Make sure the record already exists.
$this->loadRecord($id);
$this->validate($record);
$this->dbConnection->update('work_time')
->fields($record)
->condition('id', $id)
->execute();
$this->logger->notice('Api Work Time record @id has been updated.', ['@id' => $id]);
// Return the updated record in the response body.
$updated_record = $this->loadRecord($id);
return new ModifiedResourceResponse($updated_record, 200);
}
/**
* {@inheritdoc}
*/
protected function loadEntities($entity_id, $entity_field) {
$query = $this->dbConnection->select('work_time', 'w')
->fields('w')
->condition('stopped', '', 'IS NOT NULL')
->condition('entity_id', $entity_id)
->condition('entity_field', $entity_field);
$query->addExpression("FROM_UNIXTIME(created,'%Y-%m-%d')", 'date');
$work_times = $query->execute();
$records = [];
$entityManager = \Drupal::entityTypeManager();
$projects = [];
$options = ['absolute' => TRUE];
foreach ($work_times as $work_time) {
$user = User::load($work_time->uid);
if (empty($records[$work_time->uid])) {
$name = $user->getDisplayName();
preg_match_all('/(?<=\b)\w/iu', $name, $initials);
$records[$work_time->uid] = [
'name' => $name,
'label' => $work_time->label,
'initial' => strtoupper(implode('', array_slice($initials[0], 0, 2))),
'details' => [],
];
}
if (!empty($work_time->reference_id) && !empty($work_time->reference_field) && empty($projects[$work_time->reference_id])) {
$task = $entityManager->getStorage($work_time->entity_type)
->load($entity_id);
$referenceItem = $task->get($work_time->reference_field)
->referencedEntities();
$node = current($referenceItem);
$projects[$work_time->reference_id] = $node;
$work_time->project = Link::fromTextAndUrl($node->getTitle(), $node->toUrl('canonical', $options))
->toString();
}
$records[$work_time->uid]['details'][] = (array) $work_time;
}
return array_values($records);
}
/**
* {@inheritdoc}
*/
public function getCurrentPlaying($entity_id) {
$uid = \Drupal::currentUser()->id();
$query = $this->dbConnection->select('work_time', 'w')
->fields('w', ['created'])
->condition('stopped', '', 'IS NULL')
->condition('uid', '', $uid)
->condition('entity_id', $entity_id);
$query->execute();
}
/**
* {@inheritdoc}
*/
public function calculateDependencies() {
return [];
}
}
