This commit is contained in:
665
application/models/Admins_model.php
Normal file
665
application/models/Admins_model.php
Normal file
@@ -0,0 +1,665 @@
|
||||
<?php defined('BASEPATH') or exit('No direct script access allowed');
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Easy!Appointments - Online Appointment Scheduler
|
||||
*
|
||||
* @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
|
||||
* ---------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Admins model.
|
||||
*
|
||||
* Handles all the database operations of the admin resource.
|
||||
*
|
||||
* @package Models
|
||||
*/
|
||||
class Admins_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',
|
||||
'ldapDn' => 'ldap_dn',
|
||||
'roleId' => 'id_roles',
|
||||
];
|
||||
|
||||
/**
|
||||
* Save (insert or update) an admin.
|
||||
*
|
||||
* @param array $admin Associative array with the admin data.
|
||||
*
|
||||
* @return int Returns the admin ID.
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
* @throws Exception
|
||||
*/
|
||||
public function save(array $admin): int
|
||||
{
|
||||
$this->validate($admin);
|
||||
|
||||
if (empty($admin['id'])) {
|
||||
return $this->insert($admin);
|
||||
} else {
|
||||
return $this->update($admin);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the admin data.
|
||||
*
|
||||
* @param array $admin Associative array with the admin data.
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function validate(array $admin): void
|
||||
{
|
||||
// If an admin ID is provided then check whether the record really exists in the database.
|
||||
if (!empty($admin['id'])) {
|
||||
$count = $this->db->get_where('users', ['id' => $admin['id']])->num_rows();
|
||||
|
||||
if (!$count) {
|
||||
throw new InvalidArgumentException(
|
||||
'The provided admin ID does not exist in the database: ' . $admin['id'],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure all required fields are provided.
|
||||
if (
|
||||
empty($admin['first_name']) ||
|
||||
empty($admin['last_name']) ||
|
||||
empty($admin['email']) ||
|
||||
empty($admin['phone_number'])
|
||||
) {
|
||||
throw new InvalidArgumentException('Not all required fields are provided: ' . print_r($admin, true));
|
||||
}
|
||||
|
||||
// Validate the email address.
|
||||
if (!filter_var($admin['email'], FILTER_VALIDATE_EMAIL)) {
|
||||
throw new InvalidArgumentException('Invalid email address provided: ' . $admin['email']);
|
||||
}
|
||||
|
||||
// Make sure the username is unique.
|
||||
if (!empty($admin['settings']['username'])) {
|
||||
$admin_id = $admin['id'] ?? null;
|
||||
|
||||
if (!$this->validate_username($admin['settings']['username'], $admin_id)) {
|
||||
throw new InvalidArgumentException(
|
||||
'The provided username is already in use, please use a different one.',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Validate the password.
|
||||
if (!empty($admin['settings']['password'])) {
|
||||
if (strlen($admin['settings']['password']) < MIN_PASSWORD_LENGTH) {
|
||||
throw new InvalidArgumentException(
|
||||
'The admin password must be at least ' . MIN_PASSWORD_LENGTH . ' characters long.',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// New users must always have a password value set.
|
||||
if (empty($admin['id']) && empty($admin['settings']['password'])) {
|
||||
throw new InvalidArgumentException('The admin password cannot be empty when inserting a new record.');
|
||||
}
|
||||
|
||||
// Validate calendar view type value.
|
||||
if (
|
||||
!empty($admin['settings']['calendar_view']) &&
|
||||
!in_array($admin['settings']['calendar_view'], [CALENDAR_VIEW_DEFAULT, CALENDAR_VIEW_TABLE])
|
||||
) {
|
||||
throw new InvalidArgumentException(
|
||||
'The provided calendar view is invalid: ' . $admin['settings']['calendar_view'],
|
||||
);
|
||||
}
|
||||
|
||||
// Make sure the email address is unique.
|
||||
$admin_id = $admin['id'] ?? null;
|
||||
|
||||
$count = $this->db
|
||||
->select()
|
||||
->from('users')
|
||||
->join('roles', 'roles.id = users.id_roles', 'inner')
|
||||
->where('roles.slug', DB_SLUG_ADMIN)
|
||||
->where('users.email', $admin['email'])
|
||||
->where('users.id !=', $admin_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 admin username.
|
||||
*
|
||||
* @param string $username Admin username.
|
||||
* @param int|null $admin_id Admin ID.
|
||||
*
|
||||
* @return bool Returns the validation result.
|
||||
*/
|
||||
public function validate_username(string $username, ?int $admin_id = null): bool
|
||||
{
|
||||
if (!empty($admin_id)) {
|
||||
$this->db->where('id_users !=', $admin_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 admins 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 admins.
|
||||
*/
|
||||
public function get(
|
||||
array|string|null $where = null,
|
||||
?int $limit = null,
|
||||
?int $offset = null,
|
||||
?string $order_by = null,
|
||||
): array {
|
||||
$role_id = $this->get_admin_role_id();
|
||||
|
||||
if ($where !== null) {
|
||||
$this->db->where($where);
|
||||
}
|
||||
|
||||
if ($order_by !== null) {
|
||||
$this->db->order_by($order_by);
|
||||
}
|
||||
|
||||
$admins = $this->db->get_where('users', ['id_roles' => $role_id], $limit, $offset)->result_array();
|
||||
|
||||
foreach ($admins as &$admin) {
|
||||
$this->cast($admin);
|
||||
$admin['settings'] = $this->get_settings($admin['id']);
|
||||
}
|
||||
|
||||
return $admins;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the admin role ID.
|
||||
*
|
||||
* @return int Returns the role ID.
|
||||
*/
|
||||
public function get_admin_role_id(): int
|
||||
{
|
||||
$role = $this->db->get_where('roles', ['slug' => DB_SLUG_ADMIN])->row_array();
|
||||
|
||||
if (empty($role)) {
|
||||
throw new RuntimeException('The admin role was not found in the database.');
|
||||
}
|
||||
|
||||
return $role['id'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert a new admin into the database.
|
||||
*
|
||||
* @param array $admin Associative array with the admin data.
|
||||
*
|
||||
* @return int Returns the admin ID.
|
||||
*
|
||||
* @throws RuntimeException|Exception
|
||||
*/
|
||||
protected function insert(array $admin): int
|
||||
{
|
||||
$admin['id_roles'] = $this->get_admin_role_id();
|
||||
|
||||
$settings = $admin['settings'];
|
||||
|
||||
unset($admin['settings']);
|
||||
|
||||
$admin['create_datetime'] = date('Y-m-d H:i:s');
|
||||
$admin['update_datetime'] = date('Y-m-d H:i:s');
|
||||
|
||||
if (!$this->db->insert('users', $admin)) {
|
||||
throw new RuntimeException('Could not insert admin.');
|
||||
}
|
||||
|
||||
$admin['id'] = $this->db->insert_id();
|
||||
$settings['salt'] = generate_salt();
|
||||
$settings['password'] = hash_password($settings['salt'], $settings['password']);
|
||||
|
||||
$this->set_settings($admin['id'], $settings);
|
||||
|
||||
return $admin['id'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the admin settings.
|
||||
*
|
||||
* @param int $admin_id Admin ID.
|
||||
* @param array $settings Associative array with the settings data.
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function set_settings(int $admin_id, array $settings): void
|
||||
{
|
||||
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' => $admin_id])->num_rows();
|
||||
|
||||
if (!$count) {
|
||||
$this->db->insert('user_settings', ['id_users' => $admin_id]);
|
||||
}
|
||||
|
||||
foreach ($settings as $name => $value) {
|
||||
$this->set_setting($admin_id, $name, $value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the admin settings.
|
||||
*
|
||||
* @param int $admin_id Admin ID.
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function get_settings(int $admin_id): array
|
||||
{
|
||||
$settings = $this->db->get_where('user_settings', ['id_users' => $admin_id])->row_array();
|
||||
|
||||
unset($settings['id_users'], $settings['password'], $settings['salt']);
|
||||
|
||||
return $settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of an admin setting.
|
||||
*
|
||||
* @param int $admin_id Admin ID.
|
||||
* @param string $name Setting name.
|
||||
* @param mixed|null $value Setting value.
|
||||
*/
|
||||
public function set_setting(int $admin_id, string $name, mixed $value = null): void
|
||||
{
|
||||
if (!$this->db->update('user_settings', [$name => $value], ['id_users' => $admin_id])) {
|
||||
throw new RuntimeException('Could not set the new admin setting value: ' . $name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update an existing admin.
|
||||
*
|
||||
* @param array $admin Associative array with the admin data.
|
||||
*
|
||||
* @return int Returns the admin ID.
|
||||
*
|
||||
* @throws RuntimeException
|
||||
* @throws Exception
|
||||
*/
|
||||
protected function update(array $admin): int
|
||||
{
|
||||
$settings = $admin['settings'];
|
||||
|
||||
$settings['id_users'] = $admin['id'];
|
||||
|
||||
unset($admin['settings']);
|
||||
|
||||
if (!empty($settings['password'])) {
|
||||
$existing_settings = $this->db->get_where('user_settings', ['id_users' => $admin['id']])->row_array();
|
||||
|
||||
if (empty($existing_settings)) {
|
||||
throw new RuntimeException('No settings record found for admin with ID: ' . $admin['id']);
|
||||
}
|
||||
|
||||
if (empty($existing_settings['salt'])) {
|
||||
$existing_settings['salt'] = $settings['salt'] = generate_salt();
|
||||
}
|
||||
|
||||
$settings['password'] = hash_password($existing_settings['salt'], $settings['password']);
|
||||
}
|
||||
|
||||
$admin['update_datetime'] = date('Y-m-d H:i:s');
|
||||
|
||||
if (!$this->db->update('users', $admin, ['id' => $admin['id']])) {
|
||||
throw new RuntimeException('Could not update admin.');
|
||||
}
|
||||
|
||||
$this->set_settings($admin['id'], $settings);
|
||||
|
||||
return $admin['id'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove an existing admin from the database.
|
||||
*
|
||||
* @param int $admin_id Admin ID.
|
||||
*
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public function delete(int $admin_id): void
|
||||
{
|
||||
$role_id = $this->get_admin_role_id();
|
||||
|
||||
$count = $this->db->get_where('users', ['id_roles' => $role_id])->num_rows();
|
||||
|
||||
if ($count <= 1) {
|
||||
throw new RuntimeException('Record could not be deleted as the app requires at least one admin user.');
|
||||
}
|
||||
|
||||
$this->db->delete('users', ['id' => $admin_id]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a specific admin from the database.
|
||||
*
|
||||
* @param int $admin_id The ID of the record to be returned.
|
||||
*
|
||||
* @return array Returns an array with the admin data.
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function find(int $admin_id): array
|
||||
{
|
||||
$admin = $this->db->get_where('users', ['id' => $admin_id])->row_array();
|
||||
|
||||
if (!$admin) {
|
||||
throw new InvalidArgumentException('The provided admin ID was not found in the database: ' . $admin_id);
|
||||
}
|
||||
|
||||
$this->cast($admin);
|
||||
$admin['settings'] = $this->get_settings($admin['id']);
|
||||
|
||||
return $admin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a specific field value from the database.
|
||||
*
|
||||
* @param int $admin_id Admin ID.
|
||||
* @param string $field Name of the value to be returned.
|
||||
*
|
||||
* @return mixed Returns the selected admin value from the database.
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function value(int $admin_id, string $field): mixed
|
||||
{
|
||||
if (empty($field)) {
|
||||
throw new InvalidArgumentException('The field argument is cannot be empty.');
|
||||
}
|
||||
|
||||
if (empty($admin_id)) {
|
||||
throw new InvalidArgumentException('The admin ID argument cannot be empty.');
|
||||
}
|
||||
|
||||
// Check whether the admin exists.
|
||||
$query = $this->db->get_where('users', ['id' => $admin_id]);
|
||||
|
||||
if (!$query->num_rows()) {
|
||||
throw new InvalidArgumentException('The provided admin ID was not found in the database: ' . $admin_id);
|
||||
}
|
||||
|
||||
// Check if the required field is part of the admin data.
|
||||
$admin = $query->row_array();
|
||||
|
||||
$this->cast($admin);
|
||||
|
||||
if (!array_key_exists($field, $admin)) {
|
||||
throw new InvalidArgumentException('The requested field was not found in the admin data: ' . $field);
|
||||
}
|
||||
|
||||
return $admin[$field];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of an admin setting.
|
||||
*
|
||||
* @param int $admin_id Admin ID.
|
||||
* @param string $name Setting name.
|
||||
*
|
||||
* @return string Returns the value of the requested user setting.
|
||||
*/
|
||||
public function get_setting(int $admin_id, string $name): string
|
||||
{
|
||||
$settings = $this->db->get_where('user_settings', ['id_users' => $admin_id])->row_array();
|
||||
|
||||
if (!array_key_exists($name, $settings)) {
|
||||
throw new RuntimeException('The requested setting value was not found: ' . $admin_id);
|
||||
}
|
||||
|
||||
return $settings[$name];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the query builder interface, configured for use with the users (admin-filtered) table.
|
||||
*
|
||||
* @return CI_DB_query_builder
|
||||
*/
|
||||
public function query(): CI_DB_query_builder
|
||||
{
|
||||
$role_id = $this->get_admin_role_id();
|
||||
|
||||
return $this->db->from('users')->where('id_roles', $role_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Search admins 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 admins.
|
||||
*/
|
||||
public function search(string $keyword, ?int $limit = null, ?int $offset = null, ?string $order_by = null): array
|
||||
{
|
||||
$role_id = $this->get_admin_role_id();
|
||||
|
||||
$admins = $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 ($admins as &$admin) {
|
||||
$this->cast($admin);
|
||||
$admin['settings'] = $this->get_settings($admin['id']);
|
||||
}
|
||||
|
||||
return $admins;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load related resources to an admin.
|
||||
*
|
||||
* @param array $admin Associative array with the admin data.
|
||||
* @param array $resources Resource names to be attached.
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function load(array &$admin, array $resources)
|
||||
{
|
||||
// Admins do not currently have any related resources (settings are already part of the admins).
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the database admin record to the equivalent API resource.
|
||||
*
|
||||
* @param array $admin Admin data.
|
||||
*/
|
||||
public function api_encode(array &$admin): void
|
||||
{
|
||||
$encoded_resource = [
|
||||
'id' => array_key_exists('id', $admin) ? (int) $admin['id'] : null,
|
||||
'firstName' => $admin['first_name'],
|
||||
'lastName' => $admin['last_name'],
|
||||
'email' => $admin['email'],
|
||||
'mobile' => $admin['mobile_number'],
|
||||
'phone' => $admin['phone_number'],
|
||||
'address' => $admin['address'],
|
||||
'city' => $admin['city'],
|
||||
'state' => $admin['state'],
|
||||
'zip' => $admin['zip_code'],
|
||||
'notes' => $admin['notes'],
|
||||
'timezone' => $admin['timezone'],
|
||||
'language' => $admin['language'],
|
||||
'ldapDn' => $admin['ldap_dn'],
|
||||
'settings' => [
|
||||
'username' => $admin['settings']['username'],
|
||||
'notifications' => filter_var($admin['settings']['notifications'], FILTER_VALIDATE_BOOLEAN),
|
||||
'calendarView' => $admin['settings']['calendar_view'],
|
||||
],
|
||||
];
|
||||
|
||||
$admin = $encoded_resource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the API resource to the equivalent database admin record.
|
||||
*
|
||||
* @param array $admin API resource.
|
||||
* @param array|null $base Base admin data to be overwritten with the provided values (useful for updates).
|
||||
*/
|
||||
public function api_decode(array &$admin, ?array $base = null): void
|
||||
{
|
||||
$decoded_resource = $base ?? [];
|
||||
|
||||
if (array_key_exists('id', $admin)) {
|
||||
$decoded_resource['id'] = $admin['id'];
|
||||
}
|
||||
|
||||
if (array_key_exists('firstName', $admin)) {
|
||||
$decoded_resource['first_name'] = $admin['firstName'];
|
||||
}
|
||||
|
||||
if (array_key_exists('lastName', $admin)) {
|
||||
$decoded_resource['last_name'] = $admin['lastName'];
|
||||
}
|
||||
|
||||
if (array_key_exists('email', $admin)) {
|
||||
$decoded_resource['email'] = $admin['email'];
|
||||
}
|
||||
|
||||
if (array_key_exists('mobile', $admin)) {
|
||||
$decoded_resource['mobile_number'] = $admin['mobile'];
|
||||
}
|
||||
|
||||
if (array_key_exists('phone', $admin)) {
|
||||
$decoded_resource['phone_number'] = $admin['phone'];
|
||||
}
|
||||
|
||||
if (array_key_exists('address', $admin)) {
|
||||
$decoded_resource['address'] = $admin['address'];
|
||||
}
|
||||
|
||||
if (array_key_exists('city', $admin)) {
|
||||
$decoded_resource['city'] = $admin['city'];
|
||||
}
|
||||
|
||||
if (array_key_exists('state', $admin)) {
|
||||
$decoded_resource['state'] = $admin['state'];
|
||||
}
|
||||
|
||||
if (array_key_exists('zip', $admin)) {
|
||||
$decoded_resource['zip_code'] = $admin['zip'];
|
||||
}
|
||||
|
||||
if (array_key_exists('notes', $admin)) {
|
||||
$decoded_resource['notes'] = $admin['notes'];
|
||||
}
|
||||
|
||||
if (array_key_exists('timezone', $admin)) {
|
||||
$decoded_resource['timezone'] = $admin['timezone'];
|
||||
}
|
||||
|
||||
if (array_key_exists('language', $admin)) {
|
||||
$decoded_resource['language'] = $admin['language'];
|
||||
}
|
||||
|
||||
if (array_key_exists('ldapDn', $admin)) {
|
||||
$decoded_resource['ldap_dn'] = $admin['ldapDn'];
|
||||
}
|
||||
|
||||
if (array_key_exists('settings', $admin)) {
|
||||
if (empty($decoded_resource['settings'])) {
|
||||
$decoded_resource['settings'] = [];
|
||||
}
|
||||
|
||||
if (array_key_exists('username', $admin['settings'])) {
|
||||
$decoded_resource['settings']['username'] = $admin['settings']['username'];
|
||||
}
|
||||
|
||||
if (array_key_exists('password', $admin['settings'])) {
|
||||
$decoded_resource['settings']['password'] = $admin['settings']['password'];
|
||||
}
|
||||
|
||||
if (array_key_exists('notifications', $admin['settings'])) {
|
||||
$decoded_resource['settings']['notifications'] = filter_var(
|
||||
$admin['settings']['notifications'],
|
||||
FILTER_VALIDATE_BOOLEAN,
|
||||
);
|
||||
}
|
||||
|
||||
if (array_key_exists('calendarView', $admin['settings'])) {
|
||||
$decoded_resource['settings']['calendar_view'] = $admin['settings']['calendarView'];
|
||||
}
|
||||
}
|
||||
|
||||
$admin = $decoded_resource;
|
||||
}
|
||||
}
|
||||
648
application/models/Appointments_model.php
Normal file
648
application/models/Appointments_model.php
Normal file
@@ -0,0 +1,648 @@
|
||||
<?php defined('BASEPATH') or exit('No direct script access allowed');
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Easy!Appointments - Online Appointment Scheduler
|
||||
*
|
||||
* @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
|
||||
* ---------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Appointments model.
|
||||
*
|
||||
* @package Models
|
||||
*/
|
||||
class Appointments_model extends EA_Model
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected array $casts = [
|
||||
'id' => 'integer',
|
||||
'is_unavailability' => 'boolean',
|
||||
'id_users_provider' => 'integer',
|
||||
'id_users_customer' => 'integer',
|
||||
'id_services' => 'integer',
|
||||
];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected array $api_resource = [
|
||||
'id' => 'id',
|
||||
'book' => 'book_datetime',
|
||||
'start' => 'start_datetime',
|
||||
'end' => 'end_datetime',
|
||||
'location' => 'location',
|
||||
'color' => 'color',
|
||||
'status' => 'status',
|
||||
'notes' => 'notes',
|
||||
'hash' => 'hash',
|
||||
'serviceId' => 'id_services',
|
||||
'providerId' => 'id_users_provider',
|
||||
'customerId' => 'id_users_customer',
|
||||
'googleCalendarId' => 'id_google_calendar',
|
||||
'caldavCalendarId' => 'id_caldav_calendar',
|
||||
];
|
||||
|
||||
/**
|
||||
* Save (insert or update) an appointment.
|
||||
*
|
||||
* @param array $appointment Associative array with the appointment data.
|
||||
*
|
||||
* @return int Returns the appointment ID.
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function save(array $appointment): int
|
||||
{
|
||||
$this->validate($appointment);
|
||||
|
||||
if (empty($appointment['id'])) {
|
||||
return $this->insert($appointment);
|
||||
} else {
|
||||
return $this->update($appointment);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the appointment data.
|
||||
*
|
||||
* @param array $appointment Associative array with the appointment data.
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function validate(array $appointment): void
|
||||
{
|
||||
// If an appointment ID is provided then check whether the record really exists in the database.
|
||||
if (!empty($appointment['id'])) {
|
||||
$count = $this->db->get_where('appointments', ['id' => $appointment['id']])->num_rows();
|
||||
|
||||
if (!$count) {
|
||||
throw new InvalidArgumentException(
|
||||
'The provided appointment ID does not exist in the database: ' . $appointment['id'],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure all required fields are provided.
|
||||
|
||||
$require_notes = filter_var(setting('require_notes'), FILTER_VALIDATE_BOOLEAN);
|
||||
|
||||
if (
|
||||
empty($appointment['start_datetime']) ||
|
||||
empty($appointment['end_datetime']) ||
|
||||
empty($appointment['id_services']) ||
|
||||
empty($appointment['id_users_provider']) ||
|
||||
empty($appointment['id_users_customer']) ||
|
||||
(empty($appointment['notes']) && $require_notes)
|
||||
) {
|
||||
throw new InvalidArgumentException('Not all required fields are provided: ' . print_r($appointment, true));
|
||||
}
|
||||
|
||||
// Make sure that the provided appointment date time values are valid.
|
||||
if (!validate_datetime($appointment['start_datetime'])) {
|
||||
throw new InvalidArgumentException('The appointment start date time is invalid.');
|
||||
}
|
||||
|
||||
if (!validate_datetime($appointment['end_datetime'])) {
|
||||
throw new InvalidArgumentException('The appointment end date time is invalid.');
|
||||
}
|
||||
|
||||
// Make the appointment lasts longer than the minimum duration (in minutes).
|
||||
$diff = (strtotime($appointment['end_datetime']) - strtotime($appointment['start_datetime'])) / 60;
|
||||
|
||||
if ($diff < EVENT_MINIMUM_DURATION) {
|
||||
throw new InvalidArgumentException(
|
||||
'The appointment duration cannot be less than ' . EVENT_MINIMUM_DURATION . ' minutes.',
|
||||
);
|
||||
}
|
||||
|
||||
// Make sure the provider ID really exists in the database.
|
||||
$count = $this->db
|
||||
->select()
|
||||
->from('users')
|
||||
->join('roles', 'roles.id = users.id_roles', 'inner')
|
||||
->where('users.id', $appointment['id_users_provider'])
|
||||
->where('roles.slug', DB_SLUG_PROVIDER)
|
||||
->get()
|
||||
->num_rows();
|
||||
|
||||
if (!$count) {
|
||||
throw new InvalidArgumentException(
|
||||
'The appointment provider ID was not found in the database: ' . $appointment['id_users_provider'],
|
||||
);
|
||||
}
|
||||
|
||||
if (!filter_var($appointment['is_unavailability'], FILTER_VALIDATE_BOOLEAN)) {
|
||||
// Make sure the customer ID really exists in the database.
|
||||
$count = $this->db
|
||||
->select()
|
||||
->from('users')
|
||||
->join('roles', 'roles.id = users.id_roles', 'inner')
|
||||
->where('users.id', $appointment['id_users_customer'])
|
||||
->where('roles.slug', DB_SLUG_CUSTOMER)
|
||||
->get()
|
||||
->num_rows();
|
||||
|
||||
if (!$count) {
|
||||
throw new InvalidArgumentException(
|
||||
'The appointment customer ID was not found in the database: ' . $appointment['id_users_customer'],
|
||||
);
|
||||
}
|
||||
|
||||
// Make sure the service ID really exists in the database.
|
||||
$count = $this->db->get_where('services', ['id' => $appointment['id_services']])->num_rows();
|
||||
|
||||
if (!$count) {
|
||||
throw new InvalidArgumentException('Appointment service id is invalid.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all appointments 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 appointments.
|
||||
*/
|
||||
public function get(
|
||||
array|string|null $where = null,
|
||||
?int $limit = null,
|
||||
?int $offset = null,
|
||||
?string $order_by = null,
|
||||
): array {
|
||||
if ($where !== null) {
|
||||
$this->db->where($where);
|
||||
}
|
||||
|
||||
if ($order_by) {
|
||||
$this->db->order_by($order_by);
|
||||
}
|
||||
|
||||
$appointments = $this->db
|
||||
->get_where('appointments', ['is_unavailability' => false], $limit, $offset)
|
||||
->result_array();
|
||||
|
||||
foreach ($appointments as &$appointment) {
|
||||
$this->cast($appointment);
|
||||
}
|
||||
|
||||
return $appointments;
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert a new appointment into the database.
|
||||
*
|
||||
* @param array $appointment Associative array with the appointment data.
|
||||
*
|
||||
* @return int Returns the appointment ID.
|
||||
*
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
protected function insert(array $appointment): int
|
||||
{
|
||||
$appointment['book_datetime'] = date('Y-m-d H:i:s');
|
||||
$appointment['create_datetime'] = date('Y-m-d H:i:s');
|
||||
$appointment['update_datetime'] = date('Y-m-d H:i:s');
|
||||
$appointment['hash'] = random_string('alnum', 12);
|
||||
|
||||
if (!$this->db->insert('appointments', $appointment)) {
|
||||
throw new RuntimeException('Could not insert appointment.');
|
||||
}
|
||||
|
||||
return $this->db->insert_id();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update an existing appointment.
|
||||
*
|
||||
* @param array $appointment Associative array with the appointment data.
|
||||
*
|
||||
* @return int Returns the appointment ID.
|
||||
*
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
protected function update(array $appointment): int
|
||||
{
|
||||
$appointment['update_datetime'] = date('Y-m-d H:i:s');
|
||||
|
||||
if (!$this->db->update('appointments', $appointment, ['id' => $appointment['id']])) {
|
||||
throw new RuntimeException('Could not update appointment record.');
|
||||
}
|
||||
|
||||
return $appointment['id'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove an existing appointment from the database.
|
||||
*
|
||||
* @param int $appointment_id Appointment ID.
|
||||
*
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public function delete(int $appointment_id): void
|
||||
{
|
||||
$this->db->delete('appointments', ['id' => $appointment_id]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a specific appointment from the database.
|
||||
*
|
||||
* @param int $appointment_id The ID of the record to be returned.
|
||||
*
|
||||
* @return array Returns an array with the appointment data.
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function find(int $appointment_id): array
|
||||
{
|
||||
$appointment = $this->db->get_where('appointments', ['id' => $appointment_id])->row_array();
|
||||
|
||||
if (!$appointment) {
|
||||
throw new InvalidArgumentException(
|
||||
'The provided appointment ID was not found in the database: ' . $appointment_id,
|
||||
);
|
||||
}
|
||||
|
||||
$this->cast($appointment);
|
||||
|
||||
return $appointment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a specific field value from the database.
|
||||
*
|
||||
* @param int $appointment_id Appointment ID.
|
||||
* @param string $field Name of the value to be returned.
|
||||
*
|
||||
* @return mixed Returns the selected appointment value from the database.
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function value(int $appointment_id, string $field): mixed
|
||||
{
|
||||
if (empty($field)) {
|
||||
throw new InvalidArgumentException('The field argument is cannot be empty.');
|
||||
}
|
||||
|
||||
if (empty($appointment_id)) {
|
||||
throw new InvalidArgumentException('The appointment ID argument cannot be empty.');
|
||||
}
|
||||
|
||||
// Check whether the appointment exists.
|
||||
$query = $this->db->get_where('appointments', ['id' => $appointment_id]);
|
||||
|
||||
if (!$query->num_rows()) {
|
||||
throw new InvalidArgumentException(
|
||||
'The provided appointment ID was not found in the database: ' . $appointment_id,
|
||||
);
|
||||
}
|
||||
|
||||
// Check if the required field is part of the appointment data.
|
||||
$appointment = $query->row_array();
|
||||
|
||||
$this->cast($appointment);
|
||||
|
||||
if (!array_key_exists($field, $appointment)) {
|
||||
throw new InvalidArgumentException('The requested field was not found in the appointment data: ' . $field);
|
||||
}
|
||||
|
||||
return $appointment[$field];
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all the Google Calendar event IDs from appointment records.
|
||||
*
|
||||
* @param int $provider_id Matching provider ID.
|
||||
*/
|
||||
public function clear_google_sync_ids(int $provider_id): void
|
||||
{
|
||||
$this->db->update('appointments', ['id_google_calendar' => null], ['id_users_provider' => $provider_id]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all the Google Calendar event IDs from appointment records.
|
||||
*
|
||||
* @param int $provider_id Matching provider ID.
|
||||
*/
|
||||
public function clear_caldav_sync_ids(int $provider_id): void
|
||||
{
|
||||
$this->db->update('appointments', ['id_caldav_calendar' => null], ['id_users_provider' => $provider_id]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes recurring CalDAV events for the provided date period.
|
||||
*
|
||||
* @param string $start_date_time
|
||||
* @param string $end_date_time
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function delete_caldav_recurring_events(string $start_date_time, string $end_date_time): void
|
||||
{
|
||||
$this->db
|
||||
->where('start_datetime >=', $start_date_time)
|
||||
->where('end_datetime <=', $end_date_time)
|
||||
->like('id_caldav_calendar', '%RECURRENCE%')
|
||||
->delete('appointments');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the attendants number for the requested period.
|
||||
*
|
||||
* @param DateTime $start Period start.
|
||||
* @param DateTime $end Period end.
|
||||
* @param int $service_id Service ID.
|
||||
* @param int $provider_id Provider ID.
|
||||
* @param int|null $exclude_appointment_id Exclude an appointment from the result set.
|
||||
*
|
||||
* @return int Returns the number of appointments that match the provided criteria.
|
||||
*/
|
||||
public function get_attendants_number_for_period(
|
||||
DateTime $start,
|
||||
DateTime $end,
|
||||
int $service_id,
|
||||
int $provider_id,
|
||||
?int $exclude_appointment_id = null,
|
||||
): int {
|
||||
if ($exclude_appointment_id) {
|
||||
$this->db->where('id !=', $exclude_appointment_id);
|
||||
}
|
||||
|
||||
$result = $this->db
|
||||
->select('count(*) AS attendants_number')
|
||||
->from('appointments')
|
||||
->group_start()
|
||||
->group_start()
|
||||
->where('start_datetime <=', $start->format('Y-m-d H:i:s'))
|
||||
->where('end_datetime >', $start->format('Y-m-d H:i:s'))
|
||||
->group_end()
|
||||
->or_group_start()
|
||||
->where('start_datetime <', $end->format('Y-m-d H:i:s'))
|
||||
->where('end_datetime >=', $end->format('Y-m-d H:i:s'))
|
||||
->group_end()
|
||||
->group_end()
|
||||
->where('id_services', $service_id)
|
||||
->where('id_users_provider', $provider_id)
|
||||
->get()
|
||||
->row_array();
|
||||
|
||||
return $result['attendants_number'];
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Returns the number of the other service attendants number for the provided time slot.
|
||||
*
|
||||
* @param DateTime $start Period start.
|
||||
* @param DateTime $end Period end.
|
||||
* @param int $service_id Service ID.
|
||||
* @param int $provider_id Provider ID.
|
||||
* @param int|null $exclude_appointment_id Exclude an appointment from the result set.
|
||||
*
|
||||
* @return int Returns the number of appointments that match the provided criteria.
|
||||
*/
|
||||
public function get_other_service_attendants_number(
|
||||
DateTime $start,
|
||||
DateTime $end,
|
||||
int $service_id,
|
||||
int $provider_id,
|
||||
?int $exclude_appointment_id = null,
|
||||
): int {
|
||||
if ($exclude_appointment_id) {
|
||||
$this->db->where('id !=', $exclude_appointment_id);
|
||||
}
|
||||
|
||||
$result = $this->db
|
||||
->select('count(*) AS attendants_number')
|
||||
->from('appointments')
|
||||
->group_start()
|
||||
->group_start()
|
||||
->where('start_datetime <=', $start->format('Y-m-d H:i:s'))
|
||||
->where('end_datetime >', $start->format('Y-m-d H:i:s'))
|
||||
->group_end()
|
||||
->or_group_start()
|
||||
->where('start_datetime <', $end->format('Y-m-d H:i:s'))
|
||||
->where('end_datetime >=', $end->format('Y-m-d H:i:s'))
|
||||
->group_end()
|
||||
->group_end()
|
||||
->where('id_services !=', $service_id)
|
||||
->where('id_users_provider', $provider_id)
|
||||
->get()
|
||||
->row_array();
|
||||
|
||||
return $result['attendants_number'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the query builder interface, configured for use with the appointments table.
|
||||
*
|
||||
* @return CI_DB_query_builder
|
||||
*/
|
||||
public function query(): CI_DB_query_builder
|
||||
{
|
||||
return $this->db->from('appointments');
|
||||
}
|
||||
|
||||
/**
|
||||
* Search appointments 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 appointments.
|
||||
*/
|
||||
public function search(string $keyword, ?int $limit = null, ?int $offset = null, ?string $order_by = null): array
|
||||
{
|
||||
$appointments = $this->db
|
||||
->select('appointments.*')
|
||||
->from('appointments')
|
||||
->join('services', 'services.id = appointments.id_services', 'left')
|
||||
->join('users AS providers', 'providers.id = appointments.id_users_provider', 'inner')
|
||||
->join('users AS customers', 'customers.id = appointments.id_users_customer', 'left')
|
||||
->where('is_unavailability', false)
|
||||
->group_start()
|
||||
->like('appointments.start_datetime', $keyword)
|
||||
->or_like('appointments.end_datetime', $keyword)
|
||||
->or_like('appointments.location', $keyword)
|
||||
->or_like('appointments.hash', $keyword)
|
||||
->or_like('appointments.notes', $keyword)
|
||||
->or_like('services.name', $keyword)
|
||||
->or_like('services.description', $keyword)
|
||||
->or_like('providers.first_name', $keyword)
|
||||
->or_like('providers.last_name', $keyword)
|
||||
->or_like('providers.email', $keyword)
|
||||
->or_like('providers.phone_number', $keyword)
|
||||
->or_like('customers.first_name', $keyword)
|
||||
->or_like('customers.last_name', $keyword)
|
||||
->or_like('customers.email', $keyword)
|
||||
->or_like('customers.phone_number', $keyword)
|
||||
->group_end()
|
||||
->limit($limit)
|
||||
->offset($offset)
|
||||
->order_by($order_by)
|
||||
->get()
|
||||
->result_array();
|
||||
|
||||
foreach ($appointments as &$appointment) {
|
||||
$this->cast($appointment);
|
||||
}
|
||||
|
||||
return $appointments;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load related resources to an appointment.
|
||||
*
|
||||
* @param array $appointment Associative array with the appointment data.
|
||||
* @param array $resources Resource names to be attached ("service", "provider", "customer" supported).
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function load(array &$appointment, array $resources): void
|
||||
{
|
||||
if (empty($appointment) || empty($resources)) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($resources as $resource) {
|
||||
switch ($resource) {
|
||||
case 'service':
|
||||
$appointment['service'] = $this->db
|
||||
->get_where('services', [
|
||||
'id' => $appointment['id_services'] ?? ($appointment['serviceId'] ?? null),
|
||||
])
|
||||
->row_array();
|
||||
break;
|
||||
|
||||
case 'provider':
|
||||
$appointment['provider'] = $this->db
|
||||
->get_where('users', [
|
||||
'id' => $appointment['id_users_provider'] ?? ($appointment['providerId'] ?? null),
|
||||
])
|
||||
->row_array();
|
||||
break;
|
||||
|
||||
case 'customer':
|
||||
$appointment['customer'] = $this->db
|
||||
->get_where('users', [
|
||||
'id' => $appointment['id_users_customer'] ?? ($appointment['customerId'] ?? null),
|
||||
])
|
||||
->row_array();
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new InvalidArgumentException(
|
||||
'The requested appointment relation is not supported: ' . $resource,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the database appointment record to the equivalent API resource.
|
||||
*
|
||||
* @param array $appointment Appointment data.
|
||||
*/
|
||||
public function api_encode(array &$appointment): void
|
||||
{
|
||||
$encoded_resource = [
|
||||
'id' => array_key_exists('id', $appointment) ? (int) $appointment['id'] : null,
|
||||
'book' => $appointment['book_datetime'],
|
||||
'start' => $appointment['start_datetime'],
|
||||
'end' => $appointment['end_datetime'],
|
||||
'hash' => $appointment['hash'],
|
||||
'color' => $appointment['color'],
|
||||
'status' => $appointment['status'],
|
||||
'location' => $appointment['location'],
|
||||
'notes' => $appointment['notes'],
|
||||
'customerId' => $appointment['id_users_customer'] !== null ? (int) $appointment['id_users_customer'] : null,
|
||||
'providerId' => $appointment['id_users_provider'] !== null ? (int) $appointment['id_users_provider'] : null,
|
||||
'serviceId' => $appointment['id_services'] !== null ? (int) $appointment['id_services'] : null,
|
||||
'googleCalendarId' =>
|
||||
$appointment['id_google_calendar'] !== null ? $appointment['id_google_calendar'] : null,
|
||||
'caldavCalendarId' =>
|
||||
$appointment['id_caldav_calendar'] !== null ? $appointment['id_caldav_calendar'] : null,
|
||||
];
|
||||
|
||||
$appointment = $encoded_resource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the API resource to the equivalent database appointment record.
|
||||
*
|
||||
* @param array $appointment API resource.
|
||||
* @param array|null $base Base appointment data to be overwritten with the provided values (useful for updates).
|
||||
*/
|
||||
public function api_decode(array &$appointment, ?array $base = null): void
|
||||
{
|
||||
$decoded_request = $base ?: [];
|
||||
|
||||
if (array_key_exists('id', $appointment)) {
|
||||
$decoded_request['id'] = $appointment['id'];
|
||||
}
|
||||
|
||||
if (array_key_exists('book', $appointment)) {
|
||||
$decoded_request['book_datetime'] = $appointment['book'];
|
||||
}
|
||||
|
||||
if (array_key_exists('start', $appointment)) {
|
||||
$decoded_request['start_datetime'] = $appointment['start'];
|
||||
}
|
||||
|
||||
if (array_key_exists('end', $appointment)) {
|
||||
$decoded_request['end_datetime'] = $appointment['end'];
|
||||
}
|
||||
|
||||
if (array_key_exists('hash', $appointment)) {
|
||||
$decoded_request['hash'] = $appointment['hash'];
|
||||
}
|
||||
|
||||
if (array_key_exists('location', $appointment)) {
|
||||
$decoded_request['location'] = $appointment['location'];
|
||||
}
|
||||
|
||||
if (array_key_exists('status', $appointment)) {
|
||||
$decoded_request['status'] = $appointment['status'];
|
||||
}
|
||||
|
||||
if (array_key_exists('notes', $appointment)) {
|
||||
$decoded_request['notes'] = $appointment['notes'];
|
||||
}
|
||||
|
||||
if (array_key_exists('customerId', $appointment)) {
|
||||
$decoded_request['id_users_customer'] = $appointment['customerId'];
|
||||
}
|
||||
|
||||
if (array_key_exists('providerId', $appointment)) {
|
||||
$decoded_request['id_users_provider'] = $appointment['providerId'];
|
||||
}
|
||||
|
||||
if (array_key_exists('serviceId', $appointment)) {
|
||||
$decoded_request['id_services'] = $appointment['serviceId'];
|
||||
}
|
||||
|
||||
if (array_key_exists('googleCalendarId', $appointment)) {
|
||||
$decoded_request['id_google_calendar'] = $appointment['googleCalendarId'];
|
||||
}
|
||||
|
||||
if (array_key_exists('caldavCalendarId', $appointment)) {
|
||||
$decoded_request['id_caldav_calendar'] = $appointment['caldavCalendarId'];
|
||||
}
|
||||
|
||||
$decoded_request['is_unavailability'] = false;
|
||||
|
||||
$appointment = $decoded_request;
|
||||
}
|
||||
}
|
||||
413
application/models/Blocked_periods_model.php
Normal file
413
application/models/Blocked_periods_model.php
Normal file
@@ -0,0 +1,413 @@
|
||||
<?php defined('BASEPATH') or exit('No direct script access allowed');
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Easy!Appointments - Online Appointment Scheduler
|
||||
*
|
||||
* @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.5.0
|
||||
* ---------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Blocked-Periods model.
|
||||
*
|
||||
* Handles all the database operations of the blocked-period resource.
|
||||
*
|
||||
* @package Models
|
||||
*/
|
||||
class Blocked_periods_model extends EA_Model
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected array $casts = [
|
||||
'id' => 'integer',
|
||||
];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected array $api_resource = [
|
||||
'id' => 'id',
|
||||
'name' => 'name',
|
||||
'start' => 'start_datetime',
|
||||
'end' => 'end_datetime',
|
||||
'notes' => 'notes',
|
||||
];
|
||||
|
||||
/**
|
||||
* Save (insert or update) a blocked-period.
|
||||
*
|
||||
* @param array $blocked_period Associative array with the blocked-period data.
|
||||
*
|
||||
* @return int Returns the blocked-period ID.
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
* @throws Exception
|
||||
*/
|
||||
public function save(array $blocked_period): int
|
||||
{
|
||||
$this->validate($blocked_period);
|
||||
|
||||
if (empty($blocked_period['id'])) {
|
||||
return $this->insert($blocked_period);
|
||||
} else {
|
||||
return $this->update($blocked_period);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the blocked-period data.
|
||||
*
|
||||
* @param array $blocked_period Associative array with the blocked-period data.
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
* @throws Exception
|
||||
*/
|
||||
public function validate(array $blocked_period): void
|
||||
{
|
||||
// If a blocked-period ID is provided then check whether the record really exists in the database.
|
||||
if (!empty($blocked_period['id'])) {
|
||||
$count = $this->db->get_where('blocked_periods', ['id' => $blocked_period['id']])->num_rows();
|
||||
|
||||
if (!$count) {
|
||||
throw new InvalidArgumentException(
|
||||
'The provided blocked-period ID does not exist in the database: ' . $blocked_period['id'],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure all required fields are provided.
|
||||
if (
|
||||
empty($blocked_period['name']) ||
|
||||
empty($blocked_period['start_datetime']) ||
|
||||
empty($blocked_period['end_datetime'])
|
||||
) {
|
||||
throw new InvalidArgumentException(
|
||||
'Not all required fields are provided: ' . print_r($blocked_period, true),
|
||||
);
|
||||
}
|
||||
|
||||
// Make sure that the start date time is before the end.
|
||||
$start_date_time_object = new DateTime($blocked_period['start_datetime']);
|
||||
$end_date_time_object = new DateTime($blocked_period['end_datetime']);
|
||||
|
||||
if ($start_date_time_object >= $end_date_time_object) {
|
||||
throw new InvalidArgumentException('The start must be before the end date time value.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert a new blocked-period into the database.
|
||||
*
|
||||
* @param array $blocked_period Associative array with the blocked-period data.
|
||||
*
|
||||
* @return int Returns the blocked-period ID.
|
||||
*
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
protected function insert(array $blocked_period): int
|
||||
{
|
||||
$blocked_period['create_datetime'] = date('Y-m-d H:i:s');
|
||||
$blocked_period['update_datetime'] = date('Y-m-d H:i:s');
|
||||
|
||||
if (!$this->db->insert('blocked_periods', $blocked_period)) {
|
||||
throw new RuntimeException('Could not insert blocked-period.');
|
||||
}
|
||||
|
||||
return $this->db->insert_id();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update an existing blocked-period.
|
||||
*
|
||||
* @param array $blocked_period Associative array with the blocked-period data.
|
||||
*
|
||||
* @return int Returns the blocked-period ID.
|
||||
*
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
protected function update(array $blocked_period): int
|
||||
{
|
||||
$blocked_period['update_datetime'] = date('Y-m-d H:i:s');
|
||||
|
||||
if (!$this->db->update('blocked_periods', $blocked_period, ['id' => $blocked_period['id']])) {
|
||||
throw new RuntimeException('Could not update blocked periods.');
|
||||
}
|
||||
|
||||
return $blocked_period['id'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove an existing blocked-period from the database.
|
||||
*
|
||||
* @param int $blocked_period_id Blocked period ID.
|
||||
*
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public function delete(int $blocked_period_id): void
|
||||
{
|
||||
$this->db->delete('blocked_periods', ['id' => $blocked_period_id]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a specific blocked-period from the database.
|
||||
*
|
||||
* @param int $blocked_period_id The ID of the record to be returned.
|
||||
*
|
||||
* @return array Returns an array with the blocked-period data.
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function find(int $blocked_period_id): array
|
||||
{
|
||||
$blocked_period = $this->db->get_where('blocked_periods', ['id' => $blocked_period_id])->row_array();
|
||||
|
||||
if (!$blocked_period) {
|
||||
throw new InvalidArgumentException(
|
||||
'The provided blocked-period ID was not found in the database: ' . $blocked_period_id,
|
||||
);
|
||||
}
|
||||
|
||||
$this->cast($blocked_period);
|
||||
|
||||
return $blocked_period;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a specific field value from the database.
|
||||
*
|
||||
* @param int $blocked_period_id Blocked period ID.
|
||||
* @param string $field Name of the value to be returned.
|
||||
*
|
||||
* @return mixed Returns the selected blocked-period value from the database.
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function value(int $blocked_period_id, string $field): mixed
|
||||
{
|
||||
if (empty($field)) {
|
||||
throw new InvalidArgumentException('The field argument is cannot be empty.');
|
||||
}
|
||||
|
||||
if (empty($blocked_period_id)) {
|
||||
throw new InvalidArgumentException('The blocked-period ID argument cannot be empty.');
|
||||
}
|
||||
|
||||
// Check whether the service exists.
|
||||
$query = $this->db->get_where('blocked_periods', ['id' => $blocked_period_id]);
|
||||
|
||||
if (!$query->num_rows()) {
|
||||
throw new InvalidArgumentException(
|
||||
'The provided blocked-period ID was not found in the database: ' . $blocked_period_id,
|
||||
);
|
||||
}
|
||||
|
||||
// Check if the required field is part of the blocked-period data.
|
||||
$blocked_period = $query->row_array();
|
||||
|
||||
$this->cast($blocked_period);
|
||||
|
||||
if (!array_key_exists($field, $blocked_period)) {
|
||||
throw new InvalidArgumentException(
|
||||
'The requested field was not found in the blocked-period data: ' . $field,
|
||||
);
|
||||
}
|
||||
|
||||
return $blocked_period[$field];
|
||||
}
|
||||
|
||||
/**
|
||||
* Search blocked periods 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 blocked periods.
|
||||
*/
|
||||
public function search(string $keyword, ?int $limit = null, ?int $offset = null, ?string $order_by = null): array
|
||||
{
|
||||
$blocked_periods = $this->db
|
||||
->select()
|
||||
->from('blocked_periods')
|
||||
->group_start()
|
||||
->like('name', $keyword)
|
||||
->or_like('notes', $keyword)
|
||||
->group_end()
|
||||
->limit($limit)
|
||||
->offset($offset)
|
||||
->order_by($order_by)
|
||||
->get()
|
||||
->result_array();
|
||||
|
||||
foreach ($blocked_periods as &$blocked_period) {
|
||||
$this->cast($blocked_period);
|
||||
}
|
||||
|
||||
return $blocked_periods;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all services 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 blocked periods.
|
||||
*/
|
||||
public function get(
|
||||
array|string|null $where = null,
|
||||
?int $limit = null,
|
||||
?int $offset = null,
|
||||
?string $order_by = null,
|
||||
): array {
|
||||
if ($where !== null) {
|
||||
$this->db->where($where);
|
||||
}
|
||||
|
||||
if ($order_by !== null) {
|
||||
$this->db->order_by($order_by);
|
||||
}
|
||||
|
||||
$blocked_periods = $this->db->get('blocked_periods', $limit, $offset)->result_array();
|
||||
|
||||
foreach ($blocked_periods as &$blocked_period) {
|
||||
$this->cast($blocked_period);
|
||||
}
|
||||
|
||||
return $blocked_periods;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load related resources to a blocked-period.
|
||||
*
|
||||
* @param array $blocked_period Associative array with the blocked-period data.
|
||||
* @param array $resources Resource names to be attached.
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function load(array &$blocked_period, array $resources)
|
||||
{
|
||||
// Blocked periods do not currently have any related resources.
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the database blocked-period record to the equivalent API resource.
|
||||
*
|
||||
* @param array $blocked_period Blocked period data.
|
||||
*/
|
||||
public function api_encode(array &$blocked_period): void
|
||||
{
|
||||
$encoded_resource = [
|
||||
'id' => array_key_exists('id', $blocked_period) ? (int) $blocked_period['id'] : null,
|
||||
'name' => $blocked_period['name'],
|
||||
'start' => array_key_exists('start_datetime', $blocked_period) ? $blocked_period['start_datetime'] : null,
|
||||
'end' => array_key_exists('end_datetime', $blocked_period) ? $blocked_period['end_datetime'] : null,
|
||||
'notes' => array_key_exists('notes', $blocked_period) ? $blocked_period['notes'] : null,
|
||||
];
|
||||
|
||||
$blocked_period = $encoded_resource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the API resource to the equivalent database blocked-period record.
|
||||
*
|
||||
* @param array $blocked_period API resource.
|
||||
* @param array|null $base Base blocked-period data to be overwritten with the provided values (useful for updates).
|
||||
*/
|
||||
public function api_decode(array &$blocked_period, ?array $base = null): void
|
||||
{
|
||||
$decoded_resource = $base ?: [];
|
||||
|
||||
if (array_key_exists('id', $blocked_period)) {
|
||||
$decoded_resource['id'] = $blocked_period['id'];
|
||||
}
|
||||
|
||||
if (array_key_exists('name', $blocked_period)) {
|
||||
$decoded_resource['name'] = $blocked_period['name'];
|
||||
}
|
||||
|
||||
if (array_key_exists('start', $blocked_period)) {
|
||||
$decoded_resource['start_datetime'] = $blocked_period['start'];
|
||||
}
|
||||
|
||||
if (array_key_exists('end', $blocked_period)) {
|
||||
$decoded_resource['end_datetime'] = $blocked_period['end'];
|
||||
}
|
||||
|
||||
if (array_key_exists('notes', $blocked_period)) {
|
||||
$decoded_resource['notes'] = $blocked_period['notes'];
|
||||
}
|
||||
|
||||
$blocked_period = $decoded_resource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the blocked periods that are within the provided period.
|
||||
*
|
||||
* @param string $start_date
|
||||
* @param string $end_date
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_for_period(string $start_date, string $end_date): array
|
||||
{
|
||||
return $this->query()
|
||||
//
|
||||
->group_start()
|
||||
->where('DATE(start_datetime) <=', $start_date)
|
||||
->where('DATE(end_datetime) >=', $end_date)
|
||||
->group_end()
|
||||
//
|
||||
->or_group_start()
|
||||
->where('DATE(start_datetime) >=', $start_date)
|
||||
->where('DATE(end_datetime) <=', $end_date)
|
||||
->group_end()
|
||||
//
|
||||
->or_group_start()
|
||||
->where('DATE(end_datetime) >', $start_date)
|
||||
->where('DATE(end_datetime) <', $end_date)
|
||||
->group_end()
|
||||
//
|
||||
->or_group_start()
|
||||
->where('DATE(start_datetime) >', $start_date)
|
||||
->where('DATE(start_datetime) <', $end_date)
|
||||
->group_end()
|
||||
//
|
||||
->get()
|
||||
->result_array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the query builder interface, configured for use with the blocked periods table.
|
||||
*
|
||||
* @return CI_DB_query_builder
|
||||
*/
|
||||
public function query(): CI_DB_query_builder
|
||||
{
|
||||
return $this->db->from('blocked_periods');
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a date is blocked by a blocked period.
|
||||
*
|
||||
* @param string $date
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_entire_date_blocked(string $date): bool
|
||||
{
|
||||
return $this->query()
|
||||
->where('DATE(start_datetime) <=', $date)
|
||||
->where('DATE(end_datetime) >=', $date)
|
||||
->get()
|
||||
->num_rows() > 1;
|
||||
}
|
||||
}
|
||||
265
application/models/Consents_model.php
Normal file
265
application/models/Consents_model.php
Normal file
@@ -0,0 +1,265 @@
|
||||
<?php defined('BASEPATH') or exit('No direct script access allowed');
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Easy!Appointments - Online Appointment Scheduler
|
||||
*
|
||||
* @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.3.2
|
||||
* ---------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Consents model.
|
||||
*
|
||||
* Handles all the database operations of the consent resource.
|
||||
*
|
||||
* @package Models
|
||||
*/
|
||||
class Consents_model extends EA_Model
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected array $casts = [
|
||||
'id' => 'integer',
|
||||
];
|
||||
|
||||
/**
|
||||
* Save (insert or update) a consent.
|
||||
*
|
||||
* @param array $consent Associative array with the consent data.
|
||||
*
|
||||
* @return int Returns the consent ID.
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function save(array $consent): int
|
||||
{
|
||||
$this->validate($consent);
|
||||
|
||||
if (empty($consent['id'])) {
|
||||
return $this->insert($consent);
|
||||
} else {
|
||||
return $this->update($consent);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the consent data.
|
||||
*
|
||||
* @param array $consent Associative array with the consent data.
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function validate(array $consent): void
|
||||
{
|
||||
if (empty($consent['ip']) || empty($consent['type'])) {
|
||||
throw new InvalidArgumentException('Not all required fields are provided: ' . print_r($consent, true));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert a new consent into the database.
|
||||
*
|
||||
* @param array $consent Associative array with the consent data.
|
||||
*
|
||||
* @return int Returns the consent ID.
|
||||
*
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
protected function insert(array $consent): int
|
||||
{
|
||||
$consent['create_datetime'] = date('Y-m-d H:i:s');
|
||||
$consent['update_datetime'] = date('Y-m-d H:i:s');
|
||||
|
||||
if (!$this->db->insert('consents', $consent)) {
|
||||
throw new RuntimeException('Could not insert consent.');
|
||||
}
|
||||
|
||||
return $this->db->insert_id();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update an existing consent.
|
||||
*
|
||||
* @param array $consent Associative array with the consent data.
|
||||
*
|
||||
* @return int Returns the consent ID.
|
||||
*
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
protected function update(array $consent): int
|
||||
{
|
||||
$consent['update_datetime'] = date('Y-m-d H:i:s');
|
||||
|
||||
if (!$this->db->update('consents', $consent, ['id' => $consent['id']])) {
|
||||
throw new RuntimeException('Could not update consent.');
|
||||
}
|
||||
|
||||
return $consent['id'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove an existing consent from the database.
|
||||
*
|
||||
* @param int $consent_id Consent ID.
|
||||
*
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public function delete(int $consent_id): void
|
||||
{
|
||||
$this->db->delete('consents', ['id' => $consent_id]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a specific consent from the database.
|
||||
*
|
||||
* @param int $consent_id The ID of the record to be returned.
|
||||
*
|
||||
* @return array Returns an array with the consent data.
|
||||
*/
|
||||
public function find(int $consent_id): array
|
||||
{
|
||||
$consent = $this->db->get_where('consents', ['id' => $consent_id])->row_array();
|
||||
|
||||
if (!$consent) {
|
||||
throw new InvalidArgumentException('The provided consent ID was not found in the database: ' . $consent_id);
|
||||
}
|
||||
|
||||
$this->cast($consent);
|
||||
|
||||
return $consent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a specific field value from the database.
|
||||
*
|
||||
* @param int $consent_id Consent ID.
|
||||
* @param string $field Name of the value to be returned.
|
||||
*
|
||||
* @return mixed Returns the selected consent value from the database.
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function value(int $consent_id, string $field): mixed
|
||||
{
|
||||
if (empty($field)) {
|
||||
throw new InvalidArgumentException('The field argument is cannot be empty.');
|
||||
}
|
||||
|
||||
if (empty($consent_id)) {
|
||||
throw new InvalidArgumentException('The consent ID argument cannot be empty.');
|
||||
}
|
||||
|
||||
// Check whether the consent exists.
|
||||
$query = $this->db->get_where('consents', ['id' => $consent_id]);
|
||||
|
||||
if (!$query->num_rows()) {
|
||||
throw new InvalidArgumentException('The provided consent ID was not found in the database: ' . $consent_id);
|
||||
}
|
||||
|
||||
// Check if the required field is part of the consent data.
|
||||
$consent = $query->row_array();
|
||||
|
||||
$this->cast($consent);
|
||||
|
||||
if (!array_key_exists($field, $consent)) {
|
||||
throw new InvalidArgumentException('The requested field was not found in the consent data: ' . $field);
|
||||
}
|
||||
|
||||
return $consent[$field];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the query builder interface, configured for use with the consents table.
|
||||
*
|
||||
* @return CI_DB_query_builder
|
||||
*/
|
||||
public function query(): CI_DB_query_builder
|
||||
{
|
||||
return $this->db->from('consents');
|
||||
}
|
||||
|
||||
/**
|
||||
* Search consents 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 consents.
|
||||
*/
|
||||
public function search(string $keyword, ?int $limit = null, ?int $offset = null, ?string $order_by = null): array
|
||||
{
|
||||
$consents = $this->db
|
||||
->select()
|
||||
->from('consents')
|
||||
->group_start()
|
||||
->like('first_name', $keyword)
|
||||
->or_like('last_name', $keyword)
|
||||
->or_like('email', $keyword)
|
||||
->or_like('ip', $keyword)
|
||||
->group_end()
|
||||
->limit($limit)
|
||||
->offset($offset)
|
||||
->order_by($order_by)
|
||||
->get()
|
||||
->result_array();
|
||||
|
||||
foreach ($consents as &$consent) {
|
||||
$this->cast($consent);
|
||||
}
|
||||
|
||||
return $consents;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all consents 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 consents.
|
||||
*/
|
||||
public function get(
|
||||
array|string|null $where = null,
|
||||
?int $limit = null,
|
||||
?int $offset = null,
|
||||
?string $order_by = null,
|
||||
): array {
|
||||
if ($where !== null) {
|
||||
$this->db->where($where);
|
||||
}
|
||||
|
||||
if ($order_by !== null) {
|
||||
$this->db->order_by($order_by);
|
||||
}
|
||||
|
||||
$consents = $this->db->get('consents', $limit, $offset)->result_array();
|
||||
|
||||
foreach ($consents as &$consent) {
|
||||
$this->cast($consent);
|
||||
}
|
||||
|
||||
return $consents;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load related resources to a consent.
|
||||
*
|
||||
* @param array $consent Associative array with the consent data.
|
||||
* @param array $resources Resource names to be attached.
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function load(array &$consent, array $resources)
|
||||
{
|
||||
// Consents do not currently have any related resources.
|
||||
}
|
||||
}
|
||||
552
application/models/Customers_model.php
Normal file
552
application/models/Customers_model.php
Normal file
@@ -0,0 +1,552 @@
|
||||
<?php defined('BASEPATH') or exit('No direct script access allowed');
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Easy!Appointments - Online Appointment Scheduler
|
||||
*
|
||||
* @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
|
||||
* ---------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Customers model.
|
||||
*
|
||||
* Handles all the database operations of the customer resource.
|
||||
*
|
||||
* @package Models
|
||||
*/
|
||||
class Customers_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',
|
||||
'phone' => 'phone_number',
|
||||
'address' => 'address',
|
||||
'city' => 'city',
|
||||
'state' => 'state',
|
||||
'zip' => 'zip_code',
|
||||
'timezone' => 'timezone',
|
||||
'language' => 'language',
|
||||
'customField1' => 'custom_field_1',
|
||||
'customField2' => 'custom_field_2',
|
||||
'customField3' => 'custom_field_3',
|
||||
'customField4' => 'custom_field_4',
|
||||
'customField5' => 'custom_field_5',
|
||||
'notes' => 'notes',
|
||||
'ldapDn' => 'ldap_dn',
|
||||
];
|
||||
|
||||
/**
|
||||
* Save (insert or update) a customer.
|
||||
*
|
||||
* @param array $customer Associative array with the customer data.
|
||||
*
|
||||
* @return int Returns the customer ID.
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function save(array $customer): int
|
||||
{
|
||||
$this->validate($customer);
|
||||
|
||||
if ($this->exists($customer) && empty($customer['id'])) {
|
||||
$customer['id'] = $this->find_record_id($customer);
|
||||
}
|
||||
|
||||
if (empty($customer['id'])) {
|
||||
return $this->insert($customer);
|
||||
} else {
|
||||
return $this->update($customer);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the customer data.
|
||||
*
|
||||
* @param array $customer Associative array with the customer data.
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function validate(array $customer): void
|
||||
{
|
||||
// If a customer ID is provided then check whether the record really exists in the database.
|
||||
if (!empty($customer['id'])) {
|
||||
$count = $this->db->get_where('users', ['id' => $customer['id']])->num_rows();
|
||||
|
||||
if (!$count) {
|
||||
throw new InvalidArgumentException(
|
||||
'The provided customer ID does not exist in the database: ' . $customer['id'],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure all required fields are provided.
|
||||
$require_first_name = filter_var(setting('require_first_name'), FILTER_VALIDATE_BOOLEAN);
|
||||
$require_last_name = filter_var(setting('require_last_name'), FILTER_VALIDATE_BOOLEAN);
|
||||
$require_email = filter_var(setting('require_email'), FILTER_VALIDATE_BOOLEAN);
|
||||
$require_phone_number = filter_var(setting('require_phone_number'), FILTER_VALIDATE_BOOLEAN);
|
||||
$require_address = filter_var(setting('require_address'), FILTER_VALIDATE_BOOLEAN);
|
||||
$require_city = filter_var(setting('require_city'), FILTER_VALIDATE_BOOLEAN);
|
||||
$require_zip_code = filter_var(setting('require_zip_code'), FILTER_VALIDATE_BOOLEAN);
|
||||
|
||||
if (
|
||||
(empty($customer['first_name']) && $require_first_name) ||
|
||||
(empty($customer['last_name']) && $require_last_name) ||
|
||||
(empty($customer['email']) && $require_email) ||
|
||||
(empty($customer['phone_number']) && $require_phone_number) ||
|
||||
(empty($customer['address']) && $require_address) ||
|
||||
(empty($customer['city']) && $require_city) ||
|
||||
(empty($customer['zip_code']) && $require_zip_code)
|
||||
) {
|
||||
throw new InvalidArgumentException('Not all required fields are provided: ' . print_r($customer, true));
|
||||
}
|
||||
|
||||
if (!empty($customer['email'])) {
|
||||
// Validate the email address.
|
||||
if (!filter_var($customer['email'], FILTER_VALIDATE_EMAIL)) {
|
||||
throw new InvalidArgumentException('Invalid email address provided: ' . $customer['email']);
|
||||
}
|
||||
|
||||
// Make sure the email address is unique.
|
||||
$customer_id = $customer['id'] ?? null;
|
||||
|
||||
$count = $this->db
|
||||
->select()
|
||||
->from('users')
|
||||
->join('roles', 'roles.id = users.id_roles', 'inner')
|
||||
->where('roles.slug', DB_SLUG_CUSTOMER)
|
||||
->where('users.email', $customer['email'])
|
||||
->where('users.id !=', $customer_id)
|
||||
->get()
|
||||
->num_rows();
|
||||
|
||||
if ($count > 0) {
|
||||
throw new InvalidArgumentException(
|
||||
'The provided email address is already in use, please use a different one.',
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all customers 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 customers.
|
||||
*/
|
||||
public function get(
|
||||
array|string|null $where = null,
|
||||
?int $limit = null,
|
||||
?int $offset = null,
|
||||
?string $order_by = null,
|
||||
): array {
|
||||
$role_id = $this->get_customer_role_id();
|
||||
|
||||
if ($where !== null) {
|
||||
$this->db->where($where);
|
||||
}
|
||||
|
||||
if ($order_by !== null) {
|
||||
$this->db->order_by($order_by);
|
||||
}
|
||||
|
||||
$customers = $this->db->get_where('users', ['id_roles' => $role_id], $limit, $offset)->result_array();
|
||||
|
||||
foreach ($customers as &$customer) {
|
||||
$this->cast($customer);
|
||||
}
|
||||
|
||||
return $customers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the customer role ID.
|
||||
*
|
||||
* @return int Returns the role ID.
|
||||
*/
|
||||
public function get_customer_role_id(): int
|
||||
{
|
||||
$role = $this->db->get_where('roles', ['slug' => DB_SLUG_CUSTOMER])->row_array();
|
||||
|
||||
if (empty($role)) {
|
||||
throw new RuntimeException('The customer role was not found in the database.');
|
||||
}
|
||||
|
||||
return $role['id'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a particular customer record already exists in the database.
|
||||
*
|
||||
* @param array $customer Associative array with the customer data.
|
||||
*
|
||||
* @return bool Returns whether there is a record matching the provided one or not.
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function exists(array $customer): bool
|
||||
{
|
||||
if (empty($customer['email'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$count = $this->db
|
||||
->select()
|
||||
->from('users')
|
||||
->join('roles', 'roles.id = users.id_roles', 'inner')
|
||||
->where('users.email', $customer['email'])
|
||||
->where('roles.slug', DB_SLUG_CUSTOMER)
|
||||
->get()
|
||||
->num_rows();
|
||||
|
||||
return $count > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the record ID of a customer.
|
||||
*
|
||||
* @param array $customer Associative array with the customer data.
|
||||
*
|
||||
* @return int Returns the ID of the record that matches the provided argument.
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function find_record_id(array $customer): int
|
||||
{
|
||||
if (empty($customer['email'])) {
|
||||
throw new InvalidArgumentException('The customer email was not provided: ' . print_r($customer, true));
|
||||
}
|
||||
|
||||
$customer = $this->db
|
||||
->select('users.id')
|
||||
->from('users')
|
||||
->join('roles', 'roles.id = users.id_roles', 'inner')
|
||||
->where('users.email', $customer['email'])
|
||||
->where('roles.slug', DB_SLUG_CUSTOMER)
|
||||
->get()
|
||||
->row_array();
|
||||
|
||||
if (empty($customer)) {
|
||||
throw new InvalidArgumentException('Could not find customer record id.');
|
||||
}
|
||||
|
||||
return (int) $customer['id'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert a new customer into the database.
|
||||
*
|
||||
* @param array $customer Associative array with the customer data.
|
||||
*
|
||||
* @return int Returns the customer ID.
|
||||
*
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
protected function insert(array $customer): int
|
||||
{
|
||||
$customer['create_datetime'] = date('Y-m-d H:i:s');
|
||||
$customer['update_datetime'] = date('Y-m-d H:i:s');
|
||||
$customer['id_roles'] = $this->get_customer_role_id();
|
||||
|
||||
if (!$this->db->insert('users', $customer)) {
|
||||
throw new RuntimeException('Could not insert customer.');
|
||||
}
|
||||
|
||||
return $this->db->insert_id();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update an existing customer.
|
||||
*
|
||||
* @param array $customer Associative array with the customer data.
|
||||
*
|
||||
* @return int Returns the customer ID.
|
||||
*
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
protected function update(array $customer): int
|
||||
{
|
||||
$customer['update_datetime'] = date('Y-m-d H:i:s');
|
||||
|
||||
if (!$this->db->update('users', $customer, ['id' => $customer['id']])) {
|
||||
throw new RuntimeException('Could not update customer.');
|
||||
}
|
||||
|
||||
return $customer['id'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove an existing customer from the database.
|
||||
*
|
||||
* @param int $customer_id Customer ID.
|
||||
*
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public function delete(int $customer_id): void
|
||||
{
|
||||
$this->db->delete('users', ['id' => $customer_id]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a specific customer from the database.
|
||||
*
|
||||
* @param int $customer_id The ID of the record to be returned.
|
||||
*
|
||||
* @return array Returns an array with the customer data.
|
||||
*/
|
||||
public function find(int $customer_id): array
|
||||
{
|
||||
$customer = $this->db->get_where('users', ['id' => $customer_id])->row_array();
|
||||
|
||||
if (!$customer) {
|
||||
throw new InvalidArgumentException(
|
||||
'The provided customer ID was not found in the database: ' . $customer_id,
|
||||
);
|
||||
}
|
||||
|
||||
$this->cast($customer);
|
||||
|
||||
return $customer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a specific field value from the database.
|
||||
*
|
||||
* @param int $customer_id Customer ID.
|
||||
* @param string $field Name of the value to be returned.
|
||||
*
|
||||
* @return mixed Returns the selected customer value from the database.
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function value(int $customer_id, string $field): mixed
|
||||
{
|
||||
if (empty($field)) {
|
||||
throw new InvalidArgumentException('The field argument is cannot be empty.');
|
||||
}
|
||||
|
||||
if (empty($customer_id)) {
|
||||
throw new InvalidArgumentException('The customer ID argument cannot be empty.');
|
||||
}
|
||||
|
||||
// Check whether the customer exists.
|
||||
$query = $this->db->get_where('users', ['id' => $customer_id]);
|
||||
|
||||
if (!$query->num_rows()) {
|
||||
throw new InvalidArgumentException(
|
||||
'The provided customer ID was not found in the database: ' . $customer_id,
|
||||
);
|
||||
}
|
||||
|
||||
// Check if the required field is part of the customer data.
|
||||
$customer = $query->row_array();
|
||||
|
||||
$this->cast($customer);
|
||||
|
||||
if (!array_key_exists($field, $customer)) {
|
||||
throw new InvalidArgumentException('The requested field was not found in the customer data: ' . $field);
|
||||
}
|
||||
|
||||
return $customer[$field];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the query builder interface, configured for use with the users (customer-filtered) table.
|
||||
*
|
||||
* @return CI_DB_query_builder
|
||||
*/
|
||||
public function query(): CI_DB_query_builder
|
||||
{
|
||||
$role_id = $this->get_customer_role_id();
|
||||
|
||||
return $this->db->from('users')->where('id_roles', $role_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Search customers 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 customers.
|
||||
*/
|
||||
public function search(string $keyword, ?int $limit = null, ?int $offset = null, ?string $order_by = null): array
|
||||
{
|
||||
$role_id = $this->get_customer_role_id();
|
||||
|
||||
$customers = $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 ($customers as &$customer) {
|
||||
$this->cast($customer);
|
||||
}
|
||||
|
||||
return $customers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load related resources to a customer.
|
||||
*
|
||||
* @param array $customer Associative array with the customer data.
|
||||
* @param array $resources Resource names to be attached.
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function load(array &$customer, array $resources)
|
||||
{
|
||||
// Customers do not currently have any related resources.
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the database customer record to the equivalent API resource.
|
||||
*
|
||||
* @param array $customer Customer data.
|
||||
*/
|
||||
public function api_encode(array &$customer): void
|
||||
{
|
||||
$encoded_resource = [
|
||||
'id' => array_key_exists('id', $customer) ? (int) $customer['id'] : null,
|
||||
'firstName' => $customer['first_name'],
|
||||
'lastName' => $customer['last_name'],
|
||||
'email' => $customer['email'],
|
||||
'phone' => $customer['phone_number'],
|
||||
'address' => $customer['address'],
|
||||
'city' => $customer['city'],
|
||||
'zip' => $customer['zip_code'],
|
||||
'notes' => $customer['notes'],
|
||||
'timezone' => $customer['timezone'],
|
||||
'language' => $customer['language'],
|
||||
'customField1' => $customer['custom_field_1'],
|
||||
'customField2' => $customer['custom_field_2'],
|
||||
'customField3' => $customer['custom_field_3'],
|
||||
'customField4' => $customer['custom_field_4'],
|
||||
'customField5' => $customer['custom_field_5'],
|
||||
'ldapDn' => $customer['ldap_dn'],
|
||||
];
|
||||
|
||||
$customer = $encoded_resource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the API resource to the equivalent database admin record.
|
||||
*
|
||||
* @param array $customer API resource.
|
||||
* @param array|null $base Base customer data to be overwritten with the provided values (useful for updates).
|
||||
*/
|
||||
public function api_decode(array &$customer, ?array $base = null): void
|
||||
{
|
||||
$decoded_resource = $base ?: [];
|
||||
|
||||
if (array_key_exists('id', $customer)) {
|
||||
$decoded_resource['id'] = $customer['id'];
|
||||
}
|
||||
|
||||
if (array_key_exists('firstName', $customer)) {
|
||||
$decoded_resource['first_name'] = $customer['firstName'];
|
||||
}
|
||||
|
||||
if (array_key_exists('lastName', $customer)) {
|
||||
$decoded_resource['last_name'] = $customer['lastName'];
|
||||
}
|
||||
|
||||
if (array_key_exists('email', $customer)) {
|
||||
$decoded_resource['email'] = $customer['email'];
|
||||
}
|
||||
|
||||
if (array_key_exists('phone', $customer)) {
|
||||
$decoded_resource['phone_number'] = $customer['phone'];
|
||||
}
|
||||
|
||||
if (array_key_exists('address', $customer)) {
|
||||
$decoded_resource['address'] = $customer['address'];
|
||||
}
|
||||
|
||||
if (array_key_exists('city', $customer)) {
|
||||
$decoded_resource['city'] = $customer['city'];
|
||||
}
|
||||
|
||||
if (array_key_exists('zip', $customer)) {
|
||||
$decoded_resource['zip_code'] = $customer['zip'];
|
||||
}
|
||||
|
||||
if (array_key_exists('language', $customer)) {
|
||||
$decoded_resource['language'] = $customer['language'];
|
||||
}
|
||||
|
||||
if (array_key_exists('timezone', $customer)) {
|
||||
$decoded_resource['timezone'] = $customer['timezone'];
|
||||
}
|
||||
|
||||
if (array_key_exists('customField1', $customer)) {
|
||||
$decoded_resource['custom_field_1'] = $customer['customField1'];
|
||||
}
|
||||
|
||||
if (array_key_exists('customField2', $customer)) {
|
||||
$decoded_resource['custom_field_2'] = $customer['customField2'];
|
||||
}
|
||||
|
||||
if (array_key_exists('customField3', $customer)) {
|
||||
$decoded_resource['custom_field_3'] = $customer['customField3'];
|
||||
}
|
||||
|
||||
if (array_key_exists('customField4', $customer)) {
|
||||
$decoded_resource['custom_field_4'] = $customer['customField4'];
|
||||
}
|
||||
|
||||
if (array_key_exists('customField5', $customer)) {
|
||||
$decoded_resource['custom_field_5'] = $customer['customField5'];
|
||||
}
|
||||
|
||||
if (array_key_exists('ldapDn', $customer)) {
|
||||
$decoded_resource['ldap_dn'] = $customer['ldapDn'];
|
||||
}
|
||||
|
||||
if (array_key_exists('notes', $customer)) {
|
||||
$decoded_resource['notes'] = $customer['notes'];
|
||||
}
|
||||
|
||||
$customer = $decoded_resource;
|
||||
}
|
||||
}
|
||||
985
application/models/Providers_model.php
Normal file
985
application/models/Providers_model.php
Normal file
@@ -0,0 +1,985 @@
|
||||
<?php defined('BASEPATH') or exit('No direct script access allowed');
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Easy!Appointments - Online Appointment Scheduler
|
||||
*
|
||||
* @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
|
||||
* ---------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Providers model.
|
||||
*
|
||||
* Handles all the database operations of the provider resource.
|
||||
*
|
||||
* @package Models
|
||||
*/
|
||||
class Providers_model extends EA_Model
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected array $casts = [
|
||||
'id' => 'integer',
|
||||
'is_private' => 'boolean',
|
||||
'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',
|
||||
'isPrivate' => 'is_private',
|
||||
'ldapDn' => 'ldap_dn',
|
||||
'roleId' => 'id_roles',
|
||||
];
|
||||
|
||||
/**
|
||||
* Save (insert or update) a provider.
|
||||
*
|
||||
* @param array $provider Associative array with the provider data.
|
||||
*
|
||||
* @return int Returns the provider ID.
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
* @throws Exception
|
||||
*/
|
||||
public function save(array $provider): int
|
||||
{
|
||||
$this->validate($provider);
|
||||
|
||||
if (empty($provider['id'])) {
|
||||
return $this->insert($provider);
|
||||
} else {
|
||||
return $this->update($provider);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the provider data.
|
||||
*
|
||||
* @param array $provider Associative array with the provider data.
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function validate(array $provider): void
|
||||
{
|
||||
// If a provider ID is provided then check whether the record really exists in the database.
|
||||
if (!empty($provider['id'])) {
|
||||
$count = $this->db->get_where('users', ['id' => $provider['id']])->num_rows();
|
||||
|
||||
if (!$count) {
|
||||
throw new InvalidArgumentException(
|
||||
'The provided provider ID does not exist in the database: ' . $provider['id'],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure all required fields are provided.
|
||||
if (
|
||||
empty($provider['first_name']) ||
|
||||
empty($provider['last_name']) ||
|
||||
empty($provider['email']) ||
|
||||
empty($provider['phone_number'])
|
||||
) {
|
||||
throw new InvalidArgumentException('Not all required fields are provided: ' . print_r($provider, true));
|
||||
}
|
||||
|
||||
// Validate the email address.
|
||||
if (!filter_var($provider['email'], FILTER_VALIDATE_EMAIL)) {
|
||||
throw new InvalidArgumentException('Invalid email address provided: ' . $provider['email']);
|
||||
}
|
||||
|
||||
// Validate provider services.
|
||||
if (!empty($provider['services'])) {
|
||||
// Make sure the provided service entries are numeric values.
|
||||
foreach ($provider['services'] as $service_id) {
|
||||
if (!is_numeric($service_id)) {
|
||||
throw new InvalidArgumentException(
|
||||
'The provided provider services are invalid: ' . print_r($provider, true),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure the username is unique.
|
||||
if (!empty($provider['settings']['username'])) {
|
||||
$provider_id = $provider['id'] ?? null;
|
||||
|
||||
if (!$this->validate_username($provider['settings']['username'], $provider_id)) {
|
||||
throw new InvalidArgumentException(
|
||||
'The provided username is already in use, please use a different one.',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Validate the password.
|
||||
if (!empty($provider['settings']['password'])) {
|
||||
if (strlen($provider['settings']['password']) < MIN_PASSWORD_LENGTH) {
|
||||
throw new InvalidArgumentException(
|
||||
'The provider password must be at least ' . MIN_PASSWORD_LENGTH . ' characters long.',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// New users must always have a password value set.
|
||||
if (empty($provider['id']) && empty($provider['settings']['password'])) {
|
||||
throw new InvalidArgumentException('The provider password cannot be empty when inserting a new record.');
|
||||
}
|
||||
|
||||
// Validate calendar view type value.
|
||||
if (
|
||||
!empty($provider['settings']['calendar_view']) &&
|
||||
!in_array($provider['settings']['calendar_view'], [CALENDAR_VIEW_DEFAULT, CALENDAR_VIEW_TABLE])
|
||||
) {
|
||||
throw new InvalidArgumentException(
|
||||
'The provided calendar view is invalid: ' . $provider['settings']['calendar_view'],
|
||||
);
|
||||
}
|
||||
|
||||
// Make sure the email address is unique.
|
||||
$provider_id = $provider['id'] ?? null;
|
||||
|
||||
$count = $this->db
|
||||
->select()
|
||||
->from('users')
|
||||
->join('roles', 'roles.id = users.id_roles', 'inner')
|
||||
->where('roles.slug', DB_SLUG_PROVIDER)
|
||||
->where('users.email', $provider['email'])
|
||||
->where('users.id !=', $provider_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 provider username.
|
||||
*
|
||||
* @param string $username Provider username.
|
||||
* @param int|null $provider_id Provider ID.
|
||||
*
|
||||
* @return bool Returns the validation result.
|
||||
*/
|
||||
public function validate_username(string $username, ?int $provider_id = null): bool
|
||||
{
|
||||
if (!empty($provider_id)) {
|
||||
$this->db->where('id_users !=', $provider_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 providers 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 providers.
|
||||
*/
|
||||
public function get(
|
||||
array|string|null $where = null,
|
||||
?int $limit = null,
|
||||
?int $offset = null,
|
||||
?string $order_by = null,
|
||||
): array {
|
||||
$role_id = $this->get_provider_role_id();
|
||||
|
||||
if ($where !== null) {
|
||||
$this->db->where($where);
|
||||
}
|
||||
|
||||
if ($order_by !== null) {
|
||||
$this->db->order_by($order_by);
|
||||
}
|
||||
|
||||
$providers = $this->db->get_where('users', ['id_roles' => $role_id], $limit, $offset)->result_array();
|
||||
|
||||
foreach ($providers as &$provider) {
|
||||
$this->cast($provider);
|
||||
$provider['settings'] = $this->get_settings($provider['id']);
|
||||
$provider['services'] = $this->get_service_ids($provider['id']);
|
||||
}
|
||||
|
||||
return $providers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the provider role ID.
|
||||
*
|
||||
* @return int Returns the role ID.
|
||||
*/
|
||||
public function get_provider_role_id(): int
|
||||
{
|
||||
$role = $this->db->get_where('roles', ['slug' => DB_SLUG_PROVIDER])->row_array();
|
||||
|
||||
if (empty($role)) {
|
||||
throw new RuntimeException('The provider role was not found in the database.');
|
||||
}
|
||||
|
||||
return $role['id'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert a new provider into the database.
|
||||
*
|
||||
* @param array $provider Associative array with the provider data.
|
||||
*
|
||||
* @return int Returns the provider ID.
|
||||
*
|
||||
* @throws RuntimeException|Exception
|
||||
*/
|
||||
protected function insert(array $provider): int
|
||||
{
|
||||
$provider['create_datetime'] = date('Y-m-d H:i:s');
|
||||
$provider['update_datetime'] = date('Y-m-d H:i:s');
|
||||
$provider['id_roles'] = $this->get_provider_role_id();
|
||||
|
||||
$service_ids = $provider['services'];
|
||||
|
||||
$settings = $provider['settings'];
|
||||
|
||||
unset($provider['services'], $provider['settings']);
|
||||
|
||||
if (!$this->db->insert('users', $provider)) {
|
||||
throw new RuntimeException('Could not insert provider.');
|
||||
}
|
||||
|
||||
$provider['id'] = $this->db->insert_id();
|
||||
$settings['salt'] = generate_salt();
|
||||
$settings['password'] = hash_password($settings['salt'], $settings['password']);
|
||||
|
||||
$this->set_settings($provider['id'], $settings);
|
||||
$this->set_service_ids($provider['id'], $service_ids);
|
||||
|
||||
return $provider['id'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the provider settings.
|
||||
*
|
||||
* @param int $provider_id Provider ID.
|
||||
* @param array $settings Associative array with the settings data.
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function set_settings(int $provider_id, array $settings): void
|
||||
{
|
||||
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' => $provider_id])->num_rows();
|
||||
|
||||
if (!$count) {
|
||||
$this->db->insert('user_settings', ['id_users' => $provider_id]);
|
||||
}
|
||||
|
||||
foreach ($settings as $name => $value) {
|
||||
// Sort working plans exceptions in descending order that they are easier to modify later on.
|
||||
if ($name === 'working_plan_exceptions') {
|
||||
$value = json_decode($value, true);
|
||||
|
||||
if (!$value) {
|
||||
$value = [];
|
||||
}
|
||||
|
||||
krsort($value);
|
||||
|
||||
$value = json_encode(empty($value) ? new stdClass() : $value);
|
||||
}
|
||||
|
||||
$this->set_setting($provider_id, $name, $value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the provider settings.
|
||||
*
|
||||
* @param int $provider_id Provider ID.
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function get_settings(int $provider_id): array
|
||||
{
|
||||
$settings = $this->db->get_where('user_settings', ['id_users' => $provider_id])->row_array();
|
||||
|
||||
unset($settings['id_users'], $settings['password'], $settings['salt']);
|
||||
|
||||
return $settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of a provider setting.
|
||||
*
|
||||
* @param int $provider_id Provider ID.
|
||||
* @param string $name Setting name.
|
||||
* @param mixed|null $value Setting value.
|
||||
*/
|
||||
public function set_setting(int $provider_id, string $name, mixed $value = null): void
|
||||
{
|
||||
if (!$this->db->update('user_settings', [$name => $value], ['id_users' => $provider_id])) {
|
||||
throw new RuntimeException('Could not set the new provider setting value: ' . $name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update an existing provider.
|
||||
*
|
||||
* @param array $provider Associative array with the provider data.
|
||||
*
|
||||
* @return int Returns the provider ID.
|
||||
*
|
||||
* @throws RuntimeException|Exception
|
||||
*/
|
||||
protected function update(array $provider): int
|
||||
{
|
||||
$provider['update_datetime'] = date('Y-m-d H:i:s');
|
||||
|
||||
$service_ids = $provider['services'];
|
||||
|
||||
$settings = $provider['settings'];
|
||||
|
||||
unset($provider['services'], $provider['settings']);
|
||||
|
||||
if (isset($settings['password'])) {
|
||||
$existing_settings = $this->db->get_where('user_settings', ['id_users' => $provider['id']])->row_array();
|
||||
|
||||
if (empty($existing_settings)) {
|
||||
throw new RuntimeException('No settings record found for provider with ID: ' . $provider['id']);
|
||||
}
|
||||
|
||||
if (empty($existing_settings['salt'])) {
|
||||
$existing_settings['salt'] = $settings['salt'] = generate_salt();
|
||||
}
|
||||
|
||||
$settings['password'] = hash_password($existing_settings['salt'], $settings['password']);
|
||||
}
|
||||
|
||||
if (!$this->db->update('users', $provider, ['id' => $provider['id']])) {
|
||||
throw new RuntimeException('Could not update provider.');
|
||||
}
|
||||
|
||||
$this->set_settings($provider['id'], $settings);
|
||||
$this->set_service_ids($provider['id'], $service_ids);
|
||||
|
||||
return $provider['id'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the provider service IDs.
|
||||
*
|
||||
* @param int $provider_id Provider ID.
|
||||
* @param array $service_ids Service IDs.
|
||||
*/
|
||||
public function set_service_ids(int $provider_id, array $service_ids): void
|
||||
{
|
||||
// Re-insert the provider-service connections.
|
||||
$this->db->delete('services_providers', ['id_users' => $provider_id]);
|
||||
|
||||
foreach ($service_ids as $service_id) {
|
||||
$service_provider_connection = [
|
||||
'id_users' => $provider_id,
|
||||
'id_services' => $service_id,
|
||||
];
|
||||
|
||||
$this->db->insert('services_providers', $service_provider_connection);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the provider service IDs.
|
||||
*
|
||||
* @param int $provider_id Provider ID.
|
||||
*/
|
||||
public function get_service_ids(int $provider_id): array
|
||||
{
|
||||
$service_provider_connections = $this->db
|
||||
->get_where('services_providers', ['id_users' => $provider_id])
|
||||
->result_array();
|
||||
|
||||
$service_ids = [];
|
||||
|
||||
foreach ($service_provider_connections as $service_provider_connection) {
|
||||
$service_ids[] = (int) $service_provider_connection['id_services'];
|
||||
}
|
||||
|
||||
return $service_ids;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove an existing provider from the database.
|
||||
*
|
||||
* @param int $provider_id Provider ID.
|
||||
*
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public function delete(int $provider_id): void
|
||||
{
|
||||
$this->db->delete('users', ['id' => $provider_id]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a specific field value from the database.
|
||||
*
|
||||
* @param int $provider_id Provider ID.
|
||||
* @param string $field Name of the value to be returned.
|
||||
*
|
||||
* @return mixed Returns the selected provider value from the database.
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function value(int $provider_id, string $field): mixed
|
||||
{
|
||||
if (empty($field)) {
|
||||
throw new InvalidArgumentException('The field argument is cannot be empty.');
|
||||
}
|
||||
|
||||
if (empty($provider_id)) {
|
||||
throw new InvalidArgumentException('The provider ID argument cannot be empty.');
|
||||
}
|
||||
|
||||
// Check whether the provider exists.
|
||||
$query = $this->db->get_where('users', ['id' => $provider_id]);
|
||||
|
||||
if (!$query->num_rows()) {
|
||||
throw new InvalidArgumentException(
|
||||
'The provided provider ID was not found in the database: ' . $provider_id,
|
||||
);
|
||||
}
|
||||
|
||||
// Check if the required field is part of the provider data.
|
||||
$provider = $query->row_array();
|
||||
|
||||
$this->cast($provider);
|
||||
|
||||
if (!array_key_exists($field, $provider)) {
|
||||
throw new InvalidArgumentException('The requested field was not found in the provider data: ' . $field);
|
||||
}
|
||||
|
||||
return $provider[$field];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of a provider setting.
|
||||
*
|
||||
* @param int $provider_id Provider ID.
|
||||
* @param string $name Setting name.
|
||||
*
|
||||
* @return string Returns the value of the requested user setting.
|
||||
*/
|
||||
public function get_setting(int $provider_id, string $name): string
|
||||
{
|
||||
$settings = $this->db->get_where('user_settings', ['id_users' => $provider_id])->row_array();
|
||||
|
||||
if (!array_key_exists($name, $settings)) {
|
||||
throw new RuntimeException('The requested setting value was not found: ' . $provider_id);
|
||||
}
|
||||
|
||||
return $settings[$name];
|
||||
}
|
||||
|
||||
/**
|
||||
* Save a new or existing working plan exception.
|
||||
*
|
||||
* @param int $provider_id Provider ID.
|
||||
* @param string $date Working plan exception date (in YYYY-MM-DD format).
|
||||
* @param array|null $working_plan_exception Associative array with the working plan exception data.
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public function save_working_plan_exception(
|
||||
int $provider_id,
|
||||
string $date,
|
||||
?array $working_plan_exception = null,
|
||||
): void {
|
||||
// Validate the working plan exception data.
|
||||
|
||||
if (
|
||||
!empty($working_plan_exception) &&
|
||||
(empty($working_plan_exception['start']) || empty($working_plan_exception['end']))
|
||||
) {
|
||||
throw new InvalidArgumentException(
|
||||
'Empty start and/or end time provided: ' . json_encode($working_plan_exception),
|
||||
);
|
||||
}
|
||||
|
||||
if (!empty($working_plan_exception['start']) && !empty($working_plan_exception['end'])) {
|
||||
$start = date('H:i', strtotime($working_plan_exception['start']));
|
||||
|
||||
$end = date('H:i', strtotime($working_plan_exception['end']));
|
||||
|
||||
if ($start > $end) {
|
||||
throw new InvalidArgumentException('Working plan exception start date must be before the end date.');
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure the provider record exists.
|
||||
$where = [
|
||||
'id' => $provider_id,
|
||||
'id_roles' => $this->db->get_where('roles', ['slug' => DB_SLUG_PROVIDER])->row()->id,
|
||||
];
|
||||
|
||||
if ($this->db->get_where('users', $where)->num_rows() === 0) {
|
||||
throw new InvalidArgumentException('Provider ID was not found in the database: ' . $provider_id);
|
||||
}
|
||||
|
||||
$provider = $this->find($provider_id);
|
||||
|
||||
// Store the working plan exception.
|
||||
$working_plan_exceptions = json_decode($provider['settings']['working_plan_exceptions'], true);
|
||||
|
||||
if (is_array($working_plan_exception) && !isset($working_plan_exception['breaks'])) {
|
||||
$working_plan_exception['breaks'] = [];
|
||||
}
|
||||
|
||||
$working_plan_exceptions[$date] = $working_plan_exception;
|
||||
|
||||
$provider['settings']['working_plan_exceptions'] = json_encode($working_plan_exceptions);
|
||||
|
||||
$this->update($provider);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a specific provider from the database.
|
||||
*
|
||||
* @param int $provider_id The ID of the record to be returned.
|
||||
*
|
||||
* @return array Returns an array with the provider data.
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function find(int $provider_id): array
|
||||
{
|
||||
$provider = $this->db->get_where('users', ['id' => $provider_id])->row_array();
|
||||
|
||||
if (!$provider) {
|
||||
throw new InvalidArgumentException(
|
||||
'The provided provider ID was not found in the database: ' . $provider_id,
|
||||
);
|
||||
}
|
||||
|
||||
$this->cast($provider);
|
||||
$provider['settings'] = $this->get_settings($provider['id']);
|
||||
$provider['services'] = $this->get_service_ids($provider['id']);
|
||||
|
||||
return $provider;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a provider working plan exception.
|
||||
*
|
||||
* @param string $date The working plan exception date (in YYYY-MM-DD format).
|
||||
* @param int $provider_id The selected provider record id.
|
||||
*
|
||||
* @throws Exception If $provider_id argument is invalid.
|
||||
*/
|
||||
public function delete_working_plan_exception(int $provider_id, string $date): void
|
||||
{
|
||||
$provider = $this->find($provider_id);
|
||||
|
||||
$working_plan_exceptions = json_decode($provider['settings']['working_plan_exceptions'], true);
|
||||
|
||||
if (!array_key_exists($date, $working_plan_exceptions)) {
|
||||
return; // The selected date does not exist in provider's settings.
|
||||
}
|
||||
|
||||
unset($working_plan_exceptions[$date]);
|
||||
|
||||
$provider['settings']['working_plan_exceptions'] = empty($working_plan_exceptions)
|
||||
? '{}'
|
||||
: json_encode($working_plan_exceptions);
|
||||
|
||||
$this->update($provider);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the provider records that are assigned to at least one service.
|
||||
*
|
||||
* @param bool $without_private Only include the public providers.
|
||||
*
|
||||
* @return array Returns an array of providers.
|
||||
*/
|
||||
public function get_available_providers(bool $without_private = false): array
|
||||
{
|
||||
if ($without_private) {
|
||||
$this->db->where('users.is_private', false);
|
||||
}
|
||||
|
||||
$providers = $this->db
|
||||
->select('users.*')
|
||||
->from('users')
|
||||
->join('roles', 'roles.id = users.id_roles', 'inner')
|
||||
->join('services_providers', 'services_providers.id_users = users.id', 'inner')
|
||||
->where('roles.slug', DB_SLUG_PROVIDER)
|
||||
->order_by('first_name ASC, last_name ASC, email ASC')
|
||||
->group_by('users.id')
|
||||
->get()
|
||||
->result_array();
|
||||
|
||||
foreach ($providers as &$provider) {
|
||||
$this->cast($provider);
|
||||
$provider['settings'] = $this->get_settings($provider['id']);
|
||||
$provider['services'] = $this->get_service_ids($provider['id']);
|
||||
}
|
||||
|
||||
return $providers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the query builder interface, configured for use with the users (provider-filtered) table.
|
||||
*
|
||||
* @return CI_DB_query_builder
|
||||
*/
|
||||
public function query(): CI_DB_query_builder
|
||||
{
|
||||
$role_id = $this->get_provider_role_id();
|
||||
|
||||
return $this->db->from('users')->where('id_roles', $role_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Search providers 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 providers.
|
||||
*/
|
||||
public function search(string $keyword, ?int $limit = null, ?int $offset = null, ?string $order_by = null): array
|
||||
{
|
||||
$role_id = $this->get_provider_role_id();
|
||||
|
||||
$providers = $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 ($providers as &$provider) {
|
||||
$this->cast($provider);
|
||||
$provider['settings'] = $this->get_settings($provider['id']);
|
||||
$provider['services'] = $this->get_service_ids($provider['id']);
|
||||
}
|
||||
|
||||
return $providers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load related resources to a provider.
|
||||
*
|
||||
* @param array $provider Associative array with the provider data.
|
||||
* @param array $resources Resource names to be attached ("services" supported).
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function load(array &$provider, array $resources): void
|
||||
{
|
||||
if (empty($provider) || empty($resources)) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($resources as $resource) {
|
||||
$provider['services'] = match ($resource) {
|
||||
'services' => $this->db
|
||||
->select('services.*')
|
||||
->from('services')
|
||||
->join('services_providers', 'services_providers.id_services = services.id', 'inner')
|
||||
->where('id_users', $provider['id'])
|
||||
->get()
|
||||
->result_array(),
|
||||
default => throw new InvalidArgumentException(
|
||||
'The requested provider relation is not supported: ' . $resource,
|
||||
),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the database provider record to the equivalent API resource.
|
||||
*
|
||||
* @param array $provider Provider data.
|
||||
*/
|
||||
public function api_encode(array &$provider): void
|
||||
{
|
||||
$encoded_resource = [
|
||||
'id' => array_key_exists('id', $provider) ? (int) $provider['id'] : null,
|
||||
'firstName' => $provider['first_name'],
|
||||
'lastName' => $provider['last_name'],
|
||||
'email' => $provider['email'],
|
||||
'mobile' => $provider['mobile_number'],
|
||||
'phone' => $provider['phone_number'],
|
||||
'address' => $provider['address'],
|
||||
'city' => $provider['city'],
|
||||
'state' => $provider['state'],
|
||||
'zip' => $provider['zip_code'],
|
||||
'notes' => $provider['notes'],
|
||||
'isPrivate' => $provider['is_private'],
|
||||
'ldapDn' => $provider['ldap_dn'],
|
||||
'timezone' => $provider['timezone'],
|
||||
'language' => $provider['language'],
|
||||
];
|
||||
|
||||
if (array_key_exists('services', $provider)) {
|
||||
$encoded_resource['services'] = $provider['services'];
|
||||
}
|
||||
|
||||
if (array_key_exists('settings', $provider)) {
|
||||
$encoded_resource['settings'] = [
|
||||
'username' => $provider['settings']['username'],
|
||||
'notifications' => filter_var($provider['settings']['notifications'], FILTER_VALIDATE_BOOLEAN),
|
||||
'calendarView' => $provider['settings']['calendar_view'],
|
||||
'googleSync' => array_key_exists('google_sync', $provider['settings'])
|
||||
? filter_var($provider['settings']['google_sync'], FILTER_VALIDATE_BOOLEAN)
|
||||
: null,
|
||||
'googleToken' => array_key_exists('google_token', $provider['settings'])
|
||||
? $provider['settings']['google_token']
|
||||
: null,
|
||||
'googleCalendar' => array_key_exists('google_calendar', $provider['settings'])
|
||||
? $provider['settings']['google_calendar']
|
||||
: null,
|
||||
'caldavSync' => array_key_exists('caldav_sync', $provider['settings'])
|
||||
? filter_var($provider['settings']['caldav_sync'], FILTER_VALIDATE_BOOLEAN)
|
||||
: null,
|
||||
'caldavUrl' => array_key_exists('caldav_url', $provider['settings'])
|
||||
? $provider['settings']['caldav_url']
|
||||
: null,
|
||||
'caldavUsername' => array_key_exists('caldav_username', $provider['settings'])
|
||||
? $provider['settings']['caldav_username']
|
||||
: null,
|
||||
'caldavPassword' => array_key_exists('caldav_password', $provider['settings'])
|
||||
? $provider['settings']['caldav_password']
|
||||
: null,
|
||||
'syncFutureDays' => array_key_exists('sync_future_days', $provider['settings'])
|
||||
? (int) $provider['settings']['sync_future_days']
|
||||
: null,
|
||||
'syncPastDays' => array_key_exists('sync_past_days', $provider['settings'])
|
||||
? (int) $provider['settings']['sync_past_days']
|
||||
: null,
|
||||
'workingPlan' => array_key_exists('working_plan', $provider['settings'])
|
||||
? json_decode($provider['settings']['working_plan'], true)
|
||||
: null,
|
||||
'workingPlanExceptions' => array_key_exists('working_plan_exceptions', $provider['settings'])
|
||||
? json_decode($provider['settings']['working_plan_exceptions'], true)
|
||||
: null,
|
||||
];
|
||||
}
|
||||
|
||||
$provider = $encoded_resource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the API resource to the equivalent database provider record.
|
||||
*
|
||||
* @param array $provider API resource.
|
||||
* @param array|null $base Base provider data to be overwritten with the provided values (useful for updates).
|
||||
*/
|
||||
public function api_decode(array &$provider, ?array $base = null): void
|
||||
{
|
||||
$decoded_resource = $base ?: [];
|
||||
|
||||
if (array_key_exists('id', $provider)) {
|
||||
$decoded_resource['id'] = $provider['id'];
|
||||
}
|
||||
|
||||
if (array_key_exists('firstName', $provider)) {
|
||||
$decoded_resource['first_name'] = $provider['firstName'];
|
||||
}
|
||||
|
||||
if (array_key_exists('lastName', $provider)) {
|
||||
$decoded_resource['last_name'] = $provider['lastName'];
|
||||
}
|
||||
|
||||
if (array_key_exists('email', $provider)) {
|
||||
$decoded_resource['email'] = $provider['email'];
|
||||
}
|
||||
|
||||
if (array_key_exists('mobile', $provider)) {
|
||||
$decoded_resource['mobile_number'] = $provider['mobile'];
|
||||
}
|
||||
|
||||
if (array_key_exists('phone', $provider)) {
|
||||
$decoded_resource['phone_number'] = $provider['phone'];
|
||||
}
|
||||
|
||||
if (array_key_exists('address', $provider)) {
|
||||
$decoded_resource['address'] = $provider['address'];
|
||||
}
|
||||
|
||||
if (array_key_exists('city', $provider)) {
|
||||
$decoded_resource['city'] = $provider['city'];
|
||||
}
|
||||
|
||||
if (array_key_exists('state', $provider)) {
|
||||
$decoded_resource['state'] = $provider['state'];
|
||||
}
|
||||
|
||||
if (array_key_exists('zip', $provider)) {
|
||||
$decoded_resource['zip_code'] = $provider['zip'];
|
||||
}
|
||||
|
||||
if (array_key_exists('notes', $provider)) {
|
||||
$decoded_resource['notes'] = $provider['notes'];
|
||||
}
|
||||
|
||||
if (array_key_exists('timezone', $provider)) {
|
||||
$decoded_resource['timezone'] = $provider['timezone'];
|
||||
}
|
||||
|
||||
if (array_key_exists('language', $provider)) {
|
||||
$decoded_resource['language'] = $provider['language'];
|
||||
}
|
||||
|
||||
if (array_key_exists('services', $provider)) {
|
||||
$decoded_resource['services'] = $provider['services'];
|
||||
}
|
||||
|
||||
if (array_key_exists('isPrivate', $provider)) {
|
||||
$decoded_resource['is_private'] = (bool) $provider['isPrivate'];
|
||||
}
|
||||
|
||||
if (array_key_exists('ldapDn', $provider)) {
|
||||
$decoded_resource['ldap_dn'] = $provider['ldapDn'];
|
||||
}
|
||||
|
||||
if (array_key_exists('settings', $provider)) {
|
||||
if (empty($decoded_resource['settings'])) {
|
||||
$decoded_resource['settings'] = [];
|
||||
}
|
||||
|
||||
if (array_key_exists('username', $provider['settings'])) {
|
||||
$decoded_resource['settings']['username'] = $provider['settings']['username'];
|
||||
}
|
||||
|
||||
if (array_key_exists('password', $provider['settings'])) {
|
||||
$decoded_resource['settings']['password'] = $provider['settings']['password'];
|
||||
}
|
||||
|
||||
if (array_key_exists('calendarView', $provider['settings'])) {
|
||||
$decoded_resource['settings']['calendar_view'] = $provider['settings']['calendarView'];
|
||||
}
|
||||
|
||||
if (array_key_exists('notifications', $provider['settings'])) {
|
||||
$decoded_resource['settings']['notifications'] = filter_var(
|
||||
$provider['settings']['notifications'],
|
||||
FILTER_VALIDATE_BOOLEAN,
|
||||
);
|
||||
}
|
||||
|
||||
if (array_key_exists('googleSync', $provider['settings'])) {
|
||||
$decoded_resource['settings']['google_sync'] = filter_var(
|
||||
$provider['settings']['googleSync'],
|
||||
FILTER_VALIDATE_BOOLEAN,
|
||||
);
|
||||
}
|
||||
|
||||
if (array_key_exists('googleCalendar', $provider['settings'])) {
|
||||
$decoded_resource['settings']['google_calendar'] = $provider['settings']['googleCalendar'];
|
||||
}
|
||||
|
||||
if (array_key_exists('googleToken', $provider['settings'])) {
|
||||
$decoded_resource['settings']['google_token'] = $provider['settings']['googleToken'];
|
||||
}
|
||||
|
||||
if (array_key_exists('caldavSync', $provider['settings'])) {
|
||||
$decoded_resource['settings']['caldav_sync'] = $provider['settings']['caldavSync'];
|
||||
}
|
||||
|
||||
if (array_key_exists('caldavUrl', $provider['settings'])) {
|
||||
$decoded_resource['settings']['caldav_url'] = $provider['settings']['caldavUrl'];
|
||||
}
|
||||
|
||||
if (array_key_exists('caldavUsername', $provider['settings'])) {
|
||||
$decoded_resource['settings']['caldav_username'] = $provider['settings']['caldavUsername'];
|
||||
}
|
||||
|
||||
if (array_key_exists('caldavPassword', $provider['settings'])) {
|
||||
$decoded_resource['settings']['caldav_password'] = $provider['settings']['caldavPassword'];
|
||||
}
|
||||
|
||||
if (array_key_exists('syncFutureDays', $provider['settings'])) {
|
||||
$decoded_resource['settings']['sync_future_days'] = $provider['settings']['syncFutureDays'];
|
||||
}
|
||||
|
||||
if (array_key_exists('syncPastDays', $provider['settings'])) {
|
||||
$decoded_resource['settings']['sync_past_days'] = $provider['settings']['syncPastDays'];
|
||||
}
|
||||
|
||||
if (array_key_exists('workingPlan', $provider['settings'])) {
|
||||
$decoded_resource['settings']['working_plan'] = json_encode($provider['settings']['workingPlan']);
|
||||
}
|
||||
|
||||
if (array_key_exists('workingPlanExceptions', $provider['settings'])) {
|
||||
$decoded_resource['settings']['working_plan_exceptions'] = json_encode(
|
||||
$provider['settings']['workingPlanExceptions'],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$provider = $decoded_resource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Quickly check if a service is assigned to a provider.
|
||||
*
|
||||
* @param int $provider_id
|
||||
* @param int $service_id
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_service_supported(int $provider_id, int $service_id): bool
|
||||
{
|
||||
$provider = $this->find($provider_id);
|
||||
|
||||
return in_array($service_id, $provider['services']);
|
||||
}
|
||||
}
|
||||
342
application/models/Roles_model.php
Normal file
342
application/models/Roles_model.php
Normal file
@@ -0,0 +1,342 @@
|
||||
<?php defined('BASEPATH') or exit('No direct script access allowed');
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Easy!Appointments - Online Appointment Scheduler
|
||||
*
|
||||
* @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
|
||||
* ---------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Roles model.
|
||||
*
|
||||
* Handles all the database operations of the role resource.
|
||||
*
|
||||
* @package Models
|
||||
*/
|
||||
class Roles_model extends EA_Model
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected array $casts = [
|
||||
'id' => 'integer',
|
||||
'is_admin' => 'boolean',
|
||||
'appointments' => 'integer',
|
||||
'customers' => 'integer',
|
||||
'services' => 'integer',
|
||||
'users' => 'integer',
|
||||
'system_settings' => 'integer',
|
||||
'user_settings' => 'integer',
|
||||
];
|
||||
|
||||
/**
|
||||
* Save (insert or update) a role.
|
||||
*
|
||||
* @param array $role Associative array with the role data.
|
||||
*
|
||||
* @return int Returns the role ID.
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function save(array $role): int
|
||||
{
|
||||
$this->validate($role);
|
||||
|
||||
if (empty($role['id'])) {
|
||||
return $this->insert($role);
|
||||
} else {
|
||||
return $this->update($role);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the role data.
|
||||
*
|
||||
* @param array $role Associative array with the role data.
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function validate(array $role): void
|
||||
{
|
||||
// If a role ID is provided then check whether the record really exists in the database.
|
||||
if (!empty($role['id'])) {
|
||||
$count = $this->db->get_where('roles', ['id' => $role['id']])->num_rows();
|
||||
|
||||
if (!$count) {
|
||||
throw new InvalidArgumentException(
|
||||
'The provided role ID does not exist in the database: ' . $role['id'],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure all required fields are provided.
|
||||
if (empty($role['name'])) {
|
||||
throw new InvalidArgumentException('Not all required fields are provided: ' . print_r($role, true));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert a new role into the database.
|
||||
*
|
||||
* @param array $role Associative array with the role data.
|
||||
*
|
||||
* @return int Returns the role ID.
|
||||
*
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
protected function insert(array $role): int
|
||||
{
|
||||
$role['create_datetime'] = date('Y-m-d H:i:s');
|
||||
$role['update_datetime'] = date('Y-m-d H:i:s');
|
||||
|
||||
if (!$this->db->insert('roles', $role)) {
|
||||
throw new RuntimeException('Could not insert role.');
|
||||
}
|
||||
|
||||
return $this->db->insert_id();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update an existing role.
|
||||
*
|
||||
* @param array $role Associative array with the role data.
|
||||
*
|
||||
* @return int Returns the role ID.
|
||||
*
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
protected function update(array $role): int
|
||||
{
|
||||
$role['update_datetime'] = date('Y-m-d H:i:s');
|
||||
|
||||
if (!$this->db->update('roles', $role, ['id' => $role['id']])) {
|
||||
throw new RuntimeException('Could not update role.');
|
||||
}
|
||||
|
||||
return $role['id'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove an existing role from the database.
|
||||
*
|
||||
* @param int $role_id Role ID.
|
||||
*
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public function delete(int $role_id): void
|
||||
{
|
||||
$this->db->delete('roles', ['id' => $role_id]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a specific role from the database.
|
||||
*
|
||||
* @param int $role_id The ID of the record to be returned.
|
||||
*
|
||||
* @return array Returns an array with the role data.
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function find(int $role_id): array
|
||||
{
|
||||
$role = $this->db->get_where('roles', ['id' => $role_id])->row_array();
|
||||
|
||||
if (!$role) {
|
||||
throw new InvalidArgumentException('The provided role ID was not found in the database: ' . $role_id);
|
||||
}
|
||||
|
||||
$this->cast($role);
|
||||
|
||||
return $role;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a specific field value from the database.
|
||||
*
|
||||
* @param int $role_id Role ID.
|
||||
* @param string $field Name of the value to be returned.
|
||||
*
|
||||
* @return mixed Returns the selected role value from the database.
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function value(int $role_id, string $field): mixed
|
||||
{
|
||||
if (empty($field)) {
|
||||
throw new InvalidArgumentException('The field argument is cannot be empty.');
|
||||
}
|
||||
|
||||
if (empty($role_id)) {
|
||||
throw new InvalidArgumentException('The role ID argument cannot be empty.');
|
||||
}
|
||||
|
||||
// Check whether the role exists.
|
||||
$query = $this->db->get_where('roles', ['id' => $role_id]);
|
||||
|
||||
if (!$query->num_rows()) {
|
||||
throw new InvalidArgumentException('The provided role ID was not found in the database: ' . $role_id);
|
||||
}
|
||||
|
||||
// Check if the required field is part of the role data.
|
||||
$role = $query->row_array();
|
||||
|
||||
$this->cast($role);
|
||||
|
||||
if (!array_key_exists($field, $role)) {
|
||||
throw new InvalidArgumentException('The requested field was not found in the role data: ' . $field);
|
||||
}
|
||||
|
||||
return $role[$field];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the permissions array by role slug.
|
||||
*
|
||||
* The permission numbers are converted into boolean values of the four main actions:
|
||||
*
|
||||
* - view
|
||||
* - add
|
||||
* - edit
|
||||
* - delete
|
||||
*
|
||||
* After checking each individual value, you can make sure if the user is able to perform each action or not.
|
||||
*
|
||||
* @param string $slug Role slug.
|
||||
*
|
||||
* @return array Returns the permissions value.
|
||||
*/
|
||||
public function get_permissions_by_slug(string $slug): array
|
||||
{
|
||||
$role = $this->db->get_where('roles', ['slug' => $slug])->row_array();
|
||||
|
||||
$this->cast($role);
|
||||
|
||||
unset($role['id'], $role['name'], $role['slug'], $role['is_admin']);
|
||||
|
||||
// Convert the integer values to boolean.
|
||||
|
||||
$permissions = [];
|
||||
|
||||
foreach ($role as $resource => $value) {
|
||||
$permissions[$resource] = [
|
||||
'view' => false,
|
||||
'add' => false,
|
||||
'edit' => false,
|
||||
'delete' => false,
|
||||
];
|
||||
|
||||
if ($value > 0) {
|
||||
if ((int) ($value / PRIV_DELETE) === 1) {
|
||||
$permissions[$resource]['delete'] = true;
|
||||
$value -= PRIV_DELETE;
|
||||
}
|
||||
|
||||
if ((int) ($value / PRIV_EDIT) === 1) {
|
||||
$permissions[$resource]['edit'] = true;
|
||||
$value -= PRIV_EDIT;
|
||||
}
|
||||
|
||||
if ((int) ($value / PRIV_ADD) === 1) {
|
||||
$permissions[$resource]['add'] = true;
|
||||
}
|
||||
|
||||
$permissions[$resource]['view'] = true;
|
||||
}
|
||||
}
|
||||
|
||||
return $permissions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the query builder interface, configured for use with the roles table.
|
||||
*
|
||||
* @return CI_DB_query_builder
|
||||
*/
|
||||
public function query(): CI_DB_query_builder
|
||||
{
|
||||
return $this->db->from('roles');
|
||||
}
|
||||
|
||||
/**
|
||||
* Search roles 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 roles.
|
||||
*/
|
||||
public function search(string $keyword, ?int $limit = null, ?int $offset = null, ?string $order_by = null): array
|
||||
{
|
||||
$roles = $this->db
|
||||
->select()
|
||||
->from('roles')
|
||||
->group_start()
|
||||
->like('name', $keyword)
|
||||
->or_like('slug', $keyword)
|
||||
->group_end()
|
||||
->limit($limit)
|
||||
->offset($offset)
|
||||
->order_by($order_by)
|
||||
->get()
|
||||
->result_array();
|
||||
|
||||
foreach ($roles as &$role) {
|
||||
$this->cast($role);
|
||||
}
|
||||
|
||||
return $roles;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all roles 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 roles.
|
||||
*/
|
||||
public function get(
|
||||
array|string|null $where = null,
|
||||
?int $limit = null,
|
||||
?int $offset = null,
|
||||
?string $order_by = null,
|
||||
): array {
|
||||
if ($where !== null) {
|
||||
$this->db->where($where);
|
||||
}
|
||||
|
||||
if ($order_by !== null) {
|
||||
$this->db->order_by($order_by);
|
||||
}
|
||||
|
||||
$roles = $this->db->get('roles', $limit, $offset)->result_array();
|
||||
|
||||
foreach ($roles as &$role) {
|
||||
$this->cast($role);
|
||||
}
|
||||
|
||||
return $roles;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load related resources to a role.
|
||||
*
|
||||
* @param array $role Associative array with the role data.
|
||||
* @param array $resources Resource names to be attached.
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function load(array &$role, array $resources)
|
||||
{
|
||||
// Roles do not currently have any related resources.
|
||||
}
|
||||
}
|
||||
755
application/models/Secretaries_model.php
Normal file
755
application/models/Secretaries_model.php
Normal file
@@ -0,0 +1,755 @@
|
||||
<?php defined('BASEPATH') or exit('No direct script access allowed');
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Easy!Appointments - Online Appointment Scheduler
|
||||
*
|
||||
* @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',
|
||||
'ldapDn' => 'ldap_dn',
|
||||
'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): void
|
||||
{
|
||||
// If a secretary ID is provided then check whether the record really exists in the database.
|
||||
if (!empty($secretary['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 $provider_id) {
|
||||
if (!is_numeric($provider_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 secretary 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|null $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) {
|
||||
$this->cast($secretary);
|
||||
$secretary['settings'] = $this->get_settings($secretary['id']);
|
||||
$secretary['providers'] = $this->get_provider_ids($secretary['id']);
|
||||
}
|
||||
|
||||
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->set_settings($secretary['id'], $settings);
|
||||
$this->set_provider_ids($secretary['id'], $provider_ids);
|
||||
|
||||
return $secretary['id'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the secretary settings.
|
||||
*
|
||||
* @param int $secretary_id Secretary ID.
|
||||
* @param array $settings Associative array with the settings data.
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function set_settings(int $secretary_id, array $settings): void
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the secretary settings.
|
||||
*
|
||||
* @param int $secretary_id Secretary ID.
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function get_settings(int $secretary_id): array
|
||||
{
|
||||
$settings = $this->db->get_where('user_settings', ['id_users' => $secretary_id])->row_array();
|
||||
|
||||
unset($settings['id_users'], $settings['password'], $settings['salt']);
|
||||
|
||||
return $settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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): void
|
||||
{
|
||||
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']);
|
||||
}
|
||||
|
||||
if (empty($existing_settings['salt'])) {
|
||||
$existing_settings['salt'] = $settings['salt'] = generate_salt();
|
||||
}
|
||||
|
||||
$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->set_settings($secretary['id'], $settings);
|
||||
$this->set_provider_ids($secretary['id'], $provider_ids);
|
||||
|
||||
return (int) $secretary['id'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the secretary provider IDs.
|
||||
*
|
||||
* @param int $secretary_id Secretary ID.
|
||||
* @param array $provider_ids Provider IDs.
|
||||
*/
|
||||
public function set_provider_ids(int $secretary_id, array $provider_ids): void
|
||||
{
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the secretary provider IDs.
|
||||
*
|
||||
* @param int $secretary_id Secretary ID.
|
||||
*/
|
||||
public function get_provider_ids(int $secretary_id): array
|
||||
{
|
||||
$secretary_provider_connections = $this->db
|
||||
->get_where('secretaries_providers', ['id_users_secretary' => $secretary_id])
|
||||
->result_array();
|
||||
|
||||
$provider_ids = [];
|
||||
|
||||
foreach ($secretary_provider_connections as $secretary_provider_connection) {
|
||||
$provider_ids[] = (int) $secretary_provider_connection['id_users_provider'];
|
||||
}
|
||||
|
||||
return $provider_ids;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
$this->cast($secretary);
|
||||
$secretary['settings'] = $this->get_settings($secretary['id']);
|
||||
$secretary['providers'] = $this->get_provider_ids($secretary['id']);
|
||||
}
|
||||
|
||||
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): void
|
||||
{
|
||||
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): void
|
||||
{
|
||||
$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'],
|
||||
'language' => $secretary['language'],
|
||||
'ldapDn' => $secretary['ldap_dn'],
|
||||
'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): void
|
||||
{
|
||||
$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('language', $secretary)) {
|
||||
$decoded_resource['language'] = $secretary['language'];
|
||||
}
|
||||
|
||||
if (array_key_exists('ldapDn', $secretary)) {
|
||||
$decoded_resource['ldap_dn'] = $secretary['ldapDn'];
|
||||
}
|
||||
|
||||
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,
|
||||
);
|
||||
}
|
||||
|
||||
$this->cast($secretary);
|
||||
$secretary['settings'] = $this->get_settings($secretary['id']);
|
||||
$secretary['providers'] = $this->get_provider_ids($secretary['id']);
|
||||
|
||||
return $secretary;
|
||||
}
|
||||
}
|
||||
337
application/models/Service_categories_model.php
Normal file
337
application/models/Service_categories_model.php
Normal file
@@ -0,0 +1,337 @@
|
||||
<?php defined('BASEPATH') or exit('No direct script access allowed');
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Easy!Appointments - Online Appointment Scheduler
|
||||
*
|
||||
* @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
|
||||
* ---------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Service-Categories model.
|
||||
*
|
||||
* Handles all the database operations of the service-category resource.
|
||||
*
|
||||
* @package Models
|
||||
*/
|
||||
class Service_categories_model extends EA_Model
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected array $casts = [
|
||||
'id' => 'integer',
|
||||
];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected array $api_resource = [
|
||||
'id' => 'id',
|
||||
'name' => 'name',
|
||||
'description' => 'description',
|
||||
];
|
||||
|
||||
/**
|
||||
* Save (insert or update) a service-category.
|
||||
*
|
||||
* @param array $service_category Associative array with the service-category data.
|
||||
*
|
||||
* @return int Returns the service-category ID.
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function save(array $service_category): int
|
||||
{
|
||||
$this->validate($service_category);
|
||||
|
||||
if (empty($service_category['id'])) {
|
||||
return $this->insert($service_category);
|
||||
} else {
|
||||
return $this->update($service_category);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the service-category data.
|
||||
*
|
||||
* @param array $service_category Associative array with the service-category data.
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function validate(array $service_category): void
|
||||
{
|
||||
// If a service-category ID is provided then check whether the record really exists in the database.
|
||||
if (!empty($service_category['id'])) {
|
||||
$count = $this->db->get_where('service_categories', ['id' => $service_category['id']])->num_rows();
|
||||
|
||||
if (!$count) {
|
||||
throw new InvalidArgumentException(
|
||||
'The provided service-category ID does not exist in the database: ' . $service_category['id'],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure all required fields are provided.
|
||||
if (empty($service_category['name'])) {
|
||||
throw new InvalidArgumentException(
|
||||
'Not all required fields are provided: ' . print_r($service_category, true),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert a new service-category into the database.
|
||||
*
|
||||
* @param array $service_category Associative array with the service-category data.
|
||||
*
|
||||
* @return int Returns the service-category ID.
|
||||
*
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
protected function insert(array $service_category): int
|
||||
{
|
||||
$service_category['create_datetime'] = date('Y-m-d H:i:s');
|
||||
$service_category['update_datetime'] = date('Y-m-d H:i:s');
|
||||
|
||||
if (!$this->db->insert('service_categories', $service_category)) {
|
||||
throw new RuntimeException('Could not insert service-category.');
|
||||
}
|
||||
|
||||
return $this->db->insert_id();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update an existing service-category.
|
||||
*
|
||||
* @param array $service_category Associative array with the service-category data.
|
||||
*
|
||||
* @return int Returns the service-category ID.
|
||||
*
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
protected function update(array $service_category): int
|
||||
{
|
||||
$service_category['update_datetime'] = date('Y-m-d H:i:s');
|
||||
|
||||
if (!$this->db->update('service_categories', $service_category, ['id' => $service_category['id']])) {
|
||||
throw new RuntimeException('Could not update service categories.');
|
||||
}
|
||||
|
||||
return $service_category['id'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove an existing service-category from the database.
|
||||
*
|
||||
* @param int $service_category_id Service-Category ID.
|
||||
*
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public function delete(int $service_category_id): void
|
||||
{
|
||||
$this->db->delete('service_categories', ['id' => $service_category_id]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a specific service-category from the database.
|
||||
*
|
||||
* @param int $service_category_id The ID of the record to be returned.
|
||||
*
|
||||
* @return array Returns an array with the service-category data.
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function find(int $service_category_id): array
|
||||
{
|
||||
$service_category = $this->db->get_where('service_categories', ['id' => $service_category_id])->row_array();
|
||||
|
||||
if (!$service_category) {
|
||||
throw new InvalidArgumentException(
|
||||
'The provided service-category ID was not found in the database: ' . $service_category_id,
|
||||
);
|
||||
}
|
||||
|
||||
$this->cast($service_category);
|
||||
|
||||
return $service_category;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a specific field value from the database.
|
||||
*
|
||||
* @param int $service_category_id Service-Category ID.
|
||||
* @param string $field Name of the value to be returned.
|
||||
*
|
||||
* @return mixed Returns the selected service-category value from the database.
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function value(int $service_category_id, string $field): mixed
|
||||
{
|
||||
if (empty($field)) {
|
||||
throw new InvalidArgumentException('The field argument is cannot be empty.');
|
||||
}
|
||||
|
||||
if (empty($service_category_id)) {
|
||||
throw new InvalidArgumentException('The service-category ID argument cannot be empty.');
|
||||
}
|
||||
|
||||
// Check whether the service exists.
|
||||
$query = $this->db->get_where('service_categories', ['id' => $service_category_id]);
|
||||
|
||||
if (!$query->num_rows()) {
|
||||
throw new InvalidArgumentException(
|
||||
'The provided service-category ID was not found in the database: ' . $service_category_id,
|
||||
);
|
||||
}
|
||||
|
||||
// Check if the required field is part of the service-category data.
|
||||
$service_category = $query->row_array();
|
||||
|
||||
$this->cast($service_category);
|
||||
|
||||
if (!array_key_exists($field, $service_category)) {
|
||||
throw new InvalidArgumentException(
|
||||
'The requested field was not found in the service-category data: ' . $field,
|
||||
);
|
||||
}
|
||||
|
||||
return $service_category[$field];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the query builder interface, configured for use with the service categories table.
|
||||
*
|
||||
* @return CI_DB_query_builder
|
||||
*/
|
||||
public function query(): CI_DB_query_builder
|
||||
{
|
||||
return $this->db->from('service_categories');
|
||||
}
|
||||
|
||||
/**
|
||||
* Search service categories 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 service categories.
|
||||
*/
|
||||
public function search(string $keyword, ?int $limit = null, ?int $offset = null, ?string $order_by = null): array
|
||||
{
|
||||
$service_categories = $this->db
|
||||
->select()
|
||||
->from('service_categories')
|
||||
->group_start()
|
||||
->like('name', $keyword)
|
||||
->or_like('description', $keyword)
|
||||
->group_end()
|
||||
->limit($limit)
|
||||
->offset($offset)
|
||||
->order_by($order_by)
|
||||
->get()
|
||||
->result_array();
|
||||
|
||||
foreach ($service_categories as &$service_category) {
|
||||
$this->cast($service_category);
|
||||
}
|
||||
|
||||
return $service_categories;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all services 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 service categories.
|
||||
*/
|
||||
public function get(
|
||||
array|string|null $where = null,
|
||||
?int $limit = null,
|
||||
?int $offset = null,
|
||||
?string $order_by = null,
|
||||
): array {
|
||||
if ($where !== null) {
|
||||
$this->db->where($where);
|
||||
}
|
||||
|
||||
if ($order_by !== null) {
|
||||
$this->db->order_by($order_by);
|
||||
}
|
||||
|
||||
$service_categories = $this->db->get('service_categories', $limit, $offset)->result_array();
|
||||
|
||||
foreach ($service_categories as &$service_category) {
|
||||
$this->cast($service_category);
|
||||
}
|
||||
|
||||
return $service_categories;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load related resources to a service-category.
|
||||
*
|
||||
* @param array $service_category Associative array with the service-category data.
|
||||
* @param array $resources Resource names to be attached.
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function load(array &$service_category, array $resources)
|
||||
{
|
||||
// Service categories do not currently have any related resources.
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the database service-category record to the equivalent API resource.
|
||||
*
|
||||
* @param array $service_category Category data.
|
||||
*/
|
||||
public function api_encode(array &$service_category): void
|
||||
{
|
||||
$encoded_resource = [
|
||||
'id' => array_key_exists('id', $service_category) ? (int) $service_category['id'] : null,
|
||||
'name' => $service_category['name'],
|
||||
'description' => array_key_exists('description', $service_category)
|
||||
? $service_category['description']
|
||||
: null,
|
||||
];
|
||||
|
||||
$service_category = $encoded_resource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the API resource to the equivalent database service-category record.
|
||||
*
|
||||
* @param array $service_category API resource.
|
||||
* @param array|null $base Base service-category data to be overwritten with the provided values (useful for updates).
|
||||
*/
|
||||
public function api_decode(array &$service_category, ?array $base = null): void
|
||||
{
|
||||
$decoded_resource = $base ?: [];
|
||||
|
||||
if (array_key_exists('id', $service_category)) {
|
||||
$decoded_resource['id'] = $service_category['id'];
|
||||
}
|
||||
|
||||
if (array_key_exists('name', $service_category)) {
|
||||
$decoded_resource['name'] = $service_category['name'];
|
||||
}
|
||||
|
||||
if (array_key_exists('description', $service_category)) {
|
||||
$decoded_resource['description'] = $service_category['description'];
|
||||
}
|
||||
|
||||
$service_category = $decoded_resource;
|
||||
}
|
||||
}
|
||||
484
application/models/Services_model.php
Normal file
484
application/models/Services_model.php
Normal file
@@ -0,0 +1,484 @@
|
||||
<?php defined('BASEPATH') or exit('No direct script access allowed');
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Easy!Appointments - Online Appointment Scheduler
|
||||
*
|
||||
* @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
|
||||
* ---------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Services model.
|
||||
*
|
||||
* Handles all the database operations of the service resource.
|
||||
*
|
||||
* @package Models
|
||||
*/
|
||||
class Services_model extends EA_Model
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected array $casts = [
|
||||
'id' => 'integer',
|
||||
'price' => 'float',
|
||||
'attendants_number' => 'integer',
|
||||
'is_private' => 'boolean',
|
||||
'id_service_categories' => 'integer',
|
||||
];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected array $api_resource = [
|
||||
'id' => 'id',
|
||||
'name' => 'name',
|
||||
'duration' => 'duration',
|
||||
'price' => 'price',
|
||||
'currency' => 'currency',
|
||||
'description' => 'description',
|
||||
'location' => 'location',
|
||||
'color' => 'color',
|
||||
'availabilitiesType' => 'availabilities_type',
|
||||
'attendantsNumber' => 'attendants_number',
|
||||
'isPrivate' => 'is_private',
|
||||
'serviceCategoryId' => 'id_service_categories',
|
||||
];
|
||||
|
||||
/**
|
||||
* Save (insert or update) a service.
|
||||
*
|
||||
* @param array $service Associative array with the service data.
|
||||
*
|
||||
* @return int Returns the service ID.
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function save(array $service): int
|
||||
{
|
||||
$this->validate($service);
|
||||
|
||||
if (empty($service['id'])) {
|
||||
return $this->insert($service);
|
||||
} else {
|
||||
return $this->update($service);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the service data.
|
||||
*
|
||||
* @param array $service Associative array with the service data.
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function validate(array $service): void
|
||||
{
|
||||
// If a service ID is provided then check whether the record really exists in the database.
|
||||
if (!empty($service['id'])) {
|
||||
$count = $this->db->get_where('services', ['id' => $service['id']])->num_rows();
|
||||
|
||||
if (!$count) {
|
||||
throw new InvalidArgumentException(
|
||||
'The provided service ID does not exist in the database: ' . $service['id'],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure all required fields are provided.
|
||||
if (empty($service['name'])) {
|
||||
throw new InvalidArgumentException('Not all required fields are provided: ' . print_r($service, true));
|
||||
}
|
||||
|
||||
// If a category was provided then make sure it really exists in the database.
|
||||
if (!empty($service['id_service_categories'])) {
|
||||
$count = $this->db
|
||||
->get_where('service_categories', ['id' => $service['id_service_categories']])
|
||||
->num_rows();
|
||||
|
||||
if (!$count) {
|
||||
throw new InvalidArgumentException(
|
||||
'The provided category ID was not found in the database: ' . $service['id_service_categories'],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure the duration value is valid.
|
||||
if (!empty($service['duration'])) {
|
||||
if ((int) $service['duration'] < EVENT_MINIMUM_DURATION) {
|
||||
throw new InvalidArgumentException(
|
||||
'The service duration cannot be less than ' . EVENT_MINIMUM_DURATION . ' minutes long.',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Availabilities type must have the correct value.
|
||||
if (
|
||||
$service['availabilities_type'] !== null &&
|
||||
$service['availabilities_type'] !== AVAILABILITIES_TYPE_FLEXIBLE &&
|
||||
$service['availabilities_type'] !== AVAILABILITIES_TYPE_FIXED
|
||||
) {
|
||||
throw new InvalidArgumentException(
|
||||
'Service availabilities type must be either ' .
|
||||
AVAILABILITIES_TYPE_FLEXIBLE .
|
||||
' or ' .
|
||||
AVAILABILITIES_TYPE_FIXED .
|
||||
' (given ' .
|
||||
$service['availabilities_type'] .
|
||||
')',
|
||||
);
|
||||
}
|
||||
|
||||
// Validate the availabilities type value.
|
||||
if (
|
||||
!empty($service['availabilities_type']) &&
|
||||
!in_array($service['availabilities_type'], [AVAILABILITIES_TYPE_FLEXIBLE, AVAILABILITIES_TYPE_FIXED])
|
||||
) {
|
||||
throw new InvalidArgumentException(
|
||||
'The provided availabilities type is invalid: ' . $service['availabilities_type'],
|
||||
);
|
||||
}
|
||||
|
||||
// Validate the attendants number value.
|
||||
if (empty($service['attendants_number']) || (int) $service['attendants_number'] < 1) {
|
||||
throw new InvalidArgumentException(
|
||||
'The provided attendants number is invalid: ' . $service['attendants_number'],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert a new service into the database.
|
||||
*
|
||||
* @param array $service Associative array with the service data.
|
||||
*
|
||||
* @return int Returns the service ID.
|
||||
*
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
protected function insert(array $service): int
|
||||
{
|
||||
$service['create_datetime'] = date('Y-m-d H:i:s');
|
||||
$service['update_datetime'] = date('Y-m-d H:i:s');
|
||||
|
||||
if (!$this->db->insert('services', $service)) {
|
||||
throw new RuntimeException('Could not insert service.');
|
||||
}
|
||||
|
||||
return $this->db->insert_id();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update an existing service.
|
||||
*
|
||||
* @param array $service Associative array with the service data.
|
||||
*
|
||||
* @return int Returns the service ID.
|
||||
*
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
protected function update(array $service): int
|
||||
{
|
||||
$service['update_datetime'] = date('Y-m-d H:i:s');
|
||||
|
||||
if (!$this->db->update('services', $service, ['id' => $service['id']])) {
|
||||
throw new RuntimeException('Could not update service.');
|
||||
}
|
||||
|
||||
return $service['id'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove an existing service from the database.
|
||||
*
|
||||
* @param int $service_id Service ID.
|
||||
*
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public function delete(int $service_id): void
|
||||
{
|
||||
$this->db->delete('services', ['id' => $service_id]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a specific service from the database.
|
||||
*
|
||||
* @param int $service_id The ID of the record to be returned.
|
||||
*
|
||||
* @return array Returns an array with the service data.
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function find(int $service_id): array
|
||||
{
|
||||
$service = $this->db->get_where('services', ['id' => $service_id])->row_array();
|
||||
|
||||
if (!$service) {
|
||||
throw new InvalidArgumentException('The provided service ID was not found in the database: ' . $service_id);
|
||||
}
|
||||
|
||||
$this->cast($service);
|
||||
|
||||
return $service;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a specific field value from the database.
|
||||
*
|
||||
* @param int $service_id Service ID.
|
||||
* @param string $field Name of the value to be returned.
|
||||
*
|
||||
* @return mixed Returns the selected service value from the database.
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function value(int $service_id, string $field): mixed
|
||||
{
|
||||
if (empty($field)) {
|
||||
throw new InvalidArgumentException('The field argument is cannot be empty.');
|
||||
}
|
||||
|
||||
if (empty($service_id)) {
|
||||
throw new InvalidArgumentException('The service ID argument cannot be empty.');
|
||||
}
|
||||
|
||||
// Check whether the service exists.
|
||||
$query = $this->db->get_where('services', ['id' => $service_id]);
|
||||
|
||||
if (!$query->num_rows()) {
|
||||
throw new InvalidArgumentException('The provided service ID was not found in the database: ' . $service_id);
|
||||
}
|
||||
|
||||
// Check if the required field is part of the service data.
|
||||
$service = $query->row_array();
|
||||
|
||||
$this->cast($service);
|
||||
|
||||
if (!array_key_exists($field, $service)) {
|
||||
throw new InvalidArgumentException('The requested field was not found in the service data: ' . $field);
|
||||
}
|
||||
|
||||
return $service[$field];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the service records that are assigned to at least one provider.
|
||||
*
|
||||
* @param bool $without_private Only include the public services.
|
||||
*
|
||||
* @return array Returns an array of services.
|
||||
*/
|
||||
public function get_available_services(bool $without_private = false): array
|
||||
{
|
||||
if ($without_private) {
|
||||
$this->db->where('services.is_private', false);
|
||||
}
|
||||
|
||||
$services = $this->db
|
||||
->distinct()
|
||||
->select(
|
||||
'services.*, service_categories.name AS service_category_name, service_categories.id AS service_category_id',
|
||||
)
|
||||
->from('services')
|
||||
->join('services_providers', 'services_providers.id_services = services.id', 'inner')
|
||||
->join('service_categories', 'service_categories.id = services.id_service_categories', 'left')
|
||||
->order_by('name ASC')
|
||||
->get()
|
||||
->result_array();
|
||||
|
||||
foreach ($services as &$service) {
|
||||
$this->cast($service);
|
||||
}
|
||||
|
||||
return $services;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all services 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 services.
|
||||
*/
|
||||
public function get(
|
||||
array|string|null $where = null,
|
||||
?int $limit = null,
|
||||
?int $offset = null,
|
||||
?string $order_by = null,
|
||||
): array {
|
||||
if ($where !== null) {
|
||||
$this->db->where($where);
|
||||
}
|
||||
|
||||
if ($order_by !== null) {
|
||||
$this->db->order_by($order_by);
|
||||
}
|
||||
|
||||
$services = $this->db->get('services', $limit, $offset)->result_array();
|
||||
|
||||
foreach ($services as &$service) {
|
||||
$this->cast($service);
|
||||
}
|
||||
|
||||
return $services;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the query builder interface, configured for use with the services table.
|
||||
*
|
||||
* @return CI_DB_query_builder
|
||||
*/
|
||||
public function query(): CI_DB_query_builder
|
||||
{
|
||||
return $this->db->from('services');
|
||||
}
|
||||
|
||||
/**
|
||||
* Search services 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 services.
|
||||
*/
|
||||
public function search(string $keyword, ?int $limit = null, ?int $offset = null, ?string $order_by = null): array
|
||||
{
|
||||
$services = $this->db
|
||||
->select()
|
||||
->from('services')
|
||||
->group_start()
|
||||
->like('name', $keyword)
|
||||
->or_like('description', $keyword)
|
||||
->group_end()
|
||||
->limit($limit)
|
||||
->offset($offset)
|
||||
->order_by($order_by)
|
||||
->get()
|
||||
->result_array();
|
||||
|
||||
foreach ($services as &$service) {
|
||||
$this->cast($service);
|
||||
}
|
||||
|
||||
return $services;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load related resources to a service.
|
||||
*
|
||||
* @param array $service Associative array with the service data.
|
||||
* @param array $resources Resource names to be attached ("category" supported).
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function load(array &$service, array $resources): void
|
||||
{
|
||||
if (empty($service) || empty($resources)) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($resources as $resource) {
|
||||
$service['category'] = match ($resource) {
|
||||
'category' => $this->db
|
||||
->get_where('service_categories', [
|
||||
'id' => $service['id_service_categories'] ?? ($service['serviceCategoryId'] ?? null),
|
||||
])
|
||||
->row_array(),
|
||||
default => throw new InvalidArgumentException(
|
||||
'The requested appointment relation is not supported: ' . $resource,
|
||||
),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the database service record to the equivalent API resource.
|
||||
*
|
||||
* @param array $service Service data.
|
||||
*/
|
||||
public function api_encode(array &$service): void
|
||||
{
|
||||
$encoded_resource = [
|
||||
'id' => array_key_exists('id', $service) ? (int) $service['id'] : null,
|
||||
'name' => $service['name'],
|
||||
'duration' => (int) $service['duration'],
|
||||
'price' => (float) $service['price'],
|
||||
'currency' => $service['currency'],
|
||||
'description' => $service['description'],
|
||||
'location' => $service['location'],
|
||||
'availabilitiesType' => $service['availabilities_type'],
|
||||
'attendantsNumber' => (int) $service['attendants_number'],
|
||||
'isPrivate' => (bool) $service['is_private'],
|
||||
'serviceCategoryId' =>
|
||||
$service['id_service_categories'] !== null ? (int) $service['id_service_categories'] : null,
|
||||
];
|
||||
|
||||
$service = $encoded_resource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the API resource to the equivalent database service record.
|
||||
*
|
||||
* @param array $service API resource.
|
||||
* @param array|null $base Base service data to be overwritten with the provided values (useful for updates).
|
||||
*/
|
||||
public function api_decode(array &$service, ?array $base = null): void
|
||||
{
|
||||
$decoded_resource = $base ?: [];
|
||||
|
||||
if (array_key_exists('id', $service)) {
|
||||
$decoded_resource['id'] = $service['id'];
|
||||
}
|
||||
|
||||
if (array_key_exists('name', $service)) {
|
||||
$decoded_resource['name'] = $service['name'];
|
||||
}
|
||||
|
||||
if (array_key_exists('duration', $service)) {
|
||||
$decoded_resource['duration'] = $service['duration'];
|
||||
}
|
||||
|
||||
if (array_key_exists('price', $service)) {
|
||||
$decoded_resource['price'] = $service['price'];
|
||||
}
|
||||
|
||||
if (array_key_exists('currency', $service)) {
|
||||
$decoded_resource['currency'] = $service['currency'];
|
||||
}
|
||||
|
||||
if (array_key_exists('description', $service)) {
|
||||
$decoded_resource['description'] = $service['description'];
|
||||
}
|
||||
|
||||
if (array_key_exists('location', $service)) {
|
||||
$decoded_resource['location'] = $service['location'];
|
||||
}
|
||||
|
||||
if (array_key_exists('availabilitiesType', $service)) {
|
||||
$decoded_resource['availabilities_type'] = $service['availabilitiesType'];
|
||||
}
|
||||
|
||||
if (array_key_exists('attendantsNumber', $service)) {
|
||||
$decoded_resource['attendants_number'] = $service['attendantsNumber'];
|
||||
}
|
||||
|
||||
if (array_key_exists('serviceCategoryId', $service)) {
|
||||
$decoded_resource['id_service_categories'] = $service['serviceCategoryId'];
|
||||
}
|
||||
|
||||
if (array_key_exists('isPrivate', $service)) {
|
||||
$decoded_resource['is_private'] = (bool) $service['isPrivate'];
|
||||
}
|
||||
|
||||
$service = $decoded_resource;
|
||||
}
|
||||
}
|
||||
321
application/models/Settings_model.php
Normal file
321
application/models/Settings_model.php
Normal file
@@ -0,0 +1,321 @@
|
||||
<?php defined('BASEPATH') or exit('No direct script access allowed');
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Easy!Appointments - Online Appointment Scheduler
|
||||
*
|
||||
* @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
|
||||
* ---------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Settings model.
|
||||
*
|
||||
* Handles all the database operations of the setting resource.
|
||||
*
|
||||
* @package Models
|
||||
*/
|
||||
class Settings_model extends EA_Model
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected array $casts = [
|
||||
'id' => 'integer',
|
||||
];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected array $api_resource = [
|
||||
'name' => 'name',
|
||||
'value' => 'value',
|
||||
];
|
||||
|
||||
/**
|
||||
* Save (insert or update) a setting.
|
||||
*
|
||||
* @param array $setting Associative array with the setting data.
|
||||
*
|
||||
* @return int Returns the setting ID.
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function save(array $setting): int
|
||||
{
|
||||
$this->validate($setting);
|
||||
|
||||
if (empty($setting['id'])) {
|
||||
return $this->insert($setting);
|
||||
} else {
|
||||
return $this->update($setting);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the setting data.
|
||||
*
|
||||
* @param array $setting Associative array with the setting data.
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function validate(array $setting): void
|
||||
{
|
||||
// If a setting ID is provided then check whether the record really exists in the database.
|
||||
if (!empty($setting['id'])) {
|
||||
$count = $this->db->get_where('settings', ['id' => $setting['id']])->num_rows();
|
||||
|
||||
if (!$count) {
|
||||
throw new InvalidArgumentException(
|
||||
'The provided setting ID does not exist in the database: ' . $setting['id'],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure all required fields are provided.
|
||||
if (empty($setting['name'])) {
|
||||
throw new InvalidArgumentException('Not all required fields are provided: ' . print_r($setting, true));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert a new setting into the database.
|
||||
*
|
||||
* @param array $setting Associative array with the setting data.
|
||||
*
|
||||
* @return int Returns the setting ID.
|
||||
*
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
protected function insert(array $setting): int
|
||||
{
|
||||
$setting['create_datetime'] = date('Y-m-d H:i:s');
|
||||
$setting['update_datetime'] = date('Y-m-d H:i:s');
|
||||
|
||||
if (!$this->db->insert('settings', $setting)) {
|
||||
throw new RuntimeException('Could not insert setting.');
|
||||
}
|
||||
|
||||
return $this->db->insert_id();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update an existing setting.
|
||||
*
|
||||
* @param array $setting Associative array with the setting data.
|
||||
*
|
||||
* @return int Returns the setting ID.
|
||||
*
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
protected function update(array $setting): int
|
||||
{
|
||||
$setting['update_datetime'] = date('Y-m-d H:i:s');
|
||||
|
||||
if (!$this->db->update('settings', $setting, ['id' => $setting['id']])) {
|
||||
throw new RuntimeException('Could not update setting.');
|
||||
}
|
||||
|
||||
return $setting['id'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove an existing setting from the database.
|
||||
*
|
||||
* @param int $setting_id Setting ID.
|
||||
*
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public function delete(int $setting_id): void
|
||||
{
|
||||
$this->db->delete('settings', ['id' => $setting_id]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a specific setting from the database.
|
||||
*
|
||||
* @param int $setting_id The ID of the record to be returned.
|
||||
*
|
||||
* @return array Returns an array with the setting data.
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function find(int $setting_id): array
|
||||
{
|
||||
$setting = $this->db->get_where('settings', ['id' => $setting_id])->row_array();
|
||||
|
||||
if (!$setting) {
|
||||
throw new InvalidArgumentException('The provided setting ID was not found in the database: ' . $setting_id);
|
||||
}
|
||||
|
||||
$this->cast($setting);
|
||||
|
||||
return $setting;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a specific field value from the database.
|
||||
*
|
||||
* @param int $setting_id Setting ID.
|
||||
* @param string $field Name of the value to be returned.
|
||||
*
|
||||
* @return mixed Returns the selected setting value from the database.
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function value(int $setting_id, string $field): mixed
|
||||
{
|
||||
if (empty($field)) {
|
||||
throw new InvalidArgumentException('The field argument is cannot be empty.');
|
||||
}
|
||||
|
||||
if (empty($setting_id)) {
|
||||
throw new InvalidArgumentException('The setting ID argument cannot be empty.');
|
||||
}
|
||||
|
||||
// Check whether the setting exists.
|
||||
$query = $this->db->get_where('settings', ['id' => $setting_id]);
|
||||
|
||||
if (!$query->num_rows()) {
|
||||
throw new InvalidArgumentException('The provided setting ID was not found in the database: ' . $setting_id);
|
||||
}
|
||||
|
||||
// Check if the required field is part of the setting data.
|
||||
$setting = $query->row_array();
|
||||
|
||||
$this->cast($setting);
|
||||
|
||||
if (!array_key_exists($field, $setting)) {
|
||||
throw new InvalidArgumentException('The requested field was not found in the setting data: ' . $field);
|
||||
}
|
||||
|
||||
return $setting[$field];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the query builder interface, configured for use with the settings table.
|
||||
*
|
||||
* @return CI_DB_query_builder
|
||||
*/
|
||||
public function query(): CI_DB_query_builder
|
||||
{
|
||||
return $this->db->from('settings');
|
||||
}
|
||||
|
||||
/**
|
||||
* Search settings 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 settings.
|
||||
*/
|
||||
public function search(string $keyword, ?int $limit = null, ?int $offset = null, ?string $order_by = null): array
|
||||
{
|
||||
$settings = $this->db
|
||||
->select()
|
||||
->from('settings')
|
||||
->group_start()
|
||||
->like('name', $keyword)
|
||||
->or_like('value', $keyword)
|
||||
->group_end()
|
||||
->limit($limit)
|
||||
->offset($offset)
|
||||
->order_by($order_by)
|
||||
->get()
|
||||
->result_array();
|
||||
|
||||
foreach ($settings as &$setting) {
|
||||
$this->cast($setting);
|
||||
}
|
||||
|
||||
return $settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all settings 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 settings.
|
||||
*/
|
||||
public function get(
|
||||
array|string|null $where = null,
|
||||
?int $limit = null,
|
||||
?int $offset = null,
|
||||
?string $order_by = null,
|
||||
): array {
|
||||
if ($where !== null) {
|
||||
$this->db->where($where);
|
||||
}
|
||||
|
||||
if ($order_by !== null) {
|
||||
$this->db->order_by($order_by);
|
||||
}
|
||||
|
||||
$settings = $this->db->get('settings', $limit, $offset)->result_array();
|
||||
|
||||
foreach ($settings as &$setting) {
|
||||
$this->cast($setting);
|
||||
}
|
||||
|
||||
return $settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load related resources to a setting.
|
||||
*
|
||||
* @param array $setting Associative array with the setting data.
|
||||
* @param array $resources Resource names to be attached.
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function load(array &$setting, array $resources)
|
||||
{
|
||||
// Users do not currently have any related resources.
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the database setting record to the equivalent API resource.
|
||||
*
|
||||
* @param array $setting Setting data.
|
||||
*/
|
||||
public function api_encode(array &$setting): void
|
||||
{
|
||||
$encoded_resource = [
|
||||
'name' => $setting['name'],
|
||||
'value' => $setting['value'],
|
||||
];
|
||||
|
||||
$setting = $encoded_resource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the API resource to the equivalent database setting record.
|
||||
*
|
||||
* @param array $setting API resource.
|
||||
* @param array|null $base Base setting data to be overwritten with the provided values (useful for updates).
|
||||
*/
|
||||
public function api_decode(array &$setting, ?array $base = null): void
|
||||
{
|
||||
$decoded_resource = $base ?: [];
|
||||
|
||||
if (array_key_exists('name', $setting)) {
|
||||
$decoded_resource['name'] = $setting['name'];
|
||||
}
|
||||
|
||||
if (array_key_exists('value', $setting)) {
|
||||
$decoded_resource['value'] = $setting['value'];
|
||||
}
|
||||
|
||||
$setting = $decoded_resource;
|
||||
}
|
||||
}
|
||||
446
application/models/Unavailabilities_model.php
Normal file
446
application/models/Unavailabilities_model.php
Normal file
@@ -0,0 +1,446 @@
|
||||
<?php defined('BASEPATH') or exit('No direct script access allowed');
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Easy!Appointments - Online Appointment Scheduler
|
||||
*
|
||||
* @package EasyAppointments
|
||||
* @author A.Tselegidis <alextselegidis@gmail.com>
|
||||
* @copyright Copyright (c) Alex Tselegidis
|
||||
* @license https://opensource.org/licenses/GPL-3.0 - GPLv3
|
||||
* @link http://easyunavailabilities.org
|
||||
* @since v1.0.0
|
||||
* ---------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Unavailabilities model.
|
||||
*
|
||||
* @package Models
|
||||
*/
|
||||
class Unavailabilities_model extends EA_Model
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected array $casts = [
|
||||
'id' => 'integer',
|
||||
'is_unavailability' => 'boolean',
|
||||
'id_users_provider' => 'integer',
|
||||
'id_users_customer' => 'integer',
|
||||
'id_services' => 'integer',
|
||||
];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected array $api_resource = [
|
||||
'id' => 'id',
|
||||
'book' => 'book_datetime',
|
||||
'start' => 'start_datetime',
|
||||
'end' => 'end_datetime',
|
||||
'location' => 'location',
|
||||
'color' => 'color',
|
||||
'status' => 'status',
|
||||
'notes' => 'notes',
|
||||
'hash' => 'hash',
|
||||
'providerId' => 'id_users_provider',
|
||||
'googleCalendarId' => 'id_google_calendar',
|
||||
];
|
||||
|
||||
/**
|
||||
* Save (insert or update) an unavailability.
|
||||
*
|
||||
* @param array $unavailability Associative array with the unavailability data.
|
||||
*
|
||||
* @return int Returns the unavailability ID.
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function save(array $unavailability): int
|
||||
{
|
||||
$this->validate($unavailability);
|
||||
|
||||
if (empty($unavailability['id'])) {
|
||||
return $this->insert($unavailability);
|
||||
} else {
|
||||
return $this->update($unavailability);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the unavailability data.
|
||||
*
|
||||
* @param array $unavailability Associative array with the unavailability data.
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function validate(array $unavailability): void
|
||||
{
|
||||
// If an unavailability ID is provided then check whether the record really exists in the database.
|
||||
if (!empty($unavailability['id'])) {
|
||||
$count = $this->db->get_where('appointments', ['id' => $unavailability['id']])->num_rows();
|
||||
|
||||
if (!$count) {
|
||||
throw new InvalidArgumentException(
|
||||
'The provided unavailability ID does not exist in the database: ' . $unavailability['id'],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure all required fields are provided.
|
||||
if (
|
||||
empty($unavailability['start_datetime']) ||
|
||||
empty($unavailability['end_datetime']) ||
|
||||
empty($unavailability['id_users_provider'])
|
||||
) {
|
||||
throw new InvalidArgumentException(
|
||||
'Not all required fields are provided: ' . print_r($unavailability, true),
|
||||
);
|
||||
}
|
||||
|
||||
// Make sure that the provided unavailability date time values are valid.
|
||||
if (!validate_datetime($unavailability['start_datetime'])) {
|
||||
throw new InvalidArgumentException('The unavailability start date time is invalid.');
|
||||
}
|
||||
|
||||
if (!validate_datetime($unavailability['end_datetime'])) {
|
||||
throw new InvalidArgumentException('The unavailability end date time is invalid.');
|
||||
}
|
||||
|
||||
// Make the unavailability lasts longer than the minimum duration (in minutes).
|
||||
$diff = (strtotime($unavailability['end_datetime']) - strtotime($unavailability['start_datetime'])) / 60;
|
||||
|
||||
if ($diff < EVENT_MINIMUM_DURATION) {
|
||||
throw new InvalidArgumentException(
|
||||
'The unavailability duration cannot be less than ' . EVENT_MINIMUM_DURATION . ' minutes.',
|
||||
);
|
||||
}
|
||||
|
||||
// Make sure the provider ID really exists in the database.
|
||||
$count = $this->db
|
||||
->select()
|
||||
->from('users')
|
||||
->join('roles', 'roles.id = users.id_roles', 'inner')
|
||||
->where('users.id', $unavailability['id_users_provider'])
|
||||
->where('roles.slug', DB_SLUG_PROVIDER)
|
||||
->get()
|
||||
->num_rows();
|
||||
|
||||
if (!$count) {
|
||||
throw new InvalidArgumentException(
|
||||
'The unavailability provider ID was not found in the database: ' . $unavailability['id_users_provider'],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all unavailabilities 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 unavailabilities.
|
||||
*/
|
||||
public function get(
|
||||
array|string|null $where = null,
|
||||
?int $limit = null,
|
||||
?int $offset = null,
|
||||
?string $order_by = null,
|
||||
): array {
|
||||
if ($where !== null) {
|
||||
$this->db->where($where);
|
||||
}
|
||||
|
||||
if ($order_by) {
|
||||
$this->db->order_by($order_by);
|
||||
}
|
||||
|
||||
$unavailabilities = $this->db
|
||||
->get_where('appointments', ['is_unavailability' => true], $limit, $offset)
|
||||
->result_array();
|
||||
|
||||
foreach ($unavailabilities as &$unavailability) {
|
||||
$this->cast($unavailability);
|
||||
}
|
||||
|
||||
return $unavailabilities;
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert a new unavailability into the database.
|
||||
*
|
||||
* @param array $unavailability Associative array with the unavailability data.
|
||||
*
|
||||
* @return int Returns the unavailability ID.
|
||||
*
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
protected function insert(array $unavailability): int
|
||||
{
|
||||
$unavailability['book_datetime'] = date('Y-m-d H:i:s');
|
||||
$unavailability['create_datetime'] = date('Y-m-d H:i:s');
|
||||
$unavailability['update_datetime'] = date('Y-m-d H:i:s');
|
||||
$unavailability['hash'] = random_string('alnum', 12);
|
||||
$unavailability['is_unavailability'] = true;
|
||||
|
||||
if (!$this->db->insert('appointments', $unavailability)) {
|
||||
throw new RuntimeException('Could not insert unavailability.');
|
||||
}
|
||||
|
||||
return $this->db->insert_id();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update an existing unavailability.
|
||||
*
|
||||
* @param array $unavailability Associative array with the unavailability data.
|
||||
*
|
||||
* @return int Returns the unavailability ID.
|
||||
*
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
protected function update(array $unavailability): int
|
||||
{
|
||||
$unavailability['update_datetime'] = date('Y-m-d H:i:s');
|
||||
|
||||
if (!$this->db->update('appointments', $unavailability, ['id' => $unavailability['id']])) {
|
||||
throw new RuntimeException('Could not update unavailability record.');
|
||||
}
|
||||
|
||||
return $unavailability['id'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove an existing unavailability from the database.
|
||||
*
|
||||
* @param int $unavailability_id Unavailability ID.
|
||||
*
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public function delete(int $unavailability_id): void
|
||||
{
|
||||
$this->db->delete('appointments', ['id' => $unavailability_id]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a specific unavailability from the database.
|
||||
*
|
||||
* @param int $unavailability_id The ID of the record to be returned.
|
||||
*
|
||||
* @return array Returns an array with the unavailability data.
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function find(int $unavailability_id): array
|
||||
{
|
||||
$unavailability = $this->db->get_where('appointments', ['id' => $unavailability_id])->row_array();
|
||||
|
||||
if (!$unavailability) {
|
||||
throw new InvalidArgumentException(
|
||||
'The provided unavailability ID was not found in the database: ' . $unavailability_id,
|
||||
);
|
||||
}
|
||||
|
||||
$this->cast($unavailability);
|
||||
|
||||
return $unavailability;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a specific field value from the database.
|
||||
*
|
||||
* @param int $unavailability_id Unavailability ID.
|
||||
* @param string $field Name of the value to be returned.
|
||||
*
|
||||
* @return mixed Returns the selected unavailability value from the database.
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function value(int $unavailability_id, string $field): mixed
|
||||
{
|
||||
if (empty($field)) {
|
||||
throw new InvalidArgumentException('The field argument is cannot be empty.');
|
||||
}
|
||||
|
||||
if (empty($unavailability_id)) {
|
||||
throw new InvalidArgumentException('The unavailability ID argument cannot be empty.');
|
||||
}
|
||||
|
||||
// Check whether the unavailability exists.
|
||||
$query = $this->db->get_where('appointments', ['id' => $unavailability_id]);
|
||||
|
||||
if (!$query->num_rows()) {
|
||||
throw new InvalidArgumentException(
|
||||
'The provided unavailability ID was not found in the database: ' . $unavailability_id,
|
||||
);
|
||||
}
|
||||
|
||||
// Check if the required field is part of the unavailability data.
|
||||
$unavailability = $query->row_array();
|
||||
|
||||
$this->cast($unavailability);
|
||||
|
||||
if (!array_key_exists($field, $unavailability)) {
|
||||
throw new InvalidArgumentException(
|
||||
'The requested field was not found in the unavailability data: ' . $field,
|
||||
);
|
||||
}
|
||||
|
||||
return $unavailability[$field];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the query builder interface, configured for use with the unavailabilities table.
|
||||
*
|
||||
* @return CI_DB_query_builder
|
||||
*/
|
||||
public function query(): CI_DB_query_builder
|
||||
{
|
||||
return $this->db->from('appointments');
|
||||
}
|
||||
|
||||
/**
|
||||
* Search unavailabilities 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 unavailabilities.
|
||||
*/
|
||||
public function search(string $keyword, ?int $limit = null, ?int $offset = null, ?string $order_by = null): array
|
||||
{
|
||||
$unavailabilities = $this->db
|
||||
->select()
|
||||
->from('appointments')
|
||||
->join('users AS providers', 'providers.id = appointments.id_users_provider', 'inner')
|
||||
->where('is_unavailability', true)
|
||||
->group_start()
|
||||
->like('appointments.start_datetime', $keyword)
|
||||
->or_like('appointments.end_datetime', $keyword)
|
||||
->or_like('appointments.location', $keyword)
|
||||
->or_like('appointments.hash', $keyword)
|
||||
->or_like('appointments.notes', $keyword)
|
||||
->or_like('providers.first_name', $keyword)
|
||||
->or_like('providers.last_name', $keyword)
|
||||
->or_like('providers.email', $keyword)
|
||||
->or_like('providers.phone_number', $keyword)
|
||||
->group_end()
|
||||
->limit($limit)
|
||||
->offset($offset)
|
||||
->order_by($order_by)
|
||||
->get()
|
||||
->result_array();
|
||||
|
||||
foreach ($unavailabilities as &$unavailability) {
|
||||
$this->cast($unavailability);
|
||||
}
|
||||
|
||||
return $unavailabilities;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load related resources to an unavailability.
|
||||
*
|
||||
* @param array $unavailability Associative array with the unavailability data.
|
||||
* @param array $resources Resource names to be attached ("service", "provider", "customer" supported).
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function load(array &$unavailability, array $resources): void
|
||||
{
|
||||
if (empty($unavailability) || empty($resources)) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($resources as $resource) {
|
||||
$unavailability['provider'] = match ($resource) {
|
||||
'provider' => $this->db
|
||||
->get_where('users', [
|
||||
'id' => $unavailability['id_users_provider'] ?? ($unavailability['providerId'] ?? null),
|
||||
])
|
||||
->row_array(),
|
||||
default => throw new InvalidArgumentException(
|
||||
'The requested unavailability relation is not supported: ' . $resource,
|
||||
),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the database unavailability record to the equivalent API resource.
|
||||
*
|
||||
* @param array $unavailability Unavailability data.
|
||||
*/
|
||||
public function api_encode(array &$unavailability): void
|
||||
{
|
||||
$encoded_resource = [
|
||||
'id' => array_key_exists('id', $unavailability) ? (int) $unavailability['id'] : null,
|
||||
'book' => $unavailability['book_datetime'],
|
||||
'start' => $unavailability['start_datetime'],
|
||||
'end' => $unavailability['end_datetime'],
|
||||
'hash' => $unavailability['hash'],
|
||||
'location' => $unavailability['location'],
|
||||
'notes' => $unavailability['notes'],
|
||||
'providerId' =>
|
||||
$unavailability['id_users_provider'] !== null ? (int) $unavailability['id_users_provider'] : null,
|
||||
'googleCalendarId' =>
|
||||
$unavailability['id_google_calendar'] !== null ? (int) $unavailability['id_google_calendar'] : null,
|
||||
];
|
||||
|
||||
$unavailability = $encoded_resource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the API resource to the equivalent database unavailability record.
|
||||
*
|
||||
* @param array $unavailability API resource.
|
||||
* @param array|null $base Base unavailability data to be overwritten with the provided values (useful for updates).
|
||||
*/
|
||||
public function api_decode(array &$unavailability, ?array $base = null): void
|
||||
{
|
||||
$decoded_request = $base ?: [];
|
||||
|
||||
if (array_key_exists('id', $unavailability)) {
|
||||
$decoded_request['id'] = $unavailability['id'];
|
||||
}
|
||||
|
||||
if (array_key_exists('book', $unavailability)) {
|
||||
$decoded_request['book_datetime'] = $unavailability['book'];
|
||||
}
|
||||
|
||||
if (array_key_exists('start', $unavailability)) {
|
||||
$decoded_request['start_datetime'] = $unavailability['start'];
|
||||
}
|
||||
|
||||
if (array_key_exists('end', $unavailability)) {
|
||||
$decoded_request['end_datetime'] = $unavailability['end'];
|
||||
}
|
||||
|
||||
if (array_key_exists('hash', $unavailability)) {
|
||||
$decoded_request['hash'] = $unavailability['hash'];
|
||||
}
|
||||
|
||||
if (array_key_exists('location', $unavailability)) {
|
||||
$decoded_request['location'] = $unavailability['location'];
|
||||
}
|
||||
|
||||
if (array_key_exists('notes', $unavailability)) {
|
||||
$decoded_request['notes'] = $unavailability['notes'];
|
||||
}
|
||||
|
||||
if (array_key_exists('providerId', $unavailability)) {
|
||||
$decoded_request['id_users_provider'] = $unavailability['providerId'];
|
||||
}
|
||||
|
||||
if (array_key_exists('googleCalendarId', $unavailability)) {
|
||||
$decoded_request['id_google_calendar'] = $unavailability['googleCalendarId'];
|
||||
}
|
||||
|
||||
$decoded_request['is_unavailability'] = true;
|
||||
|
||||
$unavailability = $decoded_request;
|
||||
}
|
||||
}
|
||||
433
application/models/Users_model.php
Normal file
433
application/models/Users_model.php
Normal file
@@ -0,0 +1,433 @@
|
||||
<?php defined('BASEPATH') or exit('No direct script access allowed');
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Easy!Appointments - Online Appointment Scheduler
|
||||
*
|
||||
* @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
|
||||
* ---------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Users model.
|
||||
*
|
||||
* Handles all the database operations of the user resource.
|
||||
*
|
||||
* @package Models
|
||||
*/
|
||||
class Users_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',
|
||||
'ldapDn' => 'ldap_dn',
|
||||
'notes' => 'notes',
|
||||
'roleId' => 'id_roles',
|
||||
];
|
||||
|
||||
/**
|
||||
* Save (insert or update) a user.
|
||||
*
|
||||
* @param array $user Associative array with the user data.
|
||||
*
|
||||
* @return int Returns the user ID.
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
* @throws Exception
|
||||
*/
|
||||
public function save(array $user): int
|
||||
{
|
||||
$this->validate($user);
|
||||
|
||||
if (empty($user['id'])) {
|
||||
return $this->insert($user);
|
||||
} else {
|
||||
return $this->update($user);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the user data.
|
||||
*
|
||||
* @param array $user Associative array with the user data.
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function validate(array $user): void
|
||||
{
|
||||
// If a user ID is provided then check whether the record really exists in the database.
|
||||
if (!empty($user['id'])) {
|
||||
$count = $this->db->get_where('users', ['id' => $user['id']])->num_rows();
|
||||
|
||||
if (!$count) {
|
||||
throw new InvalidArgumentException(
|
||||
'The provided user ID does not exist in the database: ' . $user['id'],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure all required fields are provided.
|
||||
if (
|
||||
empty($user['first_name']) ||
|
||||
empty($user['last_name']) ||
|
||||
empty($user['email']) ||
|
||||
empty($user['phone_number'])
|
||||
) {
|
||||
throw new InvalidArgumentException('Not all required fields are provided: ' . print_r($user, true));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert a new user into the database.
|
||||
*
|
||||
* @param array $user Associative array with the user data.
|
||||
*
|
||||
* @return int Returns the user ID.
|
||||
*
|
||||
* @throws RuntimeException|Exception
|
||||
*/
|
||||
protected function insert(array $user): int
|
||||
{
|
||||
$user['create_datetime'] = date('Y-m-d H:i:s');
|
||||
$user['update_datetime'] = date('Y-m-d H:i:s');
|
||||
|
||||
$settings = $user['settings'];
|
||||
unset($user['settings']);
|
||||
|
||||
if (!$this->db->insert('users', $user)) {
|
||||
throw new RuntimeException('Could not insert user.');
|
||||
}
|
||||
|
||||
$user['id'] = $this->db->insert_id();
|
||||
$settings['salt'] = generate_salt();
|
||||
$settings['password'] = hash_password($settings['salt'], $settings['password']);
|
||||
|
||||
$this->set_settings($user['id'], $settings);
|
||||
|
||||
return $user['id'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the user settings.
|
||||
*
|
||||
* @param int $user_id User ID.
|
||||
* @param array $settings Associative array with the settings data.
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
protected function set_settings(int $user_id, array $settings): void
|
||||
{
|
||||
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' => $user_id])->num_rows();
|
||||
|
||||
if (!$count) {
|
||||
$this->db->insert('user_settings', ['id_users' => $user_id]);
|
||||
}
|
||||
|
||||
foreach ($settings as $name => $value) {
|
||||
$this->set_setting($user_id, $name, $value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the user settings.
|
||||
*
|
||||
* @param int $user_id User ID.
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function get_settings(int $user_id): array
|
||||
{
|
||||
$settings = $this->db->get_where('user_settings', ['id_users' => $user_id])->row_array();
|
||||
|
||||
unset($settings['id_users'], $settings['password'], $settings['salt']);
|
||||
|
||||
return $settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of a user setting.
|
||||
*
|
||||
* @param int $user_id User ID.
|
||||
* @param string $name Setting name.
|
||||
* @param string $value Setting value.
|
||||
*/
|
||||
public function set_setting(int $user_id, string $name, string $value): void
|
||||
{
|
||||
if (!$this->db->update('user_settings', [$name => $value], ['id_users' => $user_id])) {
|
||||
throw new RuntimeException('Could not set the new user setting value: ' . $name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update an existing user.
|
||||
*
|
||||
* @param array $user Associative array with the user data.
|
||||
*
|
||||
* @return int Returns the user ID.
|
||||
*
|
||||
* @throws RuntimeException|Exception
|
||||
*/
|
||||
protected function update(array $user): int
|
||||
{
|
||||
$user['update_datetime'] = date('Y-m-d H:i:s');
|
||||
|
||||
$settings = $user['settings'];
|
||||
unset($user['settings']);
|
||||
|
||||
if (isset($settings['password'])) {
|
||||
$existing_settings = $this->db->get_where('user_settings', ['id_users' => $user['id']])->row_array();
|
||||
|
||||
if (empty($existing_settings)) {
|
||||
throw new RuntimeException('No settings record found for user with ID: ' . $user['id']);
|
||||
}
|
||||
|
||||
$settings['password'] = hash_password($existing_settings['salt'], $settings['password']);
|
||||
}
|
||||
|
||||
if (!$this->db->update('users', $user, ['id' => $user['id']])) {
|
||||
throw new RuntimeException('Could not update user.');
|
||||
}
|
||||
|
||||
$this->set_settings($user['id'], $settings);
|
||||
|
||||
return $user['id'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove an existing user from the database.
|
||||
*
|
||||
* @param int $user_id User ID.
|
||||
*
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public function delete(int $user_id): void
|
||||
{
|
||||
$this->db->delete('users', ['id' => $user_id]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a specific user from the database.
|
||||
*
|
||||
* @param int $user_id The ID of the record to be returned.
|
||||
*
|
||||
* @return array Returns an array with the user data.
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function find(int $user_id): array
|
||||
{
|
||||
$user = $this->db->get_where('users', ['id' => $user_id])->row_array();
|
||||
|
||||
if (!$user) {
|
||||
throw new InvalidArgumentException('The provided user ID was not found in the database: ' . $user_id);
|
||||
}
|
||||
|
||||
$this->cast($user);
|
||||
|
||||
$user['settings'] = $this->get_settings($user['id']);
|
||||
|
||||
return $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a specific field value from the database.
|
||||
*
|
||||
* @param int $user_id User ID.
|
||||
* @param string $field Name of the value to be returned.
|
||||
*
|
||||
* @return mixed Returns the selected user value from the database.
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function value(int $user_id, string $field): mixed
|
||||
{
|
||||
if (empty($field)) {
|
||||
throw new InvalidArgumentException('The field argument is cannot be empty.');
|
||||
}
|
||||
|
||||
if (empty($user_id)) {
|
||||
throw new InvalidArgumentException('The user ID argument cannot be empty.');
|
||||
}
|
||||
|
||||
// Check whether the user exists.
|
||||
$query = $this->db->get_where('users', ['id' => $user_id]);
|
||||
|
||||
if (!$query->num_rows()) {
|
||||
throw new InvalidArgumentException('The provided user ID was not found in the database: ' . $user_id);
|
||||
}
|
||||
|
||||
// Check if the required field is part of the user data.
|
||||
$user = $query->row_array();
|
||||
|
||||
$this->cast($user);
|
||||
|
||||
if (!array_key_exists($field, $user)) {
|
||||
throw new InvalidArgumentException('The requested field was not found in the user data: ' . $field);
|
||||
}
|
||||
|
||||
return $user[$field];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of a user setting.
|
||||
*
|
||||
* @param int $user_id User ID.
|
||||
* @param string $name Setting name.
|
||||
*
|
||||
* @return string Returns the value of the requested user setting.
|
||||
*/
|
||||
public function get_setting(int $user_id, string $name): string
|
||||
{
|
||||
$settings = $this->db->get_where('user_settings', ['id_users' => $user_id])->row_array();
|
||||
|
||||
if (empty($settings[$name])) {
|
||||
throw new RuntimeException('The requested setting value was not found: ' . $user_id);
|
||||
}
|
||||
|
||||
return $settings[$name];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the query builder interface, configured for use with the users table.
|
||||
*
|
||||
* @return CI_DB_query_builder
|
||||
*/
|
||||
public function query(): CI_DB_query_builder
|
||||
{
|
||||
return $this->db->from('users');
|
||||
}
|
||||
|
||||
/**
|
||||
* Search users 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 settings.
|
||||
*/
|
||||
public function search(string $keyword, ?int $limit = null, ?int $offset = null, ?string $order_by = null): array
|
||||
{
|
||||
$users = $this->db
|
||||
->select()
|
||||
->from('users')
|
||||
->group_start()
|
||||
->like('first_name', $keyword)
|
||||
->or_like('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 ($users as &$user) {
|
||||
$this->cast($user);
|
||||
$user['settings'] = $this->get_settings($user['id']);
|
||||
}
|
||||
|
||||
return $users;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all users 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 users.
|
||||
*/
|
||||
public function get(
|
||||
array|string|null $where = null,
|
||||
?int $limit = null,
|
||||
?int $offset = null,
|
||||
?string $order_by = null,
|
||||
): array {
|
||||
if ($where !== null) {
|
||||
$this->db->where($where);
|
||||
}
|
||||
|
||||
if ($order_by !== null) {
|
||||
$this->db->order_by($order_by);
|
||||
}
|
||||
|
||||
$users = $this->db->get('users', $limit, $offset)->result_array();
|
||||
|
||||
foreach ($users as &$user) {
|
||||
$this->cast($user);
|
||||
$user['settings'] = $this->get_settings($user['id']);
|
||||
}
|
||||
|
||||
return $users;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load related resources to a user.
|
||||
*
|
||||
* @param array $user Associative array with the user data.
|
||||
* @param array $resources Resource names to be attached.
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function load(array &$user, array $resources)
|
||||
{
|
||||
// Users do not currently have any related resources.
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the username.
|
||||
*
|
||||
* @param string $username Username.
|
||||
* @param int|null $user_id Exclude user ID.
|
||||
*
|
||||
* @return bool Returns the validation result.
|
||||
*/
|
||||
public function validate_username(string $username, ?int $user_id = null): bool
|
||||
{
|
||||
if (!empty($user_id)) {
|
||||
$this->db->where('id_users !=', $user_id);
|
||||
}
|
||||
|
||||
return $this->db->get_where('user_settings', ['username' => $username])->num_rows() === 0;
|
||||
}
|
||||
}
|
||||
341
application/models/Webhooks_model.php
Normal file
341
application/models/Webhooks_model.php
Normal file
@@ -0,0 +1,341 @@
|
||||
<?php defined('BASEPATH') or exit('No direct script access allowed');
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Easy!Appointments - Online Appointment Scheduler
|
||||
*
|
||||
* @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.5.0
|
||||
* ---------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Webhooks model.
|
||||
*
|
||||
* Handles all the database operations of the webhook resource.
|
||||
*
|
||||
* @package Models
|
||||
*/
|
||||
class Webhooks_model extends EA_Model
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected array $casts = [
|
||||
'id' => 'integer',
|
||||
'is_active' => 'boolean',
|
||||
'is_ssl_verified' => 'boolean',
|
||||
];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected array $api_resource = [
|
||||
'id' => 'id',
|
||||
'name' => 'name',
|
||||
'url' => 'url',
|
||||
'action' => 'action',
|
||||
'secretToken' => 'secret_token',
|
||||
'isActive' => 'is_active',
|
||||
'isSslVerified' => 'is_ssl_verified',
|
||||
'notes' => 'notes',
|
||||
];
|
||||
|
||||
/**
|
||||
* Save (insert or update) a webhook.
|
||||
*
|
||||
* @param array $webhook Associative array with the webhook data.
|
||||
*
|
||||
* @return int Returns the webhook ID.
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function save(array $webhook): int
|
||||
{
|
||||
$this->validate($webhook);
|
||||
|
||||
if (empty($webhook['id'])) {
|
||||
return $this->insert($webhook);
|
||||
} else {
|
||||
return $this->update($webhook);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the webhook data.
|
||||
*
|
||||
* @param array $webhook Associative array with the webhook data.
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function validate(array $webhook): void
|
||||
{
|
||||
if (empty($webhook['name']) || empty($webhook['url'])) {
|
||||
throw new InvalidArgumentException('Not all required fields are provided: ' . print_r($webhook, true));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert a new webhook into the database.
|
||||
*
|
||||
* @param array $webhook Associative array with the webhook data.
|
||||
*
|
||||
* @return int Returns the webhook ID.
|
||||
*
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
protected function insert(array $webhook): int
|
||||
{
|
||||
$webhook['create_datetime'] = date('Y-m-d H:i:s');
|
||||
$webhook['update_datetime'] = date('Y-m-d H:i:s');
|
||||
|
||||
if (!$this->db->insert('webhooks', $webhook)) {
|
||||
throw new RuntimeException('Could not insert webhook.');
|
||||
}
|
||||
|
||||
return $this->db->insert_id();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update an existing webhook.
|
||||
*
|
||||
* @param array $webhook Associative array with the webhook data.
|
||||
*
|
||||
* @return int Returns the webhook ID.
|
||||
*
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
protected function update(array $webhook): int
|
||||
{
|
||||
$webhook['update_datetime'] = date('Y-m-d H:i:s');
|
||||
|
||||
if (!$this->db->update('webhooks', $webhook, ['id' => $webhook['id']])) {
|
||||
throw new RuntimeException('Could not update webhook.');
|
||||
}
|
||||
|
||||
return $webhook['id'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove an existing webhook from the database.
|
||||
*
|
||||
* @param int $webhook_id Webhook ID.
|
||||
*
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public function delete(int $webhook_id): void
|
||||
{
|
||||
$this->db->delete('webhooks', ['id' => $webhook_id]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a specific webhook from the database.
|
||||
*
|
||||
* @param int $webhook_id The ID of the record to be returned.
|
||||
*
|
||||
* @return array Returns an array with the webhook data.
|
||||
*/
|
||||
public function find(int $webhook_id): array
|
||||
{
|
||||
$webhook = $this->db->get_where('webhooks', ['id' => $webhook_id])->row_array();
|
||||
|
||||
if (!$webhook) {
|
||||
throw new InvalidArgumentException('The provided webhook ID was not found in the database: ' . $webhook_id);
|
||||
}
|
||||
|
||||
$this->cast($webhook);
|
||||
|
||||
return $webhook;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a specific field value from the database.
|
||||
*
|
||||
* @param int $webhook_id Webhook ID.
|
||||
* @param string $field Name of the value to be returned.
|
||||
*
|
||||
* @return mixed Returns the selected webhook value from the database.
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function value(int $webhook_id, string $field): mixed
|
||||
{
|
||||
if (empty($field)) {
|
||||
throw new InvalidArgumentException('The field argument is cannot be empty.');
|
||||
}
|
||||
|
||||
if (empty($webhook_id)) {
|
||||
throw new InvalidArgumentException('The webhook ID argument cannot be empty.');
|
||||
}
|
||||
|
||||
// Check whether the webhook exists.
|
||||
$query = $this->db->get_where('webhooks', ['id' => $webhook_id]);
|
||||
|
||||
if (!$query->num_rows()) {
|
||||
throw new InvalidArgumentException('The provided webhook ID was not found in the database: ' . $webhook_id);
|
||||
}
|
||||
|
||||
// Check if the required field is part of the webhook data.
|
||||
$webhook = $query->row_array();
|
||||
|
||||
$this->cast($webhook);
|
||||
|
||||
if (!array_key_exists($field, $webhook)) {
|
||||
throw new InvalidArgumentException('The requested field was not found in the webhook data: ' . $field);
|
||||
}
|
||||
|
||||
return $webhook[$field];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the query builder interface, configured for use with the webhooks table.
|
||||
*
|
||||
* @return CI_DB_query_builder
|
||||
*/
|
||||
public function query(): CI_DB_query_builder
|
||||
{
|
||||
return $this->db->from('webhooks');
|
||||
}
|
||||
|
||||
/**
|
||||
* Search webhooks 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 webhooks.
|
||||
*/
|
||||
public function search(string $keyword, ?int $limit = null, ?int $offset = null, ?string $order_by = null): array
|
||||
{
|
||||
$webhooks = $this->db
|
||||
->select()
|
||||
->from('webhooks')
|
||||
->group_start()
|
||||
->like('name', $keyword)
|
||||
->or_like('url', $keyword)
|
||||
->or_like('actions', $keyword)
|
||||
->group_end()
|
||||
->limit($limit)
|
||||
->offset($offset)
|
||||
->order_by($order_by)
|
||||
->get()
|
||||
->result_array();
|
||||
|
||||
foreach ($webhooks as &$webhook) {
|
||||
$this->cast($webhook);
|
||||
}
|
||||
|
||||
return $webhooks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all webhooks 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 webhooks.
|
||||
*/
|
||||
public function get(
|
||||
array|string|null $where = null,
|
||||
?int $limit = null,
|
||||
?int $offset = null,
|
||||
?string $order_by = null,
|
||||
): array {
|
||||
if ($where !== null) {
|
||||
$this->db->where($where);
|
||||
}
|
||||
|
||||
if ($order_by !== null) {
|
||||
$this->db->order_by($order_by);
|
||||
}
|
||||
|
||||
$webhooks = $this->db->get('webhooks', $limit, $offset)->result_array();
|
||||
|
||||
foreach ($webhooks as &$webhook) {
|
||||
$this->cast($webhook);
|
||||
}
|
||||
|
||||
return $webhooks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load related resources to a webhook.
|
||||
*
|
||||
* @param array $webhook Associative array with the webhook data.
|
||||
* @param array $resources Resource names to be attached.
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function load(array &$webhook, array $resources)
|
||||
{
|
||||
// Webhooks do not currently have any related resources.
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the database webhook record to the equivalent API resource.
|
||||
*
|
||||
* @param array $webhook Webhook data.
|
||||
*/
|
||||
public function api_encode(array &$webhook): void
|
||||
{
|
||||
$encoded_resource = [
|
||||
'id' => array_key_exists('id', $webhook) ? (int) $webhook['id'] : null,
|
||||
'name' => $webhook['name'],
|
||||
'url' => $webhook['url'],
|
||||
'actions' => $webhook['actions'],
|
||||
'secret_token' => $webhook['secret_token'],
|
||||
'is_ssl_verified' => $webhook['is_ssl_verified'],
|
||||
'notes' => $webhook['notes'],
|
||||
];
|
||||
|
||||
$webhook = $encoded_resource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the API resource to the equivalent database webhook record.
|
||||
*
|
||||
* @param array $webhook API resource.
|
||||
* @param array|null $base Base webhook data to be overwritten with the provided values (useful for updates).
|
||||
*/
|
||||
public function api_decode(array &$webhook, ?array $base = null): void
|
||||
{
|
||||
$decoded_resource = $base ?: [];
|
||||
|
||||
if (array_key_exists('id', $webhook)) {
|
||||
$decoded_resource['id'] = $webhook['id'];
|
||||
}
|
||||
|
||||
if (array_key_exists('name', $webhook)) {
|
||||
$decoded_resource['name'] = $webhook['name'];
|
||||
}
|
||||
|
||||
if (array_key_exists('url', $webhook)) {
|
||||
$decoded_resource['url'] = $webhook['url'];
|
||||
}
|
||||
|
||||
if (array_key_exists('actions', $webhook)) {
|
||||
$decoded_resource['actions'] = $webhook['actions'];
|
||||
}
|
||||
|
||||
if (array_key_exists('secretToken', $webhook)) {
|
||||
$decoded_resource['secret_token'] = $webhook['secretToken'];
|
||||
}
|
||||
|
||||
if (array_key_exists('isSslVerified', $webhook)) {
|
||||
$decoded_resource['is_ssl_verified'] = $webhook['isSslVerified'];
|
||||
}
|
||||
|
||||
if (array_key_exists('notes', $webhook)) {
|
||||
$decoded_resource['notes'] = $webhook['notes'];
|
||||
}
|
||||
|
||||
$webhook = $decoded_resource;
|
||||
}
|
||||
}
|
||||
10
application/models/index.html
Normal file
10
application/models/index.html
Normal file
@@ -0,0 +1,10 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>403 Forbidden</title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<p>Directory access is forbidden.</p>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user