iflrandevu/application/models/Secretaries_model.php

747 lines
25 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php defined('BASEPATH') or exit('No direct script access allowed');
/* ----------------------------------------------------------------------------
* IFLRandevu - İzmir Fen Lisesi Randevu Portalı
*
* @package EasyAppointments
* @author A.Tselegidis <alextselegidis@gmail.com>
* @copyright Copyright (c) Alex Tselegidis
* @license https://opensource.org/licenses/GPL-3.0 - GPLv3
* @link https://easyappointments.org
* @since v1.0.0
* ---------------------------------------------------------------------------- */
/**
* Secretaries model.
*
* Handles all the database operations of the secretary resource.
*
* @package Models
*/
class Secretaries_model extends EA_Model
{
/**
* @var array
*/
protected array $casts = [
'id' => 'integer',
'id_roles' => 'integer',
];
/**
* @var array
*/
protected array $api_resource = [
'id' => 'id',
'firstName' => 'first_name',
'lastName' => 'last_name',
'email' => 'email',
'mobile' => 'mobile_number',
'phone' => 'phone_number',
'address' => 'address',
'city' => 'city',
'state' => 'state',
'zip' => 'zip_code',
'timezone' => 'timezone',
'language' => 'language',
'notes' => 'notes',
'roleId' => 'id_roles',
];
/**
* Save (insert or update) a secretary.
*
* @param array $secretary Associative array with the secretary data.
*
* @return int Returns the secretary ID.
*
* @throws InvalidArgumentException
* @throws Exception
*/
public function save(array $secretary): int
{
$this->validate($secretary);
if (empty($secretary['id'])) {
return $this->insert($secretary);
} else {
return $this->update($secretary);
}
}
/**
* Validate the secretary data.
*
* @param array $secretary Associative array with the secretary data.
*
* @throws InvalidArgumentException
*/
public function validate(array $secretary)
{
// If a secretary ID is provided then check whether the record really exists in the database.
if (!empty($provider['id'])) {
$count = $this->db->get_where('users', ['id' => $secretary['id']])->num_rows();
if (!$count) {
throw new InvalidArgumentException(
'The provided secretary ID does not exist in the database: ' . $secretary['id'],
);
}
}
// Make sure all required fields are provided.
if (
empty($secretary['first_name']) ||
empty($secretary['last_name']) ||
empty($secretary['email']) ||
empty($secretary['phone_number'])
) {
throw new InvalidArgumentException('Not all required fields are provided: ' . print_r($secretary, true));
}
// Validate the email address.
if (!filter_var($secretary['email'], FILTER_VALIDATE_EMAIL)) {
throw new InvalidArgumentException('Invalid email address provided: ' . $secretary['email']);
}
// Validate secretary providers.
if (!empty($secretary['providers'])) {
// Make sure the provided provider entries are numeric values.
foreach ($secretary['providers'] as $secretary_id) {
if (!is_numeric($secretary_id)) {
throw new InvalidArgumentException(
'The provided secretary providers are invalid: ' . print_r($secretary, true),
);
}
}
}
// Make sure the username is unique.
if (!empty($secretary['settings']['username'])) {
$secretary_id = $secretary['id'] ?? null;
if (!$this->validate_username($secretary['settings']['username'], $secretary_id)) {
throw new InvalidArgumentException(
'The provided username is already in use, please use a different one.',
);
}
}
// Validate the password.
if (!empty($secretary['settings']['password'])) {
if (strlen($secretary['settings']['password']) < MIN_PASSWORD_LENGTH) {
throw new InvalidArgumentException(
'The secretary password must be at least ' . MIN_PASSWORD_LENGTH . ' characters long.',
);
}
}
// New users must always have a password value set.
if (empty($secretary['id']) && empty($secretary['settings']['password'])) {
throw new InvalidArgumentException('The provider password cannot be empty when inserting a new record.');
}
// Validate calendar view type value.
if (
!empty($secretary['settings']['calendar_view']) &&
!in_array($secretary['settings']['calendar_view'], [CALENDAR_VIEW_DEFAULT, CALENDAR_VIEW_TABLE])
) {
throw new InvalidArgumentException(
'The provided calendar view is invalid: ' . $secretary['settings']['calendar_view'],
);
}
// Make sure the email address is unique.
$secretary_id = $secretary['id'] ?? null;
$count = $this->db
->select()
->from('users')
->join('roles', 'roles.id = users.id_roles', 'inner')
->where('roles.slug', DB_SLUG_SECRETARY)
->where('users.email', $secretary['email'])
->where('users.id !=', $secretary_id)
->get()
->num_rows();
if ($count > 0) {
throw new InvalidArgumentException(
'The provided email address is already in use, please use a different one.',
);
}
}
/**
* Validate the secretary username.
*
* @param string $username Secretary username.
* @param int|null $secretary_id Secretary ID.
*
* @return bool Returns the validation result.
*/
public function validate_username(string $username, int $secretary_id = null): bool
{
if (!empty($secretary_id)) {
$this->db->where('id_users !=', $secretary_id);
}
return $this->db
->from('users')
->join('user_settings', 'user_settings.id_users = users.id', 'inner')
->where(['username' => $username])
->get()
->num_rows() === 0;
}
/**
* Get all secretaries that match the provided criteria.
*
* @param array|string|null $where Where conditions
* @param int|null $limit Record limit.
* @param int|null $offset Record offset.
* @param string|null $order_by Order by.
*
* @return array Returns an array of secretaries.
*/
public function get(
array|string $where = null,
int $limit = null,
int $offset = null,
string $order_by = null,
): array {
$role_id = $this->get_secretary_role_id();
if ($where !== null) {
$this->db->where($where);
}
if ($order_by !== null) {
$this->db->order_by($order_by);
}
$secretaries = $this->db->get_where('users', ['id_roles' => $role_id], $limit, $offset)->result_array();
foreach ($secretaries as &$secretary) {
$secretary['settings'] = $this->db
->get_where('user_settings', ['id_users' => $secretary['id']])
->row_array();
unset(
$secretary['settings']['id_users'],
$secretary['settings']['password'],
$secretary['settings']['salt'],
);
$secretary_provider_connections = $this->db
->get_where('secretaries_providers', ['id_users_secretary' => $secretary['id']])
->result_array();
$secretary['providers'] = [];
foreach ($secretary_provider_connections as $secretary_provider_connection) {
$secretary['providers'][] = (int) $secretary_provider_connection['id_users_provider'];
}
}
return $secretaries;
}
/**
* Get the secretary role ID.
*
* @return int Returns the role ID.
*/
public function get_secretary_role_id(): int
{
$role = $this->db->get_where('roles', ['slug' => DB_SLUG_SECRETARY])->row_array();
if (empty($role)) {
throw new RuntimeException('The secretary role was not found in the database.');
}
return $role['id'];
}
/**
* Insert a new secretary into the database.
*
* @param array $secretary Associative array with the secretary data.
*
* @return int Returns the secretary ID.
*
* @throws RuntimeException|Exception
*/
protected function insert(array $secretary): int
{
$secretary['create_datetime'] = date('Y-m-d H:i:s');
$secretary['update_datetime'] = date('Y-m-d H:i:s');
$secretary['id_roles'] = $this->get_secretary_role_id();
$provider_ids = $secretary['providers'] ?? [];
$settings = $secretary['settings'];
unset($secretary['providers'], $secretary['settings']);
if (!$this->db->insert('users', $secretary)) {
throw new RuntimeException('Could not insert secretary.');
}
$secretary['id'] = $this->db->insert_id();
$settings['salt'] = generate_salt();
$settings['password'] = hash_password($settings['salt'], $settings['password']);
$this->save_settings($secretary['id'], $settings);
$this->save_provider_ids($secretary['id'], $provider_ids);
return $secretary['id'];
}
/**
* Save the secretary settings.
*
* @param int $secretary_id Secretary ID.
* @param array $settings Associative array with the settings data.
*
* @throws InvalidArgumentException
*/
protected function save_settings(int $secretary_id, array $settings)
{
if (empty($settings)) {
throw new InvalidArgumentException('The settings argument cannot be empty.');
}
// Make sure the settings record exists in the database.
$count = $this->db->get_where('user_settings', ['id_users' => $secretary_id])->num_rows();
if (!$count) {
$this->db->insert('user_settings', ['id_users' => $secretary_id]);
}
foreach ($settings as $name => $value) {
$this->set_setting($secretary_id, $name, $value);
}
}
/**
* Set the value of a secretary setting.
*
* @param int $secretary_id Secretary ID.
* @param string $name Setting name.
* @param mixed|null $value Setting value.
*/
public function set_setting(int $secretary_id, string $name, mixed $value = null)
{
if (!$this->db->update('user_settings', [$name => $value], ['id_users' => $secretary_id])) {
throw new RuntimeException('Could not set the new secretary setting value: ' . $name);
}
}
/**
* Update an existing secretary.
*
* @param array $secretary Associative array with the secretary data.
*
* @return int Returns the secretary ID.
*
* @throws RuntimeException|Exception
*/
protected function update(array $secretary): int
{
$secretary['update_datetime'] = date('Y-m-d H:i:s');
$provider_ids = $secretary['providers'] ?? [];
$settings = $secretary['settings'];
unset($secretary['providers'], $secretary['settings']);
if (isset($settings['password'])) {
$existing_settings = $this->db->get_where('user_settings', ['id_users' => $secretary['id']])->row_array();
if (empty($existing_settings)) {
throw new RuntimeException('No settings record found for secretary with ID: ' . $secretary['id']);
}
$settings['password'] = hash_password($existing_settings['salt'], $settings['password']);
}
if (!$this->db->update('users', $secretary, ['id' => $secretary['id']])) {
throw new RuntimeException('Could not update secretary.');
}
$this->save_settings($secretary['id'], $settings);
$this->save_provider_ids($secretary['id'], $provider_ids);
return (int) $secretary['id'];
}
/**
* Save the secretary provider IDs.
*
* @param int $secretary_id Secretary ID.
* @param array $provider_ids Provider IDs.
*/
protected function save_provider_ids(int $secretary_id, array $provider_ids)
{
// Re-insert the secretary-provider connections.
$this->db->delete('secretaries_providers', ['id_users_secretary' => $secretary_id]);
foreach ($provider_ids as $provider_id) {
$secretary_provider_connection = [
'id_users_secretary' => $secretary_id,
'id_users_provider' => $provider_id,
];
$this->db->insert('secretaries_providers', $secretary_provider_connection);
}
}
/**
* Remove an existing secretary from the database.
*
* @param int $secretary_id Provider ID.
*
* @throws RuntimeException
*/
public function delete(int $secretary_id): void
{
$this->db->delete('users', ['id' => $secretary_id]);
}
/**
* Get a specific field value from the database.
*
* @param int $secretary_id Secretary ID.
* @param string $field Name of the value to be returned.
*
* @return mixed Returns the selected secretary value from the database.
*
* @throws InvalidArgumentException
*/
public function value(int $secretary_id, string $field): mixed
{
if (empty($field)) {
throw new InvalidArgumentException('The field argument is cannot be empty.');
}
if (empty($secretary_id)) {
throw new InvalidArgumentException('The secretary ID argument cannot be empty.');
}
// Check whether the secretary exists.
$query = $this->db->get_where('users', ['id' => $secretary_id]);
if (!$query->num_rows()) {
throw new InvalidArgumentException(
'The provided secretary ID was not found in the database: ' . $secretary_id,
);
}
// Check if the required field is part of the secretary data.
$secretary = $query->row_array();
if (!array_key_exists($field, $secretary)) {
throw new InvalidArgumentException('The requested field was not found in the secretary data: ' . $field);
}
return $secretary[$field];
}
/**
* Get the value of a secretary setting.
*
* @param int $secretary_id Secretary ID.
* @param string $name Setting name.
*
* @return string Returns the value of the requested user setting.
*/
public function get_setting(int $secretary_id, string $name): string
{
$settings = $this->db->get_where('user_settings', ['id_users' => $secretary_id])->row_array();
if (!array_key_exists($name, $settings)) {
throw new RuntimeException('The requested setting value was not found: ' . $secretary_id);
}
return $settings[$name];
}
/**
* Get the query builder interface, configured for use with the users (secretary-filtered) table.
*
* @return CI_DB_query_builder
*/
public function query(): CI_DB_query_builder
{
$role_id = $this->get_secretary_role_id();
return $this->db->from('users')->where('id_roles', $role_id);
}
/**
* Search secretaries by the provided keyword.
*
* @param string $keyword Search keyword.
* @param int|null $limit Record limit.
* @param int|null $offset Record offset.
* @param string|null $order_by Order by.
*
* @return array Returns an array of secretaries.
*/
public function search(string $keyword, int $limit = null, int $offset = null, string $order_by = null): array
{
$role_id = $this->get_secretary_role_id();
$secretaries = $this->db
->select()
->from('users')
->where('id_roles', $role_id)
->group_start()
->like('first_name', $keyword)
->or_like('last_name', $keyword)
->or_like('CONCAT_WS(" ", first_name, last_name)', $keyword)
->or_like('email', $keyword)
->or_like('phone_number', $keyword)
->or_like('mobile_number', $keyword)
->or_like('address', $keyword)
->or_like('city', $keyword)
->or_like('state', $keyword)
->or_like('zip_code', $keyword)
->or_like('notes', $keyword)
->group_end()
->limit($limit)
->offset($offset)
->order_by($order_by)
->get()
->result_array();
foreach ($secretaries as &$secretary) {
$secretary['settings'] = $this->db
->get_where('user_settings', ['id_users' => $secretary['id']])
->row_array();
unset(
$secretary['settings']['id_users'],
$secretary['settings']['password'],
$secretary['settings']['salt'],
);
$secretary_provider_connections = $this->db
->get_where('secretaries_providers', ['id_users_secretary' => $secretary['id']])
->result_array();
$secretary['providers'] = [];
foreach ($secretary_provider_connections as $secretary_provider_connection) {
$secretary['providers'][] = (int) $secretary_provider_connection['id_users_provider'];
}
}
return $secretaries;
}
/**
* Load related resources to a secretary.
*
* @param array $secretary Associative array with the secretary data.
* @param array $resources Resource names to be attached ("providers" supported).
*
* @throws InvalidArgumentException
*/
public function load(array &$secretary, array $resources)
{
if (empty($secretary) || empty($resources)) {
return;
}
foreach ($resources as $resource) {
$secretary['providers'] = match ($resource) {
'providers' => $this->db
->select('users.*')
->from('users')
->join('secretaries_providers', 'secretaries_providers.id_users_provider = users.id', 'inner')
->where('id_users_secretary', $secretary['id'])
->get()
->result_array(),
default => throw new InvalidArgumentException(
'The requested secretary relation is not supported: ' . $resource,
),
};
}
}
/**
* Convert the database secretary record to the equivalent API resource.
*
* @param array $secretary Secretary data.
*/
public function api_encode(array &$secretary)
{
$encoded_resource = [
'id' => array_key_exists('id', $secretary) ? (int) $secretary['id'] : null,
'firstName' => $secretary['first_name'],
'lastName' => $secretary['last_name'],
'email' => $secretary['email'],
'mobile' => $secretary['mobile_number'],
'phone' => $secretary['phone_number'],
'address' => $secretary['address'],
'city' => $secretary['city'],
'state' => $secretary['state'],
'zip' => $secretary['zip_code'],
'notes' => $secretary['notes'],
'providers' => $secretary['providers'],
'timezone' => $secretary['timezone'],
'settings' => [
'username' => $secretary['settings']['username'],
'notifications' => filter_var($secretary['settings']['notifications'], FILTER_VALIDATE_BOOLEAN),
'calendarView' => $secretary['settings']['calendar_view'],
],
];
$secretary = $encoded_resource;
}
/**
* Convert the API resource to the equivalent database secretary record.
*
* @param array $secretary API resource.
* @param array|null $base Base secretary data to be overwritten with the provided values (useful for updates).
*/
public function api_decode(array &$secretary, array $base = null)
{
$decoded_resource = $base ?: [];
if (array_key_exists('id', $secretary)) {
$decoded_resource['id'] = $secretary['id'];
}
if (array_key_exists('firstName', $secretary)) {
$decoded_resource['first_name'] = $secretary['firstName'];
}
if (array_key_exists('lastName', $secretary)) {
$decoded_resource['last_name'] = $secretary['lastName'];
}
if (array_key_exists('email', $secretary)) {
$decoded_resource['email'] = $secretary['email'];
}
if (array_key_exists('mobile', $secretary)) {
$decoded_resource['mobile_number'] = $secretary['mobile'];
}
if (array_key_exists('phone', $secretary)) {
$decoded_resource['phone_number'] = $secretary['phone'];
}
if (array_key_exists('address', $secretary)) {
$decoded_resource['address'] = $secretary['address'];
}
if (array_key_exists('city', $secretary)) {
$decoded_resource['city'] = $secretary['city'];
}
if (array_key_exists('state', $secretary)) {
$decoded_resource['state'] = $secretary['state'];
}
if (array_key_exists('zip', $secretary)) {
$decoded_resource['zip_code'] = $secretary['zip'];
}
if (array_key_exists('notes', $secretary)) {
$decoded_resource['notes'] = $secretary['notes'];
}
if (array_key_exists('timezone', $secretary)) {
$decoded_resource['timezone'] = $secretary['timezone'];
}
if (array_key_exists('providers', $secretary)) {
$decoded_resource['providers'] = $secretary['providers'];
}
if (array_key_exists('settings', $secretary)) {
if (empty($decoded_resource['settings'])) {
$decoded_resource['settings'] = [];
}
if (array_key_exists('username', $secretary['settings'])) {
$decoded_resource['settings']['username'] = $secretary['settings']['username'];
}
if (array_key_exists('password', $secretary['settings'])) {
$decoded_resource['settings']['password'] = $secretary['settings']['password'];
}
if (array_key_exists('notifications', $secretary['settings'])) {
$decoded_resource['settings']['notifications'] = filter_var(
$secretary['settings']['notifications'],
FILTER_VALIDATE_BOOLEAN,
);
}
if (array_key_exists('calendarView', $secretary['settings'])) {
$decoded_resource['settings']['calendar_view'] = $secretary['settings']['calendarView'];
}
}
$secretary = $decoded_resource;
}
/**
* Quickly check if a provider is assigned to a provider.
*
* @param int $secretary_id
* @param int $provider_id
*
* @return bool
*/
public function is_provider_supported(int $secretary_id, int $provider_id): bool
{
$secretary = $this->find($secretary_id);
return in_array($provider_id, $secretary['providers']);
}
/**
* Get a specific secretary from the database.
*
* @param int $secretary_id The ID of the record to be returned.
*
* @return array Returns an array with the secretary data.
*
* @throws InvalidArgumentException
*/
public function find(int $secretary_id): array
{
$secretary = $this->db->get_where('users', ['id' => $secretary_id])->row_array();
if (!$secretary) {
throw new InvalidArgumentException(
'The provided secretary ID was not found in the database: ' . $secretary_id,
);
}
$secretary['settings'] = $this->db->get_where('user_settings', ['id_users' => $secretary_id])->row_array();
unset($secretary['settings']['id_users'], $secretary['settings']['password'], $secretary['settings']['salt']);
$secretary_provider_connections = $this->db
->get_where('secretaries_providers', ['id_users_secretary' => $secretary_id])
->result_array();
$secretary['providers'] = [];
foreach ($secretary_provider_connections as $secretary_provider_connection) {
$secretary['providers'][] = (int) $secretary_provider_connection['id_users_provider'];
}
return $secretary;
}
}