Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
c72935c
Adds support for logging Stache queries
JohnathonKoster Mar 10, 2024
1818006
Update stache.php
JohnathonKoster Mar 10, 2024
4f3ea3a
Update LogsStacheQueries.php
JohnathonKoster Mar 10, 2024
c524230
Add extra FROM output from entry store
JohnathonKoster Mar 10, 2024
8b71078
Merge branch 'master' into adds-stache-query-logging-support
jasonvarga Mar 12, 2024
dd3561a
Merge branch 'master' into adds-stache-query-logging-support
jasonvarga Mar 29, 2024
3dae5ee
fix merge
jasonvarga Mar 29, 2024
dee1b0a
stache is implied by namespace
jasonvarga Mar 29, 2024
b0dd160
move into its own namespace
jasonvarga Mar 29, 2024
5e4f4e7
avoid property for enabling it
jasonvarga Mar 30, 2024
88e2e42
Use actual bindings ...
jasonvarga Mar 30, 2024
131e08b
reuse it
jasonvarga Apr 1, 2024
869aae4
move to generic spot so it can be used in non-stache query builders
jasonvarga Apr 1, 2024
929b7ff
move setting
jasonvarga Apr 1, 2024
5e254ec
tweaks ...
jasonvarga Apr 1, 2024
5a4236c
use lowercase, it looks more like what laravel normally outputs
jasonvarga Apr 1, 2024
7679a6a
tweaks ...
jasonvarga Apr 1, 2024
dcc2825
only dump the wheres in a nested where clause
jasonvarga Apr 1, 2024
87bdff3
avoid outputting "where" when there arent any
jasonvarga Apr 1, 2024
575954d
one-line everything. debugbar is weird when we provide newlines. ray …
jasonvarga Apr 1, 2024
1bf8fdb
wip
jasonvarga Apr 1, 2024
609db02
implement for assets
jasonvarga Apr 1, 2024
17c343d
and terms
jasonvarga Apr 1, 2024
ce548f9
dont fake queries in iterator builder, it's too much. we use that for…
jasonvarga Apr 1, 2024
4adb4da
move table name up a level
jasonvarga Apr 1, 2024
d85ab55
fix nested where not showing in search queries since they arent stach…
jasonvarga Apr 1, 2024
5d5817c
match was wrong. we wish it was that fast.
jasonvarga Apr 1, 2024
fe32412
add onceWithColumns ported from laravel ...
jasonvarga Apr 1, 2024
d489e6d
apply fake queries to pluck
jasonvarga Apr 1, 2024
b9a5579
nitpick
jasonvarga Apr 1, 2024
9bbb62c
nitpick
jasonvarga Apr 1, 2024
2975ace
nitpick
jasonvarga Apr 1, 2024
e191b87
nitpick
jasonvarga Apr 1, 2024
2c1ef7d
just mirror the debug setting
jasonvarga Apr 1, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions config/system.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -165,4 +165,17 @@

'row_id_handle' => 'id',

/*
|--------------------------------------------------------------------------
| Fake SQL Queries
|--------------------------------------------------------------------------
|
| When enabled, Statamic's query builders will emit events that appear
| the same way as any other query. This can be useful for debugging.
| The generated SQL statements are approximations and not exact.
|
*/

'fake_sql_queries' => config('app.debug'),

];
21 changes: 21 additions & 0 deletions src/Assets/QueryBuilder.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,6 +7,7 @@
use Statamic\Contracts\Assets\QueryBuilder as Contract;
use Statamic\Facades;
use Statamic\Stache\Query\Builder as BaseQueryBuilder;
use Statamic\Support\Arr;

class QueryBuilder extends BaseQueryBuilder implements Contract
{
Expand DownExpand Up@@ -119,4 +120,24 @@ protected function getWhereColumnKeyValuesByIndex($column)

return $items;
}

public function getTableNameForFakeQuery(): string
{
return 'assets';
}

public function prepareForFakeQuery(): array
{
$data = parent::prepareForFakeQuery();

$data['wheres'] = Arr::prepend($data['wheres'], [
'type' => 'Basic',
'column' => 'container',
'operator' => '=',
'value' => $this->getContainer()->handle(),
'boolean' => 'and',
]);

return $data;
}
}
18 changes: 18 additions & 0 deletions src/Query/Builder.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -12,9 +12,12 @@
use Statamic\Contracts\Query\Builder as Contract;
use Statamic\Extensions\Pagination\LengthAwarePaginator;
use Statamic\Facades\Pattern;
use Statamic\Query\Concerns\FakesQueries;

abstract class Builder implements Contract
{
use FakesQueries;

protected $columns;
protected $limit;
protected $offset = 0;
Expand DownExpand Up@@ -570,6 +573,21 @@ abstract public function count();

abstract public function get($columns = ['*']);

protected function onceWithColumns($columns, $callback)
{
$original = $this->columns;

if (is_null($original)) {
$this->columns = $columns;
}

$result = $callback();

$this->columns = $original;

return $result;
}

abstract public function pluck($column, $key = null);

public function when($value, $callback, $default = null)
Expand Down
42 changes: 42 additions & 0 deletions src/Query/Concerns/FakesQueries.php
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
<?php

namespace Statamic\Query\Concerns;

use Illuminate\Database\Events\QueryExecuted;
use Statamic\Query\Dumper\Dumper;

trait FakesQueries
{
protected function withFakeQueryLogging(\Closure $callback)
{
if (! config('statamic.system.fake_sql_queries', false)) {
return $callback();
}

$startTime = microtime(true);

$value = $callback();

$time = round((microtime(true) - $startTime) * 1000, 2);

event(new QueryExecuted(
($sql = new Dumper($this))->dump(),
$sql->bindings()->all(),
$time,
$sql->connection()
));

return $value;
}

public function prepareForFakeQuery(): array
{
return [
'wheres' => $this->wheres,
'columns' => $this->columns,
'orderBys' => $this->orderBys,
'limit' => $this->limit,
'offset' => $this->offset,
];
}
}
45 changes: 45 additions & 0 deletions src/Query/Dumper/Concerns/DumpsQueryParts.php
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
<?php

namespace Statamic\Query\Dumper\Concerns;

trait DumpsQueryParts
{
protected function dumpColumns(): string
{
return implode(', ', $this->columns);
}

protected function dumpLimits(): string
{
if (! $this->limit) {
return '';
}

$limit = ' limit '.$this->limit;

if ($this->offset) {
$limit .= ' offset '.$this->offset;
}

return $limit;
}

protected function dumpOrderBys(): string
{
if (count($this->orderBys) === 0) {
return '';
}

$orders = [];

foreach ($this->orderBys as $orderBy) {
if (! $orderBy->sort) {
continue;
}

$orders[] = $orderBy->sort.' '.$orderBy->direction;
}

return ' order by '.implode(', ', $orders);
}
}
24 changes: 24 additions & 0 deletions src/Query/Dumper/Concerns/DumpsQueryValues.php
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
<?php

namespace Statamic\Query\Dumper\Concerns;

trait DumpsQueryValues
{
protected function dumpQueryArrayValues($array): string
{
if (count($array) === 0) {
return '[]';
}

return collect($array)->map(function ($value) {
return $this->dumpQueryValue($value);
})->implode(', ');
}

protected function dumpQueryValue($value): string
{
$this->bindings[] = $value;

return '?';
}
}
160 changes: 160 additions & 0 deletions src/Query/Dumper/Concerns/DumpsWheres.php
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
<?php

namespace Statamic\Query\Dumper\Concerns;

use Illuminate\Support\Str;
use Statamic\Query\Dumper\Dumper;

trait DumpsWheres
{
protected function dumpBasic($where): string
{
return $where['column'].' '.$where['operator'].' '.$this->dumpQueryValue($where['value'] ?? null);
}

protected function dumpArrayWhere($keyword, $where): string
{
return $where['column'].' '.$keyword.' ('.$this->dumpQueryArrayValues($where['values'] ?? []).')';
}

protected function dumpSimpleOperatorWhere($where): string
{
return $where['column'].' '.$where['operator'].$this->dumpQueryValue($where['value'] ?? null);
}

protected function dumpIn($where): string
{
return $this->dumpArrayWhere('in', $where);
}

protected function dumpNotIn($where): string
{
return $this->dumpArrayWhere('not in', $where);
}

protected function dumpNull($where): string
{
return $where['column'].' is null';
}

protected function dumpNotNull($where): string
{
return $where['column'].' is not null';
}

protected function dumpDatePartMethod($datePart, $where): string
{
return 'DATEPART('.$datePart.', '.$where['column'].') = '.$this->dumpQueryValue($where['value'] ?? null);
}

protected function dumpMonth($where): string
{
return $this->dumpDatePartMethod('MONTH', $where);
}

protected function dumpDay($where): string
{
return $this->dumpDatePartMethod('DAY', $where);
}

protected function dumpYear($where): string
{
return $this->dumpDatePartMethod('YEAR', $where);
}

protected function dumpTime($where): string
{
return $this->dumpDatePartMethod('TIMESTAMP', $where);
}

protected function dumpBetween($where): string
{
$valueOne = $this->dumpQueryValue($where['values'][0] ?? null);
$valueTwo = $this->dumpQueryValue($where['values'][1] ?? null);
$column = $where['column'];

return $column.' between '.$valueOne.' and '.$valueTwo;
}

protected function dumpNotBetween($where): string
{
$valueOne = $this->dumpQueryValue($where['values'][0] ?? null);
$valueTwo = $this->dumpQueryValue($where['values'][1] ?? null);
$column = $where['column'];

return $column.' not between '.$valueOne.' and '.$valueTwo;
}

protected function dumpColumn($where): string
{
return $where['column'].' = '.$where['value'];
}

protected function dumpNested($where): string
{
$query = $where['query'] ?? null;

$sql = (new Dumper($query))->withBindings($this->bindings)->dumpWheres();

return "($sql)";
}

protected function dumpDate($where)
{
return $this->dumpSimpleOperatorWhere($where);
}

protected function dumpJsonMethod($where): string
{
$jsonMethod = strtoupper(Str::snake($where['type']));

if (isset($where['values'])) {
$valueString = $this->dumpQueryArrayValues($where['values']);
} else {
$valueString = $this->dumpQueryValue($where['value'] ?? null);
}

return $jsonMethod.'('.$where['column'].', '.$valueString.')';
}

protected function dumpWhere($isFirst, $where): string
{
$dumpedWhere = '';

if (! $isFirst) {
$dumpedWhere = $where['boolean'].' ';
}

$type = $where['type'];

if (Str::startsWith($type, 'Json')) {
$dumpedWhere .= $this->dumpJsonMethod($where);
} else {
$whereMethod = 'dump'.ucfirst($type);

if (method_exists($this, $whereMethod)) {
$dumpedWhere .= $this->{$whereMethod}($where);
} else {
// Fail-safe to dump "something".
$dumpedWhere .= strtoupper($type);
}
}

return $dumpedWhere;
}

protected function dumpWheres(): string
{
if (count($this->wheres) === 0) {
return '';
}

$parts = [];

for ($i = 0; $i < count($this->wheres); $i++) {
$parts[] = $this->dumpWhere($i === 0, $this->wheres[$i]);
}

return implode(' ', $parts);
}
}
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
c72935c
Adds support for logging Stache queries
JohnathonKoster Mar 10, 2024
1818006
Update stache.php
JohnathonKoster Mar 10, 2024
4f3ea3a
Update LogsStacheQueries.php
JohnathonKoster Mar 10, 2024
c524230
Add extra FROM output from entry store
JohnathonKoster Mar 10, 2024
8b71078
Merge branch 'master' into adds-stache-query-logging-support
jasonvarga Mar 12, 2024
dd3561a
Merge branch 'master' into adds-stache-query-logging-support
jasonvarga Mar 29, 2024
3dae5ee
fix merge
jasonvarga Mar 29, 2024
dee1b0a
stache is implied by namespace
jasonvarga Mar 29, 2024
b0dd160
move into its own namespace
jasonvarga Mar 29, 2024
5e4f4e7
avoid property for enabling it
jasonvarga Mar 30, 2024
88e2e42
Use actual bindings ...
jasonvarga Mar 30, 2024
131e08b
reuse it
jasonvarga Apr 1, 2024
869aae4
move to generic spot so it can be used in non-stache query builders
jasonvarga Apr 1, 2024
929b7ff
move setting
jasonvarga Apr 1, 2024
5e254ec
tweaks ...
jasonvarga Apr 1, 2024
5a4236c
use lowercase, it looks more like what laravel normally outputs
jasonvarga Apr 1, 2024
7679a6a
tweaks ...
jasonvarga Apr 1, 2024
dcc2825
only dump the wheres in a nested where clause
jasonvarga Apr 1, 2024
87bdff3
avoid outputting "where" when there arent any
jasonvarga Apr 1, 2024
575954d
one-line everything. debugbar is weird when we provide newlines. ray …
jasonvarga Apr 1, 2024
1bf8fdb
wip
jasonvarga Apr 1, 2024
609db02
implement for assets
jasonvarga Apr 1, 2024
17c343d
and terms
jasonvarga Apr 1, 2024
ce548f9
dont fake queries in iterator builder, it's too much. we use that for…
jasonvarga Apr 1, 2024
4adb4da
move table name up a level
jasonvarga Apr 1, 2024
d85ab55
fix nested where not showing in search queries since they arent stach…
jasonvarga Apr 1, 2024
5d5817c
match was wrong. we wish it was that fast.
jasonvarga Apr 1, 2024
fe32412
add onceWithColumns ported from laravel ...
jasonvarga Apr 1, 2024
d489e6d
apply fake queries to pluck
jasonvarga Apr 1, 2024
b9a5579
nitpick
jasonvarga Apr 1, 2024
9bbb62c
nitpick
jasonvarga Apr 1, 2024
2975ace
nitpick
jasonvarga Apr 1, 2024
e191b87
nitpick
jasonvarga Apr 1, 2024
2c1ef7d
just mirror the debug setting
jasonvarga Apr 1, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions config/system.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -165,4 +165,17 @@

'row_id_handle' => 'id',

/*
|--------------------------------------------------------------------------
| Fake SQL Queries
|--------------------------------------------------------------------------
|
| When enabled, Statamic's query builders will emit events that appear
| the same way as any other query. This can be useful for debugging.
| The generated SQL statements are approximations and not exact.
|
*/

'fake_sql_queries' => config('app.debug'),

];
21 changes: 21 additions & 0 deletions src/Assets/QueryBuilder.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,6 +7,7 @@
use Statamic\Contracts\Assets\QueryBuilder as Contract;
use Statamic\Facades;
use Statamic\Stache\Query\Builder as BaseQueryBuilder;
use Statamic\Support\Arr;

