1*fd9c77d3SAndreas Gohr<?php 2*fd9c77d3SAndreas Gohr 3*fd9c77d3SAndreas Gohrnamespace dokuwiki\plugin\struct\meta; 4*fd9c77d3SAndreas Gohr 5*fd9c77d3SAndreas Gohr/** 6*fd9c77d3SAndreas Gohr * Creates the SQL query using the QueryBuilder 7*fd9c77d3SAndreas Gohr * 8*fd9c77d3SAndreas Gohr * @internal This class is used by the Search class and should probably never be used directly 9*fd9c77d3SAndreas Gohr */ 10*fd9c77d3SAndreas Gohrclass SearchSQLBuilder 11*fd9c77d3SAndreas Gohr{ 12*fd9c77d3SAndreas Gohr 13*fd9c77d3SAndreas Gohr /** @var QueryBuilder */ 14*fd9c77d3SAndreas Gohr protected $qb; 15*fd9c77d3SAndreas Gohr 16*fd9c77d3SAndreas Gohr /** 17*fd9c77d3SAndreas Gohr * SearchSQLBuilder constructor. 18*fd9c77d3SAndreas Gohr */ 19*fd9c77d3SAndreas Gohr public function __construct() 20*fd9c77d3SAndreas Gohr { 21*fd9c77d3SAndreas Gohr $this->qb = new QueryBuilder(); 22*fd9c77d3SAndreas Gohr } 23*fd9c77d3SAndreas Gohr 24*fd9c77d3SAndreas Gohr /** 25*fd9c77d3SAndreas Gohr * Add the schemas to the query 26*fd9c77d3SAndreas Gohr * 27*fd9c77d3SAndreas Gohr * @param Schema[] $schemas Schema names to query 28*fd9c77d3SAndreas Gohr */ 29*fd9c77d3SAndreas Gohr public function addSchemas($schemas) 30*fd9c77d3SAndreas Gohr { 31*fd9c77d3SAndreas Gohr // basic tables 32*fd9c77d3SAndreas Gohr $first_table = ''; 33*fd9c77d3SAndreas Gohr foreach ($schemas as $schema) { 34*fd9c77d3SAndreas Gohr $datatable = 'data_' . $schema->getTable(); 35*fd9c77d3SAndreas Gohr if ($first_table) { 36*fd9c77d3SAndreas Gohr // follow up tables 37*fd9c77d3SAndreas Gohr $this->qb->addLeftJoin($first_table, $datatable, $datatable, "$first_table.pid = $datatable.pid"); 38*fd9c77d3SAndreas Gohr } else { 39*fd9c77d3SAndreas Gohr // first table 40*fd9c77d3SAndreas Gohr $this->qb->addTable($datatable); 41*fd9c77d3SAndreas Gohr 42*fd9c77d3SAndreas Gohr // add conditional page clauses if pid has a value 43*fd9c77d3SAndreas Gohr $subAnd = $this->qb->filters()->whereSubAnd(); 44*fd9c77d3SAndreas Gohr $subAnd->whereAnd("$datatable.pid = ''"); 45*fd9c77d3SAndreas Gohr $subOr = $subAnd->whereSubOr(); 46*fd9c77d3SAndreas Gohr $subOr->whereAnd("GETACCESSLEVEL($datatable.pid) > 0"); 47*fd9c77d3SAndreas Gohr $subOr->whereAnd("PAGEEXISTS($datatable.pid) = 1"); 48*fd9c77d3SAndreas Gohr $subOr->whereAnd('(ASSIGNED = 1 OR ASSIGNED IS NULL)'); 49*fd9c77d3SAndreas Gohr 50*fd9c77d3SAndreas Gohr // add conditional schema assignment check 51*fd9c77d3SAndreas Gohr $this->qb->addLeftJoin( 52*fd9c77d3SAndreas Gohr $datatable, 53*fd9c77d3SAndreas Gohr 'schema_assignments', 54*fd9c77d3SAndreas Gohr '', 55*fd9c77d3SAndreas Gohr "$datatable.pid != '' 56*fd9c77d3SAndreas Gohr AND $datatable.pid = schema_assignments.pid 57*fd9c77d3SAndreas Gohr AND schema_assignments.tbl = '{$schema->getTable()}'" 58*fd9c77d3SAndreas Gohr ); 59*fd9c77d3SAndreas Gohr 60*fd9c77d3SAndreas Gohr $this->qb->addSelectColumn($datatable, 'rid'); 61*fd9c77d3SAndreas Gohr $this->qb->addSelectColumn($datatable, 'pid', 'PID'); 62*fd9c77d3SAndreas Gohr $this->qb->addSelectColumn($datatable, 'rev'); 63*fd9c77d3SAndreas Gohr $this->qb->addSelectColumn('schema_assignments', 'assigned', 'ASSIGNED'); 64*fd9c77d3SAndreas Gohr $this->qb->addGroupByColumn($datatable, 'pid'); 65*fd9c77d3SAndreas Gohr $this->qb->addGroupByColumn($datatable, 'rid'); 66*fd9c77d3SAndreas Gohr 67*fd9c77d3SAndreas Gohr $first_table = $datatable; 68*fd9c77d3SAndreas Gohr } 69*fd9c77d3SAndreas Gohr // phpcs:ignore 70*fd9c77d3SAndreas Gohr $this->qb->filters()->whereAnd("( (IS_PUBLISHER($datatable.pid) AND $datatable.latest = 1) OR (IS_PUBLISHER($datatable.pid) !=1 AND $datatable.published = 1) )"); 71*fd9c77d3SAndreas Gohr } 72*fd9c77d3SAndreas Gohr } 73*fd9c77d3SAndreas Gohr 74*fd9c77d3SAndreas Gohr /** 75*fd9c77d3SAndreas Gohr * Add the columns to select, handling multis 76*fd9c77d3SAndreas Gohr * 77*fd9c77d3SAndreas Gohr * @param Column[] $columns 78*fd9c77d3SAndreas Gohr */ 79*fd9c77d3SAndreas Gohr public function addColumns($columns) 80*fd9c77d3SAndreas Gohr { 81*fd9c77d3SAndreas Gohr $sep = Search::CONCAT_SEPARATOR; 82*fd9c77d3SAndreas Gohr $n = 0; 83*fd9c77d3SAndreas Gohr foreach ($columns as $col) { 84*fd9c77d3SAndreas Gohr $CN = 'C' . $n++; 85*fd9c77d3SAndreas Gohr 86*fd9c77d3SAndreas Gohr if ($col->isMulti()) { 87*fd9c77d3SAndreas Gohr $datatable = "data_{$col->getTable()}"; 88*fd9c77d3SAndreas Gohr $multitable = "multi_{$col->getTable()}"; 89*fd9c77d3SAndreas Gohr $MN = $this->qb->generateTableAlias('M'); 90*fd9c77d3SAndreas Gohr 91*fd9c77d3SAndreas Gohr $this->qb->addLeftJoin( 92*fd9c77d3SAndreas Gohr $datatable, 93*fd9c77d3SAndreas Gohr $multitable, 94*fd9c77d3SAndreas Gohr $MN, 95*fd9c77d3SAndreas Gohr "$datatable.pid = $MN.pid AND $datatable.rid = $MN.rid AND 96*fd9c77d3SAndreas Gohr $datatable.rev = $MN.rev AND 97*fd9c77d3SAndreas Gohr $MN.colref = {$col->getColref()}" 98*fd9c77d3SAndreas Gohr ); 99*fd9c77d3SAndreas Gohr 100*fd9c77d3SAndreas Gohr $col->getType()->select($this->qb, $MN, 'value', $CN); 101*fd9c77d3SAndreas Gohr $sel = $this->qb->getSelectStatement($CN); 102*fd9c77d3SAndreas Gohr $this->qb->addSelectStatement("GROUP_CONCAT_DISTINCT($sel, '$sep')", $CN); 103*fd9c77d3SAndreas Gohr } else { 104*fd9c77d3SAndreas Gohr $col->getType()->select($this->qb, 'data_' . $col->getTable(), $col->getColName(), $CN); 105*fd9c77d3SAndreas Gohr $this->qb->addGroupByStatement($CN); 106*fd9c77d3SAndreas Gohr } 107*fd9c77d3SAndreas Gohr } 108*fd9c77d3SAndreas Gohr } 109*fd9c77d3SAndreas Gohr 110*fd9c77d3SAndreas Gohr /** 111*fd9c77d3SAndreas Gohr * Add the filters to the query 112*fd9c77d3SAndreas Gohr * 113*fd9c77d3SAndreas Gohr * All given filters are added to their own subclause. 114*fd9c77d3SAndreas Gohr * 115*fd9c77d3SAndreas Gohr * @param array[] $filters 116*fd9c77d3SAndreas Gohr */ 117*fd9c77d3SAndreas Gohr public function addFilters($filters) 118*fd9c77d3SAndreas Gohr { 119*fd9c77d3SAndreas Gohr if (!$filters) return; // ignore empty filters 120*fd9c77d3SAndreas Gohr 121*fd9c77d3SAndreas Gohr $subClause = $this->qb->filters()->where('AND'); 122*fd9c77d3SAndreas Gohr 123*fd9c77d3SAndreas Gohr foreach ($filters as $filter) { 124*fd9c77d3SAndreas Gohr /** @var Column $col */ 125*fd9c77d3SAndreas Gohr list($col, $value, $comp, $op) = $filter; 126*fd9c77d3SAndreas Gohr 127*fd9c77d3SAndreas Gohr $datatable = "data_{$col->getTable()}"; 128*fd9c77d3SAndreas Gohr $multitable = "multi_{$col->getTable()}"; 129*fd9c77d3SAndreas Gohr 130*fd9c77d3SAndreas Gohr /** @var $col Column */ 131*fd9c77d3SAndreas Gohr if ($col->isMulti()) { 132*fd9c77d3SAndreas Gohr $MN = $this->qb->generateTableAlias('MN'); 133*fd9c77d3SAndreas Gohr 134*fd9c77d3SAndreas Gohr $this->qb->addLeftJoin( 135*fd9c77d3SAndreas Gohr $datatable, 136*fd9c77d3SAndreas Gohr $multitable, 137*fd9c77d3SAndreas Gohr $MN, 138*fd9c77d3SAndreas Gohr "$datatable.pid = $MN.pid AND $datatable.rid = $MN.rid AND 139*fd9c77d3SAndreas Gohr $datatable.rev = $MN.rev AND 140*fd9c77d3SAndreas Gohr $MN.colref = {$col->getColref()}" 141*fd9c77d3SAndreas Gohr ); 142*fd9c77d3SAndreas Gohr $coltbl = $MN; 143*fd9c77d3SAndreas Gohr $colnam = 'value'; 144*fd9c77d3SAndreas Gohr } else { 145*fd9c77d3SAndreas Gohr $coltbl = $datatable; 146*fd9c77d3SAndreas Gohr $colnam = $col->getColName(); 147*fd9c77d3SAndreas Gohr } 148*fd9c77d3SAndreas Gohr 149*fd9c77d3SAndreas Gohr $col->getType()->filter($subClause, $coltbl, $colnam, $comp, $value, $op); // type based filter 150*fd9c77d3SAndreas Gohr } 151*fd9c77d3SAndreas Gohr } 152*fd9c77d3SAndreas Gohr 153*fd9c77d3SAndreas Gohr /** 154*fd9c77d3SAndreas Gohr * Add the sort by clauses to the query 155*fd9c77d3SAndreas Gohr * 156*fd9c77d3SAndreas Gohr * We always sort by the single val column which contains a copy of the first value of the multi column 157*fd9c77d3SAndreas Gohr * 158*fd9c77d3SAndreas Gohr * @param array[] $sorts 159*fd9c77d3SAndreas Gohr */ 160*fd9c77d3SAndreas Gohr public function addSorts($sorts) 161*fd9c77d3SAndreas Gohr { 162*fd9c77d3SAndreas Gohr foreach ($sorts as $sort) { 163*fd9c77d3SAndreas Gohr list($col, $asc, $nc) = $sort; 164*fd9c77d3SAndreas Gohr /** @var $col Column */ 165*fd9c77d3SAndreas Gohr $colname = $col->getColName(false); 166*fd9c77d3SAndreas Gohr if ($nc) $colname .= ' COLLATE NOCASE'; 167*fd9c77d3SAndreas Gohr $col->getType()->sort($this->qb, 'data_' . $col->getTable(), $colname, $asc ? 'ASC' : 'DESC'); 168*fd9c77d3SAndreas Gohr } 169*fd9c77d3SAndreas Gohr } 170*fd9c77d3SAndreas Gohr 171*fd9c77d3SAndreas Gohr /** 172*fd9c77d3SAndreas Gohr * Get the SQL query and parameters 173*fd9c77d3SAndreas Gohr * 174*fd9c77d3SAndreas Gohr * @return array ($sql, $params) 175*fd9c77d3SAndreas Gohr */ 176*fd9c77d3SAndreas Gohr public function getSQL() 177*fd9c77d3SAndreas Gohr { 178*fd9c77d3SAndreas Gohr return $this->qb->getSQL(); 179*fd9c77d3SAndreas Gohr } 180*fd9c77d3SAndreas Gohr} 181