Ferreteria/archive/smw-base-v2.php: Difference between revisions

From Woozle Writes Code
Jump to navigation Jump to search
m (7 revisions imported: moving this project here)
 
(4 intermediate revisions by one other user not shown)
Line 2: Line 2:
<php>
<php>
<?php
<?php
/*
/* ****
   PURPOSE: Semantic MediaWiki interface classes
  FILE: smw-base-v2.php
     The existing class library is poorly documented, lacking a stable API, and difficult to use.
   PURPOSE: Semantic MediaWiki interface classes for SMW schema version 2
     This class set goes directly to the data structures -- which may change over time, but the changes
     I'm calling it version 2 (instead of version 1) because there's evidence of at least one prior db schema version.
      should be easier to puzzle out than changes to the SMW class library.
     It seems to apply to SMW versions prior to 1.18 or possibly 1.19.
    I will probably not be maintaining it any further.
   REQUIRES: data.php
   REQUIRES: data.php
   TODO: this goes directly to the DatabaseMysql class for some functions; needs to be generalized to use abstract wrappers.
   TODO: this goes directly to the DatabaseMysql class for some functions; needs to be generalized to use abstract wrappers.
Line 14: Line 15:
     2013-01-24 moved clsMWData and clsDataResult_MW from to mw-base.php
     2013-01-24 moved clsMWData and clsDataResult_MW from to mw-base.php
*/
*/
define('WZL_SMW',TRUE); // flag to let other libraries know these classes are available
// is this actually needed?
clsLibMgr::AddClass('clsMWData','mw-base'); // 'mw-base' must be defined by caller
/*====
/*====
   PURPOSE: SMW-specific data functions
   PURPOSE: version-2-specific class methods for clsSMWData
*/
*/
class clsSMWData extends clsMWData {
class clsSMWData_version extends clsMWData {
 
    /*----
      ACTION: Convert title into normalized DB-key format
    */
    static public function NormalizeTitle($iTitle,$iNameSpace) {
$strTitle = Sanitizer::decodeCharReferencesAndNormalize($iTitle); // convert HTML entities
$strTitle = Title::capitalize($strTitle,$iNameSpace); // initial caps, if needed
$strTitle = str_replace( ' ', '_',$strTitle); // convert spaces to underscores
return $strTitle;
    }
    /*----
      ACTION: convert DB-key formatted title into display format
Basically, just convert underscores to spaces.
    */
    static public function VisualizeTitle($iTitle) {
$strTitle = str_replace('_',' ',$iTitle); // convert spaces to underscores
return $strTitle;
    }
     /*----
     /*----
       RETURNS: array of pages where the given property has the given value
       RETURNS: array of pages where the given property has the given value
Line 55: Line 33:


// look up property's SMW ID
// look up property's SMW ID
 
$sqlProp = SQLValue(static::NormalizeTitle($iPropName,SMW_NS_PROPERTY));
$sqlProp = SQLValue(self::NormalizeTitle($iPropName,SMW_NS_PROPERTY));
$sql = "SELECT smw_id FROM smw_ids WHERE (smw_title=$sqlProp) LIMIT 1;";
$sql = "SELECT smw_id FROM smw_ids WHERE (smw_title=$sqlProp) LIMIT 1;";
$rs = $this->DataSet($sql);
$rs = $this->DataSet($sql);
Line 66: Line 43:
}
}


$sqlVal = SQLValue(self::NormalizeTitle($iPropValue,SMW_NS_PROPERTY));
// look up value's SMW ID (TODO: this is not always applicable)
$sqlVal = SQLValue(static::NormalizeTitle($iPropValue,SMW_NS_PROPERTY));
$sql = "SELECT smw_id FROM smw_ids WHERE (smw_title=$sqlVal) LIMIT 1;";
$sql = "SELECT smw_id FROM smw_ids WHERE (smw_title=$sqlVal) LIMIT 1;";
$rs = $this->DataSet($sql);
$rs = $this->DataSet($sql);
Line 187: Line 165:
}
}


class w3smwPage {
class w3smwPage_version {
    private $objPageEnv; // wiki page environment object (from w3tpl)
    private $mwoTitle; // MediaWiki Title object, if set
 
    public function __construct(w3tpl_module $iPageEnv) {
$this->objPageEnv = $iPageEnv;
    }
    /*----
      RETURNS: database engine from the wiki page environment
    */
    protected function Engine() {
return $this->PageEnv()->Engine();
    }
    protected function PageEnv() {
return $this->objPageEnv;
    }
 
