...
 
Commits (4)
......@@ -78,7 +78,7 @@ class ProcessRockFinder extends Process {
'type' => 'markup',
'value' => "<pre><code>$code</code></pre>",
'label' => 'Resulting SQL',
'collapsed' => Inputfield::collapsedYes,
// 'collapsed' => Inputfield::collapsedYes,
]);
$form->add([
......
<?php
$info = [
'title' => 'RockFinder',
'version' => '1.0.5',
'version' => '1.0.6',
'summary' => 'Highly Efficient and Flexible SQL Finder Module to return page data without loading PW pages into memory',
'singular' => true,
'autoload' => false,
......
......@@ -26,6 +26,10 @@ class RockFinder extends WireData implements Module {
// array of custom select statements
private $selects = [];
// prefix name that is used for this finder when joining it to another finder
private $joinedFinders = [];
public $joinPrefix;
public function __construct($selector = '', $fields = []) {
$this->selector = $selector;
......@@ -151,12 +155,19 @@ class RockFinder extends WireData implements Module {
$pf = new PageFinder();
$query = $pf->find($selector, ['returnQuery' => true]);
$query['select'] = ['pages.id']; // we only need the id
$pwfinder = $query->prepare()->queryString;
$pwfinder = $this->indent($query->prepare()->queryString, 4);
// start sql statement
$sql = "SELECT\n `pages`.`id` AS `id`";
foreach($this->fields as $field) $sql .= $field->getJoinSelect();
// add the join prefix if it is set
$joinPrefix = '';
if($this->joinPrefix) {
$joinPrefix = "{$this->joinPrefix}_";
$sql = str_replace('AS `', "AS `$joinPrefix", $sql);
}
// add all select statements
foreach($this->selects as $alias=>$statement) {
$sql .= ",\n ($statement) AS $alias";
......@@ -167,21 +178,81 @@ class RockFinder extends WireData implements Module {
$rockfinder = $sql;
// join both queries
$sql = "SELECT `rockfinder`.* FROM";
$sql .= "\n\n /* original pw query */";
$sql .= "\n($pwfinder) as `pwfinder`";
$sql = "SELECT";
$sql .= "\n `rockfinder`.*";
$sql .= $this->joinedFinderSelects();
$sql .= "\nFROM";
$sql .= "\n /* original pw query */";
$sql .= "\n ($pwfinder) as `pwfinder`";
$sql .= "\n\nLEFT JOIN (";
$sql .= "\n/* rockfinder */\n";
$sql .= $rockfinder;
$sql .= "\n\n/* rockfinder */";
$sql .= "\nLEFT JOIN (";
$sql .= " " . $this->indent($rockfinder, 4);
$sql .= "\n) AS `rockfinder` ON `pwfinder`.`id` = `rockfinder`.`{$joinPrefix}id`";
$sql .= "\n/* end rockfinder */";
$sql .= "\n) AS `rockfinder` ON `pwfinder`.`id` = `rockfinder`.`id`";
$sql .= $this->joinedFinderJoins();
$this->timer('getSQL', $sqltimer, "<textarea class='noAutosize' rows=5>$sql</textarea>");
$this->sql = $sql;
return $sql;
}
/**
* return select statements for all joined finders
*/
private function joinedFinderSelects() {
if(!count($this->joinedFinders)) return;
$sql = "\n /* joined finders */";
foreach($this->joinedFinders as $finder) {
$fields = $finder[1];
$finder = $finder[0];
$sql .= "\n ,`$finder->joinPrefix`.*";
}
return $sql;
}
/**
* return join statements for all joined finders
*/
private function joinedFinderJoins() {
if(!count($this->joinedFinders)) return;
bd($this->joinedFinders);
$sql = "\n\n/* joinedFinderJoins */";
foreach($this->joinedFinders as $finder) {
foreach($finder[1] as $field1=>$field2) {} // assign key/value
// create sql statement
$finder = $finder[0];
$sql .= "\n\n/* join finder {$finder->joinPrefix} */";
$sql .= "\nLEFT JOIN (";
$sql .= "\n " . $this->indent($finder->getSQL(), 4);
$sql .= "\n) AS `" . $finder->joinPrefix . "`";
// create ON clause
// if $field2 has a dot the join is performed on an already joined table
// $finder1->join($finder2, 'contact', ['id' => 'client']);
// $finder1->join($finder3, 'referrer', ['id' => 'contact.contact_camefrom']);
if(strpos($field2, '.')) {
$field2 = str_replace('.', '`.`', $field2);
$sql .= " ON `$finder->joinPrefix`.`{$finder->joinPrefix}_$field1` = `$field2`";
}
else {
$sql .= " ON `$finder->joinPrefix`.`{$finder->joinPrefix}_$field1` = `rockfinder`.`$field2`";
}
}
return $sql;
}
/**
* indent all lines of given string
*/
private function indent($str, $chars) {
return str_replace("\n", "\n".str_repeat(" ",$chars), $str);
}
/**
* load sql from file
*/
......@@ -286,6 +357,31 @@ class RockFinder extends WireData implements Module {
return $arr;
}
/**
* join another finder
*/
public function join($finder, $prefix, $fields) {
bd('join');
// parameter checks
if(!$finder instanceof RockFinder) {
throw new WireException('First parameter needs to be a RockFinder instance');
}
if(!is_string($prefix)) throw new WireException('Second parameter needs to be a string');
if(!is_array($fields)) throw new WireException('Third parameter needs to be an array');
if(count($fields)!==1) throw new WireException('Third parameter needs to be an array with one key/value pair');
foreach($fields as $field1 => $field2) {
if(!is_string($field1) OR !is_string($field2)) {
throw new WireException('Third parameter needs to be an array with one key/value pair');
}
}
// set the join prefix for the joined finder
$finder->joinPrefix = $prefix;
// add join to array
$this->joinedFinders[] = [$finder, $fields];
}
/**
* start timer or add it to debuginfo
......
......@@ -79,9 +79,9 @@ class RockFinderFieldPage extends RockFinderField {
$sql = '';
foreach($this->columns as $i=>$column) {
if($i==0)
$sql .= ",\n `$this->alias`.`{$this->fieldAlias($column)}` AS `{$this->fieldAlias($column)}` /* hier */";
$sql .= ",\n `$this->alias`.`{$this->fieldAlias($column)}` AS `{$this->fieldAlias($column)}`";
else
$sql .= ",\n `$this->alias`.`$column` AS `{$this->fieldAlias($column)}` /* hier2 */";
$sql .= ",\n `$this->alias`.`$column` AS `{$this->fieldAlias($column)}`";
}
return $sql;
}
......
......@@ -49,6 +49,57 @@ By default uses the id column, but another one can be specified:
# Advanced Usage
## Joins
It is possible to join multiple finders. This is useful whenever you have single
page reference fields and want to show properties of the referenced page. A simple
example could be this join:
```php
$finder1 = new RockFinder('template=rockproject', ['title', 'rockproject_client']);
$finder2 = new RockFinder('template=rockcontact', ['title']);
// join finder
$finder1->join($finder2, 'contact', ['id' => 'rockproject_client']);
```
The syntac is like this:
```php
$baseFinder->join($joinedFinder, 'joinedFinderAlias', ['fieldNameOfJoinedFinder' => 'fieldNameOfBaseFinder']);
```
A more advanced example is this one, joining three finders. Notice that `$finder3`
is manually joined on a column of `$finder2` (having alias `contact`). You can
achieve this by providing not only the field name (then it would join to the base
finder) but also providing the finder-alias and the fieldname manually.
You have to use this syntax for your "fieldname": `{alias}.{alias}_{fieldname}`.
```php
$finder1 = new RockFinder('template=rockproject', [
'title',
'rockproject_client',
]);
$finder2 = new RockFinder('template=rockcontact', [
'title',
'rockcontact_camefrom',
]);
$finder3 = new RockFinder('template=rockcontact', [
'title',
]);
// join finders
$finder1->join($finder2, 'contact', ['id' => 'rockproject_client']);
$finder1->join($finder3, 'referral', ['id' => 'contact.contact_rockcontact_camefrom']);
return $finder1;
```
![complex join](screenshots/join.png)
## Custom SQL: Aggregations, Groupings, Distincts...
You can apply any custom SQL with this technique:
......