class QueryBuilder extends BaseQueryBuilder implements Contract
{
Expand DownExpand Up@@ -119,4 +120,24 @@ protected function getWhereColumnKeyValuesByIndex($column)

return $items;
}

public function getTableNameForFakeQuery(): string
{
return 'assets';
}

public function prepareForFakeQuery(): array
{
$data = parent::prepareForFakeQuery();

$data['wheres'] = Arr::prepend($data['wheres'], [
'type' => 'Basic',
'column' => 'container',
'operator' => '=',
'value' => $this->getContainer()->handle(),
'boolean' => 'and',
]);

return $data;
}
}
18 changes: 18 additions & 0 deletions src/Query/Builder.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -12,9 +12,12 @@
use Statamic\Contracts\Query\Builder as Contract;
use Statamic\Extensions\Pagination\LengthAwarePaginator;
use Statamic\Facades\Pattern;
use Statamic\Query\Concerns\FakesQueries;

abstract class Builder implements Contract
{
use FakesQueries;

protected $columns;
protected $limit;
protected $offset = 0;
Expand DownExpand Up@@ -570,6 +573,21 @@ abstract public function count();

abstract public function get($columns = ['*']);

protected function onceWithColumns($columns, $callback)
{
$original = $this->columns;

if (is_null($original)) {
$this->columns = $columns;
}

$result = $callback();

$this->columns = $original;

return $result;
}

abstract public function pluck($column, $key = null);

public function when($value, $callback, $default = null)
Expand Down
42 changes: 42 additions & 0 deletions src/Query/Concerns/FakesQueries.php
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
<?php

namespace Statamic\Query\Concerns;

use Illuminate\Database\Events\QueryExecuted;
use Statamic\Query\Dumper\Dumper;

trait FakesQueries
{
protected function withFakeQueryLogging(\Closure $callback)
{
if (! config('statamic.system.fake_sql_queries', false)) {
return $callback();
}

$startTime = microtime(true);

$value = $callback();

$time = round((microtime(true) - $startTime) * 1000, 2);

event(new QueryExecuted(
($sql = new Dumper($this))->dump(),
$sql->bindings()->all(),
$time,
$sql->connection()
));

return $value;
}

public function prepareForFakeQuery(): array
{
return [
'wheres' => $this->wheres,
'columns' => $this->columns,
'orderBys' => $this->orderBys,
'limit' => $this->limit,
'offset' => $this->offset,
];
}
}
45 changes: 45 additions & 0 deletions src/Query/Dumper/Concerns/DumpsQueryParts.php
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
<?php

namespace Statamic\Query\Dumper\Concerns;

trait DumpsQueryParts
{
protected function dumpColumns(): string
{
return implode(', ', $this->columns);
}

protected function dumpLimits(): string
{
if (! $this->limit) {
return '';
}

$limit = ' limit '.$this->limit;

if ($this->offset) {
$limit .= ' offset '.$this->offset;
}

return $limit;
}

protected function dumpOrderBys(): string
{
if (count($this->orderBys) === 0) {
return '';
}

$orders = [];

foreach ($this->orderBys as $orderBy) {
if (! $orderBy->sort) {
continue;
}

$orders[] = $orderBy->sort.' '.$orderBy->direction;
}

return ' order by '.implode(', ', $orders);
}
}
24 changes: 24 additions & 0 deletions src/Query/Dumper/Concerns/DumpsQueryValues.php
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
<?php

namespace Statamic\Query\Dumper\Concerns;

trait DumpsQueryValues
{
protected function dumpQueryArrayValues($array): string
{
if (count($array) === 0) {
return '[]';
}

return collect($array)->map(function ($value) {
return $this->dumpQueryValue($value);
})->implode(', ');
}

protected function dumpQueryValue($value): string
{
$this->bindings[] = $value;

return '?';
}
}
160 changes: 160 additions & 0 deletions src/Query/Dumper/Concerns/DumpsWheres.php
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
<?php

namespace Statamic\Query\Dumper\Concerns;

use Illuminate\Support\Str;
use Statamic\Query\Dumper\Dumper;

trait DumpsWheres
{
protected function dumpBasic($where): string
{
return $where['column'].' '.$where['operator'].' '.$this->dumpQueryValue($where['value'] ?? null);
}

protected function dumpArrayWhere($keyword, $where): string
{
return $where['column'].' '.$keyword.' ('.$this->dumpQueryArrayValues($where['values'] ?? []).')';
}

protected function dumpSimpleOperatorWhere($where): string
{
return $where['column'].' '.$where['operator'].$this->dumpQueryValue($where['value'] ?? null);
}

protected function dumpIn($where): string
{
return $this->dumpArrayWhere('in', $where);
}

protected function dumpNotIn($where): string
{
return $this->dumpArrayWhere('not in', $where);
}

protected function dumpNull($where): string
{
return $where['column'].' is null';
}

protected function dumpNotNull($where): string
{
return $where['column'].' is not null';
}

protected function dumpDatePartMethod($datePart, $where): string
{
return 'DATEPART('.$datePart.', '.$where['column'].') = '.$this->dumpQueryValue($where['value'] ?? null);
}

protected function dumpMonth($where): string
{
return $this->dumpDatePartMethod('MONTH', $where);
}

protected function dumpDay($where): string
{
return $this->dumpDatePartMethod('DAY', $where);
}

protected function dumpYear($where): string
{
return $this->dumpDatePartMethod('YEAR', $where);
}

protected function dumpTime($where): string
{
return $this->dumpDatePartMethod('TIMESTAMP', $where);
}

protected function dumpBetween($where): string
{
$valueOne = $this->dumpQueryValue($where['values'][0] ?? null);
$valueTwo = $this->dumpQueryValue($where['values'][1] ?? null);
$column = $where['column'];

return $column.' between '.$valueOne.' and '.$valueTwo;
}

protected function dumpNotBetween($where): string
{
$valueOne = $this->dumpQueryValue($where['values'][0] ?? null);
$valueTwo = $this->dumpQueryValue($where['values'][1] ?? null);
$column = $where['column'];

return $column.' not between '.$valueOne.' and '.$valueTwo;
}

protected function dumpColumn($where): string
{
return $where['column'].' = '.$where['value'];
}

protected function dumpNested($where): string
{
$query = $where['query'] ?? null;

$sql = (new Dumper($query))->withBindings($this->bindings)->dumpWheres();

return "($sql)";
}

protected function dumpDate($where)
{
return $this->dumpSimpleOperatorWhere($where);
}

protected function dumpJsonMethod($where): string
{
$jsonMethod = strtoupper(Str::snake($where['type']));

if (isset($where['values'])) {
$valueString = $this->dumpQueryArrayValues($where['values']);
} else {
$valueString = $this->dumpQueryValue($where['value'] ?? null);
}

return $jsonMethod.'('.$where['column'].', '.$valueString.')';
}

protected function dumpWhere($isFirst, $where): string
{
$dumpedWhere = '';

if (! $isFirst) {
$dumpedWhere = $where['boolean'].' ';
}

$type = $where['type'];

if (Str::startsWith($type, 'Json')) {
$dumpedWhere .= $this->dumpJsonMethod($where);
} else {
$whereMethod = 'dump'.ucfirst($type);

if (method_exists($this, $whereMethod)) {
$dumpedWhere .= $this->{$whereMethod}($where);
} else {
// Fail-safe to dump "something".
$dumpedWhere .= strtoupper($type);
}
}

return $dumpedWhere;
}

protected function dumpWheres(): string
{
if (count($this->wheres) === 0) {
return '';
}

$parts = [];

for ($i = 0; $i < count($this->wheres); $i++) {
$parts[] = $this->dumpWhere($i === 0, $this->wheres[$i]);
}

return implode(' ', $parts);
}
}
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
c72935c
Adds support for logging Stache queries
JohnathonKoster Mar 10, 2024
1818006
Update stache.php
JohnathonKoster Mar 10, 2024
4f3ea3a
Update LogsStacheQueries.php
JohnathonKoster Mar 10, 2024
c524230
Add extra FROM output from entry store
JohnathonKoster Mar 10, 2024
8b71078
Merge branch 'master' into adds-stache-query-logging-support
jasonvarga Mar 12, 2024
dd3561a
Merge branch 'master' into adds-stache-query-logging-support
jasonvarga Mar 29, 2024
3dae5ee
fix merge
jasonvarga Mar 29, 2024
dee1b0a
stache is implied by namespace
jasonvarga Mar 29, 2024
b0dd160
move into its own namespace
jasonvarga Mar 29, 2024
5e4f4e7
avoid property for enabling it
jasonvarga Mar 30, 2024
88e2e42
Use actual bindings ...
jasonvarga Mar 30, 2024
131e08b
reuse it
jasonvarga Apr 1, 2024
869aae4
move to generic spot so it can be used in non-stache query builders
jasonvarga Apr 1, 2024
929b7ff
move setting
jasonvarga Apr 1, 2024
5e254ec
tweaks ...
jasonvarga Apr 1, 2024
5a4236c
use lowercase, it looks more like what laravel normally outputs
jasonvarga Apr 1, 2024
7679a6a
tweaks ...
jasonvarga Apr 1, 2024
dcc2825
only dump the wheres in a nested where clause
jasonvarga Apr 1, 2024
87bdff3
avoid outputting "where" when there arent any
jasonvarga Apr 1, 2024
575954d
one-line everything. debugbar is weird when we provide newlines. ray …
jasonvarga Apr 1, 2024
1bf8fdb
wip
jasonvarga Apr 1, 2024
609db02
implement for assets
jasonvarga Apr 1, 2024
17c343d
and terms
jasonvarga Apr 1, 2024
ce548f9
dont fake queries in iterator builder, it's too much. we use that for…
jasonvarga Apr 1, 2024
4adb4da
move table name up a level
jasonvarga Apr 1, 2024
d85ab55
fix nested where not showing in search queries since they arent stach…
jasonvarga Apr 1, 2024
5d5817c
match was wrong. we wish it was that fast.
jasonvarga Apr 1, 2024
fe32412
add onceWithColumns ported from laravel ...
jasonvarga Apr 1, 2024
d489e6d
apply fake queries to pluck
jasonvarga Apr 1, 2024
b9a5579
nitpick
jasonvarga Apr 1, 2024
9bbb62c
nitpick
jasonvarga Apr 1, 2024
2975ace
nitpick
jasonvarga Apr 1, 2024
e191b87
nitpick
jasonvarga Apr 1, 2024
2c1ef7d
just mirror the debug setting
jasonvarga Apr 1, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions config/system.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -165,4 +165,17 @@

'row_id_handle' => 'id',

/*
|--------------------------------------------------------------------------
| Fake SQL Queries
|--------------------------------------------------------------------------
|
| When enabled, Statamic's query builders will emit events that appear
| the same way as any other query. This can be useful for debugging.
| The generated SQL statements are approximations and not exact.
|
*/

'fake_sql_queries' => config('app.debug'),

];
21 changes: 21 additions & 0 deletions src/Assets/QueryBuilder.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,6 +7,7 @@
use Statamic\Contracts\Assets\QueryBuilder as Contract;
use Statamic\Facades;
use Statamic\Stache\Query\Builder as BaseQueryBuilder;
use Statamic\Support\Arr;

