Custom data providers

Hi everyone, What I'm trying to accomplish is to use Filament as a dashboard for an application structured as a Modular Monolith. (This is a good example: https://github.com/avosalmon/modular-monolith-laravel) I have multiples modules such as Order, Invoicing, etc. and in a separate module named Dashboard I'd like to install Filament. The main limitation is that I cannot directly access Eloquent models across domain boundaries (modules), all accesses should be made through a common interface. Looking at the Filament v3 docs I can't find any way of providing a Filament Resource with custom methods able to fetch the data it needs. I've only came across the getEloquentQuery() method which expects me to return a Builder object, and which I assume extensively uses the Eloquent query builder to populate the dashboard with whatever info it requires. However, the data I can obtain through my common interface cannot be an Eloquent Builder object, most of the time it'll be an array of objects (DTOs) or similar, so I cannot return this data in the getEloquentQuery() method. Is there any way of providing Filament with my own source of data not backed by Eloquent?
31 Replies
alexbirtwell
alexbirtwell15mo ago
https://github.com/calebporzio/sushi take a look at this package it may be useful. I'm not sure how to solve your exact problem but this would be where i would start
GitHub
GitHub - calebporzio/sushi: Eloquent's missing "array" driver.
Eloquent's missing "array" driver. Contribute to calebporzio/sushi development by creating an account on GitHub.
awcodes
awcodes15mo ago
Are you using DTOs? Don’t understand the domain boundary, but even with sushi you’re going to have to have a model, so you’d have to duplicate models which wouldn’t be good.
tobyselway
tobyselwayOP15mo ago
Yes, I'm using DTOs (just edited the question to mention this). I also thought about sushi, but yeah, it'd add a lot of overhead and model duplication. The ideal solution would be for Filament to make me implement a few methods like getItems(filters): Collection, etc. I may be able to write my own Eloquent driver of sorts, but it seems like a very roundabout way of doing it. Some simple way of getting data into Filament without using Eloquent would be ideal. This would essentially open up Filament to be able to be used in a microservices architecture also
awcodes
awcodes15mo ago
I know Dan was working on it, but not sure how far he got with it. It gets really tough though for the tables, because things like filtering and searching on arrays, for example, is an entirely different beast than using what the db and eloquent handle by default. Even sushi is using sqlite under the hood to fake it. It’s still an eloquent query though.
tobyselway
tobyselwayOP15mo ago
Yep, it's definitely not as simple as filtering an array. In this case, why not just request the user to do that instead? So rather than asking the user for an array and then filtering it, give the user a list of conditions/filters and ask the user for the data already processed This way the user can filter the data using whichever mechanism they have access to
awcodes
awcodes15mo ago
Possibly, I think you’d lose the dynamic nature of the filters though. Maybe better with a collection than an array, but still, filtering and searching on anything with any sizable amount of rows will eventually be a bottle neck and hurt performance.
tobyselway
tobyselwayOP15mo ago
It all depends on how willing the user is to implement filter logic on their end
awcodes
awcodes15mo ago
Because it’ll all have to be done in memory. Not saying it can’t be done.
tobyselway
tobyselwayOP15mo ago
Yeah, I don't think that's the solution. It really should be done DB-level if possible, or somewhere close to wherever the data comes from
awcodes
awcodes15mo ago
Wonder if there’s a way to do like an aggregate index like something with scout or tnt-search the could have a unified indexed table. And one model to handle it. Just throwing out ideas. No matter how gross. Lol. Just think the domain boundaries are really going to hurt you here. I just don’t see how to have a complete app with total domain isolation. There’s going to be cross over somewhere, even with DTOs.
tobyselway
tobyselwayOP15mo ago
For search I don't think there'd be a problem in requesting the user to implement it themselves:
public static function getSearchResults(string $query): Collection {
return app(MyInterface)->search($query);
}
public static function getSearchResults(string $query): Collection {
return app(MyInterface)->search($query);
}
Filtering is the hardest part
awcodes
awcodes15mo ago
Yea, filtering without the db runs the risk of becoming really slow.
tobyselway
tobyselwayOP15mo ago
The same way microservices are developed. The only thing that's really stopping me from isolating my domains is Filament in this case, because it heavily depends on Eloquent Agreed. Would there feasibly be a way of summarizing a list of applied filters in order to request them from a user?
awcodes
awcodes15mo ago
Sorry i don’t have a solid answer for you. Tables expose a list of applied filters. You could maybe try to hijack that at some point. Probably not really documented, but who doesn’t love a good source dive.?
tobyselway
tobyselwayOP15mo ago
Even if that's possible, the main issue is getting data into Filament without it having to come from Eloquent
awcodes
awcodes15mo ago
Yea, cause the method is typed to a a builder return type.
tobyselway
tobyselwayOP15mo ago
Yep And I understand the need for it to be. Filament internally uses that Builder a lot What would be great is an alternative method in which I could provide it with a simple collection of objects, even if filtering weren't possible for Resources not based on Eloquent At least for now that'd be a solution I say "for now", even though this would still require changes to the Filament source 😂 So far I can't think of any way of making this work purely on the user end At least besides implementing my own Eloquent driver, which seems a tad overkill 😅
tobyselway
tobyselwayOP15mo ago
I'd be glad to hack away at the Filament source and try to implement something like this, however I'm not sure how well a PR/feature-discussion like this would be received as it doesn't seem like something that aligns much with the current Filament roadmap. Do you think it's worth me opening a feature discussion in https://github.com/filamentphp/filament/discussions ?
GitHub
filamentphp filament · Discussions
Explore the GitHub Discussions forum for filamentphp filament. Discuss code, ask questions & collaborate with the developer community.
awcodes
awcodes15mo ago
I wouldn’t say it’s useless since Dan was working on. There may even already be a discussion on it.
tobyselway
tobyselwayOP15mo ago
I've just went through all 16 pages of Feature discussions, and came across this one: https://github.com/filamentphp/filament/discussions/6657
GitHub
Get records from Apis or from a collection · filamentphp filament ·...
It would be nice to get records from Apis or from a collection. I have a table in another database service, and I would need to get the list of the records in filament.
awcodes
awcodes15mo ago
Maybe run a search on here. I know it’s been discussed. But yea. There’s definitely a desire for it. Just not sure if it’s warranted for core or maybe better for a plugin. Not my call though.
tobyselway
tobyselwayOP15mo ago
I've searched for a few different things, but haven't found anything yet. Not really sure what to search for as I'm guessing different people will describe it in very different ways depending on their own specific use they'd have for it
awcodes
awcodes15mo ago
True
tobyselway
tobyselwayOP15mo ago
Of course, whatever makes sense for Filament. Either way, I'm willing to contribute with whatever's needed
awcodes
awcodes15mo ago
Then I say go for it. All my plugins were created because I needed them, then figured others could maybe need them too. Lol.
tobyselway
tobyselwayOP15mo ago
fair
tobyselway
tobyselwayOP15mo ago
Regarding the filters issue, I came across this: https://github.com/filamentphp/filament/discussions/1088 If the filters can be serialized into a querystring, then they can likely be passed as an argument to a user-provided function to return filtered results
GitHub
Query String and Table Filters · filamentphp filament · Discussion ...
I figured I would open this discussion topic where we could brainstorm ideas for solving the inability to have a queryString for Table Filters. They are used pretty heavily within my app and hittin...
awcodes
awcodes15mo ago
Yea. I’m still thinking you’d need a whole separate table class though. With eloquent being so ingrained it’d probably be easy to just make a whole separate “provider” Fair warning though. The table blade view is the most complicated thing I’ve ever seen or worked with. 😅
tobyselway
tobyselwayOP15mo ago
Likely Table class, Resource class, and a few traits rewritten also... Seems like a lot of work Wondering if it'd make more sense to try and create some sort of "mock Builder" and swap it in
class UserResource extends Resource
{
protected static ?string $model = "User";

public static function getEloquentQuery(): Builder
{
return new StaticBuilder([
new StaticModel([
"id" => "1",
"name" => "John Smith",
"email" => "[email protected]",
]),
new StaticModel([
"id" => "2",
"name" => "Jane Smith",
"email" => "[email protected]",
]),
]);
}

// ...
}
class UserResource extends Resource
{
protected static ?string $model = "User";

public static function getEloquentQuery(): Builder
{
return new StaticBuilder([
new StaticModel([
"id" => "1",
"name" => "John Smith",
"email" => "[email protected]",
]),
new StaticModel([
"id" => "2",
"name" => "Jane Smith",
"email" => "[email protected]",
]),
]);
}

// ...
}
tobyselway
tobyselwayOP15mo ago
No description
tobyselway
tobyselwayOP15mo ago
Still a long way to go, but the basic concept seems to work. These are the StaticModel and StaticBuilder classes I wrote:
class StaticModel extends Model {
protected $guarded = [];
}

class StaticBuilder extends Builder
{
protected array $items;

public function __construct(array $items = [])
{
$this->items = $items;
}

public function orderBy($column, $direction = 'asc') {
// TODO: Pass params to provider
return $this;
}

public function where($column, $operator = null, $value = null, $boolean = 'and')
{
if ($column instanceof Closure && is_null($operator)) {
$column($this);
} else {
// TODO: Pass params to provider
dump(["where", $column, $operator, $value, $boolean]);
}
return $this;
}

public function get($columns = ['*'])
{
// TODO: Pass params to provider
return collect($this->items);
}

public function paginate($perPage = null, $columns = ['*'], $pageName = 'page', $page = null)
{
return new LengthAwarePaginator($this->items, sizeof($this->items), $perPage);
}

public function getModel()
{
return new StaticModel;
}

public function __clone() {}
}
class StaticModel extends Model {
protected $guarded = [];
}

class StaticBuilder extends Builder
{
protected array $items;

public function __construct(array $items = [])
{
$this->items = $items;
}

public function orderBy($column, $direction = 'asc') {
// TODO: Pass params to provider
return $this;
}

public function where($column, $operator = null, $value = null, $boolean = 'and')
{
if ($column instanceof Closure && is_null($operator)) {
$column($this);
} else {
// TODO: Pass params to provider
dump(["where", $column, $operator, $value, $boolean]);
}
return $this;
}

public function get($columns = ['*'])
{
// TODO: Pass params to provider
return collect($this->items);
}

public function paginate($perPage = null, $columns = ['*'], $pageName = 'page', $page = null)
{
return new LengthAwarePaginator($this->items, sizeof($this->items), $perPage);
}

public function getModel()
{
return new StaticModel;
}

public function __clone() {}
}
My goal is to write a simple Eloquent-compatible query builder that relays info about what is being queried to the user so they can get their data from wherever they want
Want results from more Discord servers?
Add your server