Edit via SFTP
  1. <?php namespace ferret\data;
  2. /*
  3.   PURPOSE: single-row records that use a database
  4.   TERMINOLOGY:
  5.   a "record" is a "row" that comes from a database
  6.   HISTORY:
  7.   2019-12-11 created
  8.   2019-12-29 moved fiEditableRecord here, renamed to ifEditableRecord
  9.   2021-10-07 renamed ifEditableRecord to tUpdatableRecord
  10.  
  11. */
  12. /*iiii
  13.   IMPLEMENTED BY: ferret\data\tRecordSaver
  14.   HISTORY:
  15.   2017-05-25 Tentatively, removing Save(). It may need to be replaced with GetChangeValues() or something.
  16.   2019-12-29 moved from data/forms/form-data.php to data/records/rec.php, because it was just confusing having it there
  17.   2020-01-05 I think GetActionKey() might be here by mistake. Commenting it out.
  18.   2021-03-07 some renaming due to refactoring of array stuff
  19.   tentatively removing SetValues_fromStorage() -- seems out of place
  20.   2021-03-08 Okay. The reason why SetValues_fromStorage() is needed and yet there is no
  21.   corresponding SetValues_fromDisplay() is because of the database-oriented origins
  22.   of this library. Eventually it should be refactored to be more source-agnostic.
  23.   For now, however, I'm re-enabling that function, as asymmetric as it is.
  24.   2021-10-08
  25.   * Changed GetKeyValue() to GetSelfFilter(), because that's what is actually needed for editability.
  26.   * Changed ifEditableRecord to ifUpdatableRecord, because that's more precisely what I mean by "editable".
  27.   * Found that I wanted GetSelfFilter() to be protected, which interfaces can't do; noticed that nobody
  28.   is using this interface as an argument type, so... changing to a trait, (renamed tUpdatableRecord)
  29. */
  30. trait tUpdatableRecord {
  31. abstract public function IsNew();
  32. abstract public function SetCells(array $arVals); // load native/internal values
  33. abstract public function SetValues_fromStorage(array $arVals); // load from DB read
  34. abstract public function GetCells() : array;
  35.  
  36. abstract public function ChangedFieldValues(array $arVals) : cFieldChanges;
  37. abstract protected function GetSelfFilter() : string; // needed in order to be able to update a record
  38. }
  39. /*::::
  40.   PURPOSE: basically a natively-cached record that can interact with its home recordset (and table and database)
  41.   NAMING:
  42.   "sourced" = can interact with a source recordset/table/database
  43.   a "sourced row" becomes a "record", so all descendant types can replace "RowSourced" with "Record"
  44.   can drop the "Single" prefix when referring to "Row"
  45.   TRAIT consolidated:
  46.   SOURCED ROW (was: tSourcedRow)
  47.   PURPOSE: adds data source Table and optional MultiRow, as well as providing an alias for Database access
  48.   HISTORY:
  49.   2020-01-06 Changing table argument from cDataTable to cTabloid
  50.   because sometimes the table is a sourcelessQuery, which isn't a cDataTable
  51.   and what we need is GetDatabase(), which is defined in cTabloid
  52.   2020-04-28 GetRows() was commented out; I don't know why.
  53.   Need it so Rows can be used to look up display-rendering modes (e.g. checkboxes).
  54.   Also needed to make SetRows() PUBLIC so it could be set after construction, because we can't depend on the existence of a rowset in all contexts.
  55.   2020-12-23 Making this abstract, because we should never set native fields directly from storage ...I think?
  56.   2021-02-02 There was literally nothing left here because I had moved the Frame pass up to the root
  57.   (cSingleRow) -- but I decided that should really only take place at the row->record crossover.
  58.   I think.
  59.   TODO: possibly rename cSingleRowSourced to cRecord
  60. */
  61. abstract class cSingleRowSourced extends cSingleRow {
  62.  
  63. // ++ SETUP ++ //
  64.  
  65. /*----
  66.   UNSTUB
  67.   CALLBACK FROM SetSpace()
  68.   */
  69. protected function OnSpace() : void {
  70. parent::OnSpace();
  71. $this->Space()->Record()->SetIt($this);
  72. }
  73.  
  74. // -- SETUP -- //
  75. // ++ INPUT ++ //
  76.  
  77. /*----
  78.   PURPOSE: a simplified data-loader which doesn't require field objects
  79.   INPUT: array of input values in Storage format
  80.   Interpretation is up to the application code.
  81.   HISTORY:
  82.   2021-02-25 created
  83.   2021-07-04 yes, this still gets called.
  84.   */
  85. public function SetValues_fromStorage(array $arVals) { $this->SetCells($arVals); }
  86.  
  87. // -- INPUT -- //
  88. }
  89. /*::::
  90.   TRAIT consolidated:
  91.   KEYED RECORD (tKeyedRecord)
  92.   REQUIRES:
  93.   * row belongs to a table
  94.   * row class must be aware of Table and Database
  95.   ADDS:
  96.   * row-key functions:
  97.   * self-filtering (GetSelfFilter())
  98.   * assigned-ID existence detection (IsNew())
  99.   ABSTRACT: GetKeyStatus()
  100.   HISTORY:
  101.   2019-12-23 (tKeyedRecord) Added IsNew(), because that depends on the existence of a key-field
  102.   2019-12-28 (tKeyedRecord) Renamed tKeyedRow -> tKeyedRecord
  103.   2020-01-29 created for relatively-minor class-inheritence-structure hierarchy
  104.   2021-10-09 tKeyedRow is redundant because it's in cSingleRowSourced; removed extra copy.
  105. */
  106. abstract class cRecordKeyed extends cSingleRowSourced {
  107.  
  108. // ++ FRAMEWORK ++ //
  109.  
  110. /*----
  111.   HISTORY:
  112.   2020-03-10 (todo DONE: rename to GetTable_Keyed())
  113.   2021-02-02 renamed from GetTableWrapper_Keyed() to GetTable_Keyed()
  114.   */
  115. protected function GetTable_Keyed() : ifIntKeyedTable { return $this->Space()->Table()->GetIt(); }
  116.  
  117. // -- FRAMEWORK -- //
  118. // ++ IDENTITY ++ //
  119.  
  120. public function IsNew() : bool { return $this->KeyStatus()->IsBlank(); }
  121. protected function GetKeyValue_safe() {
  122. $os = $this->KeyStatus();
  123. return $this->Space()->Database()->GetIt()->SanitizeValue(
  124. $os->GetIt() // does this need to be NzValue()
  125. );
  126. }
  127. protected function GetSelfFilter() : string {
  128. return $this->GetTable_Keyed()->GetKeyName().'='.$this->GetKeyValue_safe();
  129. }
  130.  
  131. // -- IDENTITY -- //
  132. // ++ DB OUTPUT ++ //
  133.  
  134. /*----
  135.   HISTORY:
  136.   2019-06-11 removed 2nd parameter; massage data with $db->SanitizeValueArray() instead
  137.   2019-07-26 moved from fcRecord_keyed_single to fcRecord_keyed,
  138.   because it should work as long as GetSelfFilter() is defined.
  139.   2019-11-26 creating DoUpdate() (and DoInsert()) as a way of transitioning to usage of status-object returns
  140.   */
  141. public function DoUpdate(array $arChg) : cUpdateStatus {
  142. $sqlFilt = $this->GetSelfFilter();
  143. $oStat = $this->Space()->Table()->GetIt()->DoUpdate($arChg,$sqlFilt);
  144. return $oStat;
  145. }
  146.  
  147. // -- OUTPUT -- //
  148.  
  149. ## -- TRAIT: KEYED RECORD -- ##
  150. }
  151.