Giter VIP home page Giter VIP logo

datagrid's Introduction

RollerworksDatagrid

RollerworksDatagrid provides a powerful datagrid system for your PHP applications.

Displaying an objects list is one of the most common tasks in web applications and probably the easiest one. So how can this library help you?

The Datagrid system makes the styling and transforming of your data more uniform and easier to use. Secondly the system can take care of specific (styling) issues like sorting and paginating. All without duplicating code or degrading performance.

Features

RollerworksDatagrid provides you with all features needed, including:

  • An Advanced column type system for uniform data transformation and styling in your datagrids.
  • Auto mapping of data to the datagrid.
  • Support for any data source (PHP array or any object implementing \Traversable).
  • (Optional, and coming soon) search/filter using RollerworksSearch.
  • (Optional, and coming soon) Integrated Paginating using Pagerfanta

Note: Passing a Pagerfanta object as data source does already work due to the IteratorAggregate implementation. The integration bridge however will make the rendering more uniform for datagrids.

Framework integration

RollerworksDatagrid can be used with any Framework of your choice, but for the best possible experience use the provided framework integration plug-ins.

  • Symfony Bundle
  • ZendFramework2 Plugin (coming soon)
  • Silex Plugin (coming soon)

Your favorite framework not listed? No problem, see the [Contributing Guidelines] on how you can help!

Installation and usage

Please ignore the instructions below if your use a framework integration. [Read the Documentation for master] for complete instructions and information.

Install the RollerworksDatagrid "core" library using [Composer]:

$ composer install rollerworks/datagrid

And create the DatagridFactory to get started:

use Rollerworks\Component\Datagrid\Datagrids;
use Rollerworks\Component\Datagrid\Extension\Core\Type as ColumnType;

$datagridFactory = Datagrids::createDatagridFactory();

$datagrid = $datagridFactory->createDatagridBuilder()
   ->add('id', ColumnType\NumberType::class)
   ->add('username', ColumnType\TextType::class)
   ->add('registered_on', ColumnType\DateTimeType::class)
   ->add('enabled', ColumnType\BooleanType::class, ['true_value' => 'Yes', 'false_value' => 'No'])
   ->getDatagrid('users_datagrid')
;

// Now set the data for the grid, this cannot be changed afterwards.
$datagrid->setData([
    ['id' => 1, 'username' => 'sstok', 'registered_on' => new \DateTime('2017-01-12 14:26:00 CET'), 'enabled' => true], 
    ['id' => 2, 'username' => 'doctorw', 'registered_on' => new \DateTime('1980-04-12 09:26:00 CET'), 'enabled' => false], 
    // etc...
]);

// Almost done, the datagrid needs to be rendered, see bellow.

Rendering

The core package however doesn't provide an implementation for this, you are free to use any compatible template engine you wish.

This example uses the TwigRendererEngine (which needs to be installed separately).

use Rollerworks\Component\Datagrid\Twig\Extension\DatagridExtension;
use Rollerworks\Component\Datagrid\Twig\Renderer\TwigRenderer;
use Rollerworks\Component\Datagrid\Twig\Renderer\TwigRendererEngine;

// Provide the path to the base theme.
$loader = new \Twig_Loader_Filesystem([...]);

$environment = new \Twig_Environment($loader);
$environment->addExtension(new DatagridExtension());
$environment->addRuntimeLoader(new \Twig_FactoryRuntimeLoader([TwigRenderer::class => function () uses ($environment) {
    // The second argument are filenames of datagrid themes.
    $rendererEngine = new TwigRendererEngine($environment, ['datagrid.html.twig']);
    
    return new TwigRenderer($rendererEngine);
}]));

$environment->render('my_page.html.twig', ['datagrid' => $datagrid->createView()]);

And in the my_page.html.twig twig Template simple use:

{{ rollerworks_datagrid(datagrid) }}

That's it! Your datagrid is now rendered, but not only that! Whenever you use an advanced technique like search you only need this much code in your template.

Resources

Who is behind RollerworksDatagrid?

RollerworksDatagrid is brought to you by Sebastiaan Stok.

License

RollerworksDatagrid is released under the MIT license.

The types and extensions are largely inspired on the Symfony Form Component, and contain a big amount of code from the Symfony project.

Support

[Join the chat] or use the issue tracker if your question is to complex for quick support.

Note: RollerworksDatagrid doesn't have a support forum at the moment, if you know a good free service let us know by opening an issue πŸ‘

Contributing

This is an open source project. If you'd like to contribute, please read the [Contributing Guidelines]. If you're submitting a pull request, please follow the guidelines in the [Submitting a Patch] section.

Join the chat at https://gitter.im/rollerworks/datagrid [Composer]: https://getcomposer.org/doc/00-intro.md [Contributing Guidelines]: https://github.com/rollerworks/contributing [Submitting a Patch]: https://contributing.readthedocs.org/en/latest/code/patches.html [Read the Documentation for master]: http://rollerworksdatagrid.readthedocs.org/en/latest/ [Join the chat]: https://gitter.im/rollerworks/datagrid

datagrid's People

Contributors

cordoval avatar sstok avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

Forkers

cordoval

datagrid's Issues

Empty value with array doesn't work as expected

The following snip-it with all mapping field values set to null:

$datagrid->addColumn(
    $datagridFactory->createColumn(
        'name',
        'text',
        $datagrid,
        [
            'label' => 'Name',
            'field_mapping' => ['lastName', 'firstName', 'middleName'],
            'empty_value' => ['[None]', '[None]', ''],
            'value_glue' => ', ',
        ]
    )
);

Doesn't work (array type is not accepted) and after changing that it gives the following exception:
Empty-value of mapping field "0" doesn't exists in field mapping.

The ValueFormatTransformer expects fields for 'empty_value' as an associative array 'lastName' => 'value', ... but field_mapping is a numeric-indexed array.

Add a Baseview class to ease rendering

I need a base class for all datagrid related views DatagridView, HeaderView, DatagridRowView, CellView with a vars property.

The attributes purpose should be changed as this is a bit misleading and inconsistent with the DatagridView vars property.

[SfBundle] Editable cells

Form is "bound" to the Datagrid, rows are handled as collection of Form.
The collection form has forms for all editable cells.

When binding the Datagrid the Form receives the Request.

  • How to handle global errors? (use a custom theme)
  • Values need to mapped back, what about performance?

initRuntime() switch for needs_environment option

Defining the initRuntime() method in the "rollerworks_datagrid" extension is deprecated since version 1.23. Use the needs_environment option to get the Twig_Environment instance in filters, functions, or tests; or explicitly implement Twig_Extension_InitRuntimeInterface if needed (not recommended). Hide stack trace

[Twig] Defining the initRuntime() is deprecated

DEPRECATED - Defining the initRuntime() method in the "rollerworks_datagrid" extension is deprecated. Use the needs_environment option to get the Twig_Environment instance in filters, functions, or tests; or explicitly implement Twig_Extension_InitRuntimeInterface if needed (not recommended).`

[Twig] action cell unable to render

The rollerworks_datagrid_column_type_action_cell_action_widget depend to the field_mapping_values resolved values but these are not provided at the moment.

I need to figure out how to this properly.

1.0 roadmap

#1.0 will not be released anytime soon, but here is a list of things I would like to work on.

The biggest change is immutability of the datagrid configuration and inter dependency of objects.
And improving DX while keeping performance high.

0.8.0

  • Auto configure a "label" for column (when non was set), humanize column-name.
  • Views should not be aware of the Datagrid and it's Columns, this reduces memory usage and makes it possible to cache the rendered view.
  • Make Datagrid object immutable, use the builder to build a new Datagrid;
    • Setting data is only possible once! Data cannot be changed afterwards;
    • Require that Data is in the correct format (Traversable) before setting it in the Datagrid.
      So no more event listeners like pre/post set-data

OLD note - ignore

Datagrid

The Datagrid package functionality is currently very limited, there needs
to be a clear description about what must be support by default.

The Datagrid package is responsible for:

  • Configuration of Columns (Label, data-provider, display configuration (format, locale).
  • Building a Datagrid (with pre-configured Columns), and keep a data-list for rendering.
  • Passing a DatagridBuilder className will try to create a Datagrid
    by it's name (or loader, for Service injection), must implement DatagridConfigurator;
    • buildDatagrid(DatagridBuilder $builder, array $options): Datagrid
    • Use simple class inheritance and Traits for extending. (DONE)

Each Datagrid object has it's own 'unique' name.

The Datagrid package knows nothing about the environment usage (web, cli).
Rendering and Request handling is done by other packages.

The package provides a Query interface with the following methods:

  • setSorting(['column-name' => 'ASC|DESC'])
  • getSorting(): array
  • getIterator(): Iterator

The Query interface works as a lazy loading adapter for fetching data.

Query implementations for engines are provided by external packages:

  • Doctrine DBAL
  • Doctrine ORM
  • Pomm 2
  • Propel2? (NO! Not for now)

Ideas:

  • Only allow a Traversable object for Data, use Generator for rendering rows. (REJECTED)
  • Limit view transformer to a single instance, add a ChainFormatter for combining (DONE)
  • Remove ArrayToDateTimeTransformer (unneeded, only allow a DateTimeInterface, string and timestamp) (DONE)

View rendering

Theming is a complex topic, so here's an overall breakdown:

  • The renderer has two types of themes:

    • defaultThemes (always accessible)
    • local themes (loaded explicitly loaded by the current template)
  • Themes can be set for any type of element, but are usually set on the datagrid level

  • The renderer keeps track of the hierarchy levels to ensure variables
    set at a higher level (row > cell) are accessible at a deeper level.

    And deeper variables don't leak to higher levels.
    This tracked per resource, starting from the current point.

  • Each unique block-name (plus suffix) is resolved only once,
    and then used for each rendering.

...

  • Each column has unique block prefix _[datagrid_name]_[column_name],
    used for caching and specific theming. (DONE)
  • When rendering a block, the render engine first checks if the block-name
    is already determined (by reading the cached data).
    If not the block_prefixes is traversed with type (header, cell, row, datagrid).
  • Once a suitable block is found, the name is stored for feature reference:
    • Header: Not cached, rendered only once.
    • Cell block: HeaderView (Used because the HeaderView only exists once per column)
    • Row: DatagridView

Problem: The CompoundColumnType columns must inherit the parent column
name in there block-name. But informing a column it's nested, is done 'after'
the options are set, making it impossible to get the correct block-name.

  • (ACCEPTED, DONE) Requiring all nested columns are explicitly configured is not an option,
    unless we only recommend the Builder:

    $builder
        ->createCompound($name, $options)
        ->add($name, $type, $options)
        ...
        end()
        // `end()` registers the CompoundColumn in the Datagrid, and returns the parent builder
        // all columns receive the CompoundColumn as `parent` option.
    

    This would also solve the unfriendly API for CompoundColumn registering πŸ™†πŸΌ
    which currently requires the DatagridFactory.

    • Recursion checking is not done, as anyone hitting this is already wrong.
    • Add a new method createCompound to DatagridBuilderInterface
    • Add a new class CompoundColumnBuilder (with interface)

Psr7 Handler

Handles PSR-7 ServerRequests, and handles RollerworksSearch operations.
RollerworksSearch is an optional dependency.

Operation without search enabled:

// Build a new Datagrid.
$datagrid = $datagridFactory->createDatagrid(UsersDatagrid::class, 'users_datagrid', ['options']);

// Query object which provides the actual data.
$query = ...;

// Is a Service when used with Symfony
$dataGridHandler = DatagridHandlerFactory::create();
$dataGridHandler->setQuery($query);
$dataGridHandler->handle($datagrid, $request);

// Optional
// Batch consists of an Action (config), and a Selection($ids, $all, $datagrid) object.
// $handler is a callable which processes the actual batch operation.
$batchConfig = new BatchConfig(callable Handler(BatchAction(...), Selection(...)), array $config);
$batchConfig->enableSelectAll(); // and disable.

$batchConfig->setAction('name', new BatchAction(...)); // BatchAction is an interface, custom implementations required.

// Internally this calls bind($datagrid), which produces a new configuration object.
$dataGridHandler->setBatchConfiguration($batchConfig);

// ...

// When a 'change-request' is provided, do a redirect when there are no errors.
// This only applies to batch actions (and editable cells), not search.
if ($dataGridHandler->changeRequested() && $dataGridHandler->isValid()) {
    // Do a redirect.
}

// Render the datagrid: WebDatagridView
// Errors are automatically provided for rendering.
// $dataGridHandler->createView();

Operation with Search enabled.

// Build a new Datagrid.
$datagrid = ...;

// Query object which provides the actual data.
// Query object must be a `SearchConditionAwareQuery` instance.
$query = ...;

$fieldSet = ...;

$dataGridHandler = DatagridHandlerFactory::createSearchAble($fieldSet);
$dataGridHandler->setQuery($query);

// Set sorting and search condition (if any).
$dataGridHandler->handle($datagrid, $request);

// When a 'change-request' is provided, do a redirect when there are no errors.
if ($dataGridHandler->changeRequested() && $dataGridHandler->isValid()) {
    // Do a redirect.
} else if ($dataGridHandler->searchConditionChanged() && $dataGridHandler->isValid()) {
    // Do a redirect.
    // $dataGridHandler->getSearchCode();
}

// Render the datagrid: WebDatagridView
// Errors are automatically provided for rendering.
// $dataGridHandler->createView();
$users = ; // Any Source
$datagrid = $this->get('rollerworks_datagrid.factory')->createDatagrid(UsersDatagrid::class);
$datagrid->setData($users);
$datagrid->handleRequest($request);

if ($datagrid->search->needsRedirect()) {
    return $this->redirectToRoute('users_list', ['search' => $datagrid->search->searchCode]);
}

return new Response($this->get('twig')->render('@App/users.html.twig', ['datagrid' => $datagrid->createView()]));

Twig renderer

  • Cache resolved block-name with the Datagrid/Column view instance

Datagrid Symfony Bridge (Silex service-provider & Bundle)

Both RollerworksSearch and Twig are already enabled by default.

The name of a datagrid is used for search processor prefix and sorting
configuration.

// Build a new Datagrid.
$datagrid = ...;

// Query object which provides the actual data.
// Query object must be a `SearchConditionAwareQuery` instance.
$query = ...;

$fieldSet = ...;

// TODO Search form.

$dataGridHandler = $this->get('rollerworks_datagrid.handler_factory')->create($fieldSet, $datagrid);
$dataGridHandler->setQuery($query);

// Set sorting and search condition (if any).
// NB. $request must be a PSR-7 ServerRequest object.
$dataGridHandler->handle($request);

// When a 'change-request' is provided, do a redirect when there are no errors.
if ($dataGridHandler->changeRequested() && $dataGridHandler->isValid()) {
    // Do a redirect.
} else if ($dataGridHandler->searchConditionChanged() && $dataGridHandler->isValid()) {
    // Do a redirect.
    // $dataGridHandler->getSearchCode();
}

// Render the datagrid: WebDatagridView
// Errors are automatically provided for rendering.
// $dataGridHandler->createView();

Symfony 3.0 Compatiblity

The configureOptions() of some core types needs to be updated for the setAllowedTypes()

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setRequired(['field_mapping', 'label']);
        $resolver->setDefaults(['field_mapping_single' => true]);

        // BC layer for Symfony 2.7 and 3.0
        if ($resolver instanceof OptionsResolverInterface) {
            $resolver->setAllowedTypes(
                [
                    'label' => 'string',
                    'field_mapping' => 'array',
                    'field_mapping_single' => 'bool',
                ]
            );
        } else {
            $resolver->setAllowedTypes('label', 'string');
            $resolver->setAllowedTypes('field_mapping', 'array');
            $resolver->setAllowedTypes('field_mapping_single', 'bool');
        }
    }

[Twig] humanized column label not working

There is a bug in the default theme for humanizing the column name.
This the correct version:

{% block datagrid_column_header %}
{% if label is empty %}
    {% set label = header.name|humanize %}
{% endif %}
{% spaceless %}
    <th><span>{{ label }}</span></th>
{% endspaceless %}
{% endblock %}

I can fix this problem in sec, but I would rather fix the tests that missed this bug πŸ‘

Make the "field_mapping" use associative arrays instead

At the moment "field_mapping" always results in a numeric array, making it really hard to find the value of a field.

Instead this should use an associative array where you map the mapping to a name: ['user_id' => 'user.id'].

Drop support for Twig 1.x

Twig 2.0 is now stable and backward compatible with 1.x, but introduced many improvements and dropped support for older PHP versions.

odd forcing of data duplication

suppose i have persons going through a flow

and A becomes an applicant and B becomes a business object inside

because i need to be doing grids for both, this library does not like when i have to mix grids that contain columns from A and from B, so i cannot do joins or what not.

Why this limitation was posed in the first place? Since this is a grid why the field_mapping makes it sound like implementing this is very hard?

i believe it will force things to be passed as arrays but then what about lazy loading or proxies in doctrine ..., a bit frustrated with this ... 😊

exception on transformer

There seems to be a problem with datetime column type whenever this is null or empty:

        $grid->addColumn(
            $this->factory->createColumn(
                'dischargeDate',
                'datetime',
                $grid,
                [
                    'label' => 'Discharge Date',
                    'field_mapping' => [
                        'dischargeDate' => 'dischargeDate',
                    ],
                    'format' => 'MM/dd/yyyy h:m a',
                ]
            )
        );

It does not have a empty_value or something like the others.

[URGENT PLEASE] Forgot to push function in twig extension

new \Twig_SimpleFunction('rollerworks_datagrid_column_type_action_cell_action_widget', 
[$this, 'datagridColumnActionCellActionWidget'], ['is_safe' => ['html']]),

is non existing

Attempted to call an undefined method named "datagridColumnActionCellActionWidget" 
of class "Rollerworks\Component\Datagrid\Twig\Extension\DatagridExtension".

[Bundle] Drop support for Symfony 2.3

Symfony 2.3 will be end-of-life next year, and the support turned-out to be broken anyway.
I would rather focus on supporting Symfony 2.8 (last LTS) and up, yes 2.7 is also LTS but is missing some features and late deprecations.

This component is far from being complete or stable (on the API level) so by that time I would rather not want to struggle with BC layers.

The main reason for this change is that Symfony 2.4 is required at minimum for the RequestStack.

Feature ridge functionality

The Symfony bundle should provide a full feature ridge functionality for developers.
Providing everything you expect from a good Grid system.

  • Pager (using PagerFanta)
  • Search (using RollerworksSearch)
  • Export (find a good component)
  • Mass actions ( #5 )

All with minimum amount of code.

Replace action (multiple) in favor of one action + compound_column

The action types has an internal options resolver making it impossible to extend the type.
So instead I'm proposing to replace the actions type with a new type "action", each "action" has a label, url / uri_schema, attr, url_attr etc.

url can be a full url, uri_schema is for dynamic URI's (callcack or pattern)
for SfBundle this will add route_name as alternative for URL's.

Then when you want to show multiple actions, you use the "compound_column" type to add group multiple action columns πŸ‘

Datagrid creation refactoring

Change the Datagrid object building to make a Datagrid configuration immutable after the datagrid is created. Changing the actual data itself is still possible (for updatable cells) but the overall configuration is locked.

In practice the Datagrid object gets all the required value in the constructor (no setters), and building this information is done using a DatagridBuilder.

As a datagrid needs to be reconfigured for every request I would actually want to make this cacheable somehow (build once and then reuse for every request; there is a big challenge here with closures and options). Not a big priority but really something I would like to look into :)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.