Coder Social home page Coder Social logo

spiral / data-grid Goto Github PK

View Code? Open in Web Editor NEW
6.0 6.0 3.0 219 KB

Data Grid and specification builder. Subtree split of the Spiral Data Grid component (see spiral/framework)

Home Page: https://spiral.dev/docs/component-data-grid

License: MIT License

PHP 100.00%
data-grid php query-builder specification

data-grid's Introduction


Github cover spiral application


Documentation · Discord · Twitter

Spiral Framework is a High-Performance Long-Living Full-Stack framework and group of over sixty PSR-compatible components. The Framework execution model based on a hybrid runtime where some services (GRPC, Queue, WebSockets, etc.) handled by RoadRunner application server and the PHP code of your application stays in memory permanently (anti-memory leak tools included).

Features

  • Battle-tested since 2013
  • Lightning fast full-stack PHP framework
  • PSR-{3,4,7,11,12,14,15,16,17} compliant
  • Powerful application server and resident memory application kernel
  • Native support of queue (AMQP, SQS, Beanstalk, Kafka) and background PHP workers
  • Queue supervision
  • GRPC server and client via RoadRunner bridge
  • Pub/Sub, event broadcasting
  • HTTPS, HTTP/2+Push, FastCGI
  • PCI DSS compliant
  • Encrypted cookies, signed sessions, CSRF-guard
  • MySQL, MariaDB, SQLite, PostgreSQL, SQLServer support, auto-migrations
  • The ORM you will use for the next 25 years
  • The Temporalio is the simple, scalable open source way to write and run reliable workflows
  • Intuitive scaffolding and prototyping (it literally writes code for you)
  • Helpful class discovery via static analysis
  • Authentication, RBAC security, validation, and encryption
  • Dynamic template engine to create your own HTML tags (or just use Twig)
  • MVC, HMVC, CQRS, Queue-oriented, RPC-oriented, CLI apps... any apps

Bridges

App Type Current Status
spiral/roadrunner-bridge Latest Stable Version
spiral/cycle-bridge Latest Stable Version
spiral/temporal-bridge Latest Stable Version
spiral/data-grid-bridge Latest Stable Version
spiral/sapi-bridge Latest Stable Version
spiral/nyholm-bridge Latest Stable Version

Note: You can find more community packages in spiral-packages organization.

License:

MIT License (MIT). Please see LICENSE for more information. Maintained by Spiral Scout.

data-grid's People

Contributors

butschster avatar msmakouz avatar roxblnfk avatar samsonasik avatar serafimarts avatar spiralbot avatar vvval avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

data-grid's Issues

GridFactory should be reusable

Description

Sometimes we need to change some grid handling during creation and it can be reached by DataGrid attribute and its great.
And you can define your own factory in this attribute and its great too.
BUT
I would like to change some small part of your basic \Spiral\DataGrid\GridFactory and I would like to extend it and override only create method
BUT AGAIN
In you GridFactory you work in methods only with private properties and I should duplicate whole class for my factory

AND IT IS BAD APPROACH

If your class is not finalized and it can works as parent class why did you use private access?

Could you change it to protected?

It can be actual for any not-finalized classes by OOP...

[DataGrid] Sorting by nullable values makes filter by records

Description

In case when you need sorting in DataGrid by nullable field I've detected that Sorter uses INNER JOIN on building Select Query.

If values are nullable this moment reduces amount of records.

Sorting mustn't reduce records amount

How To Reproduce

Make any simple table with nullable field. For example it can be updatedBy. This field will be changed only after update.
Insert some records with different values in this field.
Prepare grid and add sorting by updatedBy field

Additional Info

Q A
Framework Version 2.7.8
PHP version 7.4.16

Filters are applied only when using `withDefaults`

Introduction

I've encountered a potential issue while integrating the data-grid package into a Laravel project, specifically during the development of tests to evaluate the data-grid functionality. It appears that when the withDefaults method is not utilized on the GridFactory, the filters list remains empty, resulting in unfiltered data being returned.

According to the current documentation available at spiral.dev/docs/component-data-grid/current/en, my understanding is that the data-grid should automatically apply filters as defined within the PostSchema.php. This assumption is based on the premise that the schema's purpose is to dictate how data should be filtered, sorted, and paginated without necessarily requiring additional methods like withDefaults to enforce these rules explicitly.

Issue Description

Successful condition to filter data

When employing the ->withDefaults method as shown below, the data is correctly filtered according to the specified conditions:

$factory = $this->app->make(GridFactory::class);
$factory = $factory->withInput(new ArrayInput([
    'title' => $post->title(),
]))->withDefaults([
    GridFactory::KEY_FILTER => ['title' => $post->title()],
]);

This results in the expected filtered output, demonstrating that the filtering mechanism works as intended when withDefaults is specified.

array:2 [
  "posts" => array:1 [
    0 => WayOfDev\App\Entities\Post^ {#11699
      -id: "018e7640-fd62-7003-96fb-1822c4360aa4"
      -title: "Ea aliquid sit modi neque perspiciatis."
      -content: "Est rerum voluptas nesciunt placeat debitis cumque nisi. Cupiditate sequi sapiente libero ut placeat. Et et voluptatibus non atque quod veniam. Et quis ratione qui nulla deleniti et."
      -comments: Illuminate\Support\Collection^ {#4622
        #items: []
        #escapeWhenCastingToString: false
      }
    }
  ]
  "grid" => array:1 [
    "values" => array:4 [
      "filters" => array:1 [
        "title" => "Ea aliquid sit modi neque perspiciatis."
      ]
      "sorters" => []
      "count" => null
      "paginator" => array:2 [
        "limit" => 10
        "page" => 1
      ]
    ]
  ]
] // /app/tests/src/DataGridFactoryTest.php:77

Failing condition to filter data

However, omitting the withDefaults method like this:

$factory = $this->app->make(GridFactory::class);
$factory = $factory->withInput(new ArrayInput([
    'title' => $post->title(),
]));

Result:

Leads to an empty filters array, and consequently, all results are returned (10, by the Paginator), indicating that the data is not being filtered.

array:2 [
  "posts" => array:10 [
    0 => WayOfDev\App\Entities\Post^ {#11699
      -id: "018e7642-2833-724d-990f-eddb48fdbc17"
      -title: "Et pariatur voluptatum ea dignissimos."
      -content: "In explicabo esse quibusdam quidem. Distinctio alias aut rerum est. Cum eveniet mollitia eos velit voluptates sunt. Ea minus rerum incidunt quo."
      -comments: Illuminate\Support\Collection^ {#4551
        #items: []
        #escapeWhenCastingToString: false
      }
    }
    1 => WayOfDev\App\Entities\Post^ {#4438
        // Omited for demo
    }
    2 => WayOfDev\App\Entities\Post^ {#5207
        // Omited for demo
    }
    // Omited for demo
  ]
  "grid" => array:1 [
    "values" => array:4 [
      "filters" => []
      "sorters" => []
      "count" => null
      "paginator" => array:2 [
        "limit" => 10
        "page" => 1
      ]
    ]
  ]
] // /app/tests/src/DataGridFactoryTest.php:78

Reproduction Code:

Please find the detailed code for reproduction below, encapsulated within <details> tags for clarity.

DataGridFactoryTest.php
<?php

declare(strict_types=1);

namespace WayOfDev\Tests;

use Database\Factories\PostFactory;
use Illuminate\Support\Facades\Artisan;
use PHPUnit\Framework\Attributes\Test;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Spiral\DataGrid\GridFactory;
use Spiral\DataGrid\GridInterface;
use Spiral\DataGrid\Input\ArrayInput;
use WayOfDev\App\Entities\Post;
use WayOfDev\App\PostRepository;
use WayOfDev\App\PostSchema;

use function iterator_to_array;

class DataGridFactoryTest extends TestCase
{
    public function setUp(): void
    {
        parent::setUp();

        Artisan::call('cycle:migrate:init');
        Artisan::call('cycle:orm:render', ['--no-color' => true]);
        Artisan::call('cycle:orm:migrate', ['--split' => true, '--run' => true]);
    }

    /**
     * @throws ContainerExceptionInterface
     * @throws NotFoundExceptionInterface
     */
    private function getPost(): Post
    {
        $factory = PostFactory::new()->times(30)->create();
        /** @var Post $post */
        return $factory->random();
    }

    /**
     * @throws ContainerExceptionInterface
     * @throws NotFoundExceptionInterface
     */
    #[Test]
    public function it_queries_data(): void
    {
        $post = $this->getPost();

        $schema = new PostSchema();

        $factory = $this->app->make(GridFactory::class);
        $factory = $factory->withInput(new ArrayInput([
            'title' => $post->title(),
        ]));
        // ->withDefaults([
        //     GridFactory::KEY_FILTER => ['title' => $post->title()],
        // ]);

        $repository = $this->app->make(PostRepository::class);

        /** @var GridInterface $result */
        $grid = $factory->create($repository->select(), $schema);

        $values = [];

        foreach ([
            GridInterface::FILTERS,
            GridInterface::SORTERS,
            GridInterface::COUNT,
            GridInterface::PAGINATOR,
        ] as $key) {
            $values[$key] = $grid->getOption($key);
        }

        dd([
            'posts' => iterator_to_array($grid),
            'grid' => [
                'values' => $values,
            ],
        ]);
    }
}
Post.php
<?php

declare(strict_types=1);

namespace Database\Factories;

use WayOfDev\App\Entities\Post;
use WayOfDev\DatabaseSeeder\Factories\AbstractFactory;

class PostFactory extends AbstractFactory
{
    public function makeEntity(array $definition): object
    {
        return new Post(
            title: $definition['title'],
            content: $definition['content'],
        );
    }

    public function entity(): string
    {
        return Post::class;
    }

    public function definition(): array
    {
        return [
            'title' => $this->faker->sentence,
            'content' => $this->faker->paragraph,
        ];
    }
}
PostSchema.php
<?php

declare(strict_types=1);

namespace WayOfDev\App;

use Spiral\DataGrid\GridSchema;
use Spiral\DataGrid\Specification\Filter\Equals;
use Spiral\DataGrid\Specification\Pagination\PagePaginator;
use Spiral\DataGrid\Specification\Sorter\Sorter;
use Spiral\DataGrid\Specification\Value\StringValue;

class PostSchema extends GridSchema
{
    public function __construct()
    {
        // User pagination: limit results to 10 per page
        $this->setPaginator(new PagePaginator(10));

        // Sorting option: by id
        $this->addSorter('id', new Sorter('id'));

        // Filter option: find by title matching post input
        $this->addFilter('title', new Equals('title', new StringValue()));
    }
}

[data-grid] allow reading input from a custom format

instead of

{
  "filter": {
    "reviewed": true,
    "status": "active"
  },
  "sort": {
    "newest": 1,
    "countReviews": -1
  },
  "paginate": {
    "page": 3
  }
}

read from

{
  "reviewed": true,
  "status": "active",
  "sortByNewest": 1,
  "sortByCountReviews": -1,
  "page": 3
}

Add not empty compositable value

It will be useful to validate any value if it is allowed to be empty (using !empty(...) check). Example:

new NotEmpty();
new NotEmpty(new IntValue());

It will check if the passed value is not empty without any conversion. Additionally, if a sub-value passed, will perform it's accepts and converts operations.

Concerns: will not work with StringValue and ScalarValue because right now they do not allow empty values by default, except using such a construction

new NotEmpty(new StringValue(true));

which is equivalent to

new StringValue();

[DataGrid] Make Filter more flexible by configuring used JOIN

Description

In case when you need using default search filter in DataGrid by nullable field I've detected that Filter uses INNER JOIN on building Select Query.

If values are nullable this moment reduces amount of records and it is not fully suitable in cases when I want to make search by some optional fields.

It will be great if Filter was more flexible and allows to define used JOIN type.

How To Reproduce

Make any simple table with nullable field. For example it can be updatedBy. This field will be changed only after update.
Insert some records with different values in this field.
Configure your Grid with using filter search by updatedBy.email like this:

$this->addFilter(
    'search',
    new Filter\Any(
        new Filter\Like('name'),
        new Filter\Like('updatedBy.email')
    )
);

Prepare grid and use search by default grid search.

Check prepared sql by logs

Additional Info

Q A
Framework Version 2.7.8
PHP version 7.4.16

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.