work_time-1.0.x-dev/src/Plugin/rest/resource/ApiWorkTimeResource.php
src/Plugin/rest/resource/ApiWorkTimeResource.php
<?php
namespace Drupal\work_time\Plugin\rest\resource;
use Drupal\Component\Plugin\DependentPluginInterface;
use Drupal\Core\Database\Connection;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Link;
use Drupal\Core\Routing\BcRoute;
use Drupal\Core\Session\AccountProxyInterface;
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\NotFoundHttpException;
/**
* Represents Api Work Time records as resources.
*
* @RestResource (
* id = "api_work_time",
* label = @Translation("Api Work Time"),
* uri_paths = {
* "canonical" = "/api/work-time/{entity_id}/{entity_field}",
* "create" = "/api/work-time"
* }
* )
*
* @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 ApiWorkTimeResource extends ResourceBase implements DependentPluginInterface {
/**
* The database connection.
*
* @var \Drupal\Core\Database\Connection
*/
protected $dbConnection;
/**
* Entity type manager service.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected $entityTypeManager;
/**
* Current user service.
*
* @var \Drupal\Core\Session\AccountProxyInterface
*/
protected $account;
/**
* 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.
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
* The entity type manager.
* @param \Drupal\Core\Session\AccountProxyInterface $account
* The current user.
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, array $serializer_formats, LoggerInterface $logger, Connection $db_connection, EntityTypeManagerInterface $entity_type_manager, AccountProxyInterface $account) {
parent::__construct($configuration, $plugin_id, $plugin_definition, $serializer_formats, $logger);
$this->dbConnection = $db_connection;
$this->entityTypeManager = $entity_type_manager;
$this->account = $account;
}
/**
* {@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'),
$container->get('entity_type.manager'),
$container->get('current_user'),
);
}
/**
* Responds to GET requests.
*
* @param int $entity_id
* Entity id.
* @param string $entity_field
* Entity field.
*
* @return \Drupal\rest\ResourceResponse
* The response containing the record.
*/
public function get($entity_id, $entity_field) {
$records = FALSE;
if ($entity_field == 'user') {
$records = $this->getCurrentPlaying($entity_id);
}
else {
$records = $this->loadEntities($entity_id, $entity_field);
}
return new ModifiedResourceResponse($records, 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 = $this->entityTypeManager->getStorage('work_time');
$task = $type = '';
$currentTimestamp = strtotime('now');
// Load user's work time has stopped is null.
$query = $workTimeStorage->getQuery()
->condition('uid', $this->account->id())
->condition('stopped', NULL, 'IS NULL');
$workTimes = $query->execute();
if (!empty($data['entityType']) && !empty($data['entityId'])) {
$taskManager = $this->entityTypeManager->getStorage($type = $data['entityType']);
$task = $taskManager->load($idTask = $data['entityId']);
$taskManager->resetCache([$data['entityId']]);
}
// 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['entityField']) && !empty($task->get($data['entityField']))) {
$task->get($data['entityField'])->appendItem([
'value' => gmdate(DateTimeItemInterface::DATETIME_STORAGE_FORMAT, $start),
'end_value' => gmdate(DateTimeItemInterface::DATETIME_STORAGE_FORMAT, $currentTimestamp),
]);
$task->save();
}
}
}
$label = $data['label'] ?? $this->t('New task');
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();
}
}
$label = empty($label) ? $this->t('New task') : $label;
$created_record = ['data' => 'Worktime Save'];
if ($data['play'] != 'true') {
$workTime = $workTimeStorage
->create([
'label' => $label,
'uid' => $this->account->id(),
'created' => $currentTimestamp,
'entity_id' => $idTask ?? '',
'entity_type' => $type ?? '',
'entity_field' => $data['entityField'] ?? '',
'reference_id' => $data['referenceId'] ?? '',
'reference_field' => $data['referenceField'] ?? '',
'reference_type' => $data['referenceType'] ?? '',
'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 $entity_id
* The ID of the record.
* @param string $entity_field
* Type of data.
* @param mixed $data
* Data to write into the database.
*
* @return \Drupal\rest\ModifiedResourceResponse
* The HTTP response object.
*/
public function patch(int $entity_id = 0, $entity_field = '', $data = []) {
$update_record = ['data' => 'Work time update'];
if (empty($entity_id)) {
$uid = $this->account->id();
$query = $this->dbConnection->select('work_time', 'w')
->fields('w', ['id'])
->condition('stopped', '', 'IS NULL')
->condition('uid', $uid);
$wids = $query->execute()->fetchCol();
if (!empty($wids)) {
$entity_id = current($wids);
}
}
if ($entity_id) {
$update_record = $this->updateRecord($entity_id, $data);
}
// Return the newly created record in the response body.
return $update_record;
}
/**
* 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);
}
/**
* {@inheritdoc}
*/
public function calculateDependencies() {
return [];
}
/**
* {@inheritdoc}
*/
public function routes() {
$collection = parent::routes();
// Take out BC routes added in base class.
// @see https://www.drupal.org/node/2865645
// @todo Remove this in Drupal 9.
foreach ($collection as $route_name => $route) {
if ($route instanceof BcRoute) {
$collection->remove($route_name);
}
}
return $collection;
}
/**
* 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->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 = [];
$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 = $this->entityTypeManager->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}
*/
private function getCurrentPlaying($entity_id) {
$uid = $this->account->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();
}
}
