xref: /plugin/struct/meta/SearchSQLBuilder.php (revision fd9c77d37616e4134cc6c576afca1ff9ca472368)
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