    public function Use_TitleObject(Title $iTitle) {
$this->mwoTitle = $iTitle;
    }
    public function MW_Object() {
return $this->mwoTitle;
    }
    public function Use_GlobalTitle() {
global $wgTitle;
$this->Use_TitleObject($wgTitle);
    }
    public function Use_Title_Named($iName) {
$mwoTitle = Title::newFromDBkey($iName);
$this->Use_TitleObject($mwoTitle);
    }
    public function Use_Title_Keyed($iName,$iSpace=NS_MAIN) {
$mwoTitle = Title::newFromText($iName,$iSpace);
$this->Use_TitleObject($mwoTitle);
    }
    public function TitleKey() {
return $this->mwoTitle->getDBkey();
    }
    public function TitleShown() {
return $this->mwoTitle->getText();
    }
    public function TitleFull() {
return $this->mwoTitle->getPrefixedText();
    }
    public function Nspace() {
return $this->mwoTitle->getNamespace();
    }
     /*----
     /*----
       TODO:
       TODO:
Line 339: Line 271:


return $out;
return $out;
    }
    /*----
      RETURNS: array if multiple values found, otherwise just the value string
      ASSUMES: smw_sortkey is the non-underscored version of smw_title
      USAGE: when there's no reason to expect multiple values
    */
    public function GetPropVal($iPropName) {
$ar = $this->GetPropVals($iPropName);
$cnt = count($ar);
if ($cnt > 1) {
    return $ar;
} elseif ($cnt == 1) {
    return array_shift($ar); // return just the first element
} else {
    return NULL; // nothing found
}
/*
$rs = $this->GetPropData($iPropName);
if ($rs->HasRows()) {
    if ($rs->RowCount() == 1) {
$rs->NextRow(); // load the first row
$strVal = $rs->Value('value_xsd');
return $strVal;
    } else {
while ($rs->NextRow()) {
    $strVal = $rs->Value('value_xsd');
    $out[$id] = $strVal;
}
return $out;
    }
} else {
    return NULL; // problem with the query
}
*/
    }
    /*----
      RETURNS: nicely-formatted list of property values with links
Sometimes it's just easier to use the existing code.
This is just GetProperty_OLD() with the no-link option removed.
    */
    public function GetPropLinks($iPropName) {
$strPgTitle = $this->TitleKey();
$arArgs = array($strPgTitle,'?'.$iPropName);
// get list of targets (usually just one, but could be more)
$htVal = SMWQueryProcessor::getResultFromFunctionParams(
  $arArgs,
  SMW_OUTPUT_FILE,
  SMWQueryProcessor::INLINE_QUERY,
  TRUE); // treat as if #show (rather than #ask)
return $htVal;
    }
    /*----
      NOTE: This is the old klugey version of GetPropLinks(), which I'm keeping here (for now) for posterity.
    */
    public function GetProperty_OLD($iPropName,array $iarOptions=NULL) {
$strPgTitle = $this->TitleKey();
$arArgs = array($strPgTitle,'?'.$iPropName);
if (is_array($iarOptions)) {
    $doLink = NzArray($iarOptions,'link');
} else {
    $doLink = FALSE;
}
if (!$doLink) {
    $arArgs[] = 'link=none';
    // without this, SMW defaults to using links
}
// get list of targets (usually just one, but could be more)
$htVal = SMWQueryProcessor::getResultFromFunctionParams(
  $arArgs,
  SMW_OUTPUT_FILE,
  SMWQueryProcessor::INLINE_QUERY,
  TRUE); // treat as if #show (rather than #ask)
return $htVal;
     }
     }
}
}
Line 426: Line 280:
  $this->ClassSng('rcSMW_PageProp');
  $this->ClassSng('rcSMW_PageProp');
     }
     }
}
class clsDataResult_SMW extends clsDataResult {
    public function is_okay() {
    }
    /*----
      ACTION: set the record pointer so the first row in the set will be read next
    */
    public function do_rewind() {
    }
    /*----
      ACTION: Fetch the first/next row of data from a result set
    */
    public function get_next() {
    }
    /*----
      ACTION: Return the number of rows in the result set
    */
    public function get_count() {
    }
    /*----
      ACTION: Return whether row currently has data.
    */
    public function is_filled() {
    }
}
/*
2012-08-13 This function was actually written for InstaGov, but appears to represent a significant amount of time-investment in
  figuring out how to access SMW data. I don't need it for IG anymore (for now) because I'm doing things differently there now,
  but it could well be useful for creating functions here in smw-base.
*/
/*-----
  TAG: <igov>
  PARAMS:
    list=answers
    target=(name of page)
    name=(name of output array for each row)
    vpage=[name of var in which to store each page name]
*/
function efIGov( $input, $args, $parser ) {
    global $iggProperty_forResponses,$iggProperty_namespace;
    $objArgs = new W3HookArgs($args);
    if ($objArgs->Exists('list')) {
if ($objArgs->Exists('name')) {
    $strNameRaw = $objArgs->GetArgVal('name');
    $strName = trim($strNameRaw);
    $objVar = new clsW3VarName();
    $objVar->ParseName($strName); // resolve any indirection (e.g. $var)
} else {
    $out = '[must set output array name using <b>name=</b>]';
    return;
}
$dbr =& wfGetDB( DB_SLAVE ); // read-only db object
// this SQL (when finished) gets a list of pages with the SMW properties we're looking for
$sqlSMW = 'SELECT
s.smw_title AS s_t,
s.smw_namespace AS s_ns,
pg.page_id
FROM
(((smw_rels2 AS x
  LEFT JOIN smw_ids AS s ON x.s_id=s.smw_id)
  LEFT JOIN smw_ids as p ON x.p_id=p.smw_id)
  LEFT JOIN smw_ids AS o ON x.o_id=o.smw_id)
  LEFT JOIN page AS pg ON ((s.smw_namespace=pg.page_namespace) AND (s.smw_title=pg.page_title))
WHERE
(p.smw_title="'.$iggProperty_forResponses.'") AND
(p.smw_namespace='.$iggProperty_namespace.') AND
';
$strType = $objArgs->GetVal('list');
$strTarg = $objArgs->GetVal('target');
$strPgVar = $objArgs->GetVal('vpage');
$doHide = isset($args['hide']); // TRUE = only output <echo> sections
$objTarg = Title::newFromText($strTarg);
$sqlTarg = SQLValue($objTarg->getDBkey());
$intNS = $objTarg->mNamespace; // is there not a function for this?
switch($strType) {
  case 'answers':
    $sqlFiltAdd = '(o.smw_title='.$sqlTarg.') AND (o.smw_namespace='.$intNS.')';
    break;
  default:
    $out = '[unknown list type]';
}
$sqlFull = $sqlSMW.$sqlFiltAdd;
try {
    $res = $dbr->query($sqlFull);
}
catch (Exception $e) {
    $out = "W3TPL encountered a database error - ''".$dbr->lastError()."'' - from this SQL:<pre>$sqlFull</pre>";
    return $parser->recursiveTagParse($out);
}
$fPerRow = function($iRow) use($parser,$strName,$dbr,$objVar,$strPgVar) {
    global $wgW3_data;
    $idPage = $iRow->page_id;
    $sqlMW = 'SELECT pp_propname, pp_value FROM page_props WHERE pp_page=';
    $sqlFull = $sqlMW.$idPage;
    $out = NULL;
    try {
$res = $dbr->query($sqlFull);
    }
    catch (Exception $e) {
$out = "W3TPL encountered a database error - ''".$dbr->lastError()."'' - from this SQL:<pre>$sqlFull</pre>";
return $parser->recursiveTagParse($out);
   
    }
    $objPage = Title::newFromID($idPage);
    $objVar->Name = $strPgVar;
    $objVar->Value = $objPage->getPrefixedText();
    $objVar->Store();
    while( $row = $dbr->fetchObject ( $res ) ) {
$strKey = $row->pp_propname;
$txtVal = $row->pp_value;
$objVar->Name = $strKey;
$objVar->Value = $txtVal;
$objVar->Store();
    }
    return $out;
};
$out = ProcessRows($dbr,$res,$strName,$parser,$input,$doHide,$fPerRow);
    }
/* probably not using this after all
if (isset($args['type'])) {
    $strType = $args['type'];
    switch($strType) {
      case 'question':
$out = '[question is {'.$input.'}]';
break;
      case 'answer':
$out = '[rnsr is {'.$input.'}]';
break;
      default:
$out = '[unknown type]';
    }
} else {
    $out = '[type not specified]';
}
*/
    return $out;
}
}
</php>
</php>

