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