Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Grid Plugins Concept #856

Open
georgehristov opened this issue Dec 3, 2019 · 26 comments
Open

Grid Plugins Concept #856

georgehristov opened this issue Dec 3, 2019 · 26 comments

Comments

@georgehristov
Copy link
Collaborator

I figured out it would be more beneficial if I include my intended functionality for Grid/CRUD as atk4 native implementation.

Let me know if following design concept is acceptable in the atk4/ui development so I can start working on it:

  • use CollectionTrait in Grid
  • designate 'plugins' collection
  • define hook spots in Grid methods
  • define all functionality that tinkers with how and what is displayed in Grid as separate plugins. Plugins then use the hooks to achieve their functionality
    • menu plugin
    • actions plugin
    • paginator plugin
    • sorter plugin
    • quickSearch plugin
    • dragHandler plugin
      List can then be extended by the imagination of contributors:
    • filters plugin (display form with special filters that apply to the model - can start with simple form based on Grid model fields to custom defined filter elements to integrating with QueryBulder for advanced filtering, allowing users to save the filter sets, etc
    • quickJump plugin - jump to rows starting with a specified letter in the Grid records (in a defined column)
    • quickFilter plugin - favourite, recent, watched, list of saved filters groups or any other group of rows depending on what the plugin is set to do also in relation to other settings. This allows for complex workflow functionality

Comments are welcome!

@abbadon1334
Copy link
Collaborator

@georgehristov i like the idea and i already had in my todo list QueryBuilder integration, cause i think Grid and CRUD are still too spartan on functionality, at this moment you can do whatever you want but there is no rules to accomplish it.

Your approach is like a decorator over multiple grid sub-components.

I think along your idea and using a much simpler approach, we can move some grid methods in a plugin/decorator, like this :

interface iGridPluginable
{
    public function onActivate(\atk4\ui\Grid $grid);
}

class DeleteAllMenuPlugin implements iGridPluginable
{

    public function onActivate(\atk4\ui\Grid $grid)
    {
        /* $grid->menu */
        $grid->menu->addItem(['Delete All', 'icon' => 'trash', 'red active']);
    }
}

class SayHiActionPlugin implements iGridPluginable
{

    public function onActivate(\atk4\ui\Grid $grid)
    {
        $grid->addAction('Say HI', function ($j, $id) use ($grid) {
            return 'Loaded "' . $grid->model->load($id)['name'] . '" from ID=' . $id;
        });
    }
}

class NoPaginatorPlugin implements iGridPluginable
{

    public function onActivate(\atk4\ui\Grid $grid)
    {
        $grid->paginator->destroy();
    }
}

class Grid
{
    public function addPlugin(iGridPluginable $plugin)
    {
        $plugin->onActivate($this);
    }
}

I like this way of defining "Grid Plugins/Decorator", when implemented if we are asked about how to remove the Paginator, we can answer use plugin NoPaginator.

1 question and 1 suggestion :
Question : Why you need to stack/store the plugins in a collection? is not enough to inject the grid object in a method and leave the control to the plugin?
Suggestion : Wait until this release is done (i think few more days), this release is about actions and probably, at the end there will be some concept to be redefined.

@georgehristov
Copy link
Collaborator Author

@abbadon1334 thanks prompt comment. I will for sure wait for the ui 2.0 release before diving deep.
I have not refined the idea how to activate the plugins but thought CollectionTrait might be good as then we can define a list of default plugins to be activated which can be removed using the respective collection method.
Will play with this a bit as I see you are accepting the approach.
Integration with QueryBuilder will be really appreciated.
My preference would be that its output results in array or object which can directly be used as set of model conditions. Background is this set of conditions can be saved or revoked from a persistence. This in turn is a good setup for comprehensive access control implementation...

@abbadon1334 abbadon1334 added the hangout agenda 🔈 Will discuss on next hangout label Dec 3, 2019
@abbadon1334
Copy link
Collaborator

I speak on my own but probably others will agree with me, that ATK is simplicity and less code to write.

i think the best approach is to add the plugin and move all complexity in the plugin, you can act as the owner from the child, i mean even remove can be done bottom->up.

@abbadon1334 thanks prompt comment. I will for sure wait for the ui 2.0 release before diving deep.
I have not refined the idea how to activate the plugins but thought CollectionTrait might be good as then we can define a list of
default plugins to be activated which can be removed using the respective collection method.

Usually in atk there is a $default_*_class, that will be used as the default, in this case if we use CollectionTrait if (empty($this->fields)) will use the default one, but i think this not easy to be done ( calls are done in different moment of initialization), because Grid/CRUD are complex objects an alternative is to use Hooks, like you suggest and move all complexity in Plugin class, but even with this approach i think we need to add too much complexity in the Grid/CRUD.

Will play with this a bit as I see you are accepting the approach.

using InitializerTrait on add to parent/owner container will fire the init method, but the method is in the trait and cannot be type hinted or redefined, for that i suggest the use of an interface, which can standardize the plugin class and give by code a way to use it, another option is to create an abstract like :

use atk4\core\AppScopeTrait;
use atk4\core\InitializerTrait;
use atk4\core\TrackableTrait;

abstract class AbstractGridPlugin {

    use AppScopeTrait;
    use InitializerTrait;
    use TrackableTrait;

    protected $identifier; // UUID-Plugin

    public function getIdentifier() : string {
        return $this->identifier;
    }
    public function init() {
        parent::init();
        $this->onActivate($this->owner);
    }

    abstract public function onActivate(\atk4\ui\Grid $grid);
}
class Grid
{
    protected $plugins = [];

    public function addPlugin(AbstractGridPlugin $plugin)
    {
        $this->_addIntoCollection($plugin->getIdentifier(), $plugin, 'plugins');
        /* i still see no clue about having them in a collection ;) i prefer set and forget */
    }

    public function removePlugin(string $plugin_identifier)
    {
        $this->_removeFromCollection($plugin_identifier, 'plugins');
        /* i still see no clue about having them in a collection ;) i prefer set and forget */
    }
}

Or much simpler :

use atk4\core\AppScopeTrait;
use atk4\core\InitializerTrait;
use atk4\core\TrackableTrait;

class Generic {

    use AppScopeTrait;
    use InitializerTrait;
    use TrackableTrait;

    protected $identifier; // UUID-Plugin

    public function getIdentifier() : string {
        return $this->identifier;
    }
}
class Grid
{
    protected $plugins = [];
    public function addPlugin(GridPlugin\Generic $plugin)
    {
        $this->_addIntoCollection($plugin->getIdentifier(), $plugin, 'plugins');
    }
}

Working this way in onActivate you can create even a multiplugin, thinking about UX, you can have SimpleGridPlugin which is a menu with a button and a search box or an AdvancedGridPlugin which extend the SimpleGridPlugin adding on the top it a query builder.

Integration with QueryBuilder will be really appreciated.
IMHO for big application is a must.

My preference would be that its output results in array or object which can directly be used as set of model conditions.

Yes but we can add a form and on the form button add an hook onSubmit/onFilter to save the search as default or add to preferred search.

Background is this set of conditions can be saved or revoked from a persistence. This in turn is a good setup for comprehensive access control implementation...

I wait the end of release, because ACL are in develop branch of atk4\login, last time i checked was not finished and probably will change after this release, because this release is about Actions+ACL.
I'm on hold too for Auth0 integration.

The beautiful of ATK is that you feel like you are working with Lego, because anytime in the development process you can add something to a base objects with few lines of code, without extend it but moving the complexity out of it.

I labeled this with Hangout Agenda, but we will discuss it after the release of 2.0 is done.

@bedengler
Copy link
Contributor

bedengler commented Dec 3, 2019

I like the idea and for a project I'm working on (with atk4) I need the filters plugin you described (where you can define columns to show / hide, save filter results and columns etc.).
So if I can support you, I'd be happy to do so!
I'm working on this project now a lot, so I'll be available for the next 2 or 3 months as I'd make development of it part of the project...

Only downside: I started with atk4 ca. a month ago, so it may take longer to fulfil tasks or to understand some mechanisms. But I'm doing my best to dig deep into it as I think atk4's potential is awesome for the type of projects I'm working on.

Therefore please be patient with me 😉

@georgehristov
Copy link
Collaborator Author

@bedengler Happy to hear there is interest in the features. No worries, I am an atk4 'newbie' as well. I will work on a foundation of the feature and will publish for further enhancements.

@georgehristov
Copy link
Collaborator Author

@abbadon1334 Thanks for detailed discussion. My ideas are in line with yours (using abstract class for instance) so for sure we will come up with a workable solution

@jtylek
Copy link

jtylek commented Dec 5, 2019

Hello everbody, hello @georgehristov,
I would like to add to this discussion a feature that I was planning to implement in Epesi for quite some time, namely views - which sounds a lot like quickFilter plugin you describe.
By view I mean a stored filter which describes columns visible in table browse mode. In most instances a single record - for example a contact - will contain too many fields to be displayed in a table mode view. Of course all fields will be visible on a form in a single record view, but a flexible method of defining what columns to view in a table view currently is limited to a system wide mode.

Current Record Browser CRUD engine applies permissions only and picks as columns fields marked in the recordset definition as "Table view"

image

I would like to add a table where views will be stored and can be user or admin defined with proper permissions of course as well. Public views will be visible as a dropdown filter - for example address view would show address details, financial view would show unpaid balances etc.

This functionality must be currently hard coded and while it is not a lot of work the scenario is common enough to justify adding it to the core of Grid/CRUD.

View builder would use query builder similar to the one already built into Epesi:

image

where recordset fields (or alternatively a calculated field) will be selected as table headers/columns with custom caption if necessary and an order of course and maybe available action.

I am also offering my help with this project and also must confess that I am a complete beginner in ATK.

@jtylek
Copy link

jtylek commented Dec 5, 2019

@georgehristov
Regarding QuickJump feature:
image

It was implemented in Epesi from the beginning based on feature seen earlier I believe in CodeChargeStudio. However I barely used it and a better approach is to have a focus set on a search field after opening what I call Browse Records view. A default view - which could be the last used by a user view - and by having a search box limited to that particular recordset it offers a quicker way to get to the desired data, especially if the search box uses autocomplete and partial search.

@abbadon1334
Copy link
Collaborator

abbadon1334 commented Dec 5, 2019

@jtylek CodeChargeStudio 4.0 in 2003, IMHO before it was ruined and the web take is final rush in 2.0/ajax made an entire ERP with it ;)

