Skip to content

Commit

Permalink
Add Options
Browse files Browse the repository at this point in the history
  • Loading branch information
sebgm committed Jan 31, 2025
1 parent 9d1c62c commit f830760
Show file tree
Hide file tree
Showing 9 changed files with 108 additions and 65 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ This package adds a dynamic select tree field to your Laravel / Filament applica
You can install the package via composer:

```bash
composer require codewithdennis/filament-select-tree
composer require sebgm/filament-select-tree
```

```bash
Expand Down
10 changes: 5 additions & 5 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,15 @@
"filament",
"tree"
],
"homepage": "https://github.com/codewithdennis/filament-select-tree",
"homepage": "https://github.com/sebgm/filament-select-tree",
"support": {
"issues": "https://github.com/codewithdennis/filament-select-tree/issues",
"source": "https://github.com/codewithdennis/filament-select-tree"
"issues": "https://github.com/sebgm/filament-select-tree/issues",
"source": "https://github.com/sebgm/filament-select-tree"
},
"license": "MIT",
"authors": [
{
"name": "CodeWithDennis",
"name": "Sébastien Dalibard",
"role": "Developer"
}
],
Expand Down Expand Up @@ -58,7 +58,7 @@
"extra": {
"laravel": {
"providers": [
"CodeWithDennis\\FilamentSelectTree\\FilamentSelectTreeServiceProvider"
"SebGM\\FilamentSelectTree\\FilamentSelectTreeServiceProvider"
]
}
},
Expand Down
2 changes: 1 addition & 1 deletion resources/lang/en/select-tree.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?php

// translations for CodeWithDennis/FilamentSelectTree
// translations for SebGM/FilamentSelectTree
return [
//
];
4 changes: 2 additions & 2 deletions resources/views/select-tree.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@
@else
ax-load
@endif
ax-load-css="{{ FilamentAsset::getStyleHref('filament-select-tree-styles', package: 'codewithdennis/filament-select-tree') }}"
ax-load-src="{{ FilamentAsset::getAlpineComponentSrc('filament-select-tree', package: 'codewithdennis/filament-select-tree') }}"
ax-load-css="{{ FilamentAsset::getStyleHref('filament-select-tree-styles', package: 'sebgm/filament-select-tree') }}"
ax-load-src="{{ FilamentAsset::getAlpineComponentSrc('filament-select-tree', package: 'sebgm/filament-select-tree') }}"
x-data="selectTree({
name: @js($getName()),
state: $wire.{{ $applyStateBindingModifiers("\$entangle('{$getStatePath()}')") }},
Expand Down
4 changes: 2 additions & 2 deletions src/FilamentSelectTreeServiceProvider.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?php

namespace CodeWithDennis\FilamentSelectTree;
namespace SebGM\FilamentSelectTree;

use Filament\Support\Assets\AlpineComponent;
use Filament\Support\Assets\Css;
Expand All @@ -23,6 +23,6 @@ public function packageBooted(): void
FilamentAsset::register([
AlpineComponent::make('filament-select-tree', __DIR__.'/../resources/dist/filament-select-tree.js'),
Css::make('filament-select-tree-styles', __DIR__.'/../resources/dist/filament-select-tree.css'),
], 'codewithdennis/filament-select-tree');
], 'sebgm/filament-select-tree');
}
}
141 changes: 92 additions & 49 deletions src/SelectTree.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ class SelectTree extends Field implements HasAffixActions

protected bool $withCount = false;

protected bool $isMultiple = true;

protected Collection|array|null $options = [];

protected bool $alwaysOpen = false;

protected bool $independent = true;
Expand Down Expand Up @@ -88,39 +92,47 @@ protected function setUp(): void
{
// Load the state from relationships using a callback function.
$this->loadStateFromRelationshipsUsing(static function (self $component): void {
// Get the current relationship associated with the component.
$relationship = $component->getRelationship();

// Check if the relationship is a BelongsToMany relationship.
if ($relationship instanceof BelongsToMany) {
// Retrieve related model instances and extract their IDs into an array.
$state = $relationship->getResults()
->pluck($relationship->getRelatedKeyName())
->toArray();

// Set the component's state with the extracted IDs.
$component->state($state);
if(empty($component->options)) {
// Get the current relationship associated with the component.
$relationship = $component->getRelationship();

// Check if the relationship is a BelongsToMany relationship.
if ($relationship instanceof BelongsToMany) {
// Retrieve related model instances and extract their IDs into an array.
$state = $relationship->getResults()
->pluck($relationship->getRelatedKeyName())
->toArray();

// Set the component's state with the extracted IDs.
$component->state($state);
}
}
});

// Save relationships using a callback function.
$this->saveRelationshipsUsing(static function (self $component, $state) {
// Check if the component's relationship is a BelongsToMany relationship.
if ($component->getRelationship() instanceof BelongsToMany) {
// Wrap the state in a collection and convert it to an array if it's not set.
$state = Arr::wrap($state ?? []);

$pivotData = $component->getPivotData();

// Sync the relationship with the provided state (IDs).
if ($pivotData === []) {
$component->getRelationship()->sync($state ?? []);

return;
$this->saveRelationshipsUsing(static function (
self $component,
$state
) {
if(empty($component->options)) {
// Check if the component's relationship is a BelongsToMany relationship.
if ($component->relationship && $component->getRelationship() instanceof BelongsToMany) {
// Wrap the state in a collection and convert it to an array if it's not set.
$state = Arr::wrap($state ?? []);

$pivotData = $component->getPivotData();

// Sync the relationship with the provided state (IDs).
if ($pivotData === []) {
$component->getRelationship()->sync($state ?? []);

return;
}

// Sync the relationship with the provided state (IDs) plus pivot data.
$component->getRelationship()
->syncWithPivotValues($state ?? [], $pivotData);
}

// Sync the relationship with the provided state (IDs) plus pivot data.
$component->getRelationship()->syncWithPivotValues($state ?? [], $pivotData);
}
});

Expand All @@ -134,7 +146,9 @@ protected function setUp(): void
return $component->getCustomKey($record);
});

$this->dehydrated(fn (SelectTree $component): bool => ! $component->getRelationship() instanceof BelongsToMany);
if(!empty($this->relationship)) {
$this->dehydrated(fn (SelectTree $component): bool => ! $component->getRelationship() instanceof BelongsToMany);
}

$this->placeholder(static fn (SelectTree $component): ?string => $component->isDisabled() ? null : __('filament-forms::components.select.placeholder'));

Expand All @@ -145,30 +159,44 @@ protected function setUp(): void

private function buildTree(): Collection
{
// Start with two separate query builders
$nullParentQuery = $this->getRelationship()->getRelated()->query()->where($this->getParentAttribute(), $this->getParentNullValue());
$nonNullParentQuery = $this->getRelationship()->getRelated()->query()->whereNot($this->getParentAttribute(), $this->getParentNullValue());

// If we're not at the root level and a modification callback is provided, apply it to null query
if ($this->modifyQueryUsing) {
$nullParentQuery = $this->evaluate($this->modifyQueryUsing, ['query' => $nullParentQuery]);
}
if($this->options) {
$combinedResults = collect($this->options);
} else {
// Start with two separate query builders
$nullParentQuery = $this->getRelationship()
->getRelated()
->query()
->where($this->getParentAttribute(),
$this->getParentNullValue());
$nonNullParentQuery = $this->getRelationship()
->getRelated()
->query()
->whereNot($this->getParentAttribute(),
$this->getParentNullValue());

// If we're not at the root level and a modification callback is provided, apply it to null query
if ($this->modifyQueryUsing) {
$nullParentQuery = $this->evaluate($this->modifyQueryUsing,
['query' => $nullParentQuery]);
}

// If we're at the child level and a modification callback is provided, apply it to non null query
if ($this->modifyChildQueryUsing) {
$nonNullParentQuery = $this->evaluate($this->modifyChildQueryUsing, ['query' => $nonNullParentQuery]);
}
// If we're at the child level and a modification callback is provided, apply it to non null query
if ($this->modifyChildQueryUsing) {
$nonNullParentQuery = $this->evaluate($this->modifyChildQueryUsing,
['query' => $nonNullParentQuery]);
}

if ($this->withTrashed) {
$nullParentQuery->withTrashed($this->withTrashed);
$nonNullParentQuery->withTrashed($this->withTrashed);
}
if ($this->withTrashed) {
$nullParentQuery->withTrashed($this->withTrashed);
$nonNullParentQuery->withTrashed($this->withTrashed);
}

$nullParentResults = $nullParentQuery->get();
$nonNullParentResults = $nonNullParentQuery->get();
$nullParentResults = $nullParentQuery->get();
$nonNullParentResults = $nonNullParentQuery->get();

// Combine the results from both queries
$combinedResults = $nullParentResults->concat($nonNullParentResults);
// Combine the results from both queries
$combinedResults = $nullParentResults->concat($nonNullParentResults);
}

// Store results for additional functionality
if ($this->storeResults) {
Expand Down Expand Up @@ -260,6 +288,18 @@ public function relationship(string $relationship, string $titleAttribute, strin
return $this;
}

public function options(Collection $options, string $titleAttribute, string $parentAttribute, bool $isMultiple, ?Closure $modifyQueryUsing = null, ?Closure $modifyChildQueryUsing = null): self
{
$this->options = $options;
$this->titleAttribute = $titleAttribute;
$this->parentAttribute = $parentAttribute;
$this->multiple = $isMultiple;
$this->modifyQueryUsing = $modifyQueryUsing;
$this->modifyChildQueryUsing = $modifyChildQueryUsing;

return $this;
}

public function withCount(bool $withCount = true): static
{
$this->withCount = $withCount;
Expand Down Expand Up @@ -434,6 +474,9 @@ public function getWithCount(): bool

public function getMultiple(): bool
{
if($this->options) {
return $this->isMultiple;
}
return $this->evaluate($this->getRelationship() instanceof BelongsToMany);
}

Expand Down
2 changes: 1 addition & 1 deletion src/Testing/TestsFilamentSelectTree.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?php

namespace CodeWithDennis\FilamentSelectTree\Testing;
namespace SebGM\FilamentSelectTree\Testing;

use Livewire\Features\SupportTesting\Testable;

Expand Down
2 changes: 1 addition & 1 deletion tests/Pest.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?php

use CodeWithDennis\FilamentSelectTree\Tests\TestCase;
use SebGM\FilamentSelectTree\Tests\TestCase;

uses(TestCase::class)->in(__DIR__);
6 changes: 3 additions & 3 deletions tests/TestCase.php
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
<?php

namespace CodeWithDennis\FilamentSelectTree\Tests;
namespace SebGM\FilamentSelectTree\Tests;

use BladeUI\Heroicons\BladeHeroiconsServiceProvider;
use BladeUI\Icons\BladeIconsServiceProvider;
use CodeWithDennis\FilamentSelectTree\FilamentSelectTreeServiceProvider;
use SebGM\FilamentSelectTree\FilamentSelectTreeServiceProvider;
use Filament\Actions\ActionsServiceProvider;
use Filament\FilamentServiceProvider;
use Filament\Forms\FormsServiceProvider;
Expand All @@ -27,7 +27,7 @@ protected function setUp(): void
parent::setUp();

Factory::guessFactoryNamesUsing(
fn (string $modelName) => 'CodeWithDennis\\FilamentSelectTree\\Database\\Factories\\'.class_basename($modelName).'Factory'
fn (string $modelName) => 'SebGM\\FilamentSelectTree\\Database\\Factories\\'.class_basename($modelName).'Factory'
);
}

Expand Down

0 comments on commit f830760

Please sign in to comment.