Odyssey
DbLoanCreator.php
1 <?php
2 /**
3  * @copyright HomeCu 05/2019
4  *
5  * A class specifically for adding sample data to loans. Not used directly;
6  * inject this object into your consumer class then call public
7  * addSampleData() with an array of prepared/mapped data.
8  *
9  * $obj->addSampleData([array data]);
10  *
11  * Expects
12  *
13  * array[
14  * [table identifier] = [
15  * [
16  * [field identifier] => [value],
17  * // for each field to insert
18  * ]
19  * ],
20  * // for each table to insert data
21  * ]
22  *
23  * Returns array [
24  * 'success' => [
25  * [table name] => [int n count of successes],
26  * // for each table
27  * ],
28  * 'errors' => [
29  * [table name] => array[
30  * [string specific error],
31  * // for each error in this table
32  * ],
33  * // for each table
34  * ]
35  * ]
36  *
37  */
39 {
40  /** @var object the DB connection/link object injected from client */
41  protected $DbObj;
42 
43  /** @var array of data, should already be mapped to DB fields. */
44  protected $data = [];
45 
46  /** @var array with keys success empty/array records added to which tables */
47  protected $response = [];
48 
49  /**
50  * Constructor, initialize the database link object (your "$dbh")
51  * @param object $DbObject
52  * @return void
53  */
54  public function __construct($DbObject) {
55 
56  $this->Set('DbObj', $DbObject);
57  }
58 
59  /**
60  * Entry point, add data to the DB and respond with errors or success log. Note
61  * we flush out response array with each call or it will exponentially add
62  * counts and errors to earlier values.
63  * @param array $data already mapped to DB fields
64  * @return array
65  */
66  public function AddSampleData($data = []) {
67 
68  return $this
69  ->Set('data', $data)
70  ->InitResponse()
71  ->AddRecords()
72  ->ReturnResponse();
73  }
74 
75  /**
76  * The following three don't really belong in a "creator" but since there's no DB layer,
77  * here they are. GetRecord() gets a single record by a single field value (usually id.)
78  * @param string $table
79  * @param string $keyfield
80  * @param int $value
81  * @return array
82  */
83  public function GetRecord($table, $keyfield, $value) {
84 
85  $sql = "select * from $table where $keyfield = $1";
86  $msg_part = "table $table, key field $keyfield, value $value SQL $sql";
87  $result = pg_query_params($this->DbObj, $sql, [$value]);
88 
89  if (! $result) {
90  return [
91  'error' => "No DB result in getRecord $msg_part " . pg_last_error(),
92  'data' => []
93  ];
94  }
95 
96  $res = pg_fetch_assoc($result);
97 
98  return [
99  'error' => ($res)? null : "No result found for $msg_part in getRecord",
100  'data' => ($res)? $result : []
101  ];
102  }
103 
104  /**
105  * We **should** be using the native OID method OR "returning [keyfield]" on inserts
106  * but for various reasons this is problematic. Get the last inserted ID.
107  * @param string $table
108  * @param string $keyfield
109  * @return array
110  */
111  public function GetLastRecord($table, $keyfield) {
112 
113  $sql = "select $keyfield from $table order by $keyfield desc limit 1";
114  $msg_part = "table $table, key field $keyfield SQL $sql";
115  $result = pg_query($this->DbObj, $sql);
116 
117  if (! $result) {
118  return [
119  'error' => "No DB result in GetLastRecord $msg_part " . pg_last_error(),
120  'data' => []
121  ];
122  }
123 
124  $res = pg_fetch_assoc($result);
125 
126  return [
127  'error' => ($res)? null : "No result found for $msg_part in GetLastRecord",
128  'data' => ($res)? $res : []
129  ];
130  }
131 
132 
133  /**
134  * Generally used to get the entire schema template record, get multiple rows by a single keyfield.
135  * @param string $table
136  * @param string $keyfield
137  * @param int $value
138  * @return array
139  */
140  public function GetList($table, $keyfield, $value) {
141 
142  $sql = "select * from $table where $keyfield = $1 order by $keyfield asc";
143  $msg_part = "table $table, key field $keyfield value $value SQL $sql";
144  $result = pg_query_params($this->DbObj, $sql, [$value]);
145 
146  if (! $result) {
147  return [
148  'error' => "No DB result in GetList $msg_part " . pg_last_error(),
149  'data' => []
150  ];
151  }
152 
153  $res = pg_fetch_all($result);
154 
155  return [
156  'error' => ($res)? null : "No result found for $msg_part in GetList",
157  'data' => ($res)? $res : []
158  ];
159  }
160 
161  /**
162  * A typical setter, sets internal properties (DbObj, cu)
163  * Usually these are public, don't think we'll need it
164  * @param string prop property identifier
165  * @param mixed property to set
166  * @return $this
167  */
168  protected function Set($prop, $object) {
169 
170  $this->{$prop} = $object;
171  return $this;
172  }
173 
174  /**
175  * Set the response array. Response will be two arrays, successes and errors. Both
176  * are returned giving a running log of the process, see doc block at head.
177  * @return $this
178  */
179  protected function InitResponse() {
180 
181  $this->response = [
182  'success' => [],
183  'errors' => []
184  ];
185 
186  return $this;
187  }
188 
189  /**
190  * Add records using $this->data.
191  * @return $this
192  */
193  protected function AddRecords() {
194 
195  foreach ($this->data as $table => $data) {
196 
197  if ($this->IsErrors()) {
198 
199  return $this;
200  }
201 
202  if ($this->PopulateTable($table, $data)) {
203  if (isset($this->response['success'][$table])) {
204  $this->response['success'][$table]++;
205  continue;
206  }
207  // else ...
208  $this->response['success'][$table] = 1;
209  }
210  }
211 
212  return $this;
213  }
214 
215  /**
216  * Populate the table with records from the data rows, one by one, using pg_query_params
217  * to avoid futzing about with quoting and escaping. Could probably compile as one
218  * statement, but it's cmd line and light.
219  * @param string $table
220  * @param array $data
221  * @return bool
222  */
223  protected function PopulateTable($table = '', $data = []) {
224 
225  if (! $this->IsAtLeastTableAndData($table, $data)) {
226  return false;
227  }
228 
229  foreach ($data as $arr) {
230 
231  $sql = $this->BuildInsertStatement($table);
232  $result = pg_query_params($this->DbObj, $sql, $arr);
233 
234  if (! $result) {
235  $this->response['errors'][$table][] = "Failed on insert: $sql last error: " . pg_last_error();
236  return false;
237  }
238  }
239 
240  return true;
241  }
242 
243  /**
244  * Build the insert statements. Generally I would prefer to dynamically create fields and values
245  * but you have two choices in PHP for PostgreSql: fiddle about with data types and figure out
246  * which to escape and which not, or use pg_query_params which solves that and other issues
247  * (such as SQL injection.) The problem then becomes that the placeholders can't be
248  * dynamic, you need placeholders like $1, $2, etc. We only have a few tables in
249  * a test script so letting it slide. Just be sure the fields correspond . . .
250  * @param string $table
251  * @return string
252  */
253  protected function BuildInsertStatement($table) {
254 
255  switch($table) {
256 
257  case 'lnappuser':
258  return "insert into $table (email, pwd, allow_e_comm, cu, confidenceword,
259  failedloginattempts, challenge_quest_id, userlogintype, banking_user_id,
260  session_account) values($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)";
261 
262  case 'lnappschemadetail':
263  return "insert into $table (loanid, pageid, groupid, lineid, fieldtype,
264  fieldvalue, fieldattr) values($1, $2, $3, $4, $5, $6, $7)";
265 
266  case 'lnappuserresponse':
267  return "insert into $table (userid, loanid, respstatus, respcomments,
268  respcoreloanappid, respstarton, respsubmiton, respmodifiedon, respfname,
269  resplname, respmname, respssn, respdob, respmember, respphone, respapplication,
270  respstatusdesc, respamt, resplastinquire) values($1, $2, $3, $4, $5, $6, $7, $8,
271  $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19)";
272 
273  case 'lnappschemamaster':
274  return "insert into $table (cu, loantitle, loandisclosure_fragment, lastmodified) values($1, $2, $3, $4)";
275 
276  case 'lnappuser_questselect':
277  return "insert into $table(userid, questid, user_answer) values($1, $2, $3)";
278 
279  default:
280  return '';
281  }
282  }
283 
284  /**
285  * Not much validation, being a command line script you should only be sending The Good Stuff.
286  * Setting a var so we can collect multiple errors (and improve validation if needed)
287  * @param string $table
288  * @param array $data
289  * @return bool
290  */
291  protected function IsAtLeastTableAndData($table, $data) {
292 
293  $valid = true;
294 
295  if (empty($table)) {
296  $this->response['errors'][$table][] = "Empty table value found, fix it.";
297  $valid = false;
298  }
299 
300  if (count($data) == 0) {
301  $this->response['errors'][$table][] = "Empty data array found, fix it.";
302  $valid = false;
303  }
304 
305  return $valid;
306  }
307 
308  /**
309  * Return a response, this approach allows chaining of methods above it.
310  * @return array
311  */
312  protected function ReturnResponse() {
313 
314  return $this->response;
315  }
316 
317  /**
318  * Got errors?
319  * @return bool
320  */
321  protected function IsErrors() {
322 
323  return count($this->response['errors']) > 0;
324  }
325 
326 }
AddSampleData($data=[])
IsAtLeastTableAndData($table, $data)
PopulateTable($table='', $data=[])
__construct($DbObject)
Set($prop, $object)
GetLastRecord($table, $keyfield)
BuildInsertStatement($table)
GetRecord($table, $keyfield, $value)
GetList($table, $keyfield, $value)