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