class QueryBuilder extends BaseQueryBuilder implements Contract
{
Expand DownExpand Up@@ -119,4 +120,24 @@ protected function getWhereColumnKeyValuesByIndex($column)

return $items;
}

public function getTableNameForFakeQuery(): string
{
return 'assets';
}

public function prepareForFakeQuery(): array
{
$data = parent::prepareForFakeQuery();

$data['wheres'] = Arr::prepend($data['wheres'], [
'type' => 'Basic',
'column' => 'container',
'operator' => '=',
'value' => $this->getContainer()->handle(),
'boolean' => 'and',
]);

return $data;
}
}
18 changes: 18 additions & 0 deletions src/Query/Builder.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -12,9 +12,12 @@
use Statamic\Contracts\Query\Builder as Contract;
use Statamic\Extensions\Pagination\LengthAwarePaginator;
use Statamic\Facades\Pattern;
use Statamic\Query\Concerns\FakesQueries;

abstract class Builder implements Contract
{
use FakesQueries;

protected $columns;
protected $limit;
protected $offset = 0;
Expand DownExpand Up@@ -570,6 +573,21 @@ abstract public function count();

abstract public function get($columns = ['*']);

protected function onceWithColumns($columns, $callback)
{
$original = $this->columns;

if (is_null($original)) {
$this->columns = $columns;
}

$result = $callback();

$this->columns = $original;

return $result;
}

abstract public function pluck($column, $key = null);

public function when($value, $callback, $default = null)
Expand Down
42 changes: 42 additions & 0 deletions src/Query/Concerns/FakesQueries.php
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
<?php

namespace Statamic\Query\Concerns;

use Illuminate\Database\Events\QueryExecuted;
use Statamic\Query\Dumper\Dumper;

trait FakesQueries
{
protected function withFakeQueryLogging(\Closure $callback)
{
if (! config('statamic.system.fake_sql_queries', false)) {
return $callback();
}

$startTime = microtime(true);

$value = $callback();

$time = round((microtime(true) - $startTime) * 1000, 2);

event(new QueryExecuted(
($sql = new Dumper($this))->dump(),
$sql->bindings()->all(),
$time,
$sql->connection()
));

return $value;
}

public function prepareForFakeQuery(): array
{
return [
'wheres' => $this->wheres,
'columns' => $this->columns,
'orderBys' => $this->orderBys,
'limit' => $this->limit,
'offset' => $this->offset,
];
}
}
45 changes: 45 additions & 0 deletions src/Query/Dumper/Concerns/DumpsQueryParts.php
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
<?php

namespace Statamic\Query\Dumper\Concerns;

trait DumpsQueryParts
{
protected function dumpColumns(): string
{
return implode(', ', $this->columns);
}

protected function dumpLimits(): string
{
if (! $this->limit) {
return '';
}

$limit = ' limit '.$this->limit;

if ($this->offset) {
$limit .= ' offset '.$this->offset;
}

return $limit;
}

protected function dumpOrderBys(): string
{
if (count($this->orderBys) === 0) {
return '';
}

$orders = [];

foreach ($this->orderBys as $orderBy) {
if (! $orderBy->sort) {
continue;
}

$orders[] = $orderBy->sort.' '.$orderBy->direction;
}

return ' order by '.implode(', ', $orders);
}
}
24 changes: 24 additions & 0 deletions src/Query/Dumper/Concerns/DumpsQueryValues.php
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
<?php

namespace Statamic\Query\Dumper\Concerns;

trait DumpsQueryValues
{
protected function dumpQueryArrayValues($array): string
{
if (count($array) === 0) {
return '[]';
}

return collect($array)->map(function ($value) {
return $this->dumpQueryValue($value);
})->implode(', ');
}

protected function dumpQueryValue($value): string
{
$this->bindings[] = $value;

return '?';
}
}
160 changes: 160 additions & 0 deletions src/Query/Dumper/Concerns/DumpsWheres.php
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
<?php

namespace Statamic\Query\Dumper\Concerns;

use Illuminate\Support\Str;
use Statamic\Query\Dumper\Dumper;

trait DumpsWheres
{
protected function dumpBasic($where): string
{
return $where['column'].' '.$where['operator'].' '.$this->dumpQueryValue($where['value'] ?? null);
}

protected function dumpArrayWhere($keyword, $where): string
{
return $where['column'].' '.$keyword.' ('.$this->dumpQueryArrayValues($where['values'] ?? []).')';
}

protected function dumpSimpleOperatorWhere($where): string
{
return $where['column'].' '.$where['operator'].$this->dumpQueryValue($where['value'] ?? null);
}

protected function dumpIn($where): string
{
return $this->dumpArrayWhere('in', $where);
}

protected function dumpNotIn($where): string
{
return $this->dumpArrayWhere('not in', $where);
}

protected function dumpNull($where): string
{
return $where['column'].' is null';
}

protected function dumpNotNull($where): string
{
return $where['column'].' is not null';
}

protected function dumpDatePartMethod($datePart, $where): string
{
return 'DATEPART('.$datePart.', '.$where['column'].') = '.$this->dumpQueryValue($where['value'] ?? null);
}

protected function dumpMonth($where): string
{
return $this->dumpDatePartMethod('MONTH', $where);
}

protected function dumpDay($where): string
{
return $this->dumpDatePartMethod('DAY', $where);
}

protected function dumpYear($where): string
{
return $this->dumpDatePartMethod('YEAR', $where);
}

protected function dumpTime($where): string
{
return $this->dumpDatePartMethod('TIMESTAMP', $where);
}

protected function dumpBetween($where): string
{
$valueOne = $this->dumpQueryValue($where['values'][0] ?? null);
$valueTwo = $this->dumpQueryValue($where['values'][1] ?? null);
$column = $where['column'];

return $column.' between '.$valueOne.' and '.$valueTwo;
}

protected function dumpNotBetween($where): string
{
$valueOne = $this->dumpQueryValue($where['values'][0] ?? null);
$valueTwo = $this->dumpQueryValue($where['values'][1] ?? null);
$column = $where['column'];

return $column.' not between '.$valueOne.' and '.$valueTwo;
}

protected function dumpColumn($where): string
{
return $where['column'].' = '.$where['value'];
}

protected function dumpNested($where): string
{
$query = $where['query'] ?? null;

$sql = (new Dumper($query))->withBindings($this->bindings)->dumpWheres();

return "($sql)";
}

protected function dumpDate($where)
{
return $this->dumpSimpleOperatorWhere($where);
}

protected function dumpJsonMethod($where): string
{
$jsonMethod = strtoupper(Str::snake($where['type']));

if (isset($where['values'])) {
$valueString = $this->dumpQueryArrayValues($where['values']);
} else {
$valueString = $this->dumpQueryValue($where['value'] ?? null);
}

return $jsonMethod.'('.$where['column'].', '.$valueString.')';
}

protected function dumpWhere($isFirst, $where): string
{
$dumpedWhere = '';

if (! $isFirst) {
$dumpedWhere = $where['boolean'].' ';
}

$type = $where['type'];

if (Str::startsWith($type, 'Json')) {
$dumpedWhere .= $this->dumpJsonMethod($where);
} else {
$whereMethod = 'dump'.ucfirst($type);

if (method_exists($this, $whereMethod)) {
$dumpedWhere .= $this->{$whereMethod}($where);
} else {
// Fail-safe to dump "something".
$dumpedWhere .= strtoupper($type);
}
}

return $dumpedWhere;
}

protected function dumpWheres(): string
{
if (count($this->wheres) === 0) {
return '';
}

$parts = [];

for ($i = 0; $i < count($this->wheres); $i++) {
$parts[] = $this->dumpWhere($i === 0, $this->wheres[$i]);
}

return implode(' ', $parts);
}
}
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
c72935c
Adds support for logging Stache queries
JohnathonKoster Mar 10, 2024
1818006
Update stache.php
JohnathonKoster Mar 10, 2024
4f3ea3a
Update LogsStacheQueries.php
JohnathonKoster Mar 10, 2024
c524230
Add extra FROM output from entry store
JohnathonKoster Mar 10, 2024
8b71078
Merge branch 'master' into adds-stache-query-logging-support
jasonvarga Mar 12, 2024
dd3561a
Merge branch 'master' into adds-stache-query-logging-support
jasonvarga Mar 29, 2024
3dae5ee
fix merge
jasonvarga Mar 29, 2024
dee1b0a
stache is implied by namespace
jasonvarga Mar 29, 2024
b0dd160
move into its own namespace
jasonvarga Mar 29, 2024
5e4f4e7
avoid property for enabling it
jasonvarga Mar 30, 2024
88e2e42
Use actual bindings ...
jasonvarga Mar 30, 2024
131e08b
reuse it
jasonvarga Apr 1, 2024
869aae4
move to generic spot so it can be used in non-stache query builders
jasonvarga Apr 1, 2024
929b7ff
move setting
jasonvarga Apr 1, 2024
5e254ec
tweaks ...
jasonvarga Apr 1, 2024
5a4236c
use lowercase, it looks more like what laravel normally outputs
jasonvarga Apr 1, 2024
7679a6a
tweaks ...
jasonvarga Apr 1, 2024
dcc2825
only dump the wheres in a nested where clause
jasonvarga Apr 1, 2024
87bdff3
avoid outputting "where" when there arent any
jasonvarga Apr 1, 2024
575954d
one-line everything. debugbar is weird when we provide newlines. ray …
jasonvarga Apr 1, 2024
1bf8fdb
wip
jasonvarga Apr 1, 2024
609db02
implement for assets
jasonvarga Apr 1, 2024
17c343d
and terms
jasonvarga Apr 1, 2024
ce548f9
dont fake queries in iterator builder, it's too much. we use that for…
jasonvarga Apr 1, 2024
4adb4da
move table name up a level
jasonvarga Apr 1, 2024
d85ab55
fix nested where not showing in search queries since they arent stach…
jasonvarga Apr 1, 2024
5d5817c
match was wrong. we wish it was that fast.
jasonvarga Apr 1, 2024
fe32412
add onceWithColumns ported from laravel ...
jasonvarga Apr 1, 2024
d489e6d
apply fake queries to pluck
jasonvarga Apr 1, 2024
b9a5579
nitpick
jasonvarga Apr 1, 2024
9bbb62c
nitpick
jasonvarga Apr 1, 2024
2975ace
nitpick
jasonvarga Apr 1, 2024
e191b87
nitpick
jasonvarga Apr 1, 2024
2c1ef7d
just mirror the debug setting
jasonvarga Apr 1, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions config/system.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -165,4 +165,17 @@

'row_id_handle' => 'id',

/*
|--------------------------------------------------------------------------
| Fake SQL Queries
|--------------------------------------------------------------------------
|
| When enabled, Statamic's query builders will emit events that appear
| the same way as any other query. This can be useful for debugging.
| The generated SQL statements are approximations and not exact.
|
*/

'fake_sql_queries' => config('app.debug'),

];
21 changes: 21 additions & 0 deletions src/Assets/QueryBuilder.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,6 +7,7 @@
use Statamic\Contracts\Assets\QueryBuilder as Contract;
use Statamic\Facades;
use Statamic\Stache\Query\Builder as BaseQueryBuilder;
use Statamic\Support\Arr;