@jtylek I like the idea of a filter by first letter + search, following the idea of @georgehristov, it can be a grid plugin, we can have a GridSearchPlugin with an option to add this filter before the search that probably by default on mobile will be always hidden or assume a form of a dropdown.

@Julek about the idea of saved search/filter and show/hide columns, first of all i think plugins must :

  • use DIContainerTrait to be injected easy with a bunch of options
  • needs a model to make this options persistent without use of session

to make it simple the best is to have a model that make this easy to work with.

i had a long call in morning and i had the time to write down it :
Not tested, just the model in my mind, i think showing some code is better to speak about code abstracted ideas : https://gist.github.com/c3e5c33b2147335e38ca3dadc9287de1

IMHO The use of SESSIONS is out of discussion.

@georgehristov
Copy link
Collaborator Author

@abbadon1334 thanks for comments. Presently in China so cannot access the gist for some reason.
But fully agree we should let the code talk. Will work on a solution and create a PR and then we adjust according to comments and input.

@jtylek
Copy link

jtylek commented Dec 7, 2019

@abbadon1334 Well, the world is small then. I also created an ERP system called Maximus in CodeCharge. Unfortunately I picked ASP and Windows IIS at that time since I was servicing only Windows networks and it looked like a natural fit for me coming from MS Access and VBA.

