7 * @link http://www.open-emr.org
8 * @author Matthew Vita <matthewvita48@gmail.com>
9 * @author Yash Bothra <yashrajbothra786gmail.com>
10 * @copyright Copyright (c) 2018 Matthew Vita <matthewvita48@gmail.com>
11 * @license https://github.com/openemr/openemr/blob/master/LICENSE GNU General Public License 3
14 namespace OpenEMR\Services
;
16 use OpenEMR\Common\Logging\SystemLogger
;
17 use OpenEMR\Common\Uuid\UuidRegistry
;
18 use OpenEMR\Services\Search\ISearchField
;
19 use OpenEMR\Services\Search\SearchModifier
;
20 use OpenEMR\Services\Search\StringSearchField
;
21 use OpenEMR\Services\Search\TokenSearchField
;
22 use OpenEMR\Services\Search\TokenSearchValue
;
23 use OpenEMR\Validators\PractitionerValidator
;
24 use OpenEMR\Validators\ProcessingResult
;
26 class PractitionerService
extends BaseService
28 private const PRACTITIONER_TABLE
= "users";
30 * @var PractitionerValidator
32 private $practitionerValidator;
35 * Default constructor.
37 public function __construct()
39 parent
::__construct('users');
40 UuidRegistry
::createMissingUuidsForTables([self
::PRACTITIONER_TABLE
]);
41 $this->practitionerValidator
= new PractitionerValidator();
44 public function getSelectJoinTables(): array
48 ['table' => 'list_options', 'alias' => 'abook', 'type' => 'LEFT JOIN', 'column' => 'abook_type', 'join_column' => 'option_id']
49 ,['table' => 'list_options', 'alias' => 'physician', 'type' => 'LEFT JOIN', 'column' => 'physician_type', 'join_column' => 'option_id']
53 public function getSelectFields(): array
55 // since we are joining a bunch of fields we need to make sure we normalize our regular field array by adding
56 // the table name for our own table values.
57 $fields = $this->getFields();
58 $normalizedFields = [];
59 // processing is cheap
60 foreach ($fields as $field) {
61 $normalizedFields[] = '`' . $this->getTable() . '`.`' . $field . '`';
64 return array_merge($normalizedFields, ['abook.title as abook_title', 'physician.title as physician_title', 'physician.codes as physician_code']);
67 public function getUuidFields(): array
72 public function isValidPractitionerUuid($uuid)
74 $result = $this->getOne($uuid);
75 return !empty($result->getData());
78 public function search($search, $isAndCondition = true)
80 // we only retrieve from our database when our practitioners are not null
81 if (!empty($search['npi'])) {
82 if (!$search['npi'] instanceof ISearchField
) {
83 throw new SearchFieldException("npi", "field must be instance of " . ISearchField
::class);
85 if ($search['npi']->getModifier() === SearchModifier
::MISSING
) {
86 // force our value to be false as the only thing that differentiates users as practitioners is our npi number
87 $search['npi'] = new TokenSearchField('npi', [new TokenSearchValue(false)]);
90 $search['npi'] = new TokenSearchField('npi', [new TokenSearchValue(false)]);
91 $search['npi']->setModifier(SearchModifier
::MISSING
);
93 // TODO: @adunsulag check with @brady.miller or @sjpadgett and find out if all practitioners will have usernames.
94 // I noticed that in the test database that adding entries to the addressbook appears to create users... which
95 // seems bizarre and that those users don't have usernames. I noticed that all of the users displayed in the OpenEMR
96 // user selector will exclude anything w/o a username so I add the same logic here.
97 if (!empty($search['username'])) {
98 if (!$search['username'] instanceof ISearchField
) {
99 throw new SearchFieldException("username", "field must be instance of " . ISearchField
::class);
101 if ($search['username']->getModifier() === SearchModifier
::MISSING
) {
102 // force our value to be false as we don't count users as practitioners if there is no username
103 $search['username'] = new TokenSearchField('username', [new TokenSearchValue(false)]);
106 $search['username'] = new TokenSearchField('username', [new TokenSearchValue(false)]);
107 $search['username']->setModifier(SearchModifier
::MISSING
);
109 return parent
::search($search, $isAndCondition);
113 * Returns a list of practitioners matching optional search criteria.
114 * Search criteria is conveyed by array where key = field/column name, value = ISearchField|primitive value
116 * If a primitive value is provided it will do an exact match on that field. If an ISearchField is provided it will
117 * use whatever modifiers, comparators, and composite search settings that are specified in the search field.
119 * If no search criteria is provided, all records are returned.
121 * @param $search search array parameters
122 * @param $isAndCondition specifies if AND condition is used for multiple criteria. Defaults to true.
123 * @return ProcessingResult which contains validation messages, internal error messages, and the data
126 public function getAll($search = array(), $isAndCondition = true)
128 if (!empty($search)) {
129 $fields = $this->getFields();
130 $validKeys = array_combine($fields, $fields);
132 // We need to be backwards compatible with all other uses of the service so we are going to make this a
133 // exact match string param on everything, but only if they are not sending in any Search Field options
134 foreach ($search as $fieldName => $fieldValue) {
135 if (isset($validKeys[$fieldName]) && !($fieldValue instanceof ISearchField
)) {
136 $search[$fieldName] = new StringSearchField($fieldName, $fieldValue, SearchModifier
::EXACT
, $isAndCondition);
140 return $this->search($search, $isAndCondition);
143 public function getAllWithIds(array $ids)
145 $idField = new TokenSearchField('id', $ids);
146 return $this->search(['id' => $idField]);
150 * Returns a single practitioner record by id.
151 * @param $uuid - The practitioner uuid identifier in string format.
152 * @return ProcessingResult which contains validation messages, internal error messages, and the data
155 public function getOne($uuid)
157 $processingResult = new ProcessingResult();
159 $isValid = $this->practitionerValidator
->validateId("uuid", "users", $uuid, true);
161 if ($isValid !== true) {
162 $validationMessages = [
163 'uuid' => ["invalid or nonexisting value" => " value " . $uuid]
165 $processingResult->setValidationMessages($validationMessages);
166 return $processingResult;
169 // there should not be a single duplicate id so we will grab that
170 $search = ['uuid' => new TokenSearchField('uuid', new TokenSearchValue($uuid, null, true))];
171 $results = $this->search($search);
172 $data = $results->getData();
173 if (count($data) > 1) {
174 // we will log this error and return just the single value
175 $results->setData([$data[0]]);
176 (new SystemLogger())->error("PractionerService->getOne() Duplicate records found for uuid", ['uuid' => $uuid]);
183 * Inserts a new practitioner record.
185 * @param $data The practitioner fields (array) to insert.
186 * @return ProcessingResult which contains validation messages, internal error messages, and the data
189 public function insert($data)
191 $processingResult = $this->practitionerValidator
->validate(
193 PractitionerValidator
::DATABASE_INSERT_CONTEXT
196 if (!$processingResult->isValid()) {
197 return $processingResult;
200 $data['uuid'] = UuidRegistry
::getRegistryForTable(self
::PRACTITIONER_TABLE
)->createUuid();
202 $query = $this->buildInsertColumns($data);
203 $sql = " INSERT INTO users SET ";
204 $sql .= $query['set'];
206 $results = sqlInsert(
212 $processingResult->addData(array(
214 'uuid' => UuidRegistry
::uuidToString($data['uuid'])
217 $processingResult->addInternalError("error processing SQL Insert");
220 return $processingResult;
225 * Updates an existing practitioner record.
227 * @param $uuid - The practitioner uuid identifier in string format used for update.
228 * @param $data - The updated practitioner data fields
229 * @return ProcessingResult which contains validation messages, internal error messages, and the data
232 public function update($uuid, $data)
235 $processingResult = new ProcessingResult();
236 $processingResult->setValidationMessages("Invalid Data");
237 return $processingResult;
239 $data["uuid"] = $uuid;
240 $processingResult = $this->practitionerValidator
->validate(
242 PractitionerValidator
::DATABASE_UPDATE_CONTEXT
244 if (!$processingResult->isValid()) {
245 return $processingResult;
248 $query = $this->buildUpdateColumns($data);
250 $sql = " UPDATE users SET ";
251 $sql .= $query['set'];
252 $sql .= " WHERE `uuid` = ?";
255 $uuidBinary = UuidRegistry
::uuidToBytes($uuid);
256 array_push($query['bind'], $uuidBinary);
258 $sqlResult = sqlStatement($sql, $query['bind']);
261 $processingResult->addErrorMessage("error processing SQL Update");
263 $processingResult = $this->getOne($uuid);
265 return $processingResult;