Latest revision as of 16:42, 22 May 2022

Code

<php> <?php /* ****

 FILE: smw-base-v2.php
 PURPOSE: Semantic MediaWiki interface classes for SMW schema version 2
   I'm calling it version 2 (instead of version 1) because there's evidence of at least one prior db schema version.
   It seems to apply to SMW versions prior to 1.18 or possibly 1.19.
   I will probably not be maintaining it any further.
 REQUIRES: data.php
 TODO: this goes directly to the DatabaseMysql class for some functions; needs to be generalized to use abstract wrappers.
 HISTORY:
   2012-01-22 started
   2012-09-17 useful pieces working
   2013-01-24 moved clsMWData and clsDataResult_MW from to mw-base.php
  • /

/*====

 PURPOSE: version-2-specific class methods for clsSMWData
  • /

class clsSMWData_version extends clsMWData {

   /*----
     RETURNS: array of pages where the given property has the given value

(or, if iPropValue is left out, just having the given property)

     INPUT: $iarFilt is an array of properties and value restrictions, all of which must be met

[property name] => value filter value filter should be a comparison, e.g. >0 or ="things"

     RETURNS: array of rows\ arrays
     USED BY: psycrit.w3f_Show_Response_Header()
     HISTORY:

2012-11-01 revised to look up SMW ID first, reducing CPU. Return array format has SMW ID as key.

   */
   public function GetPages_forPropVal($iPropName,$iPropValue) {

// look up property's SMW ID $sqlProp = SQLValue(static::NormalizeTitle($iPropName,SMW_NS_PROPERTY)); $sql = "SELECT smw_id FROM smw_ids WHERE (smw_title=$sqlProp) LIMIT 1;"; $rs = $this->DataSet($sql); if ($rs->HasRows()) { $rs->NextRow(); // should be only one row -- get it. $idProp = $rs->Value('smw_id'); } else { // TODO: some kind of indication that the property does not seem to even exist }

// look up value's SMW ID (TODO: this is not always applicable) $sqlVal = SQLValue(static::NormalizeTitle($iPropValue,SMW_NS_PROPERTY)); $sql = "SELECT smw_id FROM smw_ids WHERE (smw_title=$sqlVal) LIMIT 1;"; $rs = $this->DataSet($sql); if ($rs->HasRows()) { $rs->NextRow(); // should be only one row -- get it. $idVal = $rs->Value('smw_id'); } else { // TODO: some kind of indication that the value does not seem to even exist throw new exception('No rows returned for query "'.$sql.'"'); }

// find all pages where that property is set

// start with smw_rels2, which is properties that are titles (the default kind of property) // there are probably other tables we need to check, but this will do for immediate needs.

$sql = 'SELECT s_id,' .' s.smw_namespace AS s_namespace' .', CAST(s.smw_title AS char) AS s_title' //.', CAST(p.smw_title AS char) AS p_title' //.', CAST(o.smw_title AS char) AS o_title' .' FROM' .' (smw_rels2 AS r' .' LEFT JOIN smw_ids AS s ON r.s_id=s.smw_id)' //.' LEFT JOIN smw_ids AS p ON r.p_id=p.smw_id)' .' LEFT JOIN smw_ids AS o ON r.o_id=o.smw_id' ." WHERE (o_id=$idVal) AND (p_id=$idProp);"; $rs = $this->DataSet($sql); if ($rs->HasRows()) { $arOut = array(); while ($rs->NextRow()) { $idPage = $rs->Value('s_id'); $arOut[$idPage] = $rs->Values(); } return $arOut; } else { // TODO: some kind of indication that the property was found but no pages matched return NULL; // nothing found }

   }
   public function GetPages_forPropVal_OLD($iPropName,$iPropValue) {

$arOut = NULL;

// 1. check RELS

$sql = 'SELECT' // .' r.s_id,' .' s.smw_title AS page_name,' // needed for output .' s.smw_namespace AS page_space,' // needed for output // .' p.smw_title AS prop_name,' // needed for filter // .' o.smw_title AS prop_val,' // needed for filter .' s.smw_namespace' .' FROM ((smw_rels2 AS r' .' LEFT JOIN smw_ids AS s ON r.s_id=s.smw_id)' .' LEFT JOIN smw_ids AS p ON r.p_id=p.smw_id)' .' LEFT JOIN smw_ids AS o ON r.o_id=o.smw_id' .' WHERE (p.smw_title="'.$iPropName.'") AND (o.smw_title="'.$iPropValue.'");';

$rs = $this->DataSet($sql); if ($rs->HasRows()) { while ($rs->NextRow()) { $objTitle = new w3smwPage($this); $objTitle->Use_Title_Keyed($rs->Value('page_name'),$rs->Value('page_space')); $arOut[] = $objTitle; } }

// 2. check ATTS $sql = 'SELECT' // .' a.s_id,' .' s.smw_title AS page_name,' // needed for output .' s.smw_namespace AS page_space,' // needed for output // .' p.smw_title AS prop_name,' // needed for filter // .' value_xsd AS prop_val,' // needed for filter .' value_num' // might be used later for filter .' FROM (smw_atts2 AS a' .' LEFT JOIN smw_ids AS s ON a.s_id=s.smw_id)' .' LEFT JOIN smw_ids AS p ON a.p_id=p.smw_id' .' WHERE (p.smw_title="'.$iPropName.'") AND (value_xsd="'.$iPropValue.'");';

$rs = $this->DataSet($sql); if ($rs->HasRows()) { while ($rs->NextRow()) { $objTitle = new w3smwPage($this); $objTitle->Use_Title_Keyed($rs->Value('page_name'),$rs->Value('page_space')); $arOut[] = $objTitle; } }

return $arOut;

   }
   /*----
     INPUT: $iarFilt is an array of properties and value restrictions, all of which must be met

[property name] => value filter value filter should be a comparison, e.g. >0 or ="things"

     WHO USES THIS?
     I don't think this ever actually worked, but keep it until certain.
   */
   public function GetPages_forProps_BAD(array $iarFilt) {

$sqlFilt = ; // convert filter into the format MediaWiki's Database class needs: foreach ($iarFilt as $fld => $req) { if ($sqlFilt != ) { $sqlFilt .= ' AND'; } $sqlFilt .= " ('$fld'$req)"; }

$sql = 'SELECT ip.smw_title AS PropName, it.smw_namespace AS PageSpace, it.smw_title AS PageName, t.value_blob AS Value' .' FROM (smw_text2 AS t' .' LEFT JOIN smw_ids AS ip ON t.p_id=ip.smw_id)' .' LEFT JOIN smw_ids AS it ON t.s_id=it.smw_id' .' WHERE'.$sqlFilt .' ORDER BY ip.smw_sortkey, it.smw_sortkey';

// in the future we can use the more operation-specific Database methods, but for now this is simpler: $rs = $this->DataSet($sql); return $rs;

   }

}

class w3smwPage_version {

   /*----
     TODO:

* Translate special properties such as "Date" -> "_dat". Is there a formal list somewhere? Do we have to always check all three tables, or can we assume that if a value is found in one table, it isn't in the others?

   */
   public function GetPropData($iPropName) {

$strPageKey = $this->TitleKey(); $strPropKey = Title::capitalize($iPropName,SMW_NS_PROPERTY); $sqlPageKey = $this->Engine()->SafeParam($strPageKey); $sqlPropKey = $this->Engine()->SafeParam($strPropKey);

$intNSpace = (int)$this->Nspace();

// PART ONE: smw_atts2 $sql = 'SELECT value_xsd, value_num' .' FROM (smw_atts2 AS a' .' LEFT JOIN smw_ids AS s ON a.s_id=s.smw_id)' .' LEFT JOIN smw_ids AS p ON a.p_id=p.smw_id' .' WHERE' ." (p.smw_title = '$sqlPropKey') AND" ." (s.smw_title = '$sqlPageKey') AND" ." (s.smw_namespace = $intNSpace);"; $rs = $this->Engine()->DataSet($sql); $arOut['atts'] = $rs;

// PART TWO: smw_rels2 $sql = 'SELECT' .' r.*,' .' CAST(s.smw_title AS char) AS s_title,' .' CAST(p.smw_title AS char) AS p_title,' .' CAST(o.smw_title AS char) AS o_title' .' FROM' .' ((smw_rels2 AS r' .' LEFT JOIN smw_ids AS s ON r.s_id=s.smw_id)' .' LEFT JOIN smw_ids AS p ON r.p_id=p.smw_id)' .' LEFT JOIN smw_ids AS o ON r.o_id=o.smw_id' .' WHERE' ." (p.smw_title = '$sqlPropKey') AND" ." (s.smw_title = '$sqlPageKey') AND" ." (s.smw_namespace = $intNSpace);"; $rs = $this->Engine()->DataSet($sql); $arOut['rels'] = $rs;

// PART THREE: smw_text2 $sql = 'SELECT' .' s_id,' .' p_id,' .' CAST(s.smw_title AS CHAR) AS s_title,' .' CAST(p.smw_title AS CHAR) AS p_title,' .' CAST(value_blob AS CHAR) AS value' .' FROM (smw_text2 AS t' .' LEFT JOIN smw_ids AS s ON t.s_id=s.smw_id)' .' LEFT JOIN smw_ids AS p ON t.p_id=p.smw_id' .' WHERE' ." (p.smw_title = '$sqlPropKey') AND" ." (s.smw_title = '$sqlPageKey') AND" ." (s.smw_namespace = $intNSpace);"; $rs = $this->Engine()->DataSet($sql); $arOut['text'] = $rs; return $arOut;

   }
   /*----
     RETURNS: array[smw_id] = value
     ASSUMES: smw_sortkey is the non-underscored version of smw_title
     USAGE: when multiple values are expected to happen sometimes
   */
   public function GetPropVals($iPropName) {

$ar = $this->GetPropData($iPropName);

$out = NULL; $idx = 0;

// first check ATTS $rs = $ar['atts']; if ($rs->HasRows()) { while ($rs->NextRow()) { $idx++; $strVal = $rs->Value('value_xsd'); $out[$idx] = $strVal; } }

// next check RELS $rs = $ar['rels']; if ($rs->HasRows()) { while ($rs->NextRow()) { $idx++; $strVal = $rs->Value('o_title'); $out[$idx] = clsSMWData::VisualizeTitle($strVal); } }

// next check TEXT $rs = $ar['text']; if ($rs->HasRows()) { while ($rs->NextRow()) { $idx++; $strVal = $rs->Value('value'); $out[$idx] = $strVal; } }

return $out;

   }

}

class tblSMW_PageProps extends clsTable_indexed {

   public function __construct($iDB) {

parent::__construct($iDB); $this->Name('smw_text2'); $this->ClassSng('rcSMW_PageProp');

   }

} </php>