Skip to content

Commit

Permalink
feat(datatable): improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
Byrby committed Aug 9, 2023
1 parent b74ff4d commit 62eb1dd
Show file tree
Hide file tree
Showing 5 changed files with 223 additions and 46 deletions.
7 changes: 4 additions & 3 deletions resources/views/datatable/datatable.blade.php
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
<div>
@if ($this->isSearchable())
<div class="mb-8 flex items-center justify-between">
<input wire:model="search" type="search" class="w-full max-w-lg rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500"
<input wire:model="search" type="search"
class="w-full max-w-lg rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500"
placeholder="Recherche ...">
</div>
@endif
Expand All @@ -17,7 +18,7 @@
@if (!$column->hidden)
@include('helium-core::datatable.header', [
'label' => $column->label,
'class' => implode(',', $column->headerClasses),
'class' => implode(' ', $column->headerClasses),
'sortable' => $column->sortable ? $column->alias : '',
])
@endif
Expand All @@ -29,7 +30,7 @@
<tr {!! $this->getDataLinkAttr($result) !!}>
@foreach ($this->columns() as $column)
@if (!$column->hidden)
<td class="{{ implode(',', $column->classes) }}">
<td class="{{ implode(' ', $column->classes) }}">
{{ $column->render($result->getAttribute($column->alias), $result) }}
</td>
@endif
Expand Down
26 changes: 26 additions & 0 deletions src/Datatable/Column.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ class Column

public bool $isRaw = false;

public bool $isCustom = false;

public string $name;

public string $alias;
Expand Down Expand Up @@ -58,6 +60,18 @@ public static function name(string $name): self
return $column;
}

public static function add(string $name): self
{
$column = new self();
$column->name = $name;
$column->isCustom = true;
$column->rawSelect = null;
$column->alias = '';
$column->label = ucfirst($column->name);

return $column;
}

public function headerClasses(string|array $headerClasses): self
{
if (is_string($headerClasses)) {
Expand All @@ -78,6 +92,18 @@ public function classes(string|array $classes): self
return $this;
}

public function columnClasses(string|array $classes): self
{
if (is_string($classes)) {
$classes = explode(' ', $classes);
}

$this->classes = $classes;
$this->headerClasses = $classes;

return $this;
}

public function alignRight(): self
{
$this->classes[] = 'text-right';
Expand Down
92 changes: 49 additions & 43 deletions src/Datatable/Datatable.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@
namespace Webup\HeliumCore\Datatable;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\HasManyThrough;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
use Livewire\Component;

class Datatable extends Component
Expand Down Expand Up @@ -119,55 +123,57 @@ private function addSelect()
$this->query->addSelect($this->model->getKeyName());

foreach ($this->columns() as $column) {
if ($column->isRaw) {
$this->query->addSelect(DB::raw($column->rawSelect));
} elseif (str_contains($column->name, '.')) {
throw new \Exception('Relationships not supported yet');
// $relations = explode('.', Str::before($column->name, ':'));
// $relationName = $relations[0];
// $relationQuery = $this->query->getRelation($relationName);
// if ($relationQuery instanceof HasMany || $relationQuery instanceof HasManyThrough || $relationQuery instanceof BelongsToMany) {
// $this->query->customWithAggregate($relationName, Str::after($column->name, ':') ?? 'count', $relations[1], $column->name);
// } else {
// $this->addSelectWithRelation($column);
// // todo gérer les contraintes
// // cette ligne est fonctionne pour les HasOne avec contrainte mais le réquete n'est pas géniale
// // https://devblogs.microsoft.com/premier-developer/using-join-with-max-to-write-efficient-queries/
// // $this->query->custom($relationName, last($relations), $column->alias);
// }
if (! $column->isCustom) {
if ($column->isRaw) {
$this->query->addSelect(DB::raw($column->rawSelect));
} elseif (str_contains($column->name, '.')) {
$relations = explode('.', Str::before($column->name, ':'));
$relationName = $relations[0];
$relationQuery = $this->query->getRelation($relationName);
if ($relationQuery instanceof HasMany || $relationQuery instanceof HasManyThrough || $relationQuery instanceof BelongsToMany) {
$this->query->customWithAggregate($relationName, Str::after($column->name, ':') ?? 'count', $relations[1], $column->alias);

Check failure on line 134 in src/Datatable/Datatable.php

View workflow job for this annotation

GitHub Actions / phpstan

Expression on left side of ?? is not nullable.
} else {
$this->addSelectWithRelation($column);
// todo gérer les contraintes
// cette ligne est fonctionne pour les HasOne avec contrainte mais le réquete n'est pas géniale
// https://devblogs.microsoft.com/premier-developer/using-join-with-max-to-write-efficient-queries/
// $this->query->custom($relationName, last($relations), $column->alias);
}
} else {
$table = $this->model->getTable();
$col = $column->name;
$alias = $column->alias;
$this->query->addSelect(DB::raw("$table.$col as $alias"));
}
}

}
}

private function addSelectWithRelation($column)
{
$relations = explode('.', Str::before($column->name, ':'));
$relatedQuery = $this->baseQuery();
$table = null;

$last = count($relations) - 1;
foreach ($relations as $i => $relationName) {
$isRelation = $i < $last;
if ($isRelation) {
$table = $relatedQuery->getRelation($relationName)->getRelated()->getTable();
$useThrough = collect($this->query->getQuery()->joins)
->pluck('table')
->contains($table);

$relatedQuery = $this->query->joinRelation($relationName, null, 'left', $useThrough, $relatedQuery);
} else {
$table = $this->model->getTable();
$col = $column->name;
$col = $relationName;
$alias = $column->alias;
$this->query->addSelect(DB::raw("$table.$col as $alias"));
$this->query->addSelect(DB::raw($table.'.'.$col.' as `'.$alias.'`'));
}
}
}

// private function addSelectWithRelation($column)
// {
// $relations = explode('.', Str::before($column->name, ':'));
// $relatedQuery = $this->baseQuery();
// $table = null;

// $last = count($relations) - 1;
// foreach ($relations as $i => $relationName) {
// $isRelation = $i < $last;
// if ($isRelation) {
// $table = $relatedQuery->getRelation($relationName)->getRelated()->getTable();
// $useThrough = collect($this->query->getQuery()->joins)
// ->pluck('table')
// ->contains($table);

// $relatedQuery = $this->query->joinRelation($relationName, null, 'left', $useThrough, $relatedQuery);
// } else {
// $col = $relationName;
// $alias = $column->alias;
// $this->query->addSelect(DB::raw($table . '.' . $col . ' as `' . $alias . '`'));
// }
// }
// }

// todo gérer la recherche avec tous les type de relations
private function addFilters()
{
Expand Down
23 changes: 23 additions & 0 deletions src/Datatable/Sidebar.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

namespace Webup\HeliumCore\Datatable;

use Livewire\Component;

class Sidebar extends Component
{
public string $sharedKey;

public array $data = [];

public function mount()
{
$this->data = session()->get('datatable.'.$this->sharedKey, []);
}

public function submitSidebar($data)
{
session()->put('datatable.'.$this->sharedKey, $data);
$this->data = $data;
}
}
121 changes: 121 additions & 0 deletions src/HeliumCoreServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

namespace Webup\HeliumCore;

use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
use Illuminate\Database\Eloquent\Relations\Relation;
use Illuminate\Database\Query\Expression;
use Spatie\LaravelPackageTools\Package;
use Spatie\LaravelPackageTools\PackageServiceProvider;
use Webup\HeliumCore\Commands\Publish;
Expand All @@ -22,4 +25,122 @@ public function configurePackage(Package $package): void
$this->loadRoutesFrom($routes);
}
}

public function registeringPackage()
{
$this->registerDatatableMacros();
}

private function registerDatatableMacros()
{
EloquentBuilder::macro('customWithAggregate', function ($relations, $aggregate, $column, $alias = null) {
if (empty($relations)) {
return $this;
}

$relations = is_array($relations) ? $relations : [$relations];

foreach ($this->parseWithRelations($relations) as $name => $constraints) {

Check failure on line 43 in src/HeliumCoreServiceProvider.php

View workflow job for this annotation

GitHub Actions / phpstan

Call to an undefined method Webup\HeliumCore\HeliumCoreServiceProvider::parseWithRelations().
$segments = explode(' ', $name);

if (count($segments) == 3 && Str::lower($segments[1]) == 'as') {

Check failure on line 46 in src/HeliumCoreServiceProvider.php

View workflow job for this annotation

GitHub Actions / phpstan

Call to static method lower() on an unknown class Webup\HeliumCore\Str.
[$name, $alias] = [$segments[0], $segments[2]];
}

$relation = $this->getRelationWithoutConstraints($name);

Check failure on line 50 in src/HeliumCoreServiceProvider.php

View workflow job for this annotation

GitHub Actions / phpstan

Call to an undefined method Webup\HeliumCore\HeliumCoreServiceProvider::getRelationWithoutConstraints().

$table = $relation->getRelated()->newQuery()->getQuery()->from === $this->getQuery()->from

Check failure on line 52 in src/HeliumCoreServiceProvider.php

View workflow job for this annotation

GitHub Actions / phpstan

Call to an undefined method Webup\HeliumCore\HeliumCoreServiceProvider::getQuery().
? $relation->getRelationCountHashWithoutIncrementing()
: ($this->query->getConnection()->getTablePrefix() ?? '').$relation->getRelated()->getTable();

Check failure on line 54 in src/HeliumCoreServiceProvider.php

View workflow job for this annotation

GitHub Actions / phpstan

Access to an undefined property Webup\HeliumCore\HeliumCoreServiceProvider::$query.

$query = $relation->getRelationExistenceAggregatesQuery(
$relation->getRelated()->newQuery(),
$this,
$aggregate,
$table.'.'.($column ?? 'id')
);

$query->callScope($constraints);

$query = $query->mergeConstraintsFrom($relation->getQuery())->toBase();

if (count($query->columns) > 1) {
$query->columns = [$query->columns[0]];
}
$columnAlias = new Expression('`'.($alias ?? collect([$relations, $column])->filter()->flatten()->join('.')).'`');
$this->selectSub($query, $columnAlias);

Check failure on line 71 in src/HeliumCoreServiceProvider.php

View workflow job for this annotation

GitHub Actions / phpstan

Call to an undefined method Webup\HeliumCore\HeliumCoreServiceProvider::selectSub().
}

return $this;
});

EloquentBuilder::macro('custom', function ($relations, $column, $alias = null) {
if (empty($relations)) {
return $this;
}

$relations = is_array($relations) ? $relations : [$relations];

foreach ($this->parseWithRelations($relations) as $name => $constraints) {

Check failure on line 84 in src/HeliumCoreServiceProvider.php

View workflow job for this annotation

GitHub Actions / phpstan

Call to an undefined method Webup\HeliumCore\HeliumCoreServiceProvider::parseWithRelations().
$segments = explode(' ', $name);

if (count($segments) == 3 && Str::lower($segments[1]) == 'as') {

Check failure on line 87 in src/HeliumCoreServiceProvider.php

View workflow job for this annotation

GitHub Actions / phpstan

Call to static method lower() on an unknown class Webup\HeliumCore\Str.
[$name, $alias] = [$segments[0], $segments[2]];
}

$relation = $this->getRelationWithoutConstraints($name);

$table = $relation->getRelated()->newQuery()->getQuery()->from === $this->getQuery()->from
? $relation->getRelationCountHashWithoutIncrementing()
: ($this->query->getConnection()->getTablePrefix() ?? '').$relation->getRelated()->getTable();

$query = $relation->getRelationExistenceQuery(
$relation->getRelated()->newQuery(),
$this,
$table.'.'.($column ?? 'id')
)->setBindings([], 'select');

$query->callScope($constraints);

$query = $query->mergeConstraintsFrom($relation->getQuery())->toBase();

if (count($query->columns) > 1) {
$query->columns = [$query->columns[0]];
}
$columnAlias = new Expression('`'.($alias ?? collect([$relations, $column])->filter()->flatten()->join('.')).'`');
$this->selectSub($query, $columnAlias);
}

return $this;
});

EloquentBuilder::macro('getRelationTable', function ($name) {
$relation = $this->getRelationWithoutConstraints($name);

$table = $relation->getRelated()->newQuery()->getQuery()->from === $this->getQuery()->from
? $relation->getRelationCountHashWithoutIncrementing()
: ($this->query->getConnection()->getTablePrefix() ?? '').$relation->getRelated()->getTable();

return $table;
});

Relation::macro('getRelationExistenceAggregatesQuery', function (EloquentBuilder $query, EloquentBuilder $parentQuery, $aggregate, $column) {
$distinct_aggregate = new Expression($aggregate."(distinct {$column} separator ', ')");

if ($query->getConnection()->getPDO()->getAttribute(\PDO::ATTR_DRIVER_NAME) === 'sqlite') {
$distinct_aggregate = new Expression($aggregate."(REPLACE(DISTINCT({$column}), '', '') , ', ')");
}

$expression = $aggregate === 'group_concat'

? $distinct_aggregate
: new Expression('COALESCE('.$aggregate."({$column}),0)");

return $this->getRelationExistenceQuery(
$query,
$parentQuery,
$expression
)->setBindings([], 'select');
});
}
}

0 comments on commit 62eb1dd

Please sign in to comment.