xref: /plugin/struct/meta/Search.php (revision 15929be2d8f1b3ae684ef1e73b344ee5c125512e)
1<?php
2
3namespace plugin\struct\meta;
4
5use Exception;
6
7class Search {
8
9    /** @var Schema[] list of schemas to query */
10    protected $schemas = array();
11
12    /** @var Column[] list of columns to select */
13    protected $columns = array();
14
15    /** @var array the sorting of the result */
16    protected $sortby = array();
17
18    /** @var array the or filters */
19    protected $filteror = array();
20
21    /** @var array the and filters */
22    protected $filterand = array();
23
24    /** @var array list of aliases tables can be referenced by */
25    protected $aliases = array();
26
27    /**
28     * Add a schema to be searched
29     *
30     * Call multiple times for multiple schemas.
31     *
32     * @param string $table
33     * @param string $alias
34     */
35    public function addSchema($table, $alias = '') {
36        $this->schemas[$table] = new Schema($table);
37        if($alias) $this->aliases[$alias] = $table;
38    }
39
40    /**
41     * Add a column to be returned by the search
42     *
43     * Call multiple times for multiple columns. Be sure the referenced tables have been
44     * added before
45     *
46     * @param string $colname may contain an alias
47     */
48    public function addColumn($colname) {
49        $col = $this->findColumn($colname);
50        if(!$col) return; //FIXME do we really want to ignore missing columns?
51        $this->columns[] = $col;
52    }
53
54    /**
55     * Add sorting options
56     *
57     * Call multiple times for multiple columns. Be sure the referenced tables have been
58     * added before
59     *
60     * @param string $colname may contain an alias
61     * @param bool $asc sort direction (ASC = true, DESC = false)
62     */
63    public function addSort($colname, $asc = true) {
64        $col = $this->findColumn($colname);
65        if(!$col) return; //FIXME do we really want to ignore missing columns?
66
67        $this->sortby[] = array($col, $asc);
68    }
69
70    /**
71     * Adds an ORed filter
72     *
73     * @param string $colname may contain an alias
74     * @param string $value
75     * @param string $comp
76     */
77    public function addFilterOr($colname, $value, $comp) {
78        $col = $this->findColumn($colname);
79        if(!$col) return; //FIXME do we really want to ignore missing columns?
80
81        $this->filteror[] = array($col, $value, $comp);
82    }
83
84    /**
85     * Adds an ANDed filter
86     *
87     * @param string $colname may contain an alias
88     * @param string $value
89     * @param string $comp
90     */
91    public function addFilterAnd($colname, $value, $comp) {
92        $col = $this->findColumn($colname);
93        if(!$col) return; //FIXME do we really want to ignore missing columns?
94
95        $this->filterand[] = array($col, $value, $comp);
96    }
97
98    /**
99     * Transform the set search parameters into a statement
100     *
101     * @todo limit to the newest data!
102     * @return string
103     */
104    public function getSQL() {
105        if(!$this->columns) throw new SearchException('nocolname');
106
107        // basic tables
108        $from = '';
109        foreach($this->schemas as $schema) {
110            $from .= 'data_' . $schema->getTable() . ', ';
111
112            // fixme join the multiple tables together by pid
113        }
114        $from = rtrim($from, ', ');
115
116        // columns to select, handling multis
117        $select = '';
118        $n = 0;
119        foreach($this->columns as $col) {
120            $CN = 'C'.$n++;
121
122            if($col->isMulti()) {
123                $tn = 'M' . $col->getColref();
124                $select .= "$tn.value AS $CN, ";
125                $from .= "\nLEFT OUTER JOIN multivals $tn";
126                $from .= " ON DATA.pid = $tn.pid AND DATA.rev = $tn.rev";
127                $from .= " AND $tn.tbl = '{$col->getTable()}' AND $tn.colref = {$col->getColref()}\n";
128            } else {
129                $select .=  'data_'.$col->getTable().'.col' . $col->getColref() . " AS $CN, ";
130            }
131        }
132        $select = rtrim($select, ', ');
133
134
135        $sql = "SELECT $select\n  FROM $from";
136        return $sql;
137    }
138
139    /**
140     * Find a column to be used in the search
141     *
142     * @param string $colname may contain an alias
143     * @return bool|Column
144     */
145    protected function findColumn($colname) {
146        if(!$this->schemas) throw new SearchException('noschemas');
147
148        // resolve the alias or table name
149        list($table, $colname) = explode('.', $colname, 2);
150        if(!$colname) {
151            $colname = $table;
152            $table = '';
153        }
154        if($table && isset($this->aliases[$table])) {
155            $table = $this->aliases[$table];
156        }
157
158        if(!$colname) throw new SearchException('nocolname');
159
160        // if table name given search only that, otherwiese try all for matching column name
161        if($table) {
162            $schemas = array($table => $this->schemas[$table]);
163        } else {
164            $schemas = $this->schemas;
165        }
166
167        // find it
168        $col = false;
169        foreach($schemas as $schema) {
170            $col = $schema->findColumn($colname);
171            if($col) break;
172        }
173
174        return $col;
175    }
176
177}
178
179/**
180 * Class SearchException
181 *
182 * A translatable exception
183 *
184 * @package plugin\struct\meta
185 */
186class SearchException extends \RuntimeException {
187    public function __construct($message, $code = -1, Exception $previous = null) {
188        /** @var \action_plugin_struct_autoloader $plugin */
189        $plugin = plugin_load('action', 'struct_autoloader');
190        $trans = $plugin->getLang('searchex_' . $message);
191        if(!$trans) $trans = $message;
192        parent::__construct($trans, $code, $previous);
193    }
194}
195