Skip to content

Commit

Permalink
Merge pull request #104 from pxlrbt/feature/ignore-formatting
Browse files Browse the repository at this point in the history
Ignore formatting
  • Loading branch information
pxlrbt authored Aug 21, 2023
2 parents 6812c58 + 084f7b0 commit 247af65
Show file tree
Hide file tree
Showing 9 changed files with 239 additions and 44 deletions.
45 changes: 45 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,51 @@ ExportAction::make()->exports([
])
```

#### Ignore Formatting

When using `->fromForm()`/`->fromTable()` the formatting is resolved from your table or form definition. If you don't want to overwrite every columns `->formatStateUsing()` method, you can ignore the formatting altogher or for specific columns by using `->ignoreFormatting()`:

```php
use pxlrbt\FilamentExcel\Actions\Tables\ExportAction;
use pxlrbt\FilamentExcel\Exports\ExcelExport;
use pxlrbt\FilamentExcel\Columns\Column;

ExportAction::make()->exports([
// Ignore all formatting
ExcelExport::make()->fromTable()->ignoreFormatting()

// Ignore specific columns
ExcelExport::make()->fromTable()->ignoreFormatting([
'created_at', 'updated_at',
]),

// Ignore columns based on Closure
ExcelExport::make()->fromTable()->ignoreFormatting(
fn (Column $column) => Str::startsWith($column->getName(), 'customer_')
),
])
```

#### Formatters

When the state of column is not a string, it is run through a formatter even if you use `->ignoreFormatting()` to make sure it's in the right format for Excel.

Currently there are 3 formatters: `ArrayFormatter`, `EnumFormatter` and `ObjectFormatter`. You can swap out any implementation via Laravel's service container, for example to use a different delimiter for the `ArrayFormatter`:

```php
use pxlrbt\FilamentExcel\Exports\Formatters\ArrayFormatter;

class AppServiceProvider extends ServiceProvider
{
public function register()
{
App::bind(ArrayFormatter::class, function () {
return new ArrayFormatter(';');
});
}
```


### User input

You can let the user pick a filename and writer type by using `->askForFilename()` and `->askForWriterType()`:
Expand Down
31 changes: 31 additions & 0 deletions src/Exports/Concerns/CanIgnoreFormatting.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php

namespace pxlrbt\FilamentExcel\Exports\Concerns;

use Closure;
use pxlrbt\FilamentExcel\Columns\Column;

trait CanIgnoreFormatting
{
public Closure|array|bool $ignoreFormattingOnColumns = false;

public function ignoreFormatting(Closure|array|bool $columns = true): static
{
$this->ignoreFormattingOnColumns = $columns;

return $this;
}

protected function shouldIgnoreFormattingForColumn(Column $column): bool
{
$shouldIgnore = $this->evaluate($this->ignoreFormattingOnColumns, [
'column' => $column,
]);

if (is_bool($shouldIgnore)) {
return $shouldIgnore;
}

return in_array($column->getName(), $shouldIgnore);
}
}
97 changes: 53 additions & 44 deletions src/Exports/Concerns/WithMapping.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Arr;
use Illuminate\Support\Collection;
use UnitEnum;
use pxlrbt\FilamentExcel\Columns\Column;
use pxlrbt\FilamentExcel\Exports\Formatters\Formatter;

trait WithMapping
{
Expand Down Expand Up @@ -37,7 +38,7 @@ public function getMapping($row): Collection
}

/**
* @param Model|mixed $row
* @param Model|mixed $record
*/
public function map($record): array
{
Expand All @@ -49,57 +50,65 @@ public function map($record): array
}

foreach ($columns as $column) {
$key = $column->getName();
$state = $this->getState($column, $record);
$state = $this->applyFormatStateUsing($column, $record, $state);

if ($this->columnsSource === 'table') {
$column->tableColumn->record($record);
$state = $column->tableColumn->getStateFromRecord();
} else {
$state = data_get($record, $key);
}
$result[$column->getName()] = app(Formatter::class)->format($state);
}

return $result;
}

private function getState(Column $column, $record)
{
$key = $column->getName();

if ($this->columnsSource === 'table') {
$column->tableColumn->record($record);
$state = $column->tableColumn->getStateFromRecord();
} else {
$state = data_get($record, $key);
}

$arrayState = $column->getStateUsing === null
? $state
: $this->evaluate($column->getStateUsing->getClosure(), [
'column' => $column->tableColumn,
'livewire' => $this->getLivewire(),
'record' => $record,
]);

if ($this->columnsSource === 'table' && is_string($arrayState) && ($separator = $column->tableColumn->getSeparator())) {
$arrayState = explode($separator, $arrayState);
$arrayState = (count($arrayState) === 1 && blank($arrayState[0])) ?
[] :
$arrayState;
}

$arrayState = $column->getStateUsing === null
return $arrayState;
}

private function applyFormatStateUsing(Column $column, $record, $state)
{
$formattedState = [];

if ($this->shouldIgnoreFormattingForColumn($column)) {
return $state;
}

foreach (Arr::wrap($state) as $state) {
$state = $column->formatStateUsing === null
? $state
: $this->evaluate($column->getStateUsing->getClosure(), [
: $this->evaluate($column->formatStateUsing->getClosure(), [
'column' => $column->tableColumn,
'livewire' => $this->getLivewire(),
'record' => $record,
'state' => $state,
]);

if ($this->columnsSource === 'table' && is_string($arrayState) && ($separator = $column->tableColumn->getSeparator())) {
$arrayState = explode($separator, $arrayState);
$arrayState = (count($arrayState) === 1 && blank($arrayState[0])) ?
[] :
$arrayState;
}

$arrayState = Arr::wrap($arrayState);
$formattedArrayState = [];

foreach ($arrayState as $state) {
$state = $column->formatStateUsing === null
? $state
: $this->evaluate($column->formatStateUsing->getClosure(), [
'column' => $column->tableColumn,
'livewire' => $this->getLivewire(),
'record' => $record,
'state' => $state,
]);

if (is_object($state)) {
$state = match (true) {
method_exists($state, 'toString') => $state->toString(),
method_exists($state, '__toString') => $state->__toString(),
function_exists('enum_exists') && $state instanceof UnitEnum => $state->value,
};
}

$formattedArrayState[] = $state;
}

$result[$key] = implode("\n", $formattedArrayState);
$formattedState[] = $state;
}

return $result;
return $formattedState;
}
}
2 changes: 2 additions & 0 deletions src/Exports/ExcelExport.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Str;
use Livewire\Component;
use pxlrbt\FilamentExcel\Exports\Concerns\CanIgnoreFormatting;
use function Livewire\invade;
use Maatwebsite\Excel\Concerns\Exportable;
use Maatwebsite\Excel\Concerns\FromQuery;
Expand Down Expand Up @@ -46,6 +47,7 @@ class ExcelExport implements HasMapping, HasHeadings, FromQuery, ShouldAutoSize,
use AskForFilename;
use AskForWriterType;
use CanModifyQuery;
use CanIgnoreFormatting;
use Except;
use Only;
use WithChunkSize;
Expand Down
26 changes: 26 additions & 0 deletions src/Exports/Formatters/ArrayFormatter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

namespace pxlrbt\FilamentExcel\Exports\Formatters;

class ArrayFormatter implements FormatterInterface
{
public function __construct(
public string $delimiter = ','
)
{
//
}

public function shouldApply($state): bool
{
return is_array($state);
}

public function format($state): string
{
return implode(
$this->delimiter,
array_map(fn ($value) => app(Formatter::class)->format($value), $state)
);
}
}
18 changes: 18 additions & 0 deletions src/Exports/Formatters/EnumFormatter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

namespace pxlrbt\FilamentExcel\Exports\Formatters;

use UnitEnum;

class EnumFormatter implements FormatterInterface
{
public function shouldApply($state): bool
{
return function_exists('enum_exists') && $state instanceof UnitEnum;
}

public function format($state): string
{
return $state->value;
}
}
39 changes: 39 additions & 0 deletions src/Exports/Formatters/Formatter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php

namespace pxlrbt\FilamentExcel\Exports\Formatters;

use Illuminate\Support\Collection;

class Formatter
{
static protected $formatters = [
ArrayFormatter::class,
EnumFormatter::class,
ObjectFormatter::class,
];

/**
* @param mixed $state
* @return mixed
*/
public function format($state)
{
if (is_string($state)) {
return $state;
}

$formatter = $this->getFormatters()
->firstWhere(function ($formatter) use ($state) {
return $formatter->shouldApply($state);
});

return $formatter?->format($state) ?? $state;
}

private function getFormatters(): Collection
{
return collect(static::$formatters)->map(
fn ($formatter) => app($formatter)
);
}
}
9 changes: 9 additions & 0 deletions src/Exports/Formatters/FormatterInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

namespace pxlrbt\FilamentExcel\Exports\Formatters;

interface FormatterInterface
{
public function shouldApply($state): bool;
public function format($state): string;
}
16 changes: 16 additions & 0 deletions src/Exports/Formatters/ObjectFormatter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

namespace pxlrbt\FilamentExcel\Exports\Formatters;

class ObjectFormatter implements FormatterInterface
{
public function shouldApply($state): bool
{
return is_object($state) && method_exists($state, '__toString');
}

public function format($state): string
{
return $state->__toString();
}
}

0 comments on commit 247af65

Please sign in to comment.