I turned to Linux or rather LAMP and open source and never look back. Mico$oft owns Github now - how ironic!
To make story short when I started Epesi in 2006 there were very few choices and now there are too many! I am so glad I found Agile UI and Data https://www.agiletoolkit.org/ and all your work and journey is very similar to mine. Our goal - and I am speaking here for myself and @georgehristov is to build upon Agile framework and rather contribute back - thus this discussion started by Georgi - then to duplicate efforts.

I am open for all suggestions and of course as Linus said - talk is cheap, show me the code :)

As for the Quick Jump list - I assume it could be very handy to fast forward to records starting with particular letter. As long as Quick Jump would accept an array of choices that would be fine. We hard coded it and now with Epesi translated to around 40 languages it works partially only in some or in cases not at all. Quick Jump is optional feature anyway and I like the idea of plugins handling it. Well - I love modularity in general.

@georgehristov
Copy link
Collaborator Author

georgehristov commented Dec 8, 2019

So here is the code to have a base for discussion:

atk4\core: introducing Plugin class and PluginTrait
atk4\ui: refactor Grid and Table to use plugins (menu, quickSearch, paginator, columnFilter)

@abbadon1334
Copy link
Collaborator

So here is the code to have a base for discussion:

atk4\core: introducing Plugin class and PluginTrait
atk4\ui: refactor Grid and Table to use plugins (menu, quickSearch, paginator, columnFilter)

i like the idea of target and require.

  • About target, to make it simple stupid, i think the best is to put in separate namespace, named by component usage, at the moment i don't think there is a case of multi component plugin so a plugin for Table can be used on Table => Grid->table => Crud->table.
  • About require i don't know if is better to inject them via DICointainerTrait and if not defined it use the default ones.

We really need remove plugins? If i add a plugin at runtime, why i need to remove it?

Just to recap : If an object $A that use InitializerTrait is added to an object $B which use ContainerTrait, the method init will be triggered on object $A, in this method will be done the magic :

  • if object $A use AppScope $A->app = $B->app
  • if object $A use TrackableTrait $A->owner = $B
    atk4\core is amazing it already has nearly everything you need for a perfect composite pattern, the only "problem" of core and atk4 in general are too many public methods and property that needs to be exposed to make it works, but using traits can be imported as protected and make in some way like internal methods.

i link here the documentation : https://agile-core.readthedocs.io/en/develop/

One question : Not a functional code, but can you shove me some sample of a possible usage ? Just to figure out if you see some things that i don't see, for example the "Alphabet" Search?

I try to look into it, before the meeting.

@georgehristov
Copy link
Collaborator Author

georgehristov commented Dec 8, 2019

Key word in my proposal is 'flexibility'

About target, to make it simple stupid, i think the best is to put in separate namespace, named by component usage, at the moment i don't think there is a case of multi component plugin so a plugin for Table can be used on Table => Grid->table => Crud->table.

You have to remember that idea is beyond the generic atk4 classes so devs have the opportunity to develop their own plugins and use them / publish them even separately. Hard coded dependencies via namespace will limit the flexibility. target can also be a list of several classes where the plugin will work. For instance a filter plugin will manipulate the model of the owner so it may be attached to owners of different classes as all ui components have a model.

About require i don't know if is better to inject them via DICointainerTrait and if not defined it use the default ones.

require is making dependency checks simple and concentrated in one piece of code. In present use in Grid there are many cross-dependencies that need to be checked before an operation is performed and sometimes the checks are missing, e.g QuickSearch needs the Menu, etc.

We really need remove plugins? If i add a plugin at runtime, why i need to remove it?

Consider a dev has activated a plugin by default. In a possible more complicated usage dev may want to remove a plugin in a sub-component that is already activated.

I was considering using atk4 native functionality for the plugin extending View as you mention but we have to consider that plugin 'injects' and controls several View and other atk4 components in its owner. As immediate example is the TableColumnFilterPlugin I created based on existing code.

The usage examples I have created already refactoring existing code of Grid and Table into plugins.

"Jump to letter search" aside the really important component I am targeting is a filter plugin - simple (on a form) and possibly advanced (using QueryBuilder). Present ColumnFilter is great but it only covers the fields in the table.

I also do not like public properties but I see in atk4 they come really handy

@romaninsh
Copy link
Member

Hey this sounds really amazing! I'm not sure what you mean by "native" implementation, but if you want it in "atk4/ui" repository, here is the process how to get there:

  1. Create addon repository in Github.
  2. Set up namespace there, implement CRUD (with necessary JS files if needed)
  3. Demonstrate complete implementation with docs etc.
  4. We can then start process of integrating it into ATK core.

@abbadon1334
Copy link
Collaborator

abbadon1334 commented Dec 9, 2019

@georgehristov is done in hurry, but i think can work as it is.

https://gist.github.com/abbadon1334/5aa4b9bfca3c48534afdd2e6c59c6028

When you come back from China we can have a one to one call ( even if you are 2 @jtylek ), just to speak about this concept and about your actual project, because i'm working on a similar project ERP including active cycle, passive cycle and the infamous bill of materials.

@georgehristov
Copy link
Collaborator Author

georgehristov commented Dec 10, 2019

@abbadon1334 we will talk when I am back then.
I am sure using the ideas we would like to implement you can build your desired functionality.
I have built a quite comprehensive system so a lot of experience of what base features are needed on top of what epesi already offers as ideas for great versatility. atk4 is very similar on base approach with more modern technologies and tooling so it is perfect as a base.

@romaninsh
Copy link
Member

How about - wednesday 6pm?

@georgehristov
Copy link
Collaborator Author

georgehristov commented Dec 18, 2019

I can participate today 1800 cet

@jtylek
Copy link

jtylek commented Dec 19, 2019

Let's talk about this integration as I would like to give Agile the entire support and direct the community of users toward Agile Toolkit. We have a typical win-win situation here.

@jtylek
Copy link

jtylek commented Dec 19, 2019

Just let me know when it's convenient for you to talk, I would like to introduce myself in person and meet you somehow maybe FaceTime or something. I am on Telegram and I love it and we use Telegram bot as notification mechanism in Epesi. Considering that this is a huge network with its own crypto I really want to keep an eye on it.

@romaninsh
Copy link
Member

Hey @jtylek - i've set up all the new channels - including telegram, so we can connect easier over the holidays. I'm off work. Lets have an experiment and see which messenger is more suitable.

@romaninsh
Copy link
Member

Also - looked deeper into Plugin concept. I think "plugin" is a bit redundant in ATK - there are already various ways how we augment things and the concept of hooks makes it even more powerful.

However - I also understand that those existing mechanics are incomplete. With the "Plugins" it would allow to make one-line enable for the plugin and make them really powerful. Lets keep talking about this and how it can make more use of existing core functionality of ATK.

@romaninsh
Copy link
Member

Any update is on this?

@jtylek
Copy link

jtylek commented Apr 2, 2020

@romaninsh Thank you for setting up the Telegram group. Since then it looks like Discord gained more popularity. I joined it and I am monitoring it.

Re: Grid Plugins - @georgehristov would be the best source of info on this matter.

@georgehristov
Copy link
Collaborator Author

I have refactored it to reduce redundancy and use atk native routines (parent / owner, collections, etc)
It is beneficial in cleaning up specific functionality in a separate file (like plugins for Table and Grid).
It needs a bit more work to be ready for PR. Intermediate results are under https://github.com/georgehristov/ui/commits/feature/plugins

@mvorisek mvorisek unpinned this issue Apr 29, 2021
@mvorisek mvorisek removed the hangout agenda 🔈 Will discuss on next hangout label Jun 18, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

7 participants