class QueryBuilder extends BaseQueryBuilder implements Contract
{
Expand DownExpand Up@@ -119,4 +120,24 @@ protected function getWhereColumnKeyValuesByIndex($column)

return $items;
}

public function getTableNameForFakeQuery(): string
{
return 'assets';
}

public function prepareForFakeQuery(): array
{
$data = parent::prepareForFakeQuery();

$data['wheres'] = Arr::prepend($data['wheres'], [
'type' => 'Basic',
'column' => 'container',
'operator' => '=',
'value' => $this->getContainer()->handle(),
'boolean' => 'and',
]);

return $data;
}
}
18 changes: 18 additions & 0 deletions src/Query/Builder.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -12,9 +12,12 @@
use Statamic\Contracts\Query\Builder as Contract;
use Statamic\Extensions\Pagination\LengthAwarePaginator;
use Statamic\Facades\Pattern;
use Statamic\Query\Concerns\FakesQueries;

abstract class Builder implements Contract
{
use FakesQueries;

protected $columns;
protected $limit;
protected $offset = 0;
Expand DownExpand Up@@ -570,6 +573,21 @@ abstract public function count();

abstract public function get($columns = ['*']);

protected function onceWithColumns($columns, $callback)
{
$original = $this->columns;

if (is_null($original)) {
$this->columns = $columns;
}

$result = $callback();

$this->columns = $original;

return $result;
}

abstract public function pluck($column, $key = null);

public function when($value, $callback, $default = null)
Expand Down
42 changes: 42 additions & 0 deletions src/Query/Concerns/FakesQueries.php
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
<?php

namespace Statamic\Query\Concerns;

use Illuminate\Database\Events\QueryExecuted;
use Statamic\Query\Dumper\Dumper;

trait FakesQueries
{
protected function withFakeQueryLogging(\Closure $callback)
{
if (! config('statamic.system.fake_sql_queries', false)) {
return $callback();
}

$startTime = microtime(true);

$value = $callback();

$time = round((microtime(true) - $startTime) * 1000, 2);

event(new QueryExecuted(
($sql = new Dumper($this))->dump(),
$sql->bindings()->all(),
$time,
$sql->connection()
));

return $value;
}

public function prepareForFakeQuery(): array
{
return [
'wheres' => $this->wheres,
'columns' => $this->columns,
'orderBys' => $this->orderBys,
'limit' => $this->limit,
'offset' => $this->offset,
];
}
}
45 changes: 45 additions & 0 deletions src/Query/Dumper/Concerns/DumpsQueryParts.php
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
<?php

namespace Statamic\Query\Dumper\Concerns;

trait DumpsQueryParts
{
protected function dumpColumns(): string
{
return implode(', ', $this->columns);
}

protected function dumpLimits(): string
{
if (! $this->limit) {
return '';
}

$limit = ' limit '.$this->limit;

if ($this->offset) {
$limit .= ' offset '.$this->offset;
}

return $limit;
}

protected function dumpOrderBys(): string
{
if (count($this->orderBys) === 0) {
return '';
}

$orders = [];

foreach ($this->orderBys as $orderBy) {
if (! $orderBy->sort) {
continue;
}

$orders[] = $orderBy->sort.' '.$orderBy->direction;
}

return ' order by '.implode(', ', $orders);
}
}
24 changes: 24 additions & 0 deletions src/Query/Dumper/Concerns/DumpsQueryValues.php
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
<?php

namespace Statamic\Query\Dumper\Concerns;

trait DumpsQueryValues
{
protected function dumpQueryArrayValues($array): string
{
if (count($array) === 0) {
return '[]';
}

return collect($array)->map(function ($value) {
return $this->dumpQueryValue($value);
})->implode(', ');
}

protected function dumpQueryValue($value): string
{
$this->bindings[] = $value;

return '?';
}
}
160 changes: 160 additions & 0 deletions src/Query/Dumper/Concerns/DumpsWheres.php
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
<?php

namespace Statamic\Query\Dumper\Concerns;

use Illuminate\Support\Str;
use Statamic\Query\Dumper\Dumper;

trait DumpsWheres
{
protected function dumpBasic($where): string
{
return $where['column'].' '.$where['operator'].' '.$this->dumpQueryValue($where['value'] ?? null);
}

protected function dumpArrayWhere($keyword, $where): string
{
return $where['column'].' '.$keyword.' ('.$this->dumpQueryArrayValues($where['values'] ?? []).')';
}

protected function dumpSimpleOperatorWhere($where): string
{
return $where['column'].' '.$where['operator'].$this->dumpQueryValue($where['value'] ?? null);
}

protected function dumpIn($where): string
{
return $this->dumpArrayWhere('in', $where);
}

protected function dumpNotIn($where): string
{
return $this->dumpArrayWhere('not in', $where);
}

protected function dumpNull($where): string
{
return $where['column'].' is null';
}

protected function dumpNotNull($where): string
{
return $where['column'].' is not null';
}

protected function dumpDatePartMethod($datePart, $where): string
{
return 'DATEPART('.$datePart.', '.$where['column'].') = '.$this->dumpQueryValue($where['value'] ?? null);
}

protected function dumpMonth($where): string
{
return $this->dumpDatePartMethod('MONTH', $where);
}

protected function dumpDay($where): string
{
return $this->dumpDatePartMethod('DAY', $where);
}

protected function dumpYear($where): string
{
return $this->dumpDatePartMethod('YEAR', $where);
}

protected function dumpTime($where): string
{
return $this->dumpDatePartMethod('TIMESTAMP', $where);
}

protected function dumpBetween($where): string
{
$valueOne = $this->dumpQueryValue($where['values'][0] ?? null);
$valueTwo = $this->dumpQueryValue($where['values'][1] ?? null);
$column = $where['column'];

return $column.' between '.$valueOne.' and '.$valueTwo;
}

protected function dumpNotBetween($where): string
{
$valueOne = $this->dumpQueryValue($where['values'][0] ?? null);
$valueTwo = $this->dumpQueryValue($where['values'][1] ?? null);
$column = $where['column'];

return $column.' not between '.$valueOne.' and '.$valueTwo;
}

protected function dumpColumn($where): string
{
return $where['column'].' = '.$where['value'];
}

protected function dumpNested($where): string
{
$query = $where['query'] ?? null;

$sql = (new Dumper($query))->withBindings($this->bindings)->dumpWheres();

return "($sql)";
}

protected function dumpDate($where)
{
return $this->dumpSimpleOperatorWhere($where);
}

protected function dumpJsonMethod($where): string
{
$jsonMethod = strtoupper(Str::snake($where['type']));

if (isset($where['values'])) {
$valueString = $this->dumpQueryArrayValues($where['values']);
} else {
$valueString = $this->dumpQueryValue($where['value'] ?? null);
}

return $jsonMethod.'('.$where['column'].', '.$valueString.')';
}

protected function dumpWhere($isFirst, $where): string
{
$dumpedWhere = '';

if (! $isFirst) {
$dumpedWhere = $where['boolean'].' ';
}

$type = $where['type'];

if (Str::startsWith($type, 'Json')) {
$dumpedWhere .= $this->dumpJsonMethod($where);
} else {
$whereMethod = 'dump'.ucfirst($type);

if (method_exists($this, $whereMethod)) {
$dumpedWhere .= $this->{$whereMethod}($where);
} else {
// Fail-safe to dump "something".
$dumpedWhere .= strtoupper($type);
}
}

return $dumpedWhere;
}

protected function dumpWheres(): string
{
if (count($this->wheres) === 0) {
return '';
}

$parts = [];

for ($i = 0; $i < count($this->wheres); $i++) {
$parts[] = $this->dumpWhere($i === 0, $this->wheres[$i]);
}

return implode(' ', $parts);
}
}
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
c72935c
Adds support for logging Stache queries
JohnathonKoster Mar 10, 2024
1818006
Update stache.php
JohnathonKoster Mar 10, 2024
4f3ea3a
Update LogsStacheQueries.php
JohnathonKoster Mar 10, 2024
c524230
Add extra FROM output from entry store
JohnathonKoster Mar 10, 2024
8b71078
Merge branch 'master' into adds-stache-query-logging-support
jasonvarga Mar 12, 2024
dd3561a
Merge branch 'master' into adds-stache-query-logging-support
jasonvarga Mar 29, 2024
3dae5ee
fix merge
jasonvarga Mar 29, 2024
dee1b0a
stache is implied by namespace
jasonvarga Mar 29, 2024
b0dd160
move into its own namespace
jasonvarga Mar 29, 2024
5e4f4e7
avoid property for enabling it
jasonvarga Mar 30, 2024
88e2e42
Use actual bindings ...
jasonvarga Mar 30, 2024
131e08b
reuse it
jasonvarga Apr 1, 2024
869aae4
move to generic spot so it can be used in non-stache query builders
jasonvarga Apr 1, 2024
929b7ff
move setting
jasonvarga Apr 1, 2024
5e254ec
tweaks ...
jasonvarga Apr 1, 2024
5a4236c
use lowercase, it looks more like what laravel normally outputs
jasonvarga Apr 1, 2024
7679a6a
tweaks ...
jasonvarga Apr 1, 2024
dcc2825
only dump the wheres in a nested where clause
jasonvarga Apr 1, 2024
87bdff3
avoid outputting "where" when there arent any
jasonvarga Apr 1, 2024
575954d
one-line everything. debugbar is weird when we provide newlines. ray …
jasonvarga Apr 1, 2024
1bf8fdb
wip
jasonvarga Apr 1, 2024
609db02
implement for assets
jasonvarga Apr 1, 2024
17c343d
and terms
jasonvarga Apr 1, 2024
ce548f9
dont fake queries in iterator builder, it's too much. we use that for…
jasonvarga Apr 1, 2024
4adb4da
move table name up a level
jasonvarga Apr 1, 2024
d85ab55
fix nested where not showing in search queries since they arent stach…
jasonvarga Apr 1, 2024
5d5817c
match was wrong. we wish it was that fast.
jasonvarga Apr 1, 2024
fe32412
add onceWithColumns ported from laravel ...
jasonvarga Apr 1, 2024
d489e6d
apply fake queries to pluck
jasonvarga Apr 1, 2024
b9a5579
nitpick
jasonvarga Apr 1, 2024
9bbb62c
nitpick
jasonvarga Apr 1, 2024
2975ace
nitpick
jasonvarga Apr 1, 2024
e191b87
nitpick
jasonvarga Apr 1, 2024
2c1ef7d
just mirror the debug setting
jasonvarga Apr 1, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions config/system.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -165,4 +165,17 @@

'row_id_handle' => 'id',

/*
|--------------------------------------------------------------------------
| Fake SQL Queries
|--------------------------------------------------------------------------
|
| When enabled, Statamic's query builders will emit events that appear
| the same way as any other query. This can be useful for debugging.
| The generated SQL statements are approximations and not exact.
|
*/

'fake_sql_queries' => config('app.debug'),

];
21 changes: 21 additions & 0 deletions src/Assets/QueryBuilder.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,6 +7,7 @@
use Statamic\Contracts\Assets\QueryBuilder as Contract;
use Statamic\Facades;
use Statamic\Stache\Query\Builder as BaseQueryBuilder;
use Statamic\Support\Arr;

