|
|
(8 intermediate revisions by 2 users not shown) |
Line 1: |
Line 1: |
| =the Features system=
| | {{fmt/title|the Features system}} |
| A "Feature", in this context, is a set of classes which work together to handle a particular type of data, traditionally represented by a table in a database. The class-set for each Feature is primarily organized around a descendent (subclass) of the Feature class. | | A "Feature", in this context, is a set of classes which work together to handle a particular type of data, traditionally represented by a table in a database. The class-set for each Feature is primarily organized around a descendent (subclass) of the Feature class. |
|
| |
|
| Each Feature subclass represents a singleton object and has: | | Each Feature subclass represents a singleton object and has a SpecSlug() static method for identifying its table specs in the config data. |
| * a SpecSlug() for identifying its table specs in the config data
| | ==Pages== |
| * an ActionKey() for being identified as the object to handle URL data | | * [[/rendering]]: how Feature data is converted to display output (typically HTML) |
| ==Notes== | | * [[/access]]: how to access a Feature object |
| I'm still trying to work out how this works. There are currently table specs and Feature registration.
| | ==Related== |
| | * {{l/ver|registry/feature|feature registry}}: translates URI slugs into Feature objects |
| | * {{l/ver|registry/table|table registry}}: allows cross-referencing of tables within an app |
| | ==Connections== |
| | A Feature that uses a Table doesn't know directly about that Table. The way the Table is accessed goes like this: |
| | * Each Feature corresponds to a Spec, which typically includes the Table name and may include other information such as the name of the key field (typically "ID"). |
| | * The Feature doesn't contain the Spec, but looks it up when needed (<code>GetMySpec()</code>). |
| | * Each Feature works with stored data via its Storage Row, which looks up the Table in the Table Registry using the Feature's Spec. |
|
| |
|
| Table specs are used/required by Card objects.
| | Note that Storage doesn't ''have'' to use a database; the Dropin-viewer Dropin just uses an in-memory array generated from reading the "dropins" folder. |
| | |
| ==Table Spec classes==
| |
| Table spec classes from /data/db/tables/spec.php:
| |
| <syntaxhighlight lang=php>
| |
| class cQuery {
| |
| private $ar;
| |
| | |
| public function __construct(array $arArgs) { $this->ar = $arArgs; }
| |
|
| |
| protected function HasArg(string $sName) : bool { return array_key_exists($this->ar,$sName); }
| |
| protected function GetArg(string $sName) : string { return $this->ar[$sName]; }
| |
|
| |
| public function SpecName() : string { return $this->GetArg('.'); }
| |
| public function DatabaseSlug() : string { return $this->GetArg('db'); }
| |
|
| |
| public function Database() : FD\caDatabase { return FD\csStocker::MakeEngine($this->DatabaseSlug()); }
| |
| }
| |
| class cTable extends cQuery {
| |
| | |
| public function TableSQName() : string { return $this->GetArg('sql'); }
| |
|
| |
| public function Table() : cTabloid { return $this->Database()->MakeTable($this->TableSQName()); }
| |
| }
| |
| class cTableK1I extends cTable {
| |
| public function KeyName() : string { return $this->GetArg('key'); }
| |
| public function ActionSlug() : string {
| |
| $skName = 'aslug';
| |
| if ($this->HasArg($skName)) {
| |
| return $this->GetArg($skName);
| |
| } else {
| |
| return $this->SpecName();
| |
| }
| |
| }
| |
| }
| |
| </syntaxhighlight>
| |
| ==Table Spec registration==
| |
| Table registration for the {{l/same|login}} Feature in /config/portable/tables-user.php:
| |
| <syntaxhighlight lang=php>
| |
| class csTableSpecs extends FC\csaTableSpecs {
| |
|
| |
| [ constants omitted ]
| |
| | |
| static protected function DefaultDatabaseSlug() : string { return self::KS_DB; }
| |
|
| |
| static public function OnSetup() {
| |
| echo 'SETUP: '.__CLASS__.'<br>';
| |
| self::Add1KeyTable (self::KS_ACCT, ['sql'=>'user_account' ,'aslug'=>'uacct']);
| |
| self::AddXrefTable (self::KS_AXG, ['sql'=>'uacct_x_ugroup']);
| |
| self::Add1KeyTable (self::KS_CLI, ['sql'=>'user_client' ,'aslug'=>'ucli']);
| |
| self::Add1KeyTable (self::KS_GROUP,['sql'=>'user_group' ,'aslug'=>'ugrp']);
| |
| self::AddXrefTable (self::KS_GXP, ['sql'=>'ugroup_x_upermit']);
| |
| self::Add1KeyTable (self::KS_PERM, ['sql'=>'user_permit' ,'aslug'=>'uperm']);
| |
| self::Add1KeyTable (self::KS_SESS, ['sql'=>'user_session' ,'aslug'=>'usess']);
| |
| self::Add1KeyTable (self::KS_TOK, ['sql'=>'user_token']);
| |
| #csStocker::AddTableSpec(self::KS_Q, new FDS\cQuery(['db'=>self::KS_DB]));
| |
| self::AddQuery (self::KS_Q, []);
| |
| echo 'Contents after setup: '.FD\cTabloid::ObjectRegistry()->DumpFull();
| |
| }
| |
|
| |
| static public function Account() : FDS\cTable { return F\data\csStocker::GetTableSpec(self::KS_ACCT); }
| |
| static public function AccountXGroup() : FDS\cTable { return F\data\csStocker::GetTableSpec(self::KS_AXG); }
| |
| static public function Group() : FDS\cTable { return F\data\csStocker::GetTableSpec(self::KS_GROUP); }
| |
| static public function GroupXPermit() : FDS\cTable { return F\data\csStocker::GetTableSpec(self::KS_GXP); }
| |
| static public function Permit() : FDS\cTable { return F\data\csStocker::GetTableSpec(self::KS_PERM); }
| |
| static public function Session() : FDS\cTable { return F\data\csStocker::GetTableSpec(self::KS_SESS); }
| |
| static public function Tokens() : FDS\cTable { return F\data\csStocker::GetTableSpec(self::KS_TOK); }
| |
| }
| |
| </syntaxhighlight>
| |
| ==Table registration services==
| |
| Table registration services in /config/portable/defaults.php:
| |
| <syntaxhighlight lang=php>
| |
| abstract class csaTableSpecs {
| |
| static abstract protected function DefaultDatabaseSlug() : string;
| |
|
| |
| static protected function Add1KeyTable(string $sSlug, array $arSpec) {
| |
| $arSpec['.'] = $sSlug;
| |
| $arSpec['key'] ='ID';
| |
| $arSpec['db'] = static::DefaultDatabaseSlug();
| |
| $oSpec = new FDS\cTableK1I($arSpec);
| |
| FD\cTabloid::ObjectRegistry()->AddItem($sSlug, $oSpec);
| |
| }
| |
| static protected function AddXrefTable(string $sSlug, array $arSpec) {
| |
| $arSpec['.'] = $sSlug;
| |
| $arSpec['db'] = static::DefaultDatabaseSlug();
| |
| $oSpec = new FDS\cTable($arSpec);
| |
| FD\cTabloid::ObjectRegistry()->AddItem($sSlug, $oSpec);
| |
| }
| |
| static protected function AddQuery(string $sSlug, array $arSpec) {
| |
| $arSpec['.'] = $sSlug;
| |
| $arSpec['db'] = static::DefaultDatabaseSlug();
| |
| $oSpec = new FDS\cQuery($arSpec);
| |
| FD\cTabloid::ObjectRegistry()->AddItem($sSlug, $oSpec);
| |
| }
| |
| }
| |
| </syntaxhighlight>
| |
| ==Feature registration==
| |
| Feature registration (incomplete) from /login/base/stocker.php:
| |
| <syntaxhighlight lang=php>
| |
| class csStocker extends FD\csaStocker {
| |
| static public function OnPreLoad() {
| |
| $oReg = caFeature::FeatureClassRegistry();
| |
| $oReg->AddFeature(account\cFeature::class);
| |
| $oReg->AddFeature(client\cFeature::class);
| |
| $oReg->AddFeature(session\cFeature::class);
| |
| // IN PROGRESS
| |
| }
| |
|
| |
| static public function Accounts() : caFeature { return self::FetchFeature(account\cFeature::SpecSlug()); }
| |
| static public function AcctXGroup() : caFeature { return self::FetchFeature(ctGroupsForAcct::SpecSlug()); }
| |
| static public function Clients() : caFeature { return self::FetchFeature(client\cFeature::SpecSlug()); }
| |
| static public function Groups() : caFeature { return self::FetchFeature(ctGroups::SpecSlug()); }
| |
| static public function Permits() : caFeature { return self::FetchFeature(ctPermits::SpecSlug()); }
| |
| static public function PermitsQuery() : caFeature { return self::FetchFeature(cqtPermits::SpecSlug()); }
| |
| static public function Sessions() : caFeature { return self::FetchFeature(session\cFeature::SpecSlug()); }
| |
|
| |
| static public function AccountsFetcher() : FD\cSelectFetcher {
| |
| return self::MakeObject(account\cFetcher::class); }
| |
| }
| |
| </syntaxhighlight>
| |