class QueryBuilder extends BaseQueryBuilder implements Contract
{
Expand DownExpand Up@@ -119,4 +120,24 @@ protected function getWhereColumnKeyValuesByIndex($column)

return $items;
}

public function getTableNameForFakeQuery(): string
{
return 'assets';
}

public function prepareForFakeQuery(): array
{
$data = parent::prepareForFakeQuery();

$data['wheres'] = Arr::prepend($data['wheres'], [
'type' => 'Basic',
'column' => 'container',
'operator' => '=',
'value' => $this->getContainer()->handle(),
'boolean' => 'and',
]);

return $data;
}
}
18 changes: 18 additions & 0 deletions src/Query/Builder.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -12,9 +12,12 @@
use Statamic\Contracts\Query\Builder as Contract;
use Statamic\Extensions\Pagination\LengthAwarePaginator;
use Statamic\Facades\Pattern;
use Statamic\Query\Concerns\FakesQueries;

abstract class Builder implements Contract
{
use FakesQueries;

protected $columns;
protected $limit;
protected $offset = 0;
Expand DownExpand Up@@ -570,6 +573,21 @@ abstract public function count();

abstract public function get($columns = ['*']);

protected function onceWithColumns($columns, $callback)
{
$original = $this->columns;

if (is_null($original)) {
$this->columns = $columns;
}

$result = $callback();

$this->columns = $original;

return $result;
}

abstract public function pluck($column, $key = null);

public function when($value, $callback, $default = null)
Expand Down
42 changes: 42 additions & 0 deletions src/Query/Concerns/FakesQueries.php
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
<?php

namespace Statamic\Query\Concerns;

use Illuminate\Database\Events\QueryExecuted;
use Statamic\Query\Dumper\Dumper;

trait FakesQueries
{
protected function withFakeQueryLogging(\Closure $callback)
{
if (! config('statamic.system.fake_sql_queries', false)) {
return $callback();
}

$startTime = microtime(true);

$value = $callback();

$time = round((microtime(true) - $startTime) * 1000, 2);

event(new QueryExecuted(
($sql = new Dumper($this))->dump(),
$sql->bindings()->all(),
$time,
$sql->connection()
));

return $value;
}

public function prepareForFakeQuery(): array
{
return [
'wheres' => $this->wheres,
'columns' => $this->columns,
'orderBys' => $this->orderBys,
'limit' => $this->limit,
'offset' => $this->offset,
];
}
}
45 changes: 45 additions & 0 deletions src/Query/Dumper/Concerns/DumpsQueryParts.php
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
<?php

namespace Statamic\Query\Dumper\Concerns;

trait DumpsQueryParts
{
protected function dumpColumns(): string
{
return implode(', ', $this->columns);
}

protected function dumpLimits(): string
{
if (! $this->limit) {
return '';
}

$limit = ' limit '.$this->limit;

if ($this->offset) {
$limit .= ' offset '.$this->offset;
}

return $limit;
}

protected function dumpOrderBys(): string
{
if (count($this->orderBys) === 0) {
return '';
}

$orders = [];

foreach ($this->orderBys as $orderBy) {
if (! $orderBy->sort) {
continue;
}

$orders[] = $orderBy->sort.' '.$orderBy->direction;
}

return ' order by '.implode(', ', $orders);
}
}
24 changes: 24 additions & 0 deletions src/Query/Dumper/Concerns/DumpsQueryValues.php
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
<?php

namespace Statamic\Query\Dumper\Concerns;

trait DumpsQueryValues
{
protected function dumpQueryArrayValues($array): string
{
if (count($array) === 0) {
return '[]';
}

return collect($array)->map(function ($value) {
return $this->dumpQueryValue($value);
})->implode(', ');
}

protected function dumpQueryValue($value): string
{
$this->bindings[] = $value;

return '?';
}
}
160 changes: 160 additions & 0 deletions src/Query/Dumper/Concerns/DumpsWheres.php
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
<?php

namespace Statamic\Query\Dumper\Concerns;

use Illuminate\Support\Str;
use Statamic\Query\Dumper\Dumper;

trait DumpsWheres
{
protected function dumpBasic($where): string
{
return $where['column'].' '.$where['operator'].' '.$this->dumpQueryValue($where['value'] ?? null);
}

protected function dumpArrayWhere($keyword, $where): string
{
return $where['column'].' '.$keyword.' ('.$this->dumpQueryArrayValues($where['values'] ?? []).')';
}

protected function dumpSimpleOperatorWhere($where): string
{
return $where['column'].' '.$where['operator'].$this->dumpQueryValue($where['value'] ?? null);
}

protected function dumpIn($where): string
{
return $this->dumpArrayWhere('in', $where);
}

protected function dumpNotIn($where): string
{
return $this->dumpArrayWhere('not in', $where);
}

protected function dumpNull($where): string
{
return $where['column'].' is null';
}

protected function dumpNotNull($where): string
{
return $where['column'].' is not null';
}

protected function dumpDatePartMethod($datePart, $where): string
{
return 'DATEPART('.$datePart.', '.$where['column'].') = '.$this->dumpQueryValue($where['value'] ?? null);
}

protected function dumpMonth($where): string
{
return $this->dumpDatePartMethod('MONTH', $where);
}

protected function dumpDay($where): string
{
return $this->dumpDatePartMethod('DAY', $where);
}

protected function dumpYear($where): string
{
return $this->dumpDatePartMethod('YEAR', $where);
}

protected function dumpTime($where): string
{
return $this->dumpDatePartMethod('TIMESTAMP', $where);
}

protected function dumpBetween($where): string
{
$valueOne = $this->dumpQueryValue($where['values'][0] ?? null);
$valueTwo = $this->dumpQueryValue($where['values'][1] ?? null);
$column = $where['column'];

return $column.' between '.$valueOne.' and '.$valueTwo;
}

protected function dumpNotBetween($where): string
{
$valueOne = $this->dumpQueryValue($where['values'][0] ?? null);
$valueTwo = $this->dumpQueryValue($where['values'][1] ?? null);
$column = $where['column'];

return $column.' not between '.$valueOne.' and '.$valueTwo;
}

protected function dumpColumn($where): string
{
return $where['column'].' = '.$where['value'];
}

protected function dumpNested($where): string
{
$query = $where['query'] ?? null;

$sql = (new Dumper($query))->withBindings($this->bindings)->dumpWheres();

return "($sql)";
}

protected function dumpDate($where)
{
return $this->dumpSimpleOperatorWhere($where);
}

protected function dumpJsonMethod($where): string
{
$jsonMethod = strtoupper(Str::snake($where['type']));

if (isset($where['values'])) {
$valueString = $this->dumpQueryArrayValues($where['values']);
} else {
$valueString = $this->dumpQueryValue($where['value'] ?? null);
}

return $jsonMethod.'('.$where['column'].', '.$valueString.')';
}

protected function dumpWhere($isFirst, $where): string
{
$dumpedWhere = '';

if (! $isFirst) {
$dumpedWhere = $where['boolean'].' ';
}

$type = $where['type'];

if (Str::startsWith($type, 'Json')) {
$dumpedWhere .= $this->dumpJsonMethod($where);
} else {
$whereMethod = 'dump'.ucfirst($type);

if (method_exists($this, $whereMethod)) {
$dumpedWhere .= $this->{$whereMethod}($where);
} else {
// Fail-safe to dump "something".
$dumpedWhere .= strtoupper($type);
}
}

return $dumpedWhere;
}

protected function dumpWheres(): string
{
if (count($this->wheres) === 0) {
return '';
}

$parts = [];

for ($i = 0; $i < count($this->wheres); $i++) {
$parts[] = $this->dumpWhere($i === 0, $this->wheres[$i]);
}

return implode(' ', $parts);
}
}
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
c72935c
Adds support for logging Stache queries
JohnathonKoster Mar 10, 2024
1818006
Update stache.php
JohnathonKoster Mar 10, 2024
4f3ea3a
Update LogsStacheQueries.php
JohnathonKoster Mar 10, 2024
c524230
Add extra FROM output from entry store
JohnathonKoster Mar 10, 2024
8b71078
Merge branch 'master' into adds-stache-query-logging-support
jasonvarga Mar 12, 2024
dd3561a
Merge branch 'master' into adds-stache-query-logging-support
jasonvarga Mar 29, 2024
3dae5ee
fix merge
jasonvarga Mar 29, 2024
dee1b0a
stache is implied by namespace
jasonvarga Mar 29, 2024
b0dd160
move into its own namespace
jasonvarga Mar 29, 2024
5e4f4e7
avoid property for enabling it
jasonvarga Mar 30, 2024
88e2e42
Use actual bindings ...
jasonvarga Mar 30, 2024
131e08b
reuse it
jasonvarga Apr 1, 2024
869aae4
move to generic spot so it can be used in non-stache query builders
jasonvarga Apr 1, 2024
929b7ff
move setting
jasonvarga Apr 1, 2024
5e254ec
tweaks ...
jasonvarga Apr 1, 2024
5a4236c
use lowercase, it looks more like what laravel normally outputs
jasonvarga Apr 1, 2024
7679a6a
tweaks ...
jasonvarga Apr 1, 2024
dcc2825
only dump the wheres in a nested where clause
jasonvarga Apr 1, 2024
87bdff3
avoid outputting "where" when there arent any
jasonvarga Apr 1, 2024
575954d
one-line everything. debugbar is weird when we provide newlines. ray …
jasonvarga Apr 1, 2024
1bf8fdb
wip
jasonvarga Apr 1, 2024
609db02
implement for assets
jasonvarga Apr 1, 2024
17c343d
and terms
jasonvarga Apr 1, 2024
ce548f9
dont fake queries in iterator builder, it's too much. we use that for…
jasonvarga Apr 1, 2024
4adb4da
move table name up a level
jasonvarga Apr 1, 2024
d85ab55
fix nested where not showing in search queries since they arent stach…
jasonvarga Apr 1, 2024
5d5817c
match was wrong. we wish it was that fast.
jasonvarga Apr 1, 2024
fe32412
add onceWithColumns ported from laravel ...
jasonvarga Apr 1, 2024
d489e6d
apply fake queries to pluck
jasonvarga Apr 1, 2024
b9a5579
nitpick
jasonvarga Apr 1, 2024
9bbb62c
nitpick
jasonvarga Apr 1, 2024
2975ace
nitpick
jasonvarga Apr 1, 2024
e191b87
nitpick
jasonvarga Apr 1, 2024
2c1ef7d
just mirror the debug setting
jasonvarga Apr 1, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions config/system.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -165,4 +165,17 @@

'row_id_handle' => 'id',

/*
|--------------------------------------------------------------------------
| Fake SQL Queries
|--------------------------------------------------------------------------
|
| When enabled, Statamic's query builders will emit events that appear
| the same way as any other query. This can be useful for debugging.
| The generated SQL statements are approximations and not exact.
|
*/

'fake_sql_queries' => config('app.debug'),

];
21 changes: 21 additions & 0 deletions src/Assets/QueryBuilder.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,6 +7,7 @@
use Statamic\Contracts\Assets\QueryBuilder as Contract;
use Statamic\Facades;
use Statamic\Stache\Query\Builder as BaseQueryBuilder;
use Statamic\Support\Arr;

class QueryBuilder extends BaseQueryBuilder implements Contract
{
Expand DownExpand Up@@ -119,4 +120,24 @@ protected function getWhereColumnKeyValuesByIndex($column)

return $items;
}

public function getTableNameForFakeQuery(): string
{
return 'assets';
}

public function prepareForFakeQuery(): array
{
$data = parent::prepareForFakeQuery();

$data['wheres'] = Arr::prepend($data['wheres'], [
'type' => 'Basic',
'column' => 'container',
'operator' => '=',
'value' => $this->getContainer()->handle(),
'boolean' => 'and',
]);

return $data;
}
}
18 changes: 18 additions & 0 deletions src/Query/Builder.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -12,9 +12,12 @@
use Statamic\Contracts\Query\Builder as Contract;
use Statamic\Extensions\Pagination\LengthAwarePaginator;
use Statamic\Facades\Pattern;
use Statamic\Query\Concerns\FakesQueries;

abstract class Builder implements Contract
{
use FakesQueries;

protected $columns;
protected $limit;
protected $offset = 0;
Expand DownExpand Up@@ -570,6 +573,21 @@ abstract public function count();

abstract public function get($columns = ['*']);

protected function onceWithColumns($columns, $callback)
{
$original = $this->columns;

if (is_null($original)) {
$this->columns = $columns;
}

$result = $callback();

$this->columns = $original;

return $result;
}

abstract public function pluck($column, $key = null);

public function when($value, $callback, $default = null)
Expand Down
42 changes: 42 additions & 0 deletions src/Query/Concerns/FakesQueries.php
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
<?php

namespace Statamic\Query\Concerns;

use Illuminate\Database\Events\QueryExecuted;
use Statamic\Query\Dumper\Dumper;

trait FakesQueries
{
protected function withFakeQueryLogging(\Closure $callback)
{
if (! config('statamic.system.fake_sql_queries', false)) {
return $callback();
}

$startTime = microtime(true);

$value = $callback();

$time = round((microtime(true) - $startTime) * 1000, 2);

event(new QueryExecuted(
($sql = new Dumper($this))->dump(),
$sql->bindings()->all(),
$time,
$sql->connection()
));

return $value;
}

public function prepareForFakeQuery(): array
{
return [
'wheres' => $this->wheres,
'columns' => $this->columns,
'orderBys' => $this->orderBys,
'limit' => $this->limit,
'offset' => $this->offset,
];
}
}
45 changes: 45 additions & 0 deletions src/Query/Dumper/Concerns/DumpsQueryParts.php
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
<?php

namespace Statamic\Query\Dumper\Concerns;

trait DumpsQueryParts
{
protected function dumpColumns(): string
{
return implode(', ', $this->columns);
}

protected function dumpLimits(): string
{
if (! $this->limit) {
return '';
}

$limit = ' limit '.$this->limit;

if ($this->offset) {
$limit .= ' offset '.$this->offset;
}

return $limit;
}

protected function dumpOrderBys(): string
{
if (count($this->orderBys) === 0) {
return '';
}

$orders = [];

foreach ($this->orderBys as $orderBy) {
if (! $orderBy->sort) {
continue;
}

$orders[] = $orderBy->sort.' '.$orderBy->direction;
}

return ' order by '.implode(', ', $orders);
}
}
24 changes: 24 additions & 0 deletions src/Query/Dumper/Concerns/DumpsQueryValues.php
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
<?php

namespace Statamic\Query\Dumper\Concerns;

trait DumpsQueryValues
{
protected function dumpQueryArrayValues($array): string
{
if (count($array) === 0) {
return '[]';
}

return collect($array)->map(function ($value) {
return $this->dumpQueryValue($value);
})->implode(', ');
}

protected function dumpQueryValue($value): string
{
$this->bindings[] = $value;

return '?';
}
}
160 changes: 160 additions & 0 deletions src/Query/Dumper/Concerns/DumpsWheres.php
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
<?php

namespace Statamic\Query\Dumper\Concerns;

use Illuminate\Support\Str;
use Statamic\Query\Dumper\Dumper;

trait DumpsWheres
{
protected function dumpBasic($where): string
{
return $where['column'].' '.$where['operator'].' '.$this->dumpQueryValue($where['value'] ?? null);
}

protected function dumpArrayWhere($keyword, $where): string
{
return $where['column'].' '.$keyword.' ('.$this->dumpQueryArrayValues($where['values'] ?? []).')';
}

protected function dumpSimpleOperatorWhere($where): string
{
return $where['column'].' '.$where['operator'].$this->dumpQueryValue($where['value'] ?? null);
}

protected function dumpIn($where): string
{
return $this->dumpArrayWhere('in', $where);
}

protected function dumpNotIn($where): string
{
return $this->dumpArrayWhere('not in', $where);
}

protected function dumpNull($where): string
{
return $where['column'].' is null';
}

protected function dumpNotNull($where): string
{
return $where['column'].' is not null';
}

protected function dumpDatePartMethod($datePart, $where): string
{
return 'DATEPART('.$datePart.', '.$where['column'].') = '.$this->dumpQueryValue($where['value'] ?? null);
}

protected function dumpMonth($where): string
{
return $this->dumpDatePartMethod('MONTH', $where);
}

protected function dumpDay($where): string
{
return $this->dumpDatePartMethod('DAY', $where);
}

protected function dumpYear($where): string
{
return $this->dumpDatePartMethod('YEAR', $where);
}

protected function dumpTime($where): string
{
return $this->dumpDatePartMethod('TIMESTAMP', $where);
}

protected function dumpBetween($where): string
{
$valueOne = $this->dumpQueryValue($where['values'][0] ?? null);
$valueTwo = $this->dumpQueryValue($where['values'][1] ?? null);
$column = $where['column'];

return $column.' between '.$valueOne.' and '.$valueTwo;
}

protected function dumpNotBetween($where): string
{
$valueOne = $this->dumpQueryValue($where['values'][0] ?? null);
$valueTwo = $this->dumpQueryValue($where['values'][1] ?? null);
$column = $where['column'];

return $column.' not between '.$valueOne.' and '.$valueTwo;
}

protected function dumpColumn($where): string
{
return $where['column'].' = '.$where['value'];
}

protected function dumpNested($where): string
{
$query = $where['query'] ?? null;

$sql = (new Dumper($query))->withBindings($this->bindings)->dumpWheres();

return "($sql)";
}

protected function dumpDate($where)
{
return $this->dumpSimpleOperatorWhere($where);
}

protected function dumpJsonMethod($where): string
{
$jsonMethod = strtoupper(Str::snake($where['type']));

if (isset($where['values'])) {
$valueString = $this->dumpQueryArrayValues($where['values']);
} else {
$valueString = $this->dumpQueryValue($where['value'] ?? null);
}

return $jsonMethod.'('.$where['column'].', '.$valueString.')';
}

protected function dumpWhere($isFirst, $where): string
{
$dumpedWhere = '';

if (! $isFirst) {
$dumpedWhere = $where['boolean'].' ';
}

$type = $where['type'];

if (Str::startsWith($type, 'Json')) {
$dumpedWhere .= $this->dumpJsonMethod($where);
} else {
$whereMethod = 'dump'.ucfirst($type);

if (method_exists($this, $whereMethod)) {
$dumpedWhere .= $this->{$whereMethod}($where);
} else {
// Fail-safe to dump "something".
$dumpedWhere .= strtoupper($type);
}
}

return $dumpedWhere;
}

protected function dumpWheres(): string
{
if (count($this->wheres) === 0) {
return '';
}

$parts = [];

for ($i = 0; $i < count($this->wheres); $i++) {
$parts[] = $this->dumpWhere($i === 0, $this->wheres[$i]);
}

return implode(' ', $parts);
}
}
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
c72935c
Adds support for logging Stache queries
JohnathonKoster Mar 10, 2024
1818006
Update stache.php
JohnathonKoster Mar 10, 2024
4f3ea3a
Update LogsStacheQueries.php
JohnathonKoster Mar 10, 2024
c524230
Add extra FROM output from entry store
JohnathonKoster Mar 10, 2024
8b71078
Merge branch 'master' into adds-stache-query-logging-support
jasonvarga Mar 12, 2024
dd3561a
Merge branch 'master' into adds-stache-query-logging-support
jasonvarga Mar 29, 2024
3dae5ee
fix merge
jasonvarga Mar 29, 2024
dee1b0a
stache is implied by namespace
jasonvarga Mar 29, 2024
b0dd160
move into its own namespace
jasonvarga Mar 29, 2024
5e4f4e7
avoid property for enabling it
jasonvarga Mar 30, 2024
88e2e42
Use actual bindings ...
jasonvarga Mar 30, 2024
131e08b
reuse it
jasonvarga Apr 1, 2024
869aae4
move to generic spot so it can be used in non-stache query builders
jasonvarga Apr 1, 2024
929b7ff
move setting
jasonvarga Apr 1, 2024
5e254ec
tweaks ...
jasonvarga Apr 1, 2024
5a4236c
use lowercase, it looks more like what laravel normally outputs
jasonvarga Apr 1, 2024
7679a6a
tweaks ...
jasonvarga Apr 1, 2024
dcc2825
only dump the wheres in a nested where clause
jasonvarga Apr 1, 2024
87bdff3
avoid outputting "where" when there arent any
jasonvarga Apr 1, 2024
575954d
one-line everything. debugbar is weird when we provide newlines. ray …
jasonvarga Apr 1, 2024
1bf8fdb
wip
jasonvarga Apr 1, 2024
609db02
implement for assets
jasonvarga Apr 1, 2024
17c343d
and terms
jasonvarga Apr 1, 2024
ce548f9
dont fake queries in iterator builder, it's too much. we use that for…
jasonvarga Apr 1, 2024
4adb4da
move table name up a level
jasonvarga Apr 1, 2024
d85ab55
fix nested where not showing in search queries since they arent stach…
jasonvarga Apr 1, 2024
5d5817c
match was wrong. we wish it was that fast.
jasonvarga Apr 1, 2024
fe32412
add onceWithColumns ported from laravel ...
jasonvarga Apr 1, 2024
d489e6d
apply fake queries to pluck
jasonvarga Apr 1, 2024
b9a5579
nitpick
jasonvarga Apr 1, 2024
9bbb62c
nitpick
jasonvarga Apr 1, 2024
2975ace
nitpick
jasonvarga Apr 1, 2024
e191b87
nitpick
jasonvarga Apr 1, 2024
2c1ef7d
just mirror the debug setting
jasonvarga Apr 1, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions config/system.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -165,4 +165,17 @@

'row_id_handle' => 'id',

/*
|--------------------------------------------------------------------------
| Fake SQL Queries
|--------------------------------------------------------------------------
|
| When enabled, Statamic's query builders will emit events that appear
| the same way as any other query. This can be useful for debugging.
| The generated SQL statements are approximations and not exact.
|
*/

'fake_sql_queries' => config('app.debug'),

];
21 changes: 21 additions & 0 deletions src/Assets/QueryBuilder.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,6 +7,7 @@
use Statamic\Contracts\Assets\QueryBuilder as Contract;
use Statamic\Facades;
use Statamic\Stache\Query\Builder as BaseQueryBuilder;
use Statamic\Support\Arr;

class QueryBuilder extends BaseQueryBuilder implements Contract
{
Expand DownExpand Up@@ -119,4 +120,24 @@ protected function getWhereColumnKeyValuesByIndex($column)

return $items;
}

public function getTableNameForFakeQuery(): string
{
return 'assets';
}

public function prepareForFakeQuery(): array
{
$data = parent::prepareForFakeQuery();

$data['wheres'] = Arr::prepend($data['wheres'], [
'type' => 'Basic',
'column' => 'container',
'operator' => '=',
'value' => $this->getContainer()->handle(),
'boolean' => 'and',
]);

return $data;
}
}
18 changes: 18 additions & 0 deletions src/Query/Builder.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -12,9 +12,12 @@
use Statamic\Contracts\Query\Builder as Contract;
use Statamic\Extensions\Pagination\LengthAwarePaginator;
use Statamic\Facades\Pattern;
use Statamic\Query\Concerns\FakesQueries;

abstract class Builder implements Contract
{
use FakesQueries;

protected $columns;
protected $limit;
protected $offset = 0;
Expand DownExpand Up@@ -570,6 +573,21 @@ abstract public function count();

abstract public function get($columns = ['*']);

protected function onceWithColumns($columns, $callback)
{
$original = $this->columns;

if (is_null($original)) {
$this->columns = $columns;
}

$result = $callback();

$this->columns = $original;

return $result;
}

abstract public function pluck($column, $key = null);

public function when($value, $callback, $default = null)
Expand Down
42 changes: 42 additions & 0 deletions src/Query/Concerns/FakesQueries.php
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
<?php

namespace Statamic\Query\Concerns;

use Illuminate\Database\Events\QueryExecuted;
use Statamic\Query\Dumper\Dumper;

trait FakesQueries
{
protected function withFakeQueryLogging(\Closure $callback)
{
if (! config('statamic.system.fake_sql_queries', false)) {
return $callback();
}

$startTime = microtime(true);

$value = $callback();

$time = round((microtime(true) - $startTime) * 1000, 2);

event(new QueryExecuted(
($sql = new Dumper($this))->dump(),
$sql->bindings()->all(),
$time,
$sql->connection()
));

return $value;
}

public function prepareForFakeQuery(): array
{
return [
'wheres' => $this->wheres,
'columns' => $this->columns,
'orderBys' => $this->orderBys,
'limit' => $this->limit,
'offset' => $this->offset,
];
}
}
45 changes: 45 additions & 0 deletions src/Query/Dumper/Concerns/DumpsQueryParts.php
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
<?php

namespace Statamic\Query\Dumper\Concerns;

trait DumpsQueryParts
{
protected function dumpColumns(): string
{
return implode(', ', $this->columns);
}

protected function dumpLimits(): string
{
if (! $this->limit) {
return '';
}

$limit = ' limit '.$this->limit;

if ($this->offset) {
$limit .= ' offset '.$this->offset;
}

return $limit;
}

protected function dumpOrderBys(): string
{
if (count($this->orderBys) === 0) {
return '';
}

$orders = [];

foreach ($this->orderBys as $orderBy) {
if (! $orderBy->sort) {
continue;
}

$orders[] = $orderBy->sort.' '.$orderBy->direction;
}

return ' order by '.implode(', ', $orders);
}
}
24 changes: 24 additions & 0 deletions src/Query/Dumper/Concerns/DumpsQueryValues.php
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
<?php

namespace Statamic\Query\Dumper\Concerns;

trait DumpsQueryValues
{
protected function dumpQueryArrayValues($array): string
{
if (count($array) === 0) {
return '[]';
}

return collect($array)->map(function ($value) {
return $this->dumpQueryValue($value);
})->implode(', ');
}

protected function dumpQueryValue($value): string
{
$this->bindings[] = $value;

return '?';
}
}
160 changes: 160 additions & 0 deletions src/Query/Dumper/Concerns/DumpsWheres.php
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
<?php

namespace Statamic\Query\Dumper\Concerns;

use Illuminate\Support\Str;
use Statamic\Query\Dumper\Dumper;

trait DumpsWheres
{
protected function dumpBasic($where): string
{
return $where['column'].' '.$where['operator'].' '.$this->dumpQueryValue($where['value'] ?? null);
}

protected function dumpArrayWhere($keyword, $where): string
{
return $where['column'].' '.$keyword.' ('.$this->dumpQueryArrayValues($where['values'] ?? []).')';
}

protected function dumpSimpleOperatorWhere($where): string
{
return $where['column'].' '.$where['operator'].$this->dumpQueryValue($where['value'] ?? null);
}

protected function dumpIn($where): string
{
return $this->dumpArrayWhere('in', $where);
}

protected function dumpNotIn($where): string
{
return $this->dumpArrayWhere('not in', $where);
}

protected function dumpNull($where): string
{
return $where['column'].' is null';
}

protected function dumpNotNull($where): string
{
return $where['column'].' is not null';
}

protected function dumpDatePartMethod($datePart, $where): string
{
return 'DATEPART('.$datePart.', '.$where['column'].') = '.$this->dumpQueryValue($where['value'] ?? null);
}

protected function dumpMonth($where): string
{
return $this->dumpDatePartMethod('MONTH', $where);
}

protected function dumpDay($where): string
{
return $this->dumpDatePartMethod('DAY', $where);
}

protected function dumpYear($where): string
{
return $this->dumpDatePartMethod('YEAR', $where);
}

protected function dumpTime($where): string
{
return $this->dumpDatePartMethod('TIMESTAMP', $where);
}

protected function dumpBetween($where): string
{
$valueOne = $this->dumpQueryValue($where['values'][0] ?? null);
$valueTwo = $this->dumpQueryValue($where['values'][1] ?? null);
$column = $where['column'];

return $column.' between '.$valueOne.' and '.$valueTwo;
}

protected function dumpNotBetween($where): string
{
$valueOne = $this->dumpQueryValue($where['values'][0] ?? null);
$valueTwo = $this->dumpQueryValue($where['values'][1] ?? null);
$column = $where['column'];

return $column.' not between '.$valueOne.' and '.$valueTwo;
}

protected function dumpColumn($where): string
{
return $where['column'].' = '.$where['value'];
}

protected function dumpNested($where): string
{
$query = $where['query'] ?? null;

$sql = (new Dumper($query))->withBindings($this->bindings)->dumpWheres();

return "($sql)";
}

protected function dumpDate($where)
{
return $this->dumpSimpleOperatorWhere($where);
}

protected function dumpJsonMethod($where): string
{
$jsonMethod = strtoupper(Str::snake($where['type']));

if (isset($where['values'])) {
$valueString = $this->dumpQueryArrayValues($where['values']);
} else {
$valueString = $this->dumpQueryValue($where['value'] ?? null);
}

return $jsonMethod.'('.$where['column'].', '.$valueString.')';
}

protected function dumpWhere($isFirst, $where): string
{
$dumpedWhere = '';

if (! $isFirst) {
$dumpedWhere = $where['boolean'].' ';
}

$type = $where['type'];

if (Str::startsWith($type, 'Json')) {
$dumpedWhere .= $this->dumpJsonMethod($where);
} else {
$whereMethod = 'dump'.ucfirst($type);

if (method_exists($this, $whereMethod)) {
$dumpedWhere .= $this->{$whereMethod}($where);
} else {
// Fail-safe to dump "something".
$dumpedWhere .= strtoupper($type);
}
}

return $dumpedWhere;
}

protected function dumpWheres(): string
{
if (count($this->wheres) === 0) {
return '';
}

$parts = [];

for ($i = 0; $i < count($this->wheres); $i++) {
$parts[] = $this->dumpWhere($i === 0, $this->wheres[$i]);
}

return implode(' ', $parts);
}
}
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
c72935c
Adds support for logging Stache queries
JohnathonKoster Mar 10, 2024
1818006
Update stache.php
JohnathonKoster Mar 10, 2024
4f3ea3a
Update LogsStacheQueries.php
JohnathonKoster Mar 10, 2024
c524230
Add extra FROM output from entry store
JohnathonKoster Mar 10, 2024
8b71078
Merge branch 'master' into adds-stache-query-logging-support
jasonvarga Mar 12, 2024
dd3561a
Merge branch 'master' into adds-stache-query-logging-support
jasonvarga Mar 29, 2024
3dae5ee
fix merge
jasonvarga Mar 29, 2024
dee1b0a
stache is implied by namespace
jasonvarga Mar 29, 2024
b0dd160
move into its own namespace
jasonvarga Mar 29, 2024
5e4f4e7
avoid property for enabling it
jasonvarga Mar 30, 2024
88e2e42
Use actual bindings ...
jasonvarga Mar 30, 2024
131e08b
reuse it
jasonvarga Apr 1, 2024
869aae4
move to generic spot so it can be used in non-stache query builders
jasonvarga Apr 1, 2024
929b7ff
move setting
jasonvarga Apr 1, 2024
5e254ec
tweaks ...
jasonvarga Apr 1, 2024
5a4236c
use lowercase, it looks more like what laravel normally outputs
jasonvarga Apr 1, 2024
7679a6a
tweaks ...
jasonvarga Apr 1, 2024
dcc2825
only dump the wheres in a nested where clause
jasonvarga Apr 1, 2024
87bdff3
avoid outputting "where" when there arent any
jasonvarga Apr 1, 2024
575954d
one-line everything. debugbar is weird when we provide newlines. ray …
jasonvarga Apr 1, 2024
1bf8fdb
wip
jasonvarga Apr 1, 2024
609db02
implement for assets
jasonvarga Apr 1, 2024
17c343d
and terms
jasonvarga Apr 1, 2024
ce548f9
dont fake queries in iterator builder, it's too much. we use that for…
jasonvarga Apr 1, 2024
4adb4da
move table name up a level
jasonvarga Apr 1, 2024
d85ab55
fix nested where not showing in search queries since they arent stach…
jasonvarga Apr 1, 2024
5d5817c
match was wrong. we wish it was that fast.
jasonvarga Apr 1, 2024
fe32412
add onceWithColumns ported from laravel ...
jasonvarga Apr 1, 2024
d489e6d
apply fake queries to pluck
jasonvarga Apr 1, 2024
b9a5579
nitpick
jasonvarga Apr 1, 2024
9bbb62c
nitpick
jasonvarga Apr 1, 2024
2975ace
nitpick
jasonvarga Apr 1, 2024
e191b87
nitpick
jasonvarga Apr 1, 2024
2c1ef7d
just mirror the debug setting
jasonvarga Apr 1, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions config/system.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -165,4 +165,17 @@

'row_id_handle' => 'id',

/*
|--------------------------------------------------------------------------
| Fake SQL Queries
|--------------------------------------------------------------------------
|
| When enabled, Statamic's query builders will emit events that appear
| the same way as any other query. This can be useful for debugging.
| The generated SQL statements are approximations and not exact.
|
*/

'fake_sql_queries' => config('app.debug'),

];
21 changes: 21 additions & 0 deletions src/Assets/QueryBuilder.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,6 +7,7 @@
use Statamic\Contracts\Assets\QueryBuilder as Contract;
use Statamic\Facades;
use Statamic\Stache\Query\Builder as BaseQueryBuilder;
use Statamic\Support\Arr;

class QueryBuilder extends BaseQueryBuilder implements Contract
{
Expand DownExpand Up@@ -119,4 +120,24 @@ protected function getWhereColumnKeyValuesByIndex($column)

return $items;
}

public function getTableNameForFakeQuery(): string
{
return 'assets';
}

public function prepareForFakeQuery(): array
{
$data = parent::prepareForFakeQuery();

$data['wheres'] = Arr::prepend($data['wheres'], [
'type' => 'Basic',
'column' => 'container',
'operator' => '=',
'value' => $this->getContainer()->handle(),
'boolean' => 'and',
]);

return $data;
}
}
18 changes: 18 additions & 0 deletions src/Query/Builder.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -12,9 +12,12 @@
use Statamic\Contracts\Query\Builder as Contract;
use Statamic\Extensions\Pagination\LengthAwarePaginator;
use Statamic\Facades\Pattern;
use Statamic\Query\Concerns\FakesQueries;

abstract class Builder implements Contract
{
use FakesQueries;

protected $columns;
protected $limit;
protected $offset = 0;
Expand DownExpand Up@@ -570,6 +573,21 @@ abstract public function count();

abstract public function get($columns = ['*']);

protected function onceWithColumns($columns, $callback)
{
$original = $this->columns;

if (is_null($original)) {
$this->columns = $columns;
}

$result = $callback();

$this->columns = $original;

return $result;
}

abstract public function pluck($column, $key = null);

public function when($value, $callback, $default = null)
Expand Down
42 changes: 42 additions & 0 deletions src/Query/Concerns/FakesQueries.php
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
<?php

namespace Statamic\Query\Concerns;

use Illuminate\Database\Events\QueryExecuted;
use Statamic\Query\Dumper\Dumper;

trait FakesQueries
{
protected function withFakeQueryLogging(\Closure $callback)
{
if (! config('statamic.system.fake_sql_queries', false)) {
return $callback();
}

$startTime = microtime(true);

$value = $callback();

$time = round((microtime(true) - $startTime) * 1000, 2);

event(new QueryExecuted(
($sql = new Dumper($this))->dump(),
$sql->bindings()->all(),
$time,
$sql->connection()
));

return $value;
}

public function prepareForFakeQuery(): array
{
return [
'wheres' => $this->wheres,
'columns' => $this->columns,
'orderBys' => $this->orderBys,
'limit' => $this->limit,
'offset' => $this->offset,
];
}
}
45 changes: 45 additions & 0 deletions src/Query/Dumper/Concerns/DumpsQueryParts.php
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
<?php

namespace Statamic\Query\Dumper\Concerns;

trait DumpsQueryParts
{
protected function dumpColumns(): string
{
return implode(', ', $this->columns);
}

protected function dumpLimits(): string
{
if (! $this->limit) {
return '';
}

$limit = ' limit '.$this->limit;

if ($this->offset) {
$limit .= ' offset '.$this->offset;
}

return $limit;
}

protected function dumpOrderBys(): string
{
if (count($this->orderBys) === 0) {
return '';
}

$orders = [];

foreach ($this->orderBys as $orderBy) {
if (! $orderBy->sort) {
continue;
}

$orders[] = $orderBy->sort.' '.$orderBy->direction;
}

return ' order by '.implode(', ', $orders);
}
}
24 changes: 24 additions & 0 deletions src/Query/Dumper/Concerns/DumpsQueryValues.php
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
<?php

namespace Statamic\Query\Dumper\Concerns;

trait DumpsQueryValues
{
protected function dumpQueryArrayValues($array): string
{
if (count($array) === 0) {
return '[]';
}

return collect($array)->map(function ($value) {
return $this->dumpQueryValue($value);
})->implode(', ');
}

protected function dumpQueryValue($value): string
{
$this->bindings[] = $value;

return '?';
}
}
160 changes: 160 additions & 0 deletions src/Query/Dumper/Concerns/DumpsWheres.php
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
<?php

namespace Statamic\Query\Dumper\Concerns;

use Illuminate\Support\Str;
use Statamic\Query\Dumper\Dumper;

trait DumpsWheres
{
protected function dumpBasic($where): string
{
return $where['column'].' '.$where['operator'].' '.$this->dumpQueryValue($where['value'] ?? null);
}

protected function dumpArrayWhere($keyword, $where): string
{
return $where['column'].' '.$keyword.' ('.$this->dumpQueryArrayValues($where['values'] ?? []).')';
}

protected function dumpSimpleOperatorWhere($where): string
{
return $where['column'].' '.$where['operator'].$this->dumpQueryValue($where['value'] ?? null);
}

protected function dumpIn($where): string
{
return $this->dumpArrayWhere('in', $where);
}

protected function dumpNotIn($where): string
{
return $this->dumpArrayWhere('not in', $where);
}

protected function dumpNull($where): string
{
return $where['column'].' is null';
}

protected function dumpNotNull($where): string
{
return $where['column'].' is not null';
}

protected function dumpDatePartMethod($datePart, $where): string
{
return 'DATEPART('.$datePart.', '.$where['column'].') = '.$this->dumpQueryValue($where['value'] ?? null);
}

protected function dumpMonth($where): string
{
return $this->dumpDatePartMethod('MONTH', $where);
}

protected function dumpDay($where): string
{
return $this->dumpDatePartMethod('DAY', $where);
}

protected function dumpYear($where): string
{
return $this->dumpDatePartMethod('YEAR', $where);
}

protected function dumpTime($where): string
{
return $this->dumpDatePartMethod('TIMESTAMP', $where);
}

protected function dumpBetween($where): string
{
$valueOne = $this->dumpQueryValue($where['values'][0] ?? null);
$valueTwo = $this->dumpQueryValue($where['values'][1] ?? null);
$column = $where['column'];

return $column.' between '.$valueOne.' and '.$valueTwo;
}

protected function dumpNotBetween($where): string
{
$valueOne = $this->dumpQueryValue($where['values'][0] ?? null);
$valueTwo = $this->dumpQueryValue($where['values'][1] ?? null);
$column = $where['column'];

return $column.' not between '.$valueOne.' and '.$valueTwo;
}

protected function dumpColumn($where): string
{
return $where['column'].' = '.$where['value'];
}

protected function dumpNested($where): string
{
$query = $where['query'] ?? null;

$sql = (new Dumper($query))->withBindings($this->bindings)->dumpWheres();

return "($sql)";
}

protected function dumpDate($where)
{
return $this->dumpSimpleOperatorWhere($where);
}

protected function dumpJsonMethod($where): string
{
$jsonMethod = strtoupper(Str::snake($where['type']));

if (isset($where['values'])) {
$valueString = $this->dumpQueryArrayValues($where['values']);
} else {
$valueString = $this->dumpQueryValue($where['value'] ?? null);
}

return $jsonMethod.'('.$where['column'].', '.$valueString.')';
}

protected function dumpWhere($isFirst, $where): string
{
$dumpedWhere = '';

if (! $isFirst) {
$dumpedWhere = $where['boolean'].' ';
}

$type = $where['type'];

if (Str::startsWith($type, 'Json')) {
$dumpedWhere .= $this->dumpJsonMethod($where);
} else {
$whereMethod = 'dump'.ucfirst($type);

if (method_exists($this, $whereMethod)) {
$dumpedWhere .= $this->{$whereMethod}($where);
} else {
// Fail-safe to dump "something".
$dumpedWhere .= strtoupper($type);
}
}

return $dumpedWhere;
}

protected function dumpWheres(): string
{
if (count($this->wheres) === 0) {
return '';
}

$parts = [];

for ($i = 0; $i < count($this->wheres); $i++) {
$parts[] = $this->dumpWhere($i === 0, $this->wheres[$i]);
}

return implode(' ', $parts);
}
}
Loading