├── .gitignore ├── .scrutinizer.yml ├── .travis.yml ├── CHANGELOG-2.0.md ├── LICENSE.md ├── README.md ├── UPGRADE-1.0.md ├── UPGRADE-2.0.md ├── composer.json ├── doc └── en │ ├── columns │ ├── action.md │ ├── boolean.md │ ├── collection.md │ ├── datetime.md │ ├── entity.md │ ├── money.md │ ├── number.md │ ├── text.md │ └── tree.md │ └── extensions │ ├── core.md │ └── doctrine.md ├── lib ├── Column │ ├── CellView.php │ ├── CellViewInterface.php │ ├── ColumnAbstractType.php │ ├── ColumnAbstractTypeExtension.php │ ├── ColumnTypeExtensionInterface.php │ ├── ColumnTypeInterface.php │ ├── HeaderView.php │ └── HeaderViewInterface.php ├── Data │ ├── DataRowset.php │ └── DataRowsetInterface.php ├── DataGrid.php ├── DataGridAbstractExtension.php ├── DataGridEvent.php ├── DataGridEventInterface.php ├── DataGridEvents.php ├── DataGridExtensionInterface.php ├── DataGridFactory.php ├── DataGridFactoryInterface.php ├── DataGridInterface.php ├── DataGridRowView.php ├── DataGridRowViewInterface.php ├── DataGridView.php ├── DataGridViewInterface.php ├── DataMapper │ ├── ChainMapper.php │ ├── DataMapperInterface.php │ ├── PropertyAccessorMapper.php │ └── ReflectionMapper.php ├── Exception │ ├── DataGridColumnException.php │ ├── DataGridException.php │ ├── DataMappingException.php │ ├── UnexpectedTypeException.php │ └── UnknownOptionException.php └── Extension │ ├── Core │ ├── ColumnType │ │ ├── Action.php │ │ ├── Batch.php │ │ ├── Boolean.php │ │ ├── Collection.php │ │ ├── DateTime.php │ │ ├── Money.php │ │ ├── Number.php │ │ └── Text.php │ ├── ColumnTypeExtension │ │ ├── DefaultColumnOptionsExtension.php │ │ └── ValueFormatColumnOptionsExtension.php │ ├── CoreExtension.php │ └── EventSubscriber │ │ └── ColumnOrder.php │ ├── Doctrine │ ├── ColumnType │ │ └── Entity.php │ ├── ColumnTypeExtension │ │ └── ValueFormatColumnOptionsExtension.php │ └── DoctrineExtension.php │ └── Gedmo │ ├── ColumnType │ └── Tree.php │ └── GedmoDoctrineExtension.php ├── phpunit.xml.dist └── tests ├── Data └── DataRowsetTest.php ├── DataGridFactoryTest.php ├── DataGridRowViewTest.php ├── DataGridTest.php ├── DataGridViewTest.php ├── DataMapper ├── ChainMapperTest.php └── ReflectionMapperTest.php ├── Extension ├── Core │ ├── ColumnType │ │ ├── ActionTest.php │ │ ├── BooleanTest.php │ │ ├── CollectionTest.php │ │ ├── DateTimeTest.php │ │ ├── MoneyTest.php │ │ ├── NumberTest.php │ │ └── TextTest.php │ ├── ColumnTypeExtension │ │ ├── DefaultColumnOptionsExtensionTest.php │ │ └── ValueFormatColumnOptionsExtensionTest.php │ └── CoreExtensionTest.php ├── Doctrine │ ├── ColumnType │ │ └── EntityTypeTest.php │ ├── ColumnTypeExtension │ │ └── ValueFormatColumnOptionsExtensionTest.php │ └── DoctrineExtensionTest.php └── Gedmo │ ├── ColumnType │ └── Tree │ │ └── TreeTypeTest.php │ └── GedmoDoctrineExtensionTest.php └── Fixtures ├── ColumnType └── FooType.php ├── Entity.php ├── EntityCategory.php ├── EntityManagerMock.php ├── EntityMapper.php ├── EntityRepositoryMock.php ├── EntityTree.php ├── EventManagerMock.php ├── FooExtension.php └── GedmoTreeListenerMock.php /.gitignore: -------------------------------------------------------------------------------- 1 | /bin 2 | /vendor 3 | /tests/temp 4 | composer.lock 5 | composer.phar 6 | autoload.php -------------------------------------------------------------------------------- /.scrutinizer.yml: -------------------------------------------------------------------------------- 1 | --- 2 | filter: 3 | excluded_paths: [vendor/*, bin/*] 4 | 5 | checks: 6 | php: 7 | code_rating: true 8 | duplication: true 9 | 10 | tools: 11 | php_cpd: true 12 | php_pdepend: 13 | excluded_dirs: [vendor] 14 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | matrix: 4 | include: 5 | - php: 7.1 6 | env: 7 | - COMPOSER_FLAGS='--prefer-lowest' 8 | - php: 7.2 9 | - php: 7.3 10 | 11 | sudo: false 12 | 13 | cache: 14 | directories: 15 | - $HOME/.composer/cache 16 | - vendor 17 | 18 | before_script: 19 | - phpenv config-rm xdebug.ini 20 | - composer validate 21 | - composer update $COMPOSER_FLAGS 22 | 23 | script: vendor/bin/phpunit 24 | -------------------------------------------------------------------------------- /CHANGELOG-2.0.md: -------------------------------------------------------------------------------- 1 | # Changelog 1.x to 2.0 2 | 3 | This is a list changes done in the 2.0 vesion. 4 | 5 | ## Deleted Symfony extension 6 | 7 | Since it was moved to [datagrid-bundle](https://github.com/fsi-open/datagrid-bundle), 8 | it has been removed from this component. 9 | 10 | ## Changed null value semantic in boolean column 11 | 12 | Boolean column now treats `null`s like non existing value instead of `false` value. It displays empty string when 13 | all input values are `null`s. 14 | 15 | ## Dropped support for PHP below 7.1 16 | 17 | To be able to fully utilize new functionality introduced in 7.1, we have decided 18 | to only support PHP versions equal or higher to it. 19 | 20 | ## Removed deprecated methods 21 | 22 | The following deprecated interfaces, classes and methods were removed because they were replaced by 23 | [data-indexer](https://github.com/fsi-open/data-indexer/) component. 24 | 25 | - `FSi\Component\DataGrid\Data\IndexingStrategyInterface` 26 | - `FSi\Component\DataGrid\Data\EntityIndexingStrategy` 27 | - `FSi\Component\DataGrid\DataGridFactoryInterface::getIndexingStrategy` 28 | - `FSi\Component\DataGrid\DataGridInterface::getIndexingStrategy` 29 | 30 | ## Added argument and return typehints 31 | 32 | All interfaces and classes are now strictly typed. 33 | 34 | ## Removed fluent interfaces 35 | 36 | All interfaces except `DataGridInterface` are no longer fluent. 37 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (C) 2012-2014 FSi Sp. z o.o. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is furnished 8 | to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /UPGRADE-1.0.md: -------------------------------------------------------------------------------- 1 | UPGRADE FROM 0.9.x to 1.0.0 2 | ============================ 3 | 4 | ## Options 5 | 6 | ### mapping_fields 7 | 8 | Before: ``mapping_fields`` 9 | After: ``field_mapping`` 10 | 11 | ### order 12 | 13 | Before: ``order`` 14 | After: ``display_order`` 15 | 16 | 17 | ### fields_options 18 | 19 | Before: ``fields_options`` 20 | Example code: 21 | ```php 22 | $datagrid->addColumn('active', 'boolean', array( 23 | 'label' => 'Active', 24 | 'editable' => true, 25 | 'true_value' => 'YES', 26 | 'false_value' => 'NO', 27 | 'form_options' => array( 28 | 'active' => array( 29 | 'type' => 'choice', 30 | 'options' => array( 31 | 'choices' => array( 32 | 0 => 'NO', 33 | 1 => 'YES' 34 | ) 35 | ) 36 | ) 37 | ) 38 | )); 39 | 40 | ``` 41 | 42 | After: ``form_options`` and ``form_type`` 43 | Example code: 44 | ```php 45 | $datagrid->addColumn('active', 'boolean', array( 46 | 'label' => 'Active', 47 | 'editable' => true, 48 | 'true_value' => 'YES', 49 | 'false_value' => 'NO', 50 | 'form_options' => array( 51 | 'active' => array( 52 | 'choices' => array( 53 | 0 => 'NO', 54 | 1 => 'YES' 55 | ) 56 | ) 57 | ), 58 | 'form_type' => array( 59 | 'active' => 'choice' 60 | ) 61 | )); 62 | ``` 63 | 64 | ### glue 65 | 66 | Before: ``glue`` 67 | After: ``value_glue`` 68 | 69 | ### format 70 | 71 | Before: ``format`` 72 | After: ``value_format`` 73 | 74 | 75 | ### input 76 | 77 | Before: ``input`` 78 | After: ``input_format`` 79 | 80 | ### mapping_fields_format 81 | 82 | Before: ``mapping_fields_format`` 83 | After: ``input_field_format`` 84 | 85 | 86 | ### actions (action) 87 | 88 | Before: ``anchor`` 89 | After: **removed** 90 | 91 | Before: ``parameters`` 92 | After: ``parameters_field_mapping`` 93 | 94 | Before: ``parameters_values`` 95 | After: ``additional_parameters`` 96 | 97 | ```php 98 | $datagrid->addColumn('action', 'action', array( 99 | 'label' => 'Actions', 100 | 'field_mapping' => array('id'), 101 | 'actions' => array( 102 | 'edit' => array( 103 | 'anchor' => 'Edit', 104 | 'absolute' => fasle, 105 | 'redirect_uri' => true, 106 | 'route_name' => '_news_edit', 107 | 'parameters' => array('id' => 'id'), 108 | 'parameters_values' => array('constant_param' => '1'), 109 | ), 110 | 'delete' => array( 111 | 'anchor' => 'Delete', 112 | 'absolute' => fasle, 113 | 'redirect_uri' => true, 114 | 'route_name' => '_news_delete', 115 | 'parameters' => array('id' => 'id'), 116 | 'parameters_values' => array('constant_param' => '1'), 117 | ) 118 | ) 119 | )); 120 | ``` 121 | 122 | After: 123 | ```php 124 | $datagrid->addColumn('action', 'action', array( 125 | 'label' => 'Actions', 126 | 'field_mapping' => array('id', 'title'), 127 | 'actions' => array( 128 | 'edit' => array( 129 | 'route_name' => '_news_edit', 130 | 'parameters_field_mapping' => array('id' => 'id'), 131 | 'additional_parameters' => array('constant_param' => '1'), 132 | ), 133 | 'delete' => array( 134 | 'route_name' => '_news_delete', 135 | 'parameters_field_mapping' => array('id' => 'id'), 136 | 'additional_parameters' => array('constant_param' => '1'), 137 | ) 138 | ) 139 | )); 140 | ``` 141 | 142 | *Important! Option anchor has been removed. From now url content is build from 143 | action name translated with translation_domain.* 144 | -------------------------------------------------------------------------------- /UPGRADE-2.0.md: -------------------------------------------------------------------------------- 1 | # This is a guide on how to update from version 1.x to 2.0 2 | 3 | ## Use datagrid-bundle to utilize Symfony extension 4 | 5 | Since it was moved to [datagrid-bundle](https://github.com/fsi-open/datagrid-bundle), 6 | you will need to install it in order to be able to use the extension. 7 | 8 | ## Use null in boolean column only when you mean it 9 | 10 | If any of your boolean columns can contain `null` then an empty string will be displayed 11 | instead of value passed as `false_value` option. You should either ensure the column does 12 | not contain `null` or that it adheres to the new behaviour. 13 | 14 | ## Upgrade to PHP 7.1 or higher 15 | 16 | In order to use this library, you will need PHP 7.1 or higher. 17 | 18 | ## Add argument and return typehints 19 | 20 | There is quite a big chance that your custom column type or column type extension 21 | need to have added typehints to most of its methods accordingly to extended 22 | class or implemented interface. 23 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fsi/datagrid", 3 | "type": "library", 4 | "description": "FSi DataGrid Component", 5 | "keywords": ["fsi", "component", "datagrid"], 6 | "license": "MIT", 7 | "authors": [ 8 | { 9 | "name": "Norbert Orzechowicz", 10 | "email": "norbert@orzechowicz.pl" 11 | } 12 | ], 13 | "require": { 14 | "php": ">=7.1", 15 | "symfony/event-dispatcher" : "^2.2|^3.0|^4.0", 16 | "symfony/property-access": "^2.3|^3.0|^4.0", 17 | "symfony/options-resolver": "^2.6|^3.0|^4.0", 18 | "fsi/reflection" : "0.9.*", 19 | "fsi/data-indexer" : "0.9.*" 20 | }, 21 | "require-dev": { 22 | "doctrine/orm": "^2.5", 23 | "doctrine/common": "^2.5", 24 | "gedmo/doctrine-extensions": "^2.3.10", 25 | "phpunit/phpunit": "^7.1" 26 | }, 27 | "autoload": { 28 | "psr-4": { 29 | "FSi\\Component\\DataGrid\\": "lib/" 30 | } 31 | }, 32 | "autoload-dev": { 33 | "psr-4": { 34 | "FSi\\Component\\DataGrid\\Tests\\": "tests/" 35 | } 36 | }, 37 | "config": { 38 | "bin-dir": "vendor/bin" 39 | }, 40 | "extra": { 41 | "branch-alias": { 42 | "dev-master": "3.0-dev", 43 | "2.0": "2.0-dev", 44 | "1.3": "1.3-dev", 45 | "1.2": "1.2-dev", 46 | "1.1": "1.1-dev", 47 | "1.0": "1.0-dev" 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /doc/en/columns/action.md: -------------------------------------------------------------------------------- 1 | # Action Column Type # 2 | 3 | Provided by ``DataGrid\Extension\Core\CoreExtension`` 4 | 5 | ## Available Options ## 6 | 7 | * ``label`` - string, by default ``[$field->getName()]`` 8 | * ``field_mapping`` - **required**, array, by default ``[$field->getName()]`` 9 | * ``display_order`` - integer 10 | * ``actions`` - **required**, array 11 | 12 | ## Options Description ## 13 | 14 | **field_mapping** Fields that should be used when data is retrieved from the source. By default there is only one field 15 | and its taken from the name under what column was registered in grid. 16 | 17 | **label** By default label value its taken from name under what column was registered in grid. 18 | 19 | **display_order** Optional integer value specifying order of column in grid. Columns in grid are sorted according 20 | to ascending value of this option. Columns without this option will stay in their natural order (between columns with 21 | positive and negative values of this option) 22 | 23 | **actions** Array of actions. Each action has following options: 24 | 25 | * ``uri_scheme`` - **required**, string, scheme of an uri 26 | * ``protocol`` - string, by default ``'http://`` 27 | * ``domain`` - string, domain for an anchor 28 | * ``redirect_uri`` - string, optional parameter in uri, might be useful to create return from actions. 29 | 30 | ## Example Usage ## 31 | 32 | ``` php 33 | addColumn('actions', 'action', [ 36 | 'label' => 'Actions', 37 | 'mapping_fields' => ['identity'], 38 | 'actions' => [ 39 | edit' => [ 40 | 'uri_scheme' => '/test/%s', 41 | 'domain' => 'fsi.pl', 42 | 'protocol' => 'https://', 43 | 'redirect_uri' => 'http://onet.pl/' 44 | ], 45 | ] 46 | ]); 47 | 48 | ``` 49 | 50 | **Important** - ``parameters_field_mapping``, ``url_attr`` and ``content`` allows \Closure function as value. It can be used to format 51 | option value depending on the field_mapping values. Closure function will be called with 2 arguments: 52 | 53 | ``function($fieldMappingValues, $rowIndex)`` 54 | -------------------------------------------------------------------------------- /doc/en/columns/boolean.md: -------------------------------------------------------------------------------- 1 | # Boolean Column Type # 2 | 3 | Provided by ``DataGrid\Extension\Core\CoreExtension`` 4 | 5 | ## Available Options ## 6 | 7 | * ``label`` - string, by default ``[$field->getName()]`` 8 | * ``field_mapping`` - **required**, array, by default ``[$field->getName()]`` 9 | * ``value_glue`` - string 10 | * ``value_format`` - string 11 | * ``display_order`` - integer 12 | * ``empty_value`` - string|array, by default ``""`` (empty string) 13 | * ``true_value`` - what should be stetted as cell view value if mapping_fields give true values. 14 | * ``false_value`` - what should be stetted as cell view value if mapping_fields give false values. 15 | * ``editable`` - **required**, boolean, by default ``false`` 16 | * ``form_options`` - array, by default ``[]`` 17 | * ``form_type`` - array, by default ``[]`` 18 | 19 | ## Options Description ## 20 | 21 | **label** Label for column. 22 | 23 | **field_mapping** Fields that should be used when data is retrieved from the source. By default there is only one 24 | field and its taken from the name under what column was registered in grid. 25 | Option is useful when you need to implode few fields from object in one column. 26 | 27 | **label** By default label value its taken from name under what column was registered in grid. 28 | 29 | **value_glue** Useful only when you need to implode data from few source object fields into one column. 30 | 31 | **value_format** Useful when you need to format value before passing it to view. Value formatted with php ``sprintf`` function. There should be at least same count of ``mapping_fields`` option 32 | values as placeholders count in format string. This option can be used with ``value_glue`` option. 33 | ``format`` option also accept ``\Closure`` function that should return valid formatted string. 34 | 35 | **display_order** Optional integer value specifying order of column in grid. Columns in grid are sorted according 36 | to ascending value of this option. Columns without this option will stay in their natural order (between columns with 37 | positive and negative values of this option) 38 | 39 | **editable** If enabled SymfonyForm object is automatically created and passed into view as attribute and you can easily use it to display quick edit. 40 | 41 | **form_options** Array of options for forms, where key is name of field (one of field_mapping) and value is 42 | options passed to form. 43 | 44 | **form_type** Array of types for forms, where key is name of field (one of field_mapping) and value is form type. 45 | 46 | **empty_value** if value from field_mapping is empty (null, !strlen) then it will be replaced with ``empty_value`` option value which by default is empty string. There is also possibility to pass ``empty_value`` to selected ``mapping_fields``. 47 | To do it you just need set ``empty_value`` as array where keys are ``mapping_fields`` keys. If mapping field value is empty and its not included in ``empty_value`` option it will be replaced with empty string. 48 | 49 | 50 | ## Example Usage ## 51 | 52 | ``` php 53 | false) 56 | //Input Data1: Object ('available' => true) 57 | //Input Data3: Object ('available' => null) 58 | $datagrid->addColumn('available', 'boolean', [ 59 | 'editable' => false, 60 | 'true_value' => '', 61 | 'false_value' => '', 62 | 'form_options' => [ 63 | 'available' => [ 64 | 'required' => false 65 | ] 66 | ] 67 | 'form_type' => [ 68 | 'available' => 'checkbox' 69 | ] 70 | )); 71 | //Output1: "" 72 | //Output2: "'" 73 | //Output3: "" 74 | 75 | //Input Data1: Object ('accessible' => true, 'visible' => true) 76 | //Input Data2: Object ('accessible' => true, 'visible' => false) 77 | //Input Data3: Object ('accessible' => false, 'visible' => false) 78 | $datagrid->addColumn('available', 'boolean', [ 79 | 'field_mapping' => ['accessible', 'visible'] 80 | 'editable' => false, 81 | 'true_value' => '', 82 | 'false_value' => '', 83 | 'form_options' => [ 84 | 'available' => [ 85 | 'required' => false 86 | ] 87 | ], 88 | 'form_type' => [ 89 | 'available' => 'checkbox' 90 | ] 91 | )); 92 | //Output1: "'" 93 | //Output2: "" 94 | //Output3: "" 95 | -------------------------------------------------------------------------------- /doc/en/columns/collection.md: -------------------------------------------------------------------------------- 1 | # Text Column Type # 2 | 3 | Provided by ``DataGrid\Extension\Core\CoreExtension`` 4 | 5 | ## Available Options ## 6 | 7 | * ``collection_glue`` - string, by default `` `` 8 | * ``label`` - string, by default ``[$field->getName()]`` 9 | * ``field_mapping`` - **required**, array, by default ``[$field->getName()]`` 10 | * ``value_glue`` - string 11 | * ``display_order`` - integer 12 | * ``empty_value`` - string|array, by default ``""`` (empty string) 13 | 14 | ## Options Description ## 15 | 16 | **collection_glue** option used to implode array elements, by default empty string " ". 17 | 18 | **label** Label for column. 19 | 20 | **mapping_fields** Fields that should be used when data is retrieved from the source. By default there is only one mapping 21 | field and its taken from name under what column was registered in grid. 22 | Option is useful when you need to implode few fields from object in one column. 23 | 24 | **label** By default label value its taken from name under what column was registered in grid. 25 | 26 | **field_mapping** Fields that should be used when data is retrieved from the source. By default there is only one 27 | field and its taken from the name under what column was registered in grid. 28 | Option is useful when you need to implode few fields from object in one column. 29 | 30 | **value_glue** Useful only when you need to implode data from few source object fields into one column. 31 | 32 | **value_format** Useful when you need to format value before passing it to view. Value formatted with php ``sprintf`` function. There should be at least same count of ``mapping_fields`` option 33 | values as placeholders count in format string. This option can be used with ``value_glue`` option. 34 | ``format`` option also accept ``\Closure`` function that should return valid formatted string. 35 | 36 | **display_order** Optional integer value specifying order of column in grid. Columns in grid are sorted according 37 | to ascending value of this option. Columns without this option will stay in their natural order (between columns with 38 | positive and negative values of this option) 39 | 40 | ## Example Usage ## 41 | 42 | ``` php 43 | ['ROLE_ADMIN, 'ROLE_USER']) 46 | $grid->addColumn('roles', 'collection', ['collection_glue' => ' | ']); 47 | //Output: "ROLE_ADMIN | ROLE_USER" 48 | 49 | ``` 50 | -------------------------------------------------------------------------------- /doc/en/columns/datetime.md: -------------------------------------------------------------------------------- 1 | # DateTime Column Type # 2 | 3 | Provided by ``DataGrid\Extension\Core\CoreExtension`` 4 | 5 | ## Available Options ## 6 | 7 | * ``datetime_format`` - **required**, string, by default ``Y-m-d H:i:s`` 8 | * ``input_type`` - string 9 | * ``input_field_format`` - array 10 | * ``label`` - string, by default ``[$field->getName()]`` 11 | * ``field_mapping`` - **required**, array, by default ``[$field->getName()]`` 12 | * ``value_glue`` - string 13 | * ``value_format`` - string 14 | * ``display_order`` - integer 15 | * ``editable`` - **required**, boolean, by default ``false`` 16 | * ``form_options`` - array, by default ``[]`` 17 | * ``form_type`` - array, by default ``[]`` 18 | * ``empty_value`` - string|array, by default ``""`` (empty string) 19 | 20 | ## Options Description ## 21 | 22 | **datetime_format** Format of showed date and/or time. 23 | 24 | **input_type** Kind of data you are giving to column (``array``, ``datetime``, ``datetime_interface``, ``string``, ``timestamp``) - if no specified, column will try to guess it. 25 | 26 | **input_field_format** Array of formats used if you specify more than one field in field_mapping option (that keys match 'field_mapping` option keys), otherwise its equal to 'datetime_format' option. 27 | 28 | **label** By default label value its taken from name under what column was registered in grid. 29 | 30 | **field_mapping** Fields that should be used when data is retrieved from the source. By default there is only one 31 | field and its taken from the name under what column was registered in grid. 32 | Option is useful when you need to implode few fields from object in one column. 33 | 34 | **value_glue** Useful only when you need to implode data from few source object fields into one column. 35 | 36 | **value_format** Useful when you need to format value before passing it to view. Value formatted with php ``sprintf`` function. There should be at least same count of ``mapping_fields`` option 37 | values as placeholders count in format string. This option can be used with ``value_glue`` option. 38 | ``format`` option also accept ``\Closure`` function that should return valid formatted string. 39 | 40 | **display_order** Optional integer value specifying order of column in grid. Columns in grid are sorted according 41 | to ascending value of this option. Columns without this option will stay in their natural order (between columns with 42 | positive and negative values of this option) 43 | 44 | **editable** If enabled SymfonyForm object is automatically created and passed into view as attribute and you can easily use it to display quick edit. 45 | 46 | **form_options** Array of options for forms, where key is name of field (one of field_mapping) and value is 47 | options passed to form. 48 | 49 | **form_type** Array of types for forms, where key is name of field (one of field_mapping) and value is form type. 50 | 51 | ## Example Usage ## 52 | 53 | ``` php 54 | addColumn('purchase_date', 'datetime', [ 58 | 'label' => 'Purchase date', 59 | 'datetime_format' => 'Y-m-d H:i:s', 60 | 'editable' => true, 61 | ]); 62 | 63 | //Shows column with date that is combination of create_date and create_time fields. 64 | $datagrid->addColumn('create_datetime', 'datetime', [ 65 | 'label' => 'Create datetime', 66 | 'field_mapping' => ['create_date', 'create_time'], 67 | 'value_glue' => ' ', 68 | 'editable' => true, 69 | 'input_type' => 'array', 70 | 'datetime_format' => ['create_date' => 'Y-m-d', 'create_time' => "H:i:s"], 71 | 'input_field_format' => ['create_date' => ['input_type' => 'datetime'], 'create_time' => {'input_type' => 'datetime']], 72 | ]); 73 | 74 | //Shows column that value is combination formatted fields create_date and timestamp. 75 | $datagrid->addColumn('create_date_timestamp', 'datetime', [ 76 | 'label' => 'Crate date from timestamp', 77 | 'field_mapping' => [create_date', 'timestamp'], 78 | 'value_glue' => '
', 79 | 'editable' => true, 80 | 'input_type' => 'array', 81 | 'datetime_format' => ['timestamp' => 'Y-m-d h:i:s', 'create_date' => 'Y-m-d'], 82 | 'input_field_format' => ['create_date' => ['input_type' => 'datetime'], 'timestamp' => ['input_type' => 'timestamp']] 83 | ]); 84 | 85 | //Shows date, that is combination of three integer fields. 86 | $datagrid->addColumn('join_date', 'datetime', [ 87 | 'label' => 'Join date', 88 | 'field_mapping' => ['int_year', 'int_month', 'int_day'], 89 | 'glue' => '-', 90 | 'input_type' => 'array', 91 | 'datetime_format' => ['int_year' => 'Y', 'int_month' => 'm', 'int_day' => 'd'], 92 | 'input_field_format' => [ 93 | 'int_year' => ['input_type' => 'string', 'datetime_format' => 'Y'], 94 | 'int_month' => ['input_type' => 'string', 'datetime_format' => 'm'], 95 | 'int_day' => ['input_type' => 'string', 'datetime_format' => 'd'] 96 | ] 97 | ]); 98 | 99 | ``` 100 | -------------------------------------------------------------------------------- /doc/en/columns/entity.md: -------------------------------------------------------------------------------- 1 | # Entity Column Type # 2 | 3 | Provided by ``FSi\Component\DataGrid\Extension\Doctrine\DoctrineExtension`` 4 | 5 | ## Available Options ## 6 | 7 | * ``label`` - string, by default ``[$field->getName()]`` 8 | * ``field_mapping`` - **required**, array, by default ``[$field->getName()]`` 9 | * ``value_glue`` - string 10 | * ``empty_value`` - string|array|null, by default ``null`` 11 | * ``glue_multiple`` - string, by default ``" "`` (space character) 12 | * ``relation_field`` - **required**, string 13 | * ``display_order`` - integer 14 | * ``editable`` - **required**, boolean, by default ``false`` 15 | * ``form_options`` - array, by default ``[]`` 16 | * ``form_type`` - array, by default ``[]`` 17 | 18 | ## Options Description ## 19 | 20 | **label** By default label value its taken from name under what column was registered in grid. 21 | 22 | **field_mapping** Fields that should be used when data is retrieved from the source. By default there is only one 23 | field and its taken from the name under what column was registered in grid. 24 | Option is useful when you need to implode few fields from object in one column. 25 | 26 | **value_format** Useful when you need to format value before passing it to view. Value formatted with php ``sprintf`` function. There should be at least same count of ``mapping_fields`` option 27 | values as placeholders count in format string. This option can be used with ``value_glue`` option. 28 | ``format`` option also accept ``\Closure`` function that should return valid formatted string. 29 | 30 | **empty_value** Useful when value is empty and you want override this value. 31 | 32 | **display_order** Optional integer value specifying order of column in grid. Columns in grid are sorted according 33 | to ascending value of this option. Columns without this option will stay in their natural order (between columns with 34 | positive and negative values of this option) 35 | 36 | **editable** If enabled SymfonyForm object is automatically created and passed into view as attribute and you can easily use it to display quick edit. 37 | 38 | **glue_multiple** Glue between objects from relation. Should be used if you want to display relation with many objects and add some separator between them. 39 | 40 | **relation_field** Field that relates to other entity (entities). 41 | 42 | **editable** If enabled SymfonyForm object is automatically created and passed into view as attribute and you can easily use it to display quick edit. 43 | 44 | **form_options** Array of options for forms, where key is name of field (one of field_mapping) and value is 45 | options passed to form. 46 | 47 | **form_type** Array of types for forms, where key is name of field (one of field_mapping) and value is form type. 48 | 49 | ## Example Usage ## 50 | 51 | ``` php 52 | Object('id' => 1, 'name' => 'Foo')) 55 | 56 | $dataGrid->addColumn('category', 'entity', [ 57 | 'label' => 'News category', 58 | 'relation_field' => 'category', 59 | 'value_format' => '%s %s', 60 | 'field_mapping' => ['id', 'name'] 61 | ]); 62 | 63 | //Output: "1 Foo" 64 | 65 | //Input Data: Object (category => Object('id' => null, 'name' => 'Foo')) 66 | 67 | $dataGrid->addColumn('category', 'entity', [ 68 | 'label' => 'News category', 69 | 'relation_field' => 'category', 70 | 'value_glue' => ' ', 71 | 'empty_value' => 'no', 72 | 'field_mapping' => ['id', 'name'] 73 | ]); 74 | 75 | //Output: "no Foo" 76 | 77 | //Input Data: Object (category => Object('id' => null, 'name' => null)) 78 | 79 | $dataGrid->addColumn('category', 'entity', [ 80 | 'label' => 'News category', 81 | 'relation_field' => 'category', 82 | 'value_glue' => ' ', 83 | 'empty_value' => ['id' => 'no', 'name' => 'no'], 84 | 'field_mapping' => ['id', 'name'] 85 | ]); 86 | 87 | //Output: "no no" 88 | 89 | //Input Data: Object (newses => [0 => Object('id' => 1, 'name' => 'Foo'), 1 => Object('id' => 2, 'name' => 'Bar')]) 90 | 91 | $dataGrid->addColumn('newses', 'entity', [ 92 | 'label' => 'Category Newses', 93 | 'relation_field' => 'newses', 94 | 'value_format' => '(%s: %s)', 95 | 'glue_multiple' => ' - ', 96 | 'field_mapping' => ['id', 'name'] 97 | ]); 98 | 99 | //Output: "(1: Foo) - (2: Bar)" 100 | ``` 101 | -------------------------------------------------------------------------------- /doc/en/columns/money.md: -------------------------------------------------------------------------------- 1 | # Money Column Type # 2 | 3 | Provided by ``DataGrid\Extension\Core\CoreExtension`` 4 | 5 | ## Available Options ## 6 | 7 | * ``round_mode`` - integer 8 | * ``precision`` - integer, by default ``2`` 9 | * ``decimals`` - integer, by default ``2`` 10 | * ``dec_point`` - string, by default ``.`` 11 | * ``thousands_sep`` - string, by default ``,`` 12 | * ``value_currency_separator`` - string, by default `` `` (space character) 13 | * ``currency_field`` - string 14 | * ``currency`` - string 15 | * ``label`` - string, by default ``[$field->getName()]`` 16 | * ``field_mapping`` - **required**, array, by default ``[$field->getName()]`` 17 | * ``value_glue`` - string 18 | * ``display_order`` - integer 19 | * ``editable`` - **required**, boolean, by default ``false`` 20 | * ``empty_value`` - string|array, by default ``""`` (empty string) 21 | * ``form_options`` - array, by default ``[]`` 22 | * ``form_type`` - array, by default ``[]`` 23 | 24 | ## Options Description ## 25 | 26 | **round_mode** One of ``Number::ROUND_HALF_UP``, ``Number::ROUND_HALF_DOWN``, ``Number::ROUND_HALF_EVEN`` or ``Number::ROUND_HALF_ODD``. 27 | 28 | **precision** Number of decimal digits to round to. 29 | 30 | **decimals** The number of decimal points. 31 | 32 | **dec_point** Decimal point. 33 | 34 | **thousands_sep** Thousands separator. 35 | 36 | **value_currency_separator** Separator between currency and value. 37 | 38 | **currency_field** Field to take actual currency from. Mandatory if currency_value not specified. 39 | 40 | **currency** Currency. Mandatory if currency_field not specified. 41 | 42 | **field_mapping** Fields that should be used when data is retrieved from the source. By default there is only one 43 | field and its taken from the name under what column was registered in grid. 44 | Option is useful when you need to implode few fields from object in one column. 45 | 46 | **value_glue** Useful only when you need to implode data from few source object fields into one column. 47 | 48 | **value_format** Useful when you need to format value before passing it to view. Value formatted with php ``sprintf`` function. There should be at least same count of ``mapping_fields`` option 49 | values as placeholders count in format string. This option can be used with ``value_glue`` option. 50 | ``format`` option also accept ``\Closure`` function that should return valid formatted string. 51 | 52 | **display_order** Optional integer value specifying order of column in grid. Columns in grid are sorted according 53 | to ascending value of this option. Columns without this option will stay in their natural order (between columns with 54 | positive and negative values of this option) 55 | 56 | **editable** If enabled SymfonyForm object is automatically created and passed into view as attribute and you can easily use it to display quick edit. 57 | 58 | **form_options** Array of options for forms, where key is name of field (one of field_mapping) and value is 59 | options passed to form. 60 | 61 | **form_type** Array of types for forms, where key is name of field (one of field_mapping) and value is form type. 62 | 63 | ## Example Usage ## 64 | 65 | ``` php 66 | addColumn('productprice', 'money', [ 70 | 'label' => 'Product price', 71 | 'field_mapping' => ['price', 'currency'], 72 | 'currency_field' => 'currency', 73 | 'value_currency_separator' => ' - ', 74 | 'value_glue' => '
', 75 | ]); 76 | 77 | //This configuration will show price from two fields ('price' and 'promo_price') with arbitrary USD currency. 78 | $datagrid->addColumn('productprice', 'money', [ 79 | 'label' => 'Product price', 80 | 'field_mapping' => ['price', 'promo_price'], 81 | 'currency' => 'USD', 82 | 'value_currency_separator' => '$ ', 83 | 'dec_point' => ',', 84 | ]); 85 | 86 | ``` 87 | -------------------------------------------------------------------------------- /doc/en/columns/number.md: -------------------------------------------------------------------------------- 1 | # Number Column Type # 2 | 3 | Provided by ``DataGrid\Extension\Core\CoreExtension`` 4 | 5 | ## Available Options ## 6 | 7 | * ``round_mode`` - integer 8 | * ``precision`` - integer, by default ``2`` 9 | * ``label`` - string, by default ``[$field->getName()]`` 10 | * ``field_mapping`` - **required**, array, by default ``[$field->getName()]`` 11 | * ``value_glue`` - string 12 | * ``display_order`` - integer 13 | * ``editable`` - **required**, boolean, by default ``false`` 14 | * ``empty_value`` - string|array, by default ``""`` (empty string) 15 | * ``form_options`` - array, by default ``[]`` 16 | * ``form_type`` - array, by default ``[]`` 17 | * ``format`` - boolean, by default ``false`` 18 | * ``format_decimals`` - integer, by default ``0`` 19 | * ``format_dec_point`` - string, by default ``.`` 20 | * ``format_thousands_sep`` - string, by default ``,`` 21 | 22 | ## Options Description ## 23 | 24 | **round_mode** One of ``Number::ROUND_HALF_UP``, ``Number::ROUND_HALF_DOWN``, ``Number::ROUND_HALF_EVEN`` or ``Number::ROUND_HALF_ODD``. 25 | 26 | **precision** Number of decimal digits to round to. 27 | 28 | **label** By default label value its taken from name under what column was registered in grid. 29 | 30 | **field_mapping** Fields that should be used when data is retrieved from the source. By default there is only one 31 | field and its taken from the name under what column was registered in grid. 32 | Option is useful when you need to implode few fields from object in one column. 33 | 34 | **value_glue** Useful only when you need to implode data from few source object fields into one column. 35 | 36 | **value_format** Useful when you need to format value before passing it to view. Value formatted with php ``sprintf`` function. There should be at least same count of ``mapping_fields`` option 37 | values as placeholders count in format string. This option can be used with ``value_glue`` option. 38 | ``format`` option also accept ``\Closure`` function that should return valid formatted string. 39 | 40 | **display_order** Optional integer value specifying order of column in grid. Columns in grid are sorted according 41 | to ascending value of this option. Columns without this option will stay in their natural order (between columns with 42 | positive and negative values of this option) 43 | 44 | **editable** If enabled SymfonyForm object is automatically created and passed into view as attribute and you can easily use it to display quick edit. 45 | 46 | **form_options** Array of options for forms, where key is name of field (one of field_mapping) and value is 47 | options passed to form. 48 | 49 | **form_type** Array of types for forms, where key is name of field (one of field_mapping) and value is form type. 50 | 51 | **format** If set to true, number will be formatted using options ``format_decimals``, ``format_dec_point`` and ``format_thousands_sep`` according to http://php.net/manual/en/function.number-format.php By default ``format`` option is disabled, so value will not be formatted in case it is phone number or some id number. 52 | 53 | **format_decimals** By default ``2``. See **format** option for description. 54 | 55 | **format_dec_point** By default ``.``. See **format** option for description. 56 | 57 | **format_thousands_sep** By default ``,``. See **format** option for description. 58 | 59 | ## Example Usage ## 60 | 61 | ``` php 62 | 10.123) 65 | $grid->addColumn('price', 'number', [ 66 | 'field_mapping' => [ 67 | 'value' 68 | ], 69 | 'round_mode' => Number::ROUND_HALF_UP 70 | 'precision' => 2 71 | ]); 72 | //Output: 10.12 73 | 74 | //Input Data: Object ('value' => 10.126) 75 | $grid->addColumn('price', 'number', [ 76 | 'field_mapping' => [ 77 | 'value' 78 | ], 79 | 'round_mode' => Number::ROUND_HALF_UP 80 | 'precision' => 2 81 | ]); 82 | 83 | //Output: 10.13 84 | ``` 85 | -------------------------------------------------------------------------------- /doc/en/columns/text.md: -------------------------------------------------------------------------------- 1 | # Text Column Type # 2 | 3 | Provided by ``DataGrid\Extension\Core\CoreExtension`` 4 | 5 | ## Available Options ## 6 | 7 | * ``trim`` - boolean, by default ``false`` 8 | * ``label`` - string, by default ``[$field->getName()]`` 9 | * ``field_mapping`` - **required**, array, by default ``[$field->getName()]`` 10 | * ``value_glue`` - string 11 | * ``display_order`` - integer 12 | * ``editable`` - **required**, boolean, by default ``false`` 13 | * ``empty_value`` - string|array, by default ``""`` (empty string) 14 | * ``form_options`` - array, by default ``[]`` 15 | * ``form_type`` - array, by default ``[]`` 16 | 17 | ## Options Description ## 18 | 19 | **trim** By default option is disabled. If enabled value from every single mapping_filed is trimmed before ``buildView`` method will pass it into view object. 20 | 21 | **label** Label for column. 22 | 23 | **mapping_fields** Fields that should be used when data is retrieved from the source. By default there is only one mapping 24 | field and its taken from name under what column was registered in grid. 25 | Option is useful when you need to implode few fields from object in one column. 26 | 27 | **label** By default label value its taken from name under what column was registered in grid. 28 | 29 | **field_mapping** Fields that should be used when data is retrieved from the source. By default there is only one 30 | field and its taken from the name under what column was registered in grid. 31 | Option is useful when you need to implode few fields from object in one column. 32 | 33 | **value_glue** Useful only when you need to implode data from few source object fields into one column. 34 | 35 | **value_format** Useful when you need to format value before passing it to view. Value formatted with php ``sprintf`` function. There should be at least same count of ``mapping_fields`` option 36 | values as placeholders count in format string. This option can be used with ``value_glue`` option. 37 | ``format`` option also accept ``\Closure`` function that should return valid formatted string. 38 | 39 | **display_order** Optional integer value specifying order of column in grid. Columns in grid are sorted according 40 | to ascending value of this option. Columns without this option will stay in their natural order (between columns with 41 | positive and negative values of this option) 42 | 43 | **editable** If enabled SymfonyForm object is automatically created and passed into view as attribute and you can easily use it to display quick edit. 44 | 45 | **form_options** Array of options for forms, where key is name of field (one of field_mapping) and value is 46 | options passed to form. 47 | 48 | **form_type** Array of types for forms, where key is name of field (one of field_mapping) and value is form type. 49 | 50 | ## Example Usage ## 51 | 52 | ``` php 53 | 'Norbert', 'surname' => 'Orzechowicz') 56 | $grid->addColumn('name_surname', 'text', [ 57 | 'field_mapping' => [ 58 | 'name', 59 | 'surname' 60 | ] 61 | ]); 62 | //Output: "Norbert Orzechowicz" 63 | 64 | //Input Data: Object ('name' => 'Norbert', 'surname' => 'Orzechowicz') 65 | $grid->addColumn('name_surname', 'text', [ 66 | 'field_mapping' => [ 67 | 'name', 68 | 'surname' 69 | ], 70 | 'glue' => '-' 71 | ]); 72 | //Output: "Norbert-Orzechowicz" 73 | 74 | //Input Data: Object ('name' => ' Norbert ') 75 | $grid->addColumn('name', 'text', ['trim' => true]); 76 | //Output: "Norbert" 77 | 78 | //Input Data: Object ('name' => 'Norbert') 79 | $grid->addColumn('name_column', 'text', [ 80 | 'field_mapping' => [ 81 | 'name' 82 | ] 83 | ]); 84 | //Output: "Norbert" 85 | 86 | //Input Data: Object ('name' => 'Norbert') 87 | $grid->addColumn('name', 'text', [ 88 | 'editable' => true 89 | ]); 90 | // $form = $column->getAttribute('form') - Symfony Form Object 91 | ``` 92 | -------------------------------------------------------------------------------- /doc/en/columns/tree.md: -------------------------------------------------------------------------------- 1 | # Tree Column Type # 2 | 3 | Provided by ``DataGrid\Extension\Gedmo\GedmoDoctrineExtension`` 4 | 5 | ## Available Options ## 6 | 7 | * ``em`` - string 8 | * ``label`` - string, by default ``[$field->getName()]`` 9 | * ``field_mapping`` - **required**, array, by default ``[$field->getName()]`` 10 | * ``value_glue`` - string 11 | * ``display_order`` - integer 12 | * ``editable`` - **required**, boolean, by default ``false`` 13 | * ``empty_value`` - string|array, by default ``""`` (empty string) 14 | * ``form_options`` - array, by default ``[]`` 15 | * ``form_type`` - array, by default ``[]`` 16 | 17 | ## Options Description ## 18 | 19 | **em** Name of entity manager, if null column takes default one. 20 | 21 | **label** By default label value its taken from name under what column was registered in grid. 22 | 23 | **field_mapping** Fields that should be used when data is retrieved from the source. By default there is only one 24 | field and its taken from the name under what column was registered in grid. 25 | Option is useful when you need to implode few fields from object in one column. 26 | 27 | **value_glue** Useful only when you need to implode data from few source object fields into one column. 28 | 29 | **value_format** Useful when you need to format value before passing it to view. Value formatted with php ``sprintf`` function. There should be at least same count of ``mapping_fields`` option 30 | values as placeholders count in format string. This option can be used with ``value_glue`` option. 31 | ``format`` option also accept ``\Closure`` function that should return valid formatted string. 32 | 33 | **display_order** Optional integer value specifying order of column in grid. Columns in grid are sorted according 34 | to ascending value of this option. Columns without this option will stay in their natural order (between columns with 35 | positive and negative values of this option) 36 | 37 | **editable** If enabled SymfonyForm object is automatically created and passed into view as attribute and you can easily use it to display quick edit. 38 | 39 | **form_options** Array of options for forms, where key is name of field (one of field_mapping) and value is 40 | options passed to form. 41 | 42 | **form_type** Array of types for forms, where key is name of field (one of field_mapping) and value is form type. 43 | 44 | ## Example Usage ## 45 | 46 | ``` php 47 | addColumn('item', 'gedmo_tree', [ 50 | 'label' => 'Item', 51 | 'editable' => true, 52 | ]); 53 | 54 | ``` 55 | 56 | Difference between ``tree`` and ``text`` column is that cellView of ``tree`` column type has few additional 57 | attributes. 58 | List of attributes and example of usage below: 59 | 60 | **Attributes** 61 | * **id** - element id 62 | * **parent** - (optional) parent id 63 | * **root** - (optional) root id of element 64 | * **left** - element left position 65 | * **right** - element right position 66 | * **level** - element nesting level 67 | * **children** - element children count 68 | 69 | **Usage** (in Twig) 70 | 71 | ``` 72 | {# datagrid_column_type_{column_type}_cell #} 73 | {% block datagrid_column_type_gedmo_tree_cell %} 74 | 75 |
76 | {% spaceless %} 77 |
83 | {% endspaceless %} 84 | {{ cell.value|raw }} 85 |
86 | {{ datagrid_column_cell_form_widget(cell) }} 87 |
88 | 89 | {% endblock %} 90 | ``` 91 | -------------------------------------------------------------------------------- /doc/en/extensions/core.md: -------------------------------------------------------------------------------- 1 | # DataGrid Extension Core # 2 | 3 | ## Column Types provided by extension ## 4 | 5 | ``FSi\Component\DataGrid\Extension\Core\ColumnType\Bollean`` 6 | ``FSi\Component\DataGrid\Extension\Core\ColumnType\Text`` 7 | ``FSi\Component\DataGrid\Extension\Core\ColumnType\Number`` 8 | ``FSi\Component\DataGrid\Extension\Core\ColumnType\Money`` 9 | ``FSi\Component\DataGrid\Extension\Core\ColumnType\DateTime`` 10 | ``FSi\Component\DataGrid\Extension\Core\ColumnType\Action`` 11 | 12 | ## Event Subscribers provided by extension ## 13 | 14 | ``FSi\Component\DataGrid\Extension\Core\EventSubscriber\ColumnOrder`` 15 | 16 | ## Column Type Extensions provided by extension ## 17 | 18 | ``FSi\Component\DataGrid\Extension\Core\ColumnTypeExtension\ValueFormatColumnOptionsExtension`` 19 | Options added by this extension: 20 | * ``value_glue`` 21 | * ``value_format`` 22 | * ``empty_value`` 23 | 24 | ``FSi\Component\DataGrid\Extension\Core\ColumnTypeExtension\DefaultColumnOptionsExtension`` 25 | Options added by this extension: 26 | * ``label`` 27 | * ``display_order`` 28 | * ``field_mapping`` -------------------------------------------------------------------------------- /doc/en/extensions/doctrine.md: -------------------------------------------------------------------------------- 1 | # DataGrid Extension Doctrine # 2 | 3 | ## Column Types provided by extension ## 4 | 5 | ``FSi\Component\DataGrid\Extension\Doctrine\ColumnType\Entity`` 6 | 7 | ## Event Subscribers provided by extension ## 8 | 9 | none 10 | 11 | ## Column Type Extensions provided by extension ## 12 | 13 | ``FSi\Component\DataGrid\Extension\Doctrine\ColumnTypeExtension\ValueFormatColumnOptionsExtension`` 14 | Options added by this extension (only for column type ``entity``): 15 | * ``value_glue`` 16 | * ``value_format`` 17 | * ``glue_multiple`` 18 | -------------------------------------------------------------------------------- /lib/Column/CellView.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | declare(strict_types=1); 11 | 12 | namespace FSi\Component\DataGrid\Column; 13 | 14 | use FSi\Component\DataGrid\DataGridViewInterface; 15 | 16 | class CellView implements CellViewInterface 17 | { 18 | /** 19 | * The original object from which the value of the cell was retrieved. 20 | * 21 | * @var mixed 22 | */ 23 | protected $source; 24 | 25 | /** 26 | * Cell value. In most cases this should be a simple string. 27 | * 28 | * @var mixed 29 | */ 30 | protected $value; 31 | 32 | /** 33 | * Cell attributes. 34 | * 35 | * @var array 36 | */ 37 | protected $attributes = []; 38 | 39 | /** 40 | * Cell name. 41 | * 42 | * @var string 43 | */ 44 | protected $name; 45 | 46 | /** 47 | * Cell type. 48 | * 49 | * @var string 50 | */ 51 | protected $type; 52 | 53 | /** 54 | * @var DataGridViewInterface 55 | */ 56 | protected $datagrid; 57 | 58 | public function __construct(string $name, string $type) 59 | { 60 | $this->name = $name; 61 | $this->type = $type; 62 | } 63 | 64 | public function getName(): string 65 | { 66 | return $this->name; 67 | } 68 | 69 | public function getType(): string 70 | { 71 | return $this->type; 72 | } 73 | 74 | public function setValue($value): void 75 | { 76 | $this->value = $value; 77 | } 78 | 79 | public function getValue() 80 | { 81 | return $this->value; 82 | } 83 | 84 | public function setAttribute(string $name, $value): void 85 | { 86 | $this->attributes[$name] = $value; 87 | } 88 | 89 | public function getAttribute(string $name) 90 | { 91 | if (array_key_exists($name, $this->attributes)) { 92 | return $this->attributes[$name]; 93 | } 94 | 95 | return null; 96 | } 97 | 98 | public function getAttributes(): array 99 | { 100 | return $this->attributes; 101 | } 102 | 103 | public function hasAttribute(string $name): bool 104 | { 105 | return array_key_exists($name, $this->attributes); 106 | } 107 | 108 | public function setSource($source): void 109 | { 110 | $this->source = $source; 111 | } 112 | 113 | public function getSource() 114 | { 115 | return $this->source; 116 | } 117 | 118 | public function setDataGridView(DataGridViewInterface $dataGrid): void 119 | { 120 | $this->datagrid = $dataGrid; 121 | } 122 | 123 | public function getDataGridView(): DataGridViewInterface 124 | { 125 | return $this->datagrid; 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /lib/Column/CellViewInterface.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | declare(strict_types=1); 11 | 12 | namespace FSi\Component\DataGrid\Column; 13 | 14 | use FSi\Component\DataGrid\DataGridViewInterface; 15 | 16 | interface CellViewInterface 17 | { 18 | public function hasAttribute(string $name): bool; 19 | 20 | public function setAttribute(string $name, $value): void; 21 | 22 | public function getAttribute(string $name); 23 | 24 | public function getAttributes(): array; 25 | 26 | public function setSource($source): void; 27 | 28 | public function getSource(); 29 | 30 | public function getValue(); 31 | 32 | public function setValue($value): void; 33 | 34 | public function getType(): string; 35 | 36 | public function getName(): string; 37 | 38 | public function setDataGridView(DataGridViewInterface $dataGrid): void; 39 | 40 | public function getDataGridView(): DataGridViewInterface; 41 | } 42 | -------------------------------------------------------------------------------- /lib/Column/ColumnAbstractType.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | declare(strict_types=1); 11 | 12 | namespace FSi\Component\DataGrid\Column; 13 | 14 | use FSi\Component\DataGrid\DataGridInterface; 15 | use FSi\Component\DataGrid\DataMapper\DataMapperInterface; 16 | use FSi\Component\DataGrid\Exception\DataGridColumnException; 17 | use FSi\Component\DataGrid\Exception\UnexpectedTypeException; 18 | use FSi\Component\DataGrid\Exception\UnknownOptionException; 19 | use Symfony\Component\OptionsResolver\OptionsResolver; 20 | 21 | abstract class ColumnAbstractType implements ColumnTypeInterface 22 | { 23 | /** 24 | * @var ColumnTypeExtensionInterface[] 25 | */ 26 | protected $extensions = []; 27 | 28 | /** 29 | * @var array 30 | */ 31 | protected $options = []; 32 | 33 | /** 34 | * @var string 35 | */ 36 | protected $name; 37 | 38 | /** 39 | * This property is used when creating column view. 40 | * After ColumnView is created it is set to null. 41 | * 42 | * @var null|string 43 | */ 44 | protected $index; 45 | 46 | /** 47 | * @var DataMapperInterface 48 | */ 49 | protected $dataMapper; 50 | 51 | /** 52 | * @var DataGridInterface 53 | */ 54 | protected $dataGrid; 55 | 56 | /** 57 | * @var OptionsResolver 58 | */ 59 | private $optionsResolver; 60 | 61 | public function getName(): string 62 | { 63 | if (null === $this->name) { 64 | throw new DataGridColumnException('Use setName method to define column name in data grid'); 65 | } 66 | 67 | return $this->name; 68 | } 69 | 70 | public function setName(string $name): void 71 | { 72 | $this->name = $name; 73 | } 74 | 75 | public function setDataGrid(DataGridInterface $dataGrid): void 76 | { 77 | $this->dataGrid = $dataGrid; 78 | } 79 | 80 | public function getDataGrid(): DataGridInterface 81 | { 82 | return $this->dataGrid; 83 | } 84 | 85 | public function setDataMapper(DataMapperInterface $dataMapper): void 86 | { 87 | $this->dataMapper = $dataMapper; 88 | } 89 | 90 | public function getDataMapper(): DataMapperInterface 91 | { 92 | if (null === $this->dataMapper) { 93 | $this->setDataMapper($this->dataGrid->getDataMapper()); 94 | } 95 | 96 | return $this->dataMapper; 97 | } 98 | 99 | public function getValue($object) 100 | { 101 | $values = []; 102 | if (!$this->hasOption('field_mapping') || !count($this->getOption('field_mapping'))) { 103 | throw new DataGridColumnException( 104 | sprintf('"field_mapping" option is missing in column "%s"', $this->getName()) 105 | ); 106 | } 107 | 108 | foreach ($this->getOption('field_mapping') as $field) { 109 | $values[$field] = $this->getDataMapper()->getData($field, $object); 110 | } 111 | 112 | return $values; 113 | } 114 | 115 | public function createCellView($object, $index): CellViewInterface 116 | { 117 | $this->setIndex($index); 118 | 119 | $view = new CellView($this->getName(), $this->getId()); 120 | $view->setSource($object); 121 | $view->setAttribute('row', $index); 122 | $dataMapper = $this->getDataMapper(); 123 | 124 | if (!$dataMapper instanceof DataMapperInterface) { 125 | throw new UnexpectedTypeException($dataMapper, DataMapperInterface::class); 126 | } 127 | 128 | $values = $this->getValue($object); 129 | 130 | foreach ($this->getExtensions() as $extension) { 131 | $values = $extension->filterValue($this, $values); 132 | } 133 | 134 | $value = $this->filterValue($values); 135 | $view->setValue($value); 136 | 137 | foreach ($this->getExtensions() as $extension) { 138 | $extension->buildCellView($this, $view); 139 | } 140 | 141 | $this->buildCellView($view); 142 | $this->setIndex(null); 143 | 144 | return $view; 145 | } 146 | 147 | public function buildCellView(CellViewInterface $view): void 148 | { 149 | } 150 | 151 | public function createHeaderView(): HeaderViewInterface 152 | { 153 | $view = new HeaderView($this->getName(), $this->getId()); 154 | 155 | foreach ($this->getExtensions() as $extension) { 156 | $extension->buildHeaderView($this, $view); 157 | } 158 | 159 | $this->buildHeaderView($view); 160 | 161 | return $view; 162 | } 163 | 164 | public function buildHeaderView(HeaderViewInterface $view): void 165 | { 166 | } 167 | 168 | public function setOption(string $name, $value): void 169 | { 170 | $this->options = $this->getOptionsResolver()->resolve(array_merge( 171 | is_array($this->options) 172 | ? $this->options 173 | : [], 174 | [$name => $value] 175 | )); 176 | } 177 | 178 | public function setOptions(array $options): void 179 | { 180 | $this->options = $this->getOptionsResolver()->resolve($options); 181 | } 182 | 183 | public function getOption(string $name) 184 | { 185 | if (!array_key_exists($name, $this->options)) { 186 | throw new UnknownOptionException(sprintf('Option "%s" is not available in column type "%s".', $name, $this->getId())); 187 | } 188 | 189 | return $this->options[$name]; 190 | } 191 | 192 | public function hasOption(string $name): bool 193 | { 194 | return array_key_exists($name, $this->options); 195 | } 196 | 197 | public function bindData($data, $object, $index): void 198 | { 199 | foreach ($this->extensions as $extension) { 200 | $extension->bindData($this, $data, $object, $index); 201 | } 202 | } 203 | 204 | public function setExtensions(array $extensions): void 205 | { 206 | foreach ($extensions as $extension) { 207 | if (!$extension instanceof ColumnTypeExtensionInterface) { 208 | throw new UnexpectedTypeException($extension, ColumnTypeExtensionInterface::class); 209 | } 210 | } 211 | 212 | $this->extensions = $extensions; 213 | } 214 | 215 | public function addExtension(ColumnTypeExtensionInterface $extension): void 216 | { 217 | $this->extensions[] = $extension; 218 | } 219 | 220 | public function getExtensions(): array 221 | { 222 | return $this->extensions; 223 | } 224 | 225 | public function getOptionsResolver(): OptionsResolver 226 | { 227 | if (null === $this->optionsResolver) { 228 | $this->optionsResolver = new OptionsResolver(); 229 | } 230 | 231 | return $this->optionsResolver; 232 | } 233 | 234 | public function initOptions(): void 235 | { 236 | } 237 | 238 | /** 239 | * @param int|string|null $index 240 | * @return void 241 | */ 242 | protected function setIndex($index): void 243 | { 244 | $this->index = $index; 245 | } 246 | 247 | /** 248 | * @return int|string|null 249 | */ 250 | protected function getIndex() 251 | { 252 | return $this->index; 253 | } 254 | } 255 | -------------------------------------------------------------------------------- /lib/Column/ColumnAbstractTypeExtension.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | declare(strict_types=1); 11 | 12 | namespace FSi\Component\DataGrid\Column; 13 | 14 | abstract class ColumnAbstractTypeExtension implements ColumnTypeExtensionInterface 15 | { 16 | public function bindData(ColumnTypeInterface $column, $data, $object, $index): void 17 | { 18 | } 19 | 20 | public function buildCellView(ColumnTypeInterface $column, CellViewInterface $view): void 21 | { 22 | } 23 | 24 | public function buildHeaderView(ColumnTypeInterface $column, HeaderViewInterface $view): void 25 | { 26 | } 27 | 28 | public function initOptions(ColumnTypeInterface $column): void 29 | { 30 | } 31 | 32 | public function filterValue(ColumnTypeInterface $column, $value) 33 | { 34 | return $value; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /lib/Column/ColumnTypeExtensionInterface.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | declare(strict_types=1); 11 | 12 | namespace FSi\Component\DataGrid\Column; 13 | 14 | interface ColumnTypeExtensionInterface 15 | { 16 | public function bindData(ColumnTypeInterface $column, $data, $object, $index): void; 17 | 18 | public function buildCellView(ColumnTypeInterface $column, CellViewInterface $view): void; 19 | 20 | public function buildHeaderView(ColumnTypeInterface $column, HeaderViewInterface $view): void; 21 | 22 | public function filterValue(ColumnTypeInterface $column, $value); 23 | 24 | public function initOptions(ColumnTypeInterface $column); 25 | 26 | /** 27 | * @return string[] 28 | */ 29 | public function getExtendedColumnTypes(): array; 30 | } 31 | -------------------------------------------------------------------------------- /lib/Column/ColumnTypeInterface.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | declare(strict_types=1); 11 | 12 | namespace FSi\Component\DataGrid\Column; 13 | 14 | use FSi\Component\DataGrid\DataGridInterface; 15 | use FSi\Component\DataGrid\DataMapper\DataMapperInterface; 16 | use Symfony\Component\OptionsResolver\OptionsResolver; 17 | 18 | interface ColumnTypeInterface 19 | { 20 | public function getId(): string; 21 | 22 | public function getName(): string; 23 | 24 | public function setName(string $name): void; 25 | 26 | public function setDataGrid(DataGridInterface $dataGrid): void; 27 | 28 | public function getDataGrid(): DataGridInterface; 29 | 30 | public function setDataMapper(DataMapperInterface $dataMapper): void; 31 | 32 | public function getDataMapper(): DataMapperInterface; 33 | 34 | public function filterValue($value); 35 | 36 | public function getValue($object); 37 | 38 | public function createCellView($object, $index): CellViewInterface; 39 | 40 | public function buildCellView(CellViewInterface $view): void; 41 | 42 | public function createHeaderView(): HeaderViewInterface; 43 | 44 | public function buildHeaderView(HeaderViewInterface $view): void; 45 | 46 | public function bindData($data, $object, $index): void; 47 | 48 | public function initOptions(): void; 49 | 50 | public function setOption(string $name, $value): void; 51 | 52 | public function setOptions(array $options): void; 53 | 54 | public function getOption(string $name); 55 | 56 | public function hasOption(string $name): bool; 57 | 58 | /** 59 | * @param ColumnTypeExtensionInterface[] $extensions 60 | */ 61 | public function setExtensions(array $extensions): void; 62 | 63 | public function addExtension(ColumnTypeExtensionInterface $extension): void; 64 | 65 | /** 66 | * @return ColumnTypeExtensionInterface[] 67 | */ 68 | public function getExtensions(): array; 69 | 70 | public function getOptionsResolver(): OptionsResolver; 71 | } 72 | -------------------------------------------------------------------------------- /lib/Column/HeaderView.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | declare(strict_types=1); 11 | 12 | namespace FSi\Component\DataGrid\Column; 13 | 14 | use FSi\Component\DataGrid\DataGridViewInterface; 15 | 16 | class HeaderView implements HeaderViewInterface 17 | { 18 | /** 19 | * @var string|null 20 | */ 21 | protected $label; 22 | 23 | /** 24 | * @var string 25 | */ 26 | protected $name; 27 | 28 | /** 29 | * @var string 30 | */ 31 | protected $type; 32 | 33 | /** 34 | * @var array 35 | */ 36 | protected $attributes = []; 37 | 38 | /** 39 | * @var DataGridViewInterface 40 | */ 41 | protected $datagrid; 42 | 43 | public function __construct(string $name, string $type) 44 | { 45 | $this->name = $name; 46 | $this->type = $type; 47 | } 48 | 49 | public function setAttribute(string $name, $value): void 50 | { 51 | $this->attributes[$name] = $value; 52 | } 53 | 54 | public function getAttribute(string $name) 55 | { 56 | if (array_key_exists($name, $this->attributes)) { 57 | return $this->attributes[$name]; 58 | } 59 | 60 | return null; 61 | } 62 | 63 | public function hasAttribute(string $name): bool 64 | { 65 | return array_key_exists($name, $this->attributes); 66 | } 67 | 68 | public function getAttributes(): array 69 | { 70 | return $this->attributes; 71 | } 72 | 73 | public function setLabel(string $label): void 74 | { 75 | $this->label = $label; 76 | } 77 | 78 | public function getLabel(): ?string 79 | { 80 | return $this->label; 81 | } 82 | 83 | public function getName(): string 84 | { 85 | return $this->name; 86 | } 87 | 88 | public function getType(): string 89 | { 90 | return $this->type; 91 | } 92 | 93 | public function setDataGridView(DataGridViewInterface $dataGrid): void 94 | { 95 | $this->datagrid = $dataGrid; 96 | } 97 | 98 | public function getDataGridView(): DataGridViewInterface 99 | { 100 | return $this->datagrid; 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /lib/Column/HeaderViewInterface.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | declare(strict_types=1); 11 | 12 | namespace FSi\Component\DataGrid\Column; 13 | 14 | use FSi\Component\DataGrid\DataGridViewInterface; 15 | 16 | interface HeaderViewInterface 17 | { 18 | public function setAttribute(string $name, $value): void; 19 | 20 | public function getAttribute(string $name); 21 | 22 | public function hasAttribute(string $name): bool; 23 | 24 | public function getAttributes(): array; 25 | 26 | public function getLabel(): ?string; 27 | 28 | public function setLabel(string $value): void; 29 | 30 | public function getName(): string; 31 | 32 | public function getType(): string; 33 | 34 | public function setDataGridView(DataGridViewInterface $dataGrid): void; 35 | 36 | public function getDataGridView(): DataGridViewInterface; 37 | } 38 | -------------------------------------------------------------------------------- /lib/Data/DataRowset.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | declare(strict_types=1); 11 | 12 | namespace FSi\Component\DataGrid\Data; 13 | 14 | use InvalidArgumentException; 15 | use RuntimeException; 16 | 17 | class DataRowset implements DataRowsetInterface 18 | { 19 | /** 20 | * @var array 21 | */ 22 | protected $data = []; 23 | 24 | public function __construct(iterable $data) 25 | { 26 | foreach ($data as $id => $element) { 27 | $this->data[$id] = $element; 28 | } 29 | } 30 | 31 | public function count(): int 32 | { 33 | return count($this->data); 34 | } 35 | 36 | public function current() 37 | { 38 | return current($this->data); 39 | } 40 | 41 | public function key() 42 | { 43 | return key($this->data); 44 | } 45 | 46 | public function next(): void 47 | { 48 | next($this->data); 49 | } 50 | 51 | public function rewind(): void 52 | { 53 | reset($this->data); 54 | } 55 | 56 | public function valid(): bool 57 | { 58 | return $this->key() !== null; 59 | } 60 | 61 | public function offsetExists($offset): bool 62 | { 63 | return array_key_exists($offset, $this->data); 64 | } 65 | 66 | public function offsetGet($offset) 67 | { 68 | if ($this->offsetExists($offset)) { 69 | return $this->data[$offset]; 70 | } 71 | 72 | throw new InvalidArgumentException(sprintf('Row "%s" does not exist in rowset.', $offset)); 73 | } 74 | 75 | public function offsetSet($offset, $value): void 76 | { 77 | throw new RuntimeException('Method not implemented'); 78 | } 79 | 80 | public function offsetUnset($offset): void 81 | { 82 | throw new RuntimeException('Method not implemented'); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /lib/Data/DataRowsetInterface.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | declare(strict_types=1); 11 | 12 | namespace FSi\Component\DataGrid\Data; 13 | 14 | interface DataRowsetInterface extends \Iterator, \Countable, \ArrayAccess 15 | { 16 | } 17 | -------------------------------------------------------------------------------- /lib/DataGridAbstractExtension.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | declare(strict_types=1); 11 | 12 | namespace FSi\Component\DataGrid; 13 | 14 | use FSi\Component\DataGrid\Column\ColumnTypeInterface; 15 | use FSi\Component\DataGrid\Column\ColumnTypeExtensionInterface; 16 | use FSi\Component\DataGrid\Exception\UnexpectedTypeException; 17 | use FSi\Component\DataGrid\Exception\DataGridException; 18 | use Symfony\Component\EventDispatcher\EventSubscriberInterface; 19 | 20 | abstract class DataGridAbstractExtension implements DataGridExtensionInterface 21 | { 22 | /** 23 | * @var ColumnTypeExtensionInterface[][] 24 | */ 25 | protected $columnTypesExtensions; 26 | 27 | /** 28 | * @var ColumnTypeInterface[] 29 | */ 30 | protected $columnTypes; 31 | 32 | public function getColumnType(string $type): ColumnTypeInterface 33 | { 34 | if (null === $this->columnTypes) { 35 | $this->initColumnTypes(); 36 | } 37 | 38 | if (!array_key_exists($type, $this->columnTypes)) { 39 | throw new DataGridException(sprintf( 40 | 'The column type "%s" can not be loaded by this extension', 41 | $type 42 | )); 43 | } 44 | 45 | return $this->columnTypes[$type]; 46 | } 47 | 48 | public function hasColumnType(string $type): bool 49 | { 50 | if (null === $this->columnTypes) { 51 | $this->initColumnTypes(); 52 | } 53 | 54 | return array_key_exists($type, $this->columnTypes); 55 | } 56 | 57 | public function hasColumnTypeExtensions(string $type): bool 58 | { 59 | if (null === $this->columnTypesExtensions) { 60 | $this->initColumnTypesExtensions(); 61 | } 62 | 63 | return array_key_exists($type, $this->columnTypesExtensions); 64 | } 65 | 66 | public function getColumnTypeExtensions(string $type): array 67 | { 68 | if (null === $this->columnTypesExtensions) { 69 | $this->initColumnTypesExtensions(); 70 | } 71 | 72 | if (!array_key_exists($type, $this->columnTypesExtensions)) { 73 | throw new DataGridException(sprintf( 74 | 'Extension for column type "%s" can not be loaded by this data grid extension', 75 | $type 76 | )); 77 | } 78 | 79 | return $this->columnTypesExtensions[$type]; 80 | } 81 | 82 | public function registerSubscribers(DataGridInterface $dataGrid): void 83 | { 84 | $subscribers = $this->loadSubscribers(); 85 | 86 | foreach ($subscribers as $subscriber) { 87 | if (!$subscriber instanceof EventSubscriberInterface) { 88 | throw new UnexpectedTypeException(sprintf( 89 | '"%s" is not instance of "%s"', 90 | $subscriber, 91 | EventSubscriberInterface::class 92 | )); 93 | } 94 | 95 | $dataGrid->addEventSubscriber($subscriber); 96 | } 97 | } 98 | 99 | /** 100 | * @return ColumnTypeInterface[] 101 | */ 102 | protected function loadColumnTypes(): array 103 | { 104 | return []; 105 | } 106 | 107 | /** 108 | * @return EventSubscriberInterface[] 109 | */ 110 | protected function loadSubscribers(): array 111 | { 112 | return []; 113 | } 114 | 115 | /** 116 | * @return ColumnTypeExtensionInterface[] 117 | */ 118 | protected function loadColumnTypesExtensions(): array 119 | { 120 | return []; 121 | } 122 | 123 | private function initColumnTypes(): void 124 | { 125 | $this->columnTypes = []; 126 | 127 | $columnTypes = $this->loadColumnTypes(); 128 | 129 | foreach ($columnTypes as $columnType) { 130 | if (!$columnType instanceof ColumnTypeInterface) { 131 | throw new UnexpectedTypeException(sprintf( 132 | 'Column type must implement "%s"', 133 | ColumnTypeInterface::class 134 | )); 135 | } 136 | 137 | $this->columnTypes[$columnType->getId()] = $columnType; 138 | } 139 | } 140 | 141 | private function initColumnTypesExtensions(): void 142 | { 143 | $columnTypesExtensions = $this->loadColumnTypesExtensions(); 144 | $this->columnTypesExtensions = []; 145 | 146 | foreach ($columnTypesExtensions as $extension) { 147 | if (!$extension instanceof ColumnTypeExtensionInterface) { 148 | throw new UnexpectedTypeException(sprintf( 149 | 'Extension must implement %s', 150 | ColumnTypeExtensionInterface::class 151 | )); 152 | } 153 | 154 | $types = $extension->getExtendedColumnTypes(); 155 | foreach ($types as $type) { 156 | if (!array_key_exists($type, $this->columnTypesExtensions)) { 157 | $this->columnTypesExtensions[$type] = []; 158 | } 159 | 160 | $this->columnTypesExtensions[$type][] = $extension; 161 | } 162 | } 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /lib/DataGridEvent.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | declare(strict_types=1); 11 | 12 | namespace FSi\Component\DataGrid; 13 | 14 | use FSi\Component\DataGrid\DataGridInterface; 15 | use FSi\Component\DataGrid\DataGridEventInterface; 16 | use Symfony\Component\EventDispatcher\Event; 17 | 18 | class DataGridEvent extends Event implements DataGridEventInterface 19 | { 20 | /** 21 | * @var DataGridInterface 22 | */ 23 | protected $dataGrid; 24 | 25 | /** 26 | * @var mixed 27 | */ 28 | protected $data; 29 | 30 | public function __construct(DataGridInterface $dataGrid, $data) 31 | { 32 | $this->dataGrid = $dataGrid; 33 | $this->data = $data; 34 | } 35 | 36 | public function getDataGrid(): DataGridInterface 37 | { 38 | return $this->dataGrid; 39 | } 40 | 41 | public function getData() 42 | { 43 | return $this->data; 44 | } 45 | 46 | public function setData($data): void 47 | { 48 | $this->data = $data; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /lib/DataGridEventInterface.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | declare(strict_types=1); 11 | 12 | namespace FSi\Component\DataGrid; 13 | 14 | interface DataGridEventInterface 15 | { 16 | public function getDataGrid(): DataGridInterface; 17 | 18 | public function getData(); 19 | 20 | public function setData($data): void; 21 | } 22 | -------------------------------------------------------------------------------- /lib/DataGridEvents.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | declare(strict_types=1); 11 | 12 | namespace FSi\Component\DataGrid; 13 | 14 | final class DataGridEvents 15 | { 16 | public const PRE_SET_DATA = 'datagrid.pre_set_data'; 17 | 18 | public const POST_SET_DATA = 'datagrid.post_set_data'; 19 | 20 | public const PRE_BIND_DATA = 'datagrid.pre_bind_data'; 21 | 22 | public const POST_BIND_DATA = 'datagrid.post_bind_data'; 23 | 24 | public const PRE_BUILD_VIEW = 'datagrid.pre_build_view'; 25 | 26 | public const POST_BUILD_VIEW = 'datagrid.post_build_view'; 27 | } 28 | -------------------------------------------------------------------------------- /lib/DataGridExtensionInterface.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | declare(strict_types=1); 11 | 12 | namespace FSi\Component\DataGrid; 13 | 14 | use FSi\Component\DataGrid\Column\ColumnTypeExtensionInterface; 15 | use FSi\Component\DataGrid\Column\ColumnTypeInterface; 16 | 17 | interface DataGridExtensionInterface 18 | { 19 | public function registerSubscribers(DataGridInterface $dataGrid): void; 20 | 21 | public function hasColumnType(string $type): bool; 22 | 23 | public function getColumnType(string $type): ColumnTypeInterface; 24 | 25 | public function hasColumnTypeExtensions(string $type): bool; 26 | 27 | /** 28 | * @param string $type 29 | * @return ColumnTypeExtensionInterface[] 30 | */ 31 | public function getColumnTypeExtensions(string $type): array; 32 | } 33 | -------------------------------------------------------------------------------- /lib/DataGridFactory.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | declare(strict_types=1); 11 | 12 | namespace FSi\Component\DataGrid; 13 | 14 | use FSi\Component\DataGrid\Column\ColumnTypeInterface; 15 | use FSi\Component\DataGrid\Exception\DataGridColumnException; 16 | use FSi\Component\DataGrid\Exception\UnexpectedTypeException; 17 | use FSi\Component\DataGrid\DataMapper\DataMapperInterface; 18 | use InvalidArgumentException; 19 | 20 | class DataGridFactory implements DataGridFactoryInterface 21 | { 22 | /** 23 | * @var DataGridInterface[] 24 | */ 25 | protected $dataGrids = []; 26 | 27 | /** 28 | * @var ColumnTypeInterface[] 29 | */ 30 | protected $columnTypes = []; 31 | 32 | /** 33 | * @var DataMapperInterface 34 | */ 35 | protected $dataMapper; 36 | 37 | /** 38 | * @var DataGridExtensionInterface[] 39 | */ 40 | protected $extensions = []; 41 | 42 | /** 43 | * @param DataGridExtensionInterface[] $extensions 44 | * @param DataMapperInterface $dataMapper 45 | * @throws InvalidArgumentException 46 | */ 47 | public function __construct(array $extensions, DataMapperInterface $dataMapper) 48 | { 49 | foreach ($extensions as $extension) { 50 | if (!$extension instanceof DataGridExtensionInterface) { 51 | throw new InvalidArgumentException(sprintf( 52 | 'Each extension must implement "%s"', 53 | DataGridExtensionInterface::class 54 | )); 55 | } 56 | } 57 | 58 | $this->dataMapper = $dataMapper; 59 | $this->extensions = $extensions; 60 | } 61 | 62 | public function createDataGrid(string $name = 'grid'): DataGridInterface 63 | { 64 | if (array_key_exists($name, $this->dataGrids)) { 65 | throw new DataGridColumnException(sprintf( 66 | 'Datagrid name "%s" is not uniqe, it was used before to create datagrid', 67 | $name 68 | )); 69 | } 70 | 71 | $this->dataGrids[$name] = new DataGrid($name, $this, $this->dataMapper); 72 | 73 | return $this->dataGrids[$name]; 74 | } 75 | 76 | public function hasColumnType(string $type): bool 77 | { 78 | if (array_key_exists($type, $this->columnTypes)) { 79 | return true; 80 | } 81 | 82 | try { 83 | $this->loadColumnType($type); 84 | } catch (UnexpectedTypeException $e) { 85 | return false; 86 | } 87 | 88 | return true; 89 | } 90 | 91 | /** 92 | * @param string $type 93 | * @return ColumnTypeInterface 94 | * @throws UnexpectedTypeException 95 | */ 96 | public function getColumnType(string $type): ColumnTypeInterface 97 | { 98 | if ($this->hasColumnType($type)) { 99 | return clone $this->columnTypes[$type]; 100 | } 101 | 102 | $this->loadColumnType($type); 103 | 104 | return clone $this->columnTypes[$type]; 105 | } 106 | 107 | public function getExtensions(): array 108 | { 109 | return $this->extensions; 110 | } 111 | 112 | public function getDataMapper(): DataMapperInterface 113 | { 114 | return $this->dataMapper; 115 | } 116 | 117 | /** 118 | * @param string $type 119 | * @throws UnexpectedTypeException 120 | */ 121 | private function loadColumnType(string $type): void 122 | { 123 | if (isset($this->columnTypes[$type])) { 124 | return; 125 | } 126 | 127 | $typeInstance = null; 128 | foreach ($this->extensions as $extension) { 129 | if ($extension->hasColumnType($type)) { 130 | $typeInstance = $extension->getColumnType($type); 131 | break; 132 | } 133 | } 134 | 135 | if (null === $typeInstance) { 136 | throw new UnexpectedTypeException(sprintf( 137 | 'There is no column with type "%s" registered in factory.', 138 | $type 139 | )); 140 | } 141 | 142 | foreach ($this->extensions as $extension) { 143 | if ($extension->hasColumnTypeExtensions($type)) { 144 | $columnExtensions = $extension->getColumnTypeExtensions($type); 145 | foreach ($columnExtensions as $columnExtension) { 146 | $typeInstance->addExtension($columnExtension); 147 | } 148 | } 149 | } 150 | 151 | $this->columnTypes[$type] = $typeInstance; 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /lib/DataGridFactoryInterface.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | declare(strict_types=1); 11 | 12 | namespace FSi\Component\DataGrid; 13 | 14 | use FSi\Component\DataGrid\Column\ColumnTypeInterface; 15 | use FSi\Component\DataGrid\DataMapper\DataMapperInterface; 16 | 17 | interface DataGridFactoryInterface 18 | { 19 | public function hasColumnType(string $type): bool; 20 | 21 | public function getColumnType(string $type): ColumnTypeInterface; 22 | 23 | /** 24 | * @return DataGridExtensionInterface[] 25 | */ 26 | public function getExtensions(): array; 27 | 28 | public function createDataGrid(string $name = 'grid'): DataGridInterface; 29 | 30 | public function getDataMapper(): DataMapperInterface; 31 | } 32 | -------------------------------------------------------------------------------- /lib/DataGridInterface.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | declare(strict_types=1); 11 | 12 | namespace FSi\Component\DataGrid; 13 | 14 | use FSi\Component\DataGrid\Column\ColumnTypeInterface; 15 | use FSi\Component\DataGrid\DataMapper\DataMapperInterface; 16 | use Symfony\Component\EventDispatcher\EventSubscriberInterface; 17 | 18 | interface DataGridInterface 19 | { 20 | public function getName(): string; 21 | 22 | public function getDataMapper(): DataMapperInterface; 23 | 24 | public function addColumn($name, string $type = 'text', array $options = []): DataGridInterface; 25 | 26 | public function removeColumn(string $name): DataGridInterface; 27 | 28 | public function clearColumns(): DataGridInterface; 29 | 30 | public function getColumn(string $name): ColumnTypeInterface; 31 | 32 | /** 33 | * @return ColumnTypeInterface[] 34 | */ 35 | public function getColumns(): array; 36 | 37 | public function hasColumn(string $name): bool; 38 | 39 | public function hasColumnType(string $type): bool; 40 | 41 | public function createView(): DataGridViewInterface; 42 | 43 | public function setData(iterable $data): void; 44 | 45 | public function bindData($data): void; 46 | 47 | public function addEventListener(string $eventName, callable $listener, int $priority = 0): DataGridInterface; 48 | 49 | public function addEventSubscriber(EventSubscriberInterface $subscriber): DataGridInterface; 50 | } 51 | -------------------------------------------------------------------------------- /lib/DataGridRowView.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | declare(strict_types=1); 11 | 12 | namespace FSi\Component\DataGrid; 13 | 14 | use FSi\Component\DataGrid\Column\CellViewInterface; 15 | use FSi\Component\DataGrid\Column\ColumnTypeInterface; 16 | use FSi\Component\DataGrid\Exception\UnexpectedTypeException; 17 | use InvalidArgumentException; 18 | use RuntimeException; 19 | 20 | class DataGridRowView implements DataGridRowViewInterface 21 | { 22 | /** 23 | * @var CellViewInterface[] 24 | */ 25 | protected $cellViews = []; 26 | 27 | /** 28 | * @var mixed 29 | */ 30 | protected $source; 31 | 32 | /** 33 | * @var int 34 | */ 35 | protected $index; 36 | 37 | /** 38 | * @param DataGridViewInterface $dataGridView 39 | * @param ColumnTypeInterface[] $columns 40 | * @param mixed $source 41 | * @param int $index 42 | * @throws Exception\UnexpectedTypeException 43 | */ 44 | public function __construct(DataGridViewInterface $dataGridView, array $columns, $source, $index) 45 | { 46 | $this->source = $source; 47 | $this->index = $index; 48 | foreach ($columns as $name => $column) { 49 | if (!$column instanceof ColumnTypeInterface) { 50 | throw new UnexpectedTypeException(sprintf( 51 | 'Column object must implement "%s"', 52 | ColumnTypeInterface::class 53 | )); 54 | } 55 | 56 | $cellView = $column->createCellView($this->source, $index); 57 | $cellView->setDataGridView($dataGridView); 58 | 59 | $this->cellViews[$name] = $cellView; 60 | } 61 | } 62 | 63 | public function getIndex() 64 | { 65 | return $this->index; 66 | } 67 | 68 | public function getSource() 69 | { 70 | return $this->source; 71 | } 72 | 73 | public function count(): int 74 | { 75 | return count($this->cellViews); 76 | } 77 | 78 | public function current(): CellViewInterface 79 | { 80 | return current($this->cellViews); 81 | } 82 | 83 | public function key(): ?string 84 | { 85 | return key($this->cellViews); 86 | } 87 | 88 | public function next(): void 89 | { 90 | next($this->cellViews); 91 | } 92 | 93 | public function rewind(): void 94 | { 95 | reset($this->cellViews); 96 | } 97 | 98 | public function valid(): bool 99 | { 100 | return $this->key() !== null; 101 | } 102 | 103 | public function offsetExists($offset): bool 104 | { 105 | return array_key_exists($offset, $this->cellViews); 106 | } 107 | 108 | public function offsetGet($offset): CellViewInterface 109 | { 110 | if ($this->offsetExists($offset)) { 111 | return $this->cellViews[$offset]; 112 | } 113 | 114 | throw new InvalidArgumentException(sprintf('Column "%s" does not exist in row.', $offset)); 115 | } 116 | 117 | public function offsetSet($offset, $value): void 118 | { 119 | throw new RuntimeException('Method not implemented'); 120 | } 121 | 122 | public function offsetUnset($offset): void 123 | { 124 | throw new RuntimeException('Method not implemented'); 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /lib/DataGridRowViewInterface.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | declare(strict_types=1); 11 | 12 | namespace FSi\Component\DataGrid; 13 | 14 | interface DataGridRowViewInterface extends \Iterator, \Countable, \ArrayAccess 15 | { 16 | public function getIndex(); 17 | 18 | public function getSource(); 19 | } 20 | -------------------------------------------------------------------------------- /lib/DataGridView.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | declare(strict_types=1); 11 | 12 | namespace FSi\Component\DataGrid; 13 | 14 | use FSi\Component\DataGrid\Data\DataRowsetInterface; 15 | use FSi\Component\DataGrid\Column\ColumnTypeInterface; 16 | use FSi\Component\DataGrid\Column\HeaderViewInterface; 17 | use RuntimeException; 18 | 19 | class DataGridView implements DataGridViewInterface 20 | { 21 | /** 22 | * @var ColumnTypeInterface[] 23 | */ 24 | protected $columns = []; 25 | 26 | /** 27 | * @var HeaderViewInterface[] 28 | */ 29 | protected $columnsHeaders = []; 30 | 31 | /** 32 | * @var string 33 | */ 34 | protected $name; 35 | 36 | /** 37 | * @var DataRowsetInterface 38 | */ 39 | protected $rowset; 40 | 41 | /** 42 | * @param string $name 43 | * @param ColumnTypeInterface[] $columns 44 | * @param DataRowsetInterface $rowset 45 | * @throws \InvalidArgumentException 46 | */ 47 | public function __construct(string $name, array $columns = [], DataRowsetInterface $rowset) 48 | { 49 | foreach ($columns as $column) { 50 | if (!$column instanceof ColumnTypeInterface) { 51 | throw new \InvalidArgumentException('Column must implement FSi\Component\DataGrid\Column\ColumnTypeInterface'); 52 | } 53 | 54 | $this->columns[$column->getName()] = $column; 55 | $headerView = $column->createHeaderView(); 56 | $headerView->setDataGridView($this); 57 | $this->columnsHeaders[$column->getName()] = $headerView; 58 | } 59 | 60 | $this->name = $name; 61 | $this->rowset = $rowset; 62 | } 63 | 64 | public function getName(): string 65 | { 66 | return $this->name; 67 | } 68 | 69 | public function hasColumn(string $name): bool 70 | { 71 | return array_key_exists($name, $this->columnsHeaders); 72 | } 73 | 74 | public function hasColumnType(string $type): bool 75 | { 76 | foreach ($this->columnsHeaders as $header) { 77 | if ($header->getType() === $type) { 78 | return true; 79 | } 80 | } 81 | 82 | return false; 83 | } 84 | 85 | public function removeColumn(string $name): void 86 | { 87 | if (array_key_exists($name, $this->columnsHeaders)) { 88 | unset($this->columnsHeaders[$name]); 89 | } 90 | } 91 | 92 | public function getColumn(string $name): HeaderViewInterface 93 | { 94 | if ($this->hasColumn($name)) { 95 | return $this->columnsHeaders[$name]; 96 | } 97 | 98 | throw new \InvalidArgumentException(sprintf('Column "%s" does not exist in data grid.', $name)); 99 | } 100 | 101 | public function getColumns(): array 102 | { 103 | return $this->columnsHeaders; 104 | } 105 | 106 | public function clearColumns(): void 107 | { 108 | $this->columnsHeaders = []; 109 | } 110 | 111 | public function addColumn(HeaderViewInterface $column): void 112 | { 113 | if (!array_key_exists($column->getName(), $this->columns)) { 114 | throw new \InvalidArgumentException(sprintf( 115 | 'Column with name "%s" was never registred in datagrid "%s"', 116 | $column->getName(), 117 | $this->getName() 118 | )); 119 | } 120 | 121 | $this->columnsHeaders[$column->getName()] = $column; 122 | } 123 | 124 | public function setColumns(array $columns): void 125 | { 126 | $this->columnsHeaders = []; 127 | 128 | foreach ($columns as $column) { 129 | if (!$column instanceof HeaderViewInterface) { 130 | throw new \InvalidArgumentException('Column must implement FSi\Component\DataGrid\Column\HeaderViewInterface'); 131 | } 132 | 133 | if (!array_key_exists($column->getName(), $this->columns)) { 134 | throw new \InvalidArgumentException(sprintf( 135 | 'Column with name "%s" was never registred in datagrid "%s"', 136 | $column->getName(), 137 | $this->getName() 138 | )); 139 | } 140 | 141 | $this->columnsHeaders[$column->getName()] = $column; 142 | } 143 | } 144 | 145 | public function count(): int 146 | { 147 | return $this->rowset->count(); 148 | } 149 | 150 | /** 151 | * @return string[] 152 | */ 153 | public function getIndexes(): array 154 | { 155 | $indexes = []; 156 | foreach ($this->rowset as $index => $row) { 157 | $indexes[] = $index; 158 | } 159 | 160 | return $indexes; 161 | } 162 | 163 | public function current(): DataGridRowViewInterface 164 | { 165 | $index = $this->rowset->key(); 166 | 167 | return new DataGridRowView($this, $this->getOriginColumns(), $this->rowset->current(), $index); 168 | } 169 | 170 | public function key() 171 | { 172 | return $this->rowset->key(); 173 | } 174 | 175 | public function next(): void 176 | { 177 | $this->rowset->next(); 178 | } 179 | 180 | public function rewind(): void 181 | { 182 | $this->rowset->rewind(); 183 | } 184 | 185 | public function valid(): bool 186 | { 187 | return $this->rowset->valid(); 188 | } 189 | 190 | public function offsetExists($offset): bool 191 | { 192 | return isset($this->rowset[$offset]); 193 | } 194 | 195 | public function offsetGet($offset) 196 | { 197 | if ($this->offsetExists($offset)) { 198 | return new DataGridRowView($this, $this->getOriginColumns(), $this->rowset[$offset], $offset); 199 | } 200 | 201 | throw new \InvalidArgumentException(sprintf('Row "%s" does not exist in rowset.', $offset)); 202 | } 203 | 204 | public function offsetSet($offset, $value): void 205 | { 206 | throw new RuntimeException('Method not implemented'); 207 | } 208 | 209 | public function offsetUnset($offset): void 210 | { 211 | throw new RuntimeException('Method not implemented'); 212 | } 213 | 214 | /** 215 | * @return ColumnTypeInterface[] 216 | */ 217 | protected function getOriginColumns(): array 218 | { 219 | $columns = []; 220 | foreach ($this->columnsHeaders as $name => $header) { 221 | $columns[$name] = $this->columns[$name]; 222 | } 223 | 224 | return $columns; 225 | } 226 | } 227 | -------------------------------------------------------------------------------- /lib/DataGridViewInterface.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | declare(strict_types=1); 11 | 12 | namespace FSi\Component\DataGrid; 13 | 14 | use FSi\Component\DataGrid\Column\HeaderViewInterface; 15 | 16 | interface DataGridViewInterface extends \Iterator, \Countable, \ArrayAccess 17 | { 18 | public function getName(): string; 19 | 20 | public function hasColumn(string $name): bool; 21 | 22 | public function hasColumnType(string $type): bool; 23 | 24 | public function removeColumn(string $name): void; 25 | 26 | public function getColumn(string $name): HeaderViewInterface; 27 | 28 | /** 29 | * @return HeaderViewInterface[] 30 | */ 31 | public function getColumns(): array; 32 | 33 | public function clearColumns(): void; 34 | 35 | public function addColumn(HeaderViewInterface $column): void; 36 | 37 | /** 38 | * @param HeaderViewInterface[] $columns 39 | */ 40 | public function setColumns(array $columns): void; 41 | } 42 | -------------------------------------------------------------------------------- /lib/DataMapper/ChainMapper.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | declare(strict_types=1); 11 | 12 | namespace FSi\Component\DataGrid\DataMapper; 13 | 14 | use FSi\Component\DataGrid\Exception\DataMappingException; 15 | use InvalidArgumentException; 16 | 17 | class ChainMapper implements DataMapperInterface 18 | { 19 | /** 20 | * @var DataMapperInterface[] 21 | */ 22 | protected $mappers = []; 23 | 24 | /** 25 | * @param DataMapperInterface[] $mappers 26 | * @throws InvalidArgumentException 27 | */ 28 | public function __construct(array $mappers) 29 | { 30 | if (!count($mappers)) { 31 | throw new InvalidArgumentException('There must be at least one mapper in chain.'); 32 | } 33 | 34 | foreach ($mappers as $mapper) { 35 | if (!$mapper instanceof DataMapperInterface) { 36 | throw new InvalidArgumentException( 37 | sprintf('Mapper needs to implement "%s"', DataMapperInterface::class) 38 | ); 39 | } 40 | 41 | $this->mappers[] = $mapper; 42 | } 43 | } 44 | 45 | /** 46 | * @inheritdoc 47 | */ 48 | public function getData(string $field, $object) 49 | { 50 | $data = null; 51 | $dataFound = false; 52 | $lastMsg = null; 53 | 54 | foreach ($this->mappers as $mapper) { 55 | try { 56 | $data = $mapper->getData($field, $object); 57 | } catch (DataMappingException $e) { 58 | $data = null; 59 | $lastMsg = $e->getMessage(); 60 | 61 | continue; 62 | } 63 | 64 | $dataFound = true; 65 | break; 66 | } 67 | 68 | if (!$dataFound) { 69 | if (null === $lastMsg) { 70 | $lastMsg = sprintf('Cant find any data that fit "%s" field.', $field); 71 | } 72 | 73 | throw new DataMappingException($lastMsg); 74 | } 75 | 76 | return $data; 77 | } 78 | 79 | /** 80 | * @inheritdoc 81 | */ 82 | public function setData(string $field, $object, $value): void 83 | { 84 | $dataChanged = false; 85 | $lastMsg = null; 86 | 87 | foreach ($this->mappers as $mapper) { 88 | try { 89 | $mapper->setData($field, $object, $value); 90 | } catch (DataMappingException $e) { 91 | $lastMsg = $e->getMessage(); 92 | continue; 93 | } 94 | 95 | $dataChanged = true; 96 | break; 97 | } 98 | 99 | if (!$dataChanged) { 100 | if (!isset($lastMsg)) { 101 | $lastMsg = sprintf('Cant find any data that fit "%s" field.', $field); 102 | } 103 | 104 | throw new DataMappingException($lastMsg); 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /lib/DataMapper/DataMapperInterface.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | declare(strict_types=1); 11 | 12 | namespace FSi\Component\DataGrid\DataMapper; 13 | 14 | use FSi\Component\DataGrid\Exception\DataMappingException; 15 | 16 | interface DataMapperInterface 17 | { 18 | /** 19 | * @param string $field 20 | * @param mixed $object 21 | * @return mixed 22 | * @throws DataMappingException 23 | */ 24 | public function getData(string $field, $object); 25 | 26 | /** 27 | * @param string $field 28 | * @param mixed $object 29 | * @param mixed $value 30 | * @throws DataMappingException 31 | */ 32 | public function setData(string $field, $object, $value): void; 33 | } 34 | -------------------------------------------------------------------------------- /lib/DataMapper/PropertyAccessorMapper.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | declare(strict_types=1); 11 | 12 | namespace FSi\Component\DataGrid\DataMapper; 13 | 14 | use FSi\Component\DataGrid\Exception\DataMappingException; 15 | use Symfony\Component\PropertyAccess\Exception\RuntimeException; 16 | use Symfony\Component\PropertyAccess\PropertyAccess; 17 | 18 | class PropertyAccessorMapper implements DataMapperInterface 19 | { 20 | /** 21 | * @inheritdoc 22 | */ 23 | public function getData(string $field, $object) 24 | { 25 | $accessor = PropertyAccess::createPropertyAccessor(); 26 | 27 | try { 28 | $data = $accessor->getValue($object, $field); 29 | } catch (RuntimeException $e) { 30 | throw new DataMappingException($e->getMessage()); 31 | } 32 | 33 | return $data; 34 | } 35 | 36 | /** 37 | * @inheritdoc 38 | */ 39 | public function setData(string $field, $object, $value): void 40 | { 41 | $accessor = PropertyAccess::createPropertyAccessor(); 42 | 43 | try { 44 | $accessor->setValue($object, $field, $value); 45 | } catch (RuntimeException $e) { 46 | throw new DataMappingException($e->getMessage()); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /lib/DataMapper/ReflectionMapper.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | declare(strict_types=1); 11 | 12 | namespace FSi\Component\DataGrid\DataMapper; 13 | 14 | use FSi\Component\Reflection\ReflectionClass; 15 | use FSi\Component\DataGrid\Exception\DataMappingException; 16 | 17 | class ReflectionMapper implements DataMapperInterface 18 | { 19 | /** 20 | * @inheritdoc 21 | */ 22 | public function getData(string $field, $object) 23 | { 24 | if (!is_object($object)) { 25 | throw new DataMappingException('Reflection mapper needs object to retrieve data.'); 26 | } 27 | 28 | $objectReflection = ReflectionClass::factory($object); 29 | $camelField = $this->camelize($field); 30 | $getter = 'get' . $camelField; 31 | $isser = 'is' . $camelField; 32 | $hasser = 'has' . $camelField; 33 | 34 | if ($objectReflection->hasMethod($getter)) { 35 | if (!$objectReflection->getMethod($getter)->isPublic()) { 36 | throw new DataMappingException(sprintf('Method "%s()" is not public in class "%s"', $getter, $objectReflection->name)); 37 | } 38 | 39 | return $object->$getter(); 40 | } 41 | 42 | if ($objectReflection->hasMethod($isser)) { 43 | if (!$objectReflection->getMethod($isser)->isPublic()) { 44 | throw new DataMappingException(sprintf('Method "%s()" is not public in class "%s"', $isser, $objectReflection->name)); 45 | } 46 | 47 | return $object->$isser(); 48 | } 49 | 50 | if ($objectReflection->hasMethod($hasser)) { 51 | if (!$objectReflection->getMethod($hasser)->isPublic()) { 52 | throw new DataMappingException(sprintf('Method "%s()" is not public in class "%s"', $hasser, $objectReflection->name)); 53 | } 54 | 55 | return $object->$hasser(); 56 | } 57 | 58 | if ($objectReflection->hasProperty($field)) { 59 | if (!$objectReflection->getProperty($field)->isPublic()) { 60 | throw new DataMappingException(sprintf('Property "%s" is not public in class "%s". Maybe you should create the method "%s()" or "%s()"?', $field, $objectReflection->name, $getter, $isser)); 61 | } 62 | $property = $objectReflection->getProperty($field); 63 | return $property->getValue($object); 64 | } 65 | 66 | throw new DataMappingException(sprintf('Neither property "%s" nor method "%s()" nor method "%s()" exists in class "%s"', $field, $getter, $isser, $objectReflection->name)); 67 | } 68 | 69 | /** 70 | * @inheritdoc 71 | */ 72 | public function setData(string $field, $object, $value): void 73 | { 74 | if (!is_object($object)) { 75 | throw new DataMappingException('Reflection mapper needs object to retrieve data.'); 76 | } 77 | 78 | $objectReflection = ReflectionClass::factory($object); 79 | $camelField = $this->camelize($field); 80 | $setter = 'set' . $camelField; 81 | $adder = 'add' . $camelField; 82 | 83 | if ($objectReflection->hasMethod($setter)) { 84 | if (!$objectReflection->getMethod($setter)->isPublic()) { 85 | throw new DataMappingException(sprintf( 86 | 'Method "%s()" is not public in class "%s"', 87 | $setter, 88 | $objectReflection->name 89 | )); 90 | } 91 | 92 | $object->$setter($value); 93 | 94 | return; 95 | } 96 | 97 | if ($objectReflection->hasMethod($adder)) { 98 | if (!$objectReflection->getMethod($adder)->isPublic()) { 99 | throw new DataMappingException(sprintf( 100 | 'Method "%s()" is not public in class "%s"', 101 | $adder, 102 | $objectReflection->name 103 | )); 104 | } 105 | 106 | $object->$adder($value); 107 | 108 | return; 109 | } 110 | 111 | if ($objectReflection->hasProperty($field)) { 112 | if (!$objectReflection->getProperty($field)->isPublic()) { 113 | throw new DataMappingException(sprintf( 114 | 'Property "%s" is not public in class "%s". Maybe you should create method "%s()" or "%s()"?', 115 | $field, 116 | $objectReflection->name, 117 | $setter, 118 | $adder 119 | )); 120 | } 121 | 122 | $property = $objectReflection->getProperty($field); 123 | $property->setValue($object, $value); 124 | 125 | return; 126 | } 127 | 128 | throw new DataMappingException(sprintf( 129 | 'Neither property "%s" nor method "%s()" exists in class "%s"', 130 | $setter, 131 | $adder, 132 | $objectReflection->name 133 | )); 134 | } 135 | 136 | private function camelize(string $string): string 137 | { 138 | return preg_replace_callback('/(^|_|\.)+(.)/', function ($match) { 139 | return ('.' === $match[1] ? '_' : '').strtoupper($match[2]); 140 | }, $string); 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /lib/Exception/DataGridColumnException.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | declare(strict_types=1); 11 | 12 | namespace FSi\Component\DataGrid\Exception; 13 | 14 | class DataGridColumnException extends DataGridException 15 | { 16 | } 17 | -------------------------------------------------------------------------------- /lib/Exception/DataGridException.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | declare(strict_types=1); 11 | 12 | namespace FSi\Component\DataGrid\Exception; 13 | 14 | class DataGridException extends \Exception 15 | { 16 | } 17 | -------------------------------------------------------------------------------- /lib/Exception/DataMappingException.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | declare(strict_types=1); 11 | 12 | namespace FSi\Component\DataGrid\Exception; 13 | 14 | class DataMappingException extends DataGridException 15 | { 16 | } 17 | -------------------------------------------------------------------------------- /lib/Exception/UnexpectedTypeException.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | declare(strict_types=1); 11 | 12 | namespace FSi\Component\DataGrid\Exception; 13 | 14 | class UnexpectedTypeException extends DataGridException 15 | { 16 | } 17 | -------------------------------------------------------------------------------- /lib/Exception/UnknownOptionException.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | declare(strict_types=1); 11 | 12 | namespace FSi\Component\DataGrid\Exception; 13 | 14 | class UnknownOptionException extends DataGridException 15 | { 16 | } 17 | -------------------------------------------------------------------------------- /lib/Extension/Core/ColumnType/Action.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | declare(strict_types=1); 11 | 12 | namespace FSi\Component\DataGrid\Extension\Core\ColumnType; 13 | 14 | use FSi\Component\DataGrid\Column\ColumnAbstractType; 15 | use Symfony\Component\OptionsResolver\OptionsResolver; 16 | use Symfony\Component\OptionsResolver\Options; 17 | 18 | class Action extends ColumnAbstractType 19 | { 20 | /** 21 | * @var OptionsResolver 22 | */ 23 | protected $actionOptionsResolver; 24 | 25 | public function __construct() 26 | { 27 | $this->actionOptionsResolver = new OptionsResolver(); 28 | } 29 | 30 | public function getId(): string 31 | { 32 | return 'action'; 33 | } 34 | 35 | public function filterValue($value) 36 | { 37 | $return = []; 38 | $actions = $this->getOption('actions'); 39 | 40 | foreach ($actions as $name => $options) { 41 | $options = $this->actionOptionsResolver->resolve((array) $options); 42 | $return[$name] = []; 43 | 44 | $url = (isset($options['protocol'], $options['domain'])) ? $options['protocol'] . $options['domain'] : ''; 45 | $url .= vsprintf ($options['uri_scheme'], $value); 46 | 47 | if (isset($options['redirect_uri']) && is_string($options['redirect_uri'])) { 48 | if (strpos($url, '?') !== false) { 49 | $url .= '&redirect_uri=' . urlencode($options['redirect_uri']); 50 | } else { 51 | $url .= '?redirect_uri=' . urlencode($options['redirect_uri']); 52 | } 53 | } 54 | 55 | $return[$name]['url'] = $url; 56 | $return[$name]['field_mapping_values'] = $value; 57 | } 58 | 59 | return $return; 60 | } 61 | 62 | public function initOptions(): void 63 | { 64 | $this->getOptionsResolver()->setDefaults([ 65 | 'actions' => [], 66 | ]); 67 | 68 | $this->getOptionsResolver()->setAllowedTypes('actions', 'array'); 69 | 70 | $this->actionOptionsResolver->setDefaults([ 71 | 'redirect_uri' => null, 72 | 'domain' => null, 73 | 'protocol' => 'http://' 74 | ]); 75 | 76 | $this->actionOptionsResolver->setRequired([ 77 | 'uri_scheme' 78 | ]); 79 | 80 | $this->actionOptionsResolver->setAllowedTypes('redirect_uri', ['string', 'null']); 81 | $this->actionOptionsResolver->setAllowedTypes('uri_scheme', 'string'); 82 | $this->actionOptionsResolver->setAllowedValues('protocol', ['http://', 'https://']); 83 | } 84 | 85 | public function getActionOptionsResolver(): OptionsResolver 86 | { 87 | return $this->actionOptionsResolver; 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /lib/Extension/Core/ColumnType/Batch.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | declare(strict_types=1); 11 | 12 | namespace FSi\Component\DataGrid\Extension\Core\ColumnType; 13 | 14 | use FSi\Component\DataGrid\Column\ColumnAbstractType; 15 | use FSi\Component\DataGrid\Column\CellViewInterface; 16 | use FSi\Component\DataGrid\Column\HeaderViewInterface; 17 | 18 | class Batch extends ColumnAbstractType 19 | { 20 | public function getId(): string 21 | { 22 | return 'batch'; 23 | } 24 | 25 | public function filterValue($value) 26 | { 27 | return $this->getIndex(); 28 | } 29 | 30 | public function getValue($object) 31 | { 32 | return null; 33 | } 34 | 35 | public function buildCellView(CellViewInterface $view): void 36 | { 37 | $view->setAttribute('datagrid_name', $this->getDataGrid()->getName()); 38 | } 39 | 40 | public function buildHeaderView(HeaderViewInterface $view): void 41 | { 42 | $view->setAttribute('datagrid_name', $this->getDataGrid()->getName()); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /lib/Extension/Core/ColumnType/Boolean.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | declare(strict_types=1); 11 | 12 | namespace FSi\Component\DataGrid\Extension\Core\ColumnType; 13 | 14 | use FSi\Component\DataGrid\Column\ColumnAbstractType; 15 | 16 | class Boolean extends ColumnAbstractType 17 | { 18 | public function getId(): string 19 | { 20 | return 'boolean'; 21 | } 22 | 23 | public function filterValue($value) 24 | { 25 | $value = (array) $value; 26 | 27 | $boolValue = null; 28 | foreach ($value as $val) { 29 | if ($val === null) { 30 | continue; 31 | } 32 | 33 | if ((bool) $val === false) { 34 | $boolValue = false; 35 | break; 36 | } 37 | 38 | $boolValue = true; 39 | } 40 | 41 | if (null === $boolValue) { 42 | return ''; 43 | } 44 | 45 | return $this->getOption($boolValue ? 'true_value' : 'false_value'); 46 | } 47 | 48 | public function initOptions(): void 49 | { 50 | $this->getOptionsResolver()->setDefaults([ 51 | 'true_value' => '', 52 | 'false_value' => '' 53 | ]); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /lib/Extension/Core/ColumnType/Collection.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | declare(strict_types=1); 11 | 12 | namespace FSi\Component\DataGrid\Extension\Core\ColumnType; 13 | 14 | use FSi\Component\DataGrid\Column\ColumnAbstractType; 15 | 16 | class Collection extends ColumnAbstractType 17 | { 18 | public function getId(): string 19 | { 20 | return 'collection'; 21 | } 22 | 23 | public function filterValue($value) 24 | { 25 | $value = (array) $value; 26 | foreach ($value as &$val) { 27 | if (!is_array($val)) { 28 | continue; 29 | } 30 | 31 | $val = implode($this->getOption('collection_glue'), $val); 32 | } 33 | 34 | return $value; 35 | } 36 | 37 | public function initOptions(): void 38 | { 39 | $this->getOptionsResolver()->setDefaults([ 40 | 'collection_glue' => ' ' 41 | ]); 42 | 43 | $this->getOptionsResolver()->setAllowedTypes('collection_glue', 'string'); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /lib/Extension/Core/ColumnType/Money.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | declare(strict_types=1); 11 | 12 | namespace FSi\Component\DataGrid\Extension\Core\ColumnType; 13 | 14 | use FSi\Component\DataGrid\Column\ColumnAbstractType; 15 | use FSi\Component\DataGrid\Exception\DataGridColumnException; 16 | 17 | class Money extends ColumnAbstractType 18 | { 19 | public const ROUND_HALF_UP = PHP_ROUND_HALF_UP; 20 | public const ROUND_HALF_DOWN = PHP_ROUND_HALF_DOWN; 21 | public const ROUND_HALF_EVEN = PHP_ROUND_HALF_EVEN; 22 | public const ROUND_HALF_ODD = PHP_ROUND_HALF_ODD; 23 | 24 | public function getId(): string 25 | { 26 | return 'money'; 27 | } 28 | 29 | public function filterValue($value) 30 | { 31 | $precision = $this->getOption('precision'); 32 | $roundmode = $this->getOption('round_mode'); 33 | $decimals = $this->getOption('decimals'); 34 | $decPoint = $this->getOption('dec_point'); 35 | $thousands = $this->getOption('thousands_sep'); 36 | $currencyField = $this->getOption('currency_field'); 37 | $currencyValue = $this->getOption('currency'); 38 | $mappingFields = $this->getOption('field_mapping'); 39 | $currencySeparator = $this->getOption('value_currency_separator'); 40 | 41 | if (null === $currencyField && null === $currencyValue) { 42 | throw new DataGridColumnException(sprintf( 43 | 'At least one option from "currency" and "currency_field" must be defined in "%s" field.', 44 | $this->getName() 45 | )); 46 | } 47 | 48 | $currency = $currencyValue; 49 | if (null !== $currencyField) { 50 | if (!in_array($currencyField, $mappingFields)) { 51 | throw new DataGridColumnException( 52 | sprintf('There is no field with name "%s".', $currencyField) 53 | ); 54 | } 55 | 56 | $currency = $value[$currencyField]; 57 | unset($value[$currencyField]); 58 | } 59 | 60 | foreach ($value as $fieldName => &$val) { 61 | if (empty($val)) { 62 | continue; 63 | } 64 | 65 | $val = round($val, $precision, $roundmode); 66 | $val = number_format($val, $decimals, $decPoint, $thousands); 67 | 68 | $val = $val . $currencySeparator . $currency; 69 | } 70 | 71 | return $value; 72 | } 73 | 74 | public function initOptions(): void 75 | { 76 | $this->getOptionsResolver()->setDefaults([ 77 | 'round_mode' => self::ROUND_HALF_UP, 78 | 'precision' => 2, 79 | 'decimals' => 2, 80 | 'dec_point' => '.', 81 | 'thousands_sep' => ',', 82 | 'value_currency_separator' => ' ', 83 | 'currency' => null, 84 | 'currency_field' => null, 85 | ]); 86 | 87 | $this->getOptionsResolver()->setAllowedTypes('round_mode', 'integer'); 88 | $this->getOptionsResolver()->setAllowedTypes('precision', 'integer'); 89 | $this->getOptionsResolver()->setAllowedTypes('decimals', 'integer'); 90 | $this->getOptionsResolver()->setAllowedTypes('decimals', 'integer'); 91 | $this->getOptionsResolver()->setAllowedTypes('dec_point', 'string'); 92 | $this->getOptionsResolver()->setAllowedTypes('thousands_sep', 'string'); 93 | $this->getOptionsResolver()->setAllowedTypes('value_currency_separator', 'string'); 94 | $this->getOptionsResolver()->setAllowedTypes('currency', ['null', 'string']); 95 | $this->getOptionsResolver()->setAllowedTypes('currency_field', ['null', 'string']); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /lib/Extension/Core/ColumnType/Number.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | declare(strict_types=1); 11 | 12 | namespace FSi\Component\DataGrid\Extension\Core\ColumnType; 13 | 14 | use FSi\Component\DataGrid\Column\ColumnAbstractType; 15 | 16 | class Number extends ColumnAbstractType 17 | { 18 | public const ROUND_HALF_UP = PHP_ROUND_HALF_UP; 19 | public const ROUND_HALF_DOWN = PHP_ROUND_HALF_DOWN; 20 | public const ROUND_HALF_EVEN = PHP_ROUND_HALF_EVEN; 21 | public const ROUND_HALF_ODD = PHP_ROUND_HALF_ODD; 22 | 23 | public function getId(): string 24 | { 25 | return 'number'; 26 | } 27 | 28 | public function filterValue($value) 29 | { 30 | $precision = (int) $this->getOption('precision'); 31 | $roundmode = $this->getOption('round_mode'); 32 | 33 | $format = $this->getOption('format'); 34 | $format_decimals = $this->getOption('format_decimals'); 35 | $format_dec_point = $this->getOption('format_dec_point'); 36 | $format_thousands_sep = $this->getOption('format_thousands_sep'); 37 | 38 | foreach ($value as &$val) { 39 | if (empty($val)) { 40 | continue; 41 | } 42 | 43 | if (isset($roundmode)) { 44 | $val = round($val, $precision, $roundmode); 45 | } 46 | 47 | if ($format) { 48 | $val = number_format((float) $val, $format_decimals, $format_dec_point, $format_thousands_sep); 49 | } 50 | } 51 | 52 | return $value; 53 | } 54 | 55 | public function initOptions(): void 56 | { 57 | $this->options = [ 58 | 'round_mode' => null, 59 | 'precision' => 2, 60 | 'format' => false, 61 | 'format_decimals' => 2, 62 | 'format_dec_point' => '.', 63 | 'format_thousands_sep' => ',', 64 | ]; 65 | 66 | $this->getOptionsResolver()->setDefaults($this->options); 67 | 68 | $this->getOptionsResolver()->setAllowedTypes('precision', 'integer'); 69 | $this->getOptionsResolver()->setAllowedTypes('format', 'bool'); 70 | $this->getOptionsResolver()->setAllowedTypes('format_decimals', 'integer'); 71 | 72 | $this->getOptionsResolver()->setAllowedValues('round_mode', [ 73 | null, 74 | self::ROUND_HALF_UP, 75 | self::ROUND_HALF_DOWN, 76 | self::ROUND_HALF_EVEN, 77 | self::ROUND_HALF_ODD, 78 | ]); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /lib/Extension/Core/ColumnType/Text.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | declare(strict_types=1); 11 | 12 | namespace FSi\Component\DataGrid\Extension\Core\ColumnType; 13 | 14 | use FSi\Component\DataGrid\Column\ColumnAbstractType; 15 | 16 | class Text extends ColumnAbstractType 17 | { 18 | public function getId(): string 19 | { 20 | return 'text'; 21 | } 22 | 23 | public function filterValue($value) 24 | { 25 | $trim = $this->getOption('trim'); 26 | if ($trim === true) { 27 | foreach ($value as &$val) { 28 | if (empty($val)) { 29 | continue; 30 | } 31 | 32 | $val = trim($val); 33 | } 34 | } 35 | 36 | return $value; 37 | } 38 | 39 | public function initOptions(): void 40 | { 41 | $this->getOptionsResolver()->setDefaults([ 42 | 'trim' => false 43 | ]); 44 | 45 | $this->getOptionsResolver()->setAllowedTypes('trim', 'bool'); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /lib/Extension/Core/ColumnTypeExtension/DefaultColumnOptionsExtension.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | declare(strict_types=1); 11 | 12 | namespace FSi\Component\DataGrid\Extension\Core\ColumnTypeExtension; 13 | 14 | use FSi\Component\DataGrid\Column\ColumnTypeInterface; 15 | use FSi\Component\DataGrid\Column\HeaderViewInterface; 16 | use FSi\Component\DataGrid\Column\ColumnAbstractTypeExtension; 17 | 18 | class DefaultColumnOptionsExtension extends ColumnAbstractTypeExtension 19 | { 20 | public function buildHeaderView(ColumnTypeInterface $column, HeaderViewInterface $view): void 21 | { 22 | $view->setLabel($column->getOption('label')); 23 | $order = $column->getOption('display_order'); 24 | if (null !== $order) { 25 | $view->setAttribute('display_order', $order); 26 | } 27 | } 28 | 29 | public function getExtendedColumnTypes(): array 30 | { 31 | return [ 32 | 'batch', 33 | 'text', 34 | 'boolean', 35 | 'collection', 36 | 'datetime', 37 | 'number', 38 | 'money', 39 | 'gedmo_tree', 40 | 'entity', 41 | 'action', 42 | ]; 43 | } 44 | 45 | public function initOptions(ColumnTypeInterface $column): void 46 | { 47 | $column->getOptionsResolver()->setDefaults([ 48 | 'label' => $column->getName(), 49 | 'display_order' => null, 50 | 'field_mapping' => [$column->getName()] 51 | ]); 52 | 53 | $column->getOptionsResolver()->setAllowedTypes('label', 'string'); 54 | $column->getOptionsResolver()->setAllowedTypes('field_mapping', 'array'); 55 | $column->getOptionsResolver()->setAllowedTypes('display_order', ['integer', 'null']); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /lib/Extension/Core/ColumnTypeExtension/ValueFormatColumnOptionsExtension.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | declare(strict_types=1); 11 | 12 | namespace FSi\Component\DataGrid\Extension\Core\ColumnTypeExtension; 13 | 14 | use FSi\Component\DataGrid\Column\ColumnTypeInterface; 15 | use FSi\Component\DataGrid\Column\CellViewInterface; 16 | use FSi\Component\DataGrid\Column\ColumnAbstractTypeExtension; 17 | 18 | class ValueFormatColumnOptionsExtension extends ColumnAbstractTypeExtension 19 | { 20 | public function buildCellView(ColumnTypeInterface $column, CellViewInterface $view): void 21 | { 22 | $this->validateEmptyValueOption($column); 23 | $value = $this->populateValue($view->getValue(), $column->getOption('empty_value')); 24 | $glue = $column->getOption('value_glue'); 25 | $format = $column->getOption('value_format'); 26 | 27 | $value = $this->formatValue($value, $format, $glue); 28 | 29 | if (!isset($glue, $format) && is_array($value)) { 30 | throw new \InvalidArgumentException(sprintf( 31 | 'At least one of "value_format" or "value_glue" option is missing in column: "%s".', 32 | $column->getName() 33 | )); 34 | } 35 | 36 | $view->setValue($value); 37 | } 38 | 39 | public function getExtendedColumnTypes(): array 40 | { 41 | return [ 42 | 'text', 43 | 'boolean', 44 | 'datetime', 45 | 'collection', 46 | 'number', 47 | 'money', 48 | 'gedmo_tree', 49 | ]; 50 | } 51 | 52 | public function initOptions(ColumnTypeInterface $column): void 53 | { 54 | $column->getOptionsResolver()->setDefaults([ 55 | 'value_glue' => null, 56 | 'value_format' => null, 57 | 'empty_value' => '', 58 | ]); 59 | 60 | $column->getOptionsResolver()->setAllowedTypes('value_glue', ['string', 'null']); 61 | $column->getOptionsResolver()->setAllowedTypes('value_format', ['string', 'Closure', 'null']); 62 | $column->getOptionsResolver()->setAllowedTypes('empty_value', 'string'); 63 | } 64 | 65 | private function validateEmptyValueOption(ColumnTypeInterface $column): void 66 | { 67 | $emptyValue = $column->getOption('empty_value'); 68 | $mappingFields = $column->getOption('field_mapping'); 69 | 70 | if (is_string($emptyValue)) { 71 | return; 72 | } 73 | 74 | if (!is_array($emptyValue)) { 75 | throw new \InvalidArgumentException( 76 | sprintf('Option "empty_value" in column: "%s" must be a array.', $column->getName()) 77 | ); 78 | } 79 | 80 | foreach ($emptyValue as $field => $value) { 81 | if (!in_array($field, $mappingFields)) { 82 | throw new \InvalidArgumentException( 83 | sprintf( 84 | 'Mapping field "%s" doesn\'t exist in column: "%s".', 85 | $field, 86 | $column->getName() 87 | ) 88 | ); 89 | } 90 | 91 | if (!is_string($value)) { 92 | throw new \InvalidArgumentException( 93 | sprintf( 94 | 'Option "empty_value" for field "%s" in column: "%s" must be a string.', 95 | $field, 96 | $column->getName() 97 | ) 98 | ); 99 | } 100 | } 101 | } 102 | 103 | /** 104 | * @param mixed $value 105 | * @param mixed $emptyValue 106 | * @return array|string 107 | */ 108 | private function populateValue($value, $emptyValue) 109 | { 110 | if (is_string($emptyValue)) { 111 | if (!isset($value) || (is_string($value) && !strlen($value))) { 112 | return $emptyValue; 113 | } 114 | 115 | if (is_array($value)) { 116 | foreach ($value as &$val) { 117 | if (!isset($val) || (is_string($val) && !strlen($val))) { 118 | $val = $emptyValue; 119 | } 120 | } 121 | } 122 | 123 | return $value; 124 | } 125 | 126 | /** 127 | * If value is simple string and $empty_value is array there is no way 128 | * to guess which empty_value should be used. 129 | */ 130 | if (is_string($value)) { 131 | return $value; 132 | } 133 | 134 | if (is_array($value)) { 135 | foreach ($value as $field => &$fieldValue) { 136 | if (empty($fieldValue)) { 137 | $fieldValue = array_key_exists($field, $emptyValue) 138 | ? $emptyValue[$field] 139 | : ''; 140 | } 141 | } 142 | } 143 | 144 | return $value; 145 | } 146 | 147 | private function formatValue($value, $format = null, ?string $glue = null) 148 | { 149 | if (is_array($value) && isset($glue) && !isset($format)) { 150 | $value = implode($glue, $value); 151 | } 152 | 153 | if (isset($format)) { 154 | if (is_array($value)) { 155 | if (isset($glue)) { 156 | $renderedValues = []; 157 | foreach ($value as $val) { 158 | $renderedValues[] = $this->formatSingleValue($val, $format); 159 | } 160 | 161 | $value = implode($glue, $renderedValues); 162 | } else { 163 | $value = $this->formatMultipleValues($value, $format); 164 | } 165 | } else { 166 | $value = $this->formatSingleValue($value, $format); 167 | } 168 | } 169 | 170 | if (is_array($value) && count($value) === 1) { 171 | reset($value); 172 | $value = current($value); 173 | } 174 | 175 | return $value; 176 | } 177 | 178 | /** 179 | * @param mixed $value 180 | * @param string|\Closure $template 181 | * @return string 182 | */ 183 | private function formatSingleValue($value, $template): string 184 | { 185 | if ($template instanceof \Closure) { 186 | return $template($value); 187 | } 188 | 189 | return sprintf($template, $value); 190 | } 191 | 192 | /** 193 | * @param mixed $value 194 | * @param string|\Closure $template 195 | * @return string 196 | */ 197 | private function formatMultipleValues($value, $template): string 198 | { 199 | if ($template instanceof \Closure) { 200 | return $template($value); 201 | } 202 | 203 | return vsprintf($template, $value); 204 | } 205 | } 206 | -------------------------------------------------------------------------------- /lib/Extension/Core/CoreExtension.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | declare(strict_types=1); 11 | 12 | namespace FSi\Component\DataGrid\Extension\Core; 13 | 14 | use FSi\Component\DataGrid\DataGridAbstractExtension; 15 | use FSi\Component\DataGrid\Extension\Core\ColumnType; 16 | use FSi\Component\DataGrid\Extension\Core\ColumnTypeExtension; 17 | use FSi\Component\DataGrid\Extension\Core\EventSubscriber; 18 | 19 | class CoreExtension extends DataGridAbstractExtension 20 | { 21 | protected function loadColumnTypes(): array 22 | { 23 | return [ 24 | new ColumnType\Text(), 25 | new ColumnType\Number(), 26 | new ColumnType\Collection(), 27 | new ColumnType\DateTime(), 28 | new ColumnType\Action(), 29 | new ColumnType\Money(), 30 | new ColumnType\Action(), 31 | ]; 32 | } 33 | 34 | protected function loadColumnTypesExtensions(): array 35 | { 36 | return [ 37 | new ColumnTypeExtension\DefaultColumnOptionsExtension(), 38 | new ColumnTypeExtension\ValueFormatColumnOptionsExtension(), 39 | ]; 40 | } 41 | 42 | protected function loadSubscribers(): array 43 | { 44 | return [ 45 | new EventSubscriber\ColumnOrder(), 46 | ]; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /lib/Extension/Core/EventSubscriber/ColumnOrder.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | declare(strict_types=1); 11 | 12 | namespace FSi\Component\DataGrid\Extension\Core\EventSubscriber; 13 | 14 | use FSi\Component\DataGrid\DataGridEventInterface; 15 | use FSi\Component\DataGrid\DataGridEvents; 16 | use FSi\Component\DataGrid\DataGridViewInterface; 17 | use Symfony\Component\EventDispatcher\EventSubscriberInterface; 18 | 19 | class ColumnOrder implements EventSubscriberInterface 20 | { 21 | public static function getSubscribedEvents(): array 22 | { 23 | return [DataGridEvents::POST_BUILD_VIEW => ['postBuildView', 128]]; 24 | } 25 | 26 | /** 27 | * {@inheritdoc} 28 | */ 29 | public function postBuildView(DataGridEventInterface $event) 30 | { 31 | /** @var DataGridViewInterface $view */ 32 | $view = $event->getData(); 33 | $columns = $view->getColumns(); 34 | 35 | if (count($columns)) { 36 | $positive = []; 37 | $negative = []; 38 | $neutral = []; 39 | 40 | $indexedColumns = []; 41 | foreach ($columns as $column) { 42 | if ($column->hasAttribute('display_order')) { 43 | if (($order = $column->getAttribute('display_order')) >= 0) { 44 | $positive[$column->getName()] = $order; 45 | } else { 46 | $negative[$column->getName()] = $order; 47 | } 48 | $indexedColumns[$column->getName()] = $column; 49 | } else { 50 | $neutral[] = $column; 51 | } 52 | } 53 | 54 | asort($positive); 55 | asort($negative); 56 | 57 | $columns = []; 58 | foreach ($negative as $name => $order) { 59 | $columns[] = $indexedColumns[$name]; 60 | } 61 | 62 | $columns = array_merge($columns, $neutral); 63 | foreach ($positive as $name => $order) { 64 | $columns[] = $indexedColumns[$name]; 65 | } 66 | 67 | $view->setColumns($columns); 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /lib/Extension/Doctrine/ColumnType/Entity.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | declare(strict_types=1); 11 | 12 | namespace FSi\Component\DataGrid\Extension\Doctrine\ColumnType; 13 | 14 | use Doctrine\Common\Collections\Collection; 15 | use FSi\Component\DataGrid\Column\ColumnAbstractType; 16 | 17 | class Entity extends ColumnAbstractType 18 | { 19 | public function getId(): string 20 | { 21 | return 'entity'; 22 | } 23 | 24 | public function getValue($object) 25 | { 26 | return $this->getDataMapper()->getData($this->getOption('relation_field'), $object); 27 | } 28 | 29 | public function filterValue($value) 30 | { 31 | if ($value instanceof Collection) { 32 | $value = $value->toArray(); 33 | } 34 | 35 | $values = []; 36 | $objectValues = []; 37 | $mappingFields = $this->getOption('field_mapping'); 38 | 39 | if (is_array($value)) { 40 | foreach ($value as $object) { 41 | foreach ($mappingFields as $field) { 42 | $objectValues[$field] = $this->getDataMapper()->getData($field, $object); 43 | } 44 | 45 | $values[] = $objectValues; 46 | } 47 | } else { 48 | foreach ($mappingFields as $field) { 49 | $objectValues[$field] = isset($value) 50 | ? $this->getDataMapper()->getData($field, $value) 51 | : null; 52 | } 53 | 54 | $values[] = $objectValues; 55 | } 56 | 57 | return $values; 58 | } 59 | 60 | public function initOptions(): void 61 | { 62 | $this->getOptionsResolver()->setDefaults([ 63 | 'relation_field' => $this->getName(), 64 | ]); 65 | 66 | $this->getOptionsResolver()->setAllowedTypes('relation_field', 'string'); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /lib/Extension/Doctrine/ColumnTypeExtension/ValueFormatColumnOptionsExtension.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | declare(strict_types=1); 11 | 12 | namespace FSi\Component\DataGrid\Extension\Doctrine\ColumnTypeExtension; 13 | 14 | use FSi\Component\DataGrid\Column\ColumnTypeInterface; 15 | use FSi\Component\DataGrid\Column\CellViewInterface; 16 | use FSi\Component\DataGrid\Column\ColumnAbstractTypeExtension; 17 | use FSi\Component\DataGrid\Exception\DataGridException; 18 | 19 | class ValueFormatColumnOptionsExtension extends ColumnAbstractTypeExtension 20 | { 21 | public function buildCellView(ColumnTypeInterface $column, CellViewInterface $view): void 22 | { 23 | $value = []; 24 | $values = $view->getValue(); 25 | if (($emptyValue = $column->getOption('empty_value')) !== null) { 26 | $values = $this->populateValues($values, $emptyValue); 27 | } 28 | $glue = $column->getOption('value_glue'); 29 | $format = $column->getOption('value_format'); 30 | 31 | foreach ($values as $val) { 32 | $objectValue = null; 33 | 34 | if (isset($glue) && !isset($format)) { 35 | $objectValue = implode($glue, $val); 36 | } 37 | 38 | if (isset($format)) { 39 | if (isset($glue)) { 40 | $formattedValues = []; 41 | foreach ($val as $fieldValue) { 42 | $formattedValues[] = sprintf($format, $fieldValue); 43 | } 44 | 45 | $objectValue = implode($glue, $formattedValues); 46 | } else { 47 | $objectValue = vsprintf($format, $val); 48 | } 49 | } 50 | 51 | $value[] = $objectValue; 52 | } 53 | 54 | $value = implode($column->getOption('glue_multiple'), $value); 55 | 56 | $view->setValue($value); 57 | } 58 | 59 | public function getExtendedColumnTypes(): array 60 | { 61 | return [ 62 | 'entity', 63 | ]; 64 | } 65 | 66 | public function initOptions(ColumnTypeInterface $column): void 67 | { 68 | $column->getOptionsResolver()->setDefaults([ 69 | 'glue_multiple' => ' ', 70 | 'value_glue' => ' ', 71 | 'value_format' => '%s', 72 | 'empty_value' => null 73 | ]); 74 | 75 | $column->getOptionsResolver()->setAllowedTypes('glue_multiple', ['string']); 76 | $column->getOptionsResolver()->setAllowedTypes('value_glue', ['string', 'null']); 77 | $column->getOptionsResolver()->setAllowedTypes('value_format', ['string', 'null']); 78 | $column->getOptionsResolver()->setAllowedTypes('empty_value', ['array', 'string', 'null']); 79 | } 80 | 81 | private function populateValues(array $values, $emptyValue): array 82 | { 83 | foreach ($values as &$val) { 84 | foreach ($val as $fieldKey => &$fieldValue) { 85 | if (!isset($fieldValue)) { 86 | $fieldValue = $this->populateValue($fieldKey, $fieldValue, $emptyValue); 87 | } 88 | } 89 | } 90 | 91 | return $values; 92 | } 93 | 94 | /** 95 | * @param string $key 96 | * @param mixed $value 97 | * @param mixed $emptyValue 98 | * @return mixed 99 | * @throws \FSi\Component\DataGrid\Exception\DataGridException 100 | */ 101 | private function populateValue(string $key, $value, $emptyValue) 102 | { 103 | if (is_string($emptyValue)) { 104 | $value = $emptyValue; 105 | } 106 | 107 | if (is_array($emptyValue)) { 108 | if (isset($emptyValue[$key])) { 109 | $value = $emptyValue[$key]; 110 | } else { 111 | throw new DataGridException( 112 | sprintf('Not found key "%s" in empty_value array', $key) 113 | ); 114 | } 115 | } 116 | 117 | return $value; 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /lib/Extension/Doctrine/DoctrineExtension.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | declare(strict_types=1); 11 | 12 | namespace FSi\Component\DataGrid\Extension\Doctrine; 13 | 14 | use FSi\Component\DataGrid\DataGridAbstractExtension; 15 | use FSi\Component\DataGrid\Extension\Doctrine\ColumnType; 16 | 17 | class DoctrineExtension extends DataGridAbstractExtension 18 | { 19 | protected function loadColumnTypes(): array 20 | { 21 | return [ 22 | new ColumnType\Entity(), 23 | ]; 24 | } 25 | 26 | protected function loadColumnTypesExtensions(): array 27 | { 28 | return [ 29 | new ColumnTypeExtension\ValueFormatColumnOptionsExtension(), 30 | ]; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /lib/Extension/Gedmo/ColumnType/Tree.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | declare(strict_types=1); 11 | 12 | namespace FSi\Component\DataGrid\Extension\Gedmo\ColumnType; 13 | 14 | use Doctrine\Common\Persistence\ManagerRegistry; 15 | use Doctrine\Common\Persistence\ObjectManager; 16 | use Doctrine\ORM\EntityManager; 17 | use FSi\Component\DataGrid\Column\CellViewInterface; 18 | use FSi\Component\DataGrid\Column\ColumnAbstractType; 19 | use FSi\Component\DataGrid\Exception\DataGridColumnException; 20 | use FSi\Component\DataIndexer\DoctrineDataIndexer; 21 | use Gedmo\Tree\RepositoryInterface as TreeRepositoryInterface; 22 | use Gedmo\Tree\Strategy; 23 | use Gedmo\Tree\TreeListener; 24 | use Symfony\Component\PropertyAccess\PropertyAccess; 25 | 26 | class Tree extends ColumnAbstractType 27 | { 28 | /** 29 | * @var ManagerRegistry 30 | */ 31 | protected $registry; 32 | 33 | /** 34 | * @var array 35 | */ 36 | protected $allowedStrategies; 37 | 38 | /** 39 | * @var array 40 | */ 41 | private $viewAttributes; 42 | 43 | /** 44 | * @var array 45 | */ 46 | private $classStrategies; 47 | 48 | public function __construct(ManagerRegistry $registry) 49 | { 50 | $this->registry = $registry; 51 | $this->viewAttributes = []; 52 | $this->classStrategies = []; 53 | $this->allowedStrategies = ['nested']; 54 | } 55 | 56 | public function getId(): string 57 | { 58 | return 'gedmo_tree'; 59 | } 60 | 61 | public function getValue($object) 62 | { 63 | if (!is_object($object)) { 64 | throw new \InvalidArgumentException('Column "gedmo_tree" must read value from object.'); 65 | } 66 | 67 | $value = parent::getValue($object); 68 | $objectManager = $this->registry->getManager($this->getOption('em')); 69 | 70 | // Check if tree listener is registred. 71 | $treeListener = $this->getTreeListener($objectManager); 72 | 73 | // Get Tree strategy. 74 | $strategy = $this->getClassStrategy($objectManager, $treeListener, get_class($object)); 75 | $this->validateStrategy($object, $strategy); 76 | 77 | $config = $treeListener->getConfiguration($objectManager, get_class($object)); 78 | $doctrineDataIndexer = new DoctrineDataIndexer($this->registry, get_class($object)); 79 | $propertyAccessor = PropertyAccess::createPropertyAccessor(); 80 | 81 | $this->viewAttributes = [ 82 | 'id' => $doctrineDataIndexer->getIndex($object), 83 | 'root' => isset($config['root']) ? $propertyAccessor->getValue($object, $config['root']) : null, 84 | 'left' => isset($config['left']) ? $propertyAccessor->getValue($object, $config['left']) : null, 85 | 'right' => isset($config['right']) ? $propertyAccessor->getValue($object, $config['right']) : null, 86 | 'level' => (isset($config['level'])) ? $propertyAccessor->getValue($object, $config['level']) : null, 87 | 'children' => $this->getTreeRepository(get_class($object), $objectManager)->childCount($object), 88 | ]; 89 | 90 | $parent = (isset($config['parent'])) ? $propertyAccessor->getValue($object, $config['parent']) : null; 91 | if (isset($parent)) { 92 | $this->viewAttributes['parent'] = $doctrineDataIndexer->getIndex($parent); 93 | } 94 | 95 | return $value; 96 | } 97 | 98 | /** 99 | * {@inheritdoc} 100 | */ 101 | public function filterValue($value) 102 | { 103 | return $value; 104 | } 105 | 106 | public function buildCellView(CellViewInterface $view): void 107 | { 108 | foreach ($this->getViewAttributes() as $attrName => $attrValue) { 109 | $view->setAttribute($attrName, $attrValue); 110 | } 111 | } 112 | 113 | public function initOptions(): void 114 | { 115 | $this->getOptionsResolver()->setDefaults([ 116 | 'em' => null, 117 | ]); 118 | } 119 | 120 | private function getViewAttributes(): array 121 | { 122 | return $this->viewAttributes; 123 | } 124 | 125 | private function getClassStrategy(ObjectManager $om, TreeListener $listener, string $class): Strategy 126 | { 127 | if (array_key_exists($class, $this->classStrategies)) { 128 | return $this->classStrategies[$class]; 129 | } 130 | 131 | $this->classStrategies[$class] = null; 132 | $classParents = array_merge( 133 | [$class], 134 | class_parents($class) 135 | ); 136 | 137 | foreach ($classParents as $parent) { 138 | try { 139 | $this->classStrategies[$class] = $listener->getStrategy($om, $parent); 140 | break; 141 | } catch (\Exception $e) { 142 | // we don't like to throw exception because there might be a strategy for class parents 143 | } 144 | } 145 | 146 | return $this->classStrategies[$class]; 147 | } 148 | 149 | /** 150 | * @param ObjectManager $om 151 | * @throws \FSi\Component\DataGrid\Exception\DataGridColumnException 152 | * @return TreeListener 153 | */ 154 | private function getTreeListener(ObjectManager $om) 155 | { 156 | $treeListener = null; 157 | 158 | if ($om instanceof EntityManager) { 159 | foreach ($om->getEventManager()->getListeners() as $listeners) { 160 | foreach ($listeners as $listener) { 161 | if ($listener instanceof TreeListener) { 162 | $treeListener = $listener; 163 | break; 164 | } 165 | } 166 | if ($treeListener) { 167 | break; 168 | } 169 | } 170 | } 171 | 172 | if (!isset($treeListener)) { 173 | throw new DataGridColumnException('Gedmo TreeListener was not found in your entity manager.'); 174 | } 175 | 176 | return $treeListener; 177 | } 178 | 179 | /** 180 | * @param $class 181 | * @param \Doctrine\Common\Persistence\ObjectManager $em 182 | * @throws \RuntimeException 183 | * @return TreeRepositoryInterface 184 | */ 185 | private function getTreeRepository($class, ObjectManager $em) 186 | { 187 | $repository = $em->getRepository($class); 188 | if (!$repository instanceof TreeRepositoryInterface) { 189 | throw new \RuntimeException( 190 | sprintf("%s must be an instance of Gedmo tree repository", get_class($repository)) 191 | ); 192 | } 193 | 194 | return $repository; 195 | } 196 | 197 | /** 198 | * @param $object 199 | * @param $strategy 200 | * @throws \FSi\Component\DataGrid\Exception\DataGridColumnException 201 | */ 202 | private function validateStrategy($object, $strategy) 203 | { 204 | if (!isset($strategy) && !$strategy instanceof Strategy) { 205 | throw new DataGridColumnException( 206 | sprintf('"%s" is not implementing gedmo tree strategy. Maybe you should consider using a different column type?', get_class($object)) 207 | ); 208 | } 209 | 210 | if (!in_array($strategy->getName(), $this->allowedStrategies)) { 211 | throw new DataGridColumnException( 212 | sprintf('Strategy "%s" is not supported by "%s" column.', $strategy->getName(), $this->getId()) 213 | ); 214 | } 215 | } 216 | } 217 | -------------------------------------------------------------------------------- /lib/Extension/Gedmo/GedmoDoctrineExtension.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | declare(strict_types=1); 11 | 12 | namespace FSi\Component\DataGrid\Extension\Gedmo; 13 | 14 | use Doctrine\Common\Persistence\ManagerRegistry; 15 | use FSi\Component\DataGrid\DataGridAbstractExtension; 16 | use FSi\Component\DataGrid\Extension\Gedmo\ColumnType; 17 | 18 | class GedmoDoctrineExtension extends DataGridAbstractExtension 19 | { 20 | /** 21 | * @var ManagerRegistry 22 | */ 23 | protected $registry; 24 | 25 | public function __construct(ManagerRegistry $registry) 26 | { 27 | $this->registry = $registry; 28 | } 29 | 30 | protected function loadColumnTypes(): array 31 | { 32 | return [ 33 | new ColumnType\Tree($this->registry), 34 | ]; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | tests 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /tests/Data/DataRowsetTest.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | declare(strict_types=1); 11 | 12 | namespace FSi\Component\DataGrid\Tests\Data; 13 | 14 | use FSi\Component\DataGrid\Tests\Fixtures\Entity; 15 | use FSi\Component\DataGrid\Data\DataRowset; 16 | use PHPUnit\Framework\TestCase; 17 | use TypeError; 18 | 19 | class DataRowsetTest extends TestCase 20 | { 21 | public function testCreateWithInvalidData() 22 | { 23 | $this->expectException(TypeError::class); 24 | 25 | new DataRowset('Invalid Data'); 26 | } 27 | 28 | public function testCreateRowset() 29 | { 30 | $data = [ 31 | 'e1' => new Entity('entity1'), 32 | 'e2' => new Entity('entity2') 33 | ]; 34 | 35 | $rowset = new DataRowset($data); 36 | 37 | foreach ($rowset as $index => $row) { 38 | $this->assertSame($data[$index], $row); 39 | } 40 | 41 | $this->assertSame(2, $rowset->count()); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /tests/DataGridFactoryTest.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | declare(strict_types=1); 11 | 12 | namespace FSi\Component\DataGrid\Tests; 13 | 14 | use FSi\Component\DataGrid\DataGridFactory; 15 | use FSi\Component\DataGrid\DataGridFactoryInterface; 16 | use FSi\Component\DataGrid\Tests\Fixtures\FooExtension; 17 | use FSi\Component\DataGrid\DataMapper\DataMapperInterface; 18 | use FSi\Component\DataGrid\Exception\UnexpectedTypeException; 19 | use FSi\Component\DataGrid\Exception\DataGridColumnException; 20 | use FSi\Component\DataGrid\Tests\Fixtures\ColumnType\FooType; 21 | use PHPUnit\Framework\TestCase; 22 | 23 | class DataGridFactoryTest extends TestCase 24 | { 25 | /** 26 | * @var DataGridFactoryInterface 27 | */ 28 | private $factory; 29 | 30 | protected function setUp() 31 | { 32 | $extensions = [ 33 | new FooExtension(), 34 | ]; 35 | 36 | $dataMapper = $this->createMock(DataMapperInterface::class); 37 | 38 | $this->factory = new DataGridFactory($extensions, $dataMapper); 39 | } 40 | 41 | public function testCreateGrids() 42 | { 43 | $grid = $this->factory->createDataGrid(); 44 | $this->assertSame('grid',$grid->getName()); 45 | 46 | $this->expectException(DataGridColumnException::class); 47 | $this->expectExceptionMessage('Datagrid name "grid" is not uniqe, it was used before to create datagrid'); 48 | $this->factory->createDataGrid('grid'); 49 | } 50 | 51 | public function testHasColumnType() 52 | { 53 | $this->assertTrue($this->factory->hasColumnType('foo')); 54 | $this->assertFalse($this->factory->hasColumnType('bar')); 55 | } 56 | 57 | public function testGetColumnType() 58 | { 59 | $this->assertInstanceOf(FooType::class, $this->factory->getColumnType('foo')); 60 | 61 | $this->expectException(UnexpectedTypeException::class); 62 | $this->expectExceptionMessage('There is no column with type "bar" registered in factory.'); 63 | $this->factory->getColumnType('bar'); 64 | } 65 | 66 | public function testGetDataMapper() 67 | { 68 | $this->assertInstanceOf(DataMapperInterface::class, $this->factory->getDataMapper()); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /tests/DataGridRowViewTest.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | declare(strict_types=1); 11 | 12 | namespace FSi\Component\DataGrid\Tests; 13 | 14 | use FSi\Component\DataGrid\DataGridRowView; 15 | use FSi\Component\DataGrid\Column\ColumnTypeInterface; 16 | use FSi\Component\DataGrid\Column\CellViewInterface; 17 | use FSi\Component\DataGrid\DataGridViewInterface; 18 | use PHPUnit\Framework\TestCase; 19 | 20 | class DataGridRowViewTest extends TestCase 21 | { 22 | public function testCreateDataGridRowView() 23 | { 24 | $source = 'SOURCE'; 25 | 26 | $dataGridView = $this->createMock(DataGridViewInterface::class); 27 | 28 | $cellView = $this->createMock(CellViewInterface::class); 29 | 30 | $column = $this->createMock(ColumnTypeInterface::class); 31 | $column->expects($this->atLeastOnce()) 32 | ->method('createCellView') 33 | ->with($source, 0) 34 | ->will($this->returnValue($cellView)); 35 | 36 | $columns = [ 37 | 'foo' =>$column 38 | ]; 39 | 40 | $gridRow = new DataGridRowView($dataGridView, $columns, $source, 0); 41 | $this->assertSame($gridRow->current(), $cellView); 42 | 43 | $this->assertSame($gridRow->getSource(), $source); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /tests/DataGridTest.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | declare(strict_types=1); 11 | 12 | namespace FSi\Component\DataGrid\Tests; 13 | 14 | use FSi\Component\DataGrid\DataGrid; 15 | use FSi\Component\DataGrid\Tests\Fixtures\FooExtension; 16 | use FSi\Component\DataGrid\Tests\Fixtures\ColumnType\FooType; 17 | use FSi\Component\DataGrid\Tests\Fixtures\Entity; 18 | use FSi\Component\DataGrid\DataGridViewInterface; 19 | use FSi\Component\DataGrid\DataMapper\DataMapperInterface; 20 | use FSi\Component\DataGrid\DataGridFactoryInterface; 21 | use InvalidArgumentException; 22 | use PHPUnit\Framework\TestCase; 23 | use TypeError; 24 | 25 | class DataGridTest extends TestCase 26 | { 27 | /** 28 | * @var DataGridFactoryInterface 29 | */ 30 | private $factory; 31 | 32 | /** 33 | * @var DataMapperInterface 34 | */ 35 | private $dataMapper; 36 | 37 | /** 38 | * @var DataGrid 39 | */ 40 | private $datagrid; 41 | 42 | protected function setUp() 43 | { 44 | $this->dataMapper = $this->createMock(DataMapperInterface::class); 45 | $this->dataMapper->expects($this->any()) 46 | ->method('getData') 47 | ->will($this->returnCallback(function($field, $object){ 48 | switch($field) { 49 | case 'name': 50 | return $object->getName(); 51 | } 52 | })); 53 | 54 | $this->dataMapper->expects($this->any()) 55 | ->method('setData') 56 | ->will($this->returnCallback(function($field, $object, $value){ 57 | switch($field) { 58 | case 'name': 59 | $object->setName($value); 60 | break; 61 | } 62 | })); 63 | 64 | $this->factory = $this->createMock(DataGridFactoryInterface::class); 65 | $this->factory->expects($this->any()) 66 | ->method('getExtensions') 67 | ->will($this->returnValue([ 68 | new FooExtension(), 69 | ])); 70 | 71 | $this->factory->expects($this->any()) 72 | ->method('getColumnType') 73 | ->with($this->equalTo('foo')) 74 | ->will($this->returnValue( 75 | new FooType() 76 | )); 77 | 78 | $this->factory->expects($this->any()) 79 | ->method('hasColumnType') 80 | ->with($this->equalTo('foo')) 81 | ->will($this->returnValue(true)); 82 | 83 | $this->datagrid = new DataGrid('grid', $this->factory, $this->dataMapper); 84 | } 85 | 86 | public function testGetName() 87 | { 88 | $this->assertSame('grid', $this->datagrid->getName()); 89 | } 90 | 91 | public function testHasAddGetRemoveClearColumn() 92 | { 93 | $this->assertFalse($this->datagrid->hasColumn('foo1')); 94 | $this->datagrid->addColumn('foo1', 'foo'); 95 | $this->assertTrue($this->datagrid->hasColumn('foo1')); 96 | $this->assertTrue($this->datagrid->hasColumnType('foo')); 97 | $this->assertFalse($this->datagrid->hasColumnType('this_type_cant_exists')); 98 | 99 | $this->assertInstanceOf(FooType::class, $this->datagrid->getColumn('foo1')); 100 | 101 | $this->assertTrue($this->datagrid->hasColumn('foo1')); 102 | $column = $this->datagrid->getColumn('foo1'); 103 | 104 | $this->datagrid->removeColumn('foo1'); 105 | $this->assertFalse($this->datagrid->hasColumn('foo1')); 106 | 107 | $this->datagrid->addColumn($column); 108 | $this->assertEquals($column, $this->datagrid->getColumn('foo1')); 109 | 110 | $this->assertCount(1, $this->datagrid->getColumns()); 111 | 112 | $this->datagrid->clearColumns(); 113 | $this->assertCount(0, $this->datagrid->getColumns()); 114 | 115 | $this->expectException(InvalidArgumentException::class); 116 | $this->expectExceptionMessage('Column "bar" does not exist in data grid.'); 117 | $this->datagrid->getColumn('bar'); 118 | } 119 | 120 | public function testGetDataMapper() 121 | { 122 | $this->assertInstanceOf(DataMapperInterface::class, $this->datagrid->getDataMapper()); 123 | } 124 | 125 | public function testSetData() 126 | { 127 | $gridData = [ 128 | new Entity('entity1'), 129 | new Entity('entity2') 130 | ]; 131 | 132 | $this->datagrid->setData($gridData); 133 | 134 | $this->assertEquals(count($gridData), count($this->datagrid->createView())); 135 | 136 | $gridData = [ 137 | ['some', 'data'], 138 | ['next', 'data'] 139 | ]; 140 | 141 | $this->datagrid->setData($gridData); 142 | 143 | $this->assertEquals(count($gridData), count($this->datagrid->createView())); 144 | 145 | $gridBrokenData = false; 146 | $this->expectException(TypeError::class); 147 | $this->datagrid->setData($gridBrokenData); 148 | } 149 | 150 | public function testCreateView() 151 | { 152 | $this->datagrid->addColumn('foo1', 'foo'); 153 | $gridData = [ 154 | new Entity('entity1'), 155 | new Entity('entity2') 156 | ]; 157 | 158 | $this->datagrid->setData($gridData); 159 | $this->assertInstanceOf(DataGridViewInterface::class,$this->datagrid->createView()); 160 | } 161 | 162 | public function testSetDataForArray() 163 | { 164 | $gridData = [ 165 | ['one'], 166 | ['two'], 167 | ['three'], 168 | ['four'], 169 | ['bazinga!'], 170 | ['five'], 171 | ]; 172 | 173 | $this->datagrid->setData($gridData); 174 | $view = $this->datagrid->createView(); 175 | 176 | $keys = []; 177 | foreach ($view as $row) { 178 | $keys[] = $row->getIndex(); 179 | } 180 | 181 | $this->assertEquals(array_keys($gridData), $keys); 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /tests/DataGridViewTest.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | declare(strict_types=1); 11 | 12 | namespace FSi\Component\DataGrid\Tests; 13 | 14 | use FSi\Component\DataGrid\DataGridView; 15 | use FSi\Component\DataGrid\Data\DataRowsetInterface; 16 | use FSi\Component\DataGrid\Column\HeaderViewInterface; 17 | use FSi\Component\DataGrid\Column\ColumnTypeInterface; 18 | use PHPUnit\Framework\TestCase; 19 | 20 | class DataGridViewTest extends TestCase 21 | { 22 | /** 23 | * @var DataRowsetInterface 24 | */ 25 | private $rowset; 26 | 27 | /** 28 | * @var DataGridView 29 | */ 30 | private $gridView; 31 | 32 | public function testAddHasGetRemoveColumn() 33 | { 34 | $self = $this; 35 | 36 | $column = $this->createMock(ColumnTypeInterface::class); 37 | $column->expects($this->any()) 38 | ->method('createHeaderView') 39 | ->will($this->returnCallback(function() use ($self) { 40 | $headerView = $self->createMock(HeaderViewInterface::class); 41 | $headerView->expects($self->any()) 42 | ->method('getName') 43 | ->will($self->returnValue('ColumnHeaderView')); 44 | 45 | $headerView->expects($self->any()) 46 | ->method('getType') 47 | ->will($self->returnValue('foo-type')); 48 | 49 | return $headerView; 50 | })); 51 | 52 | $column->expects($this->any()) 53 | ->method('getName') 54 | ->will($this->returnValue('foo')); 55 | 56 | $columnHeader = $this->createMock(HeaderViewInterface::class); 57 | $columnHeader->expects($this->any()) 58 | ->method('getName') 59 | ->will($this->returnValue('foo')); 60 | 61 | $columnHeader->expects($this->any()) 62 | ->method('getType') 63 | ->will($this->returnValue('foo-type')); 64 | 65 | $columnHeader->expects($this->any()) 66 | ->method('setDataGridView'); 67 | 68 | $this->rowset = $this->createMock(DataRowsetInterface::class); 69 | $this->gridView = new DataGridView('test-grid-view', [$column], $this->rowset); 70 | 71 | $this->assertSame('test-grid-view', $this->gridView->getName()); 72 | 73 | $this->assertTrue($this->gridView->hasColumn('foo')); 74 | $this->assertTrue($this->gridView->hasColumnType('foo-type')); 75 | $this->assertCount(1, $this->gridView->getColumns()); 76 | $this->assertSame($this->gridView->getColumn('foo')->getName(), 'ColumnHeaderView'); 77 | $this->gridView->removeColumn('foo'); 78 | $this->assertFalse($this->gridView->hasColumn('foo')); 79 | 80 | $this->gridView->addColumn($columnHeader); 81 | $this->assertTrue($this->gridView->hasColumn('foo')); 82 | 83 | $this->gridView->clearColumns(); 84 | $this->assertFalse($this->gridView->hasColumn('foo')); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /tests/DataMapper/ChainMapperTest.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | declare(strict_types=1); 11 | 12 | namespace FSi\Component\DataGrid\Tests\DataMapper; 13 | 14 | use FSi\Component\DataGrid\DataMapper\ChainMapper; 15 | use FSi\Component\DataGrid\Exception\DataMappingException; 16 | use FSi\Component\DataGrid\DataMapper\DataMapperInterface; 17 | use InvalidArgumentException; 18 | use PHPUnit\Framework\TestCase; 19 | 20 | class ChainMapperTest extends TestCase 21 | { 22 | public function testMappersInChainWithInvalidMappers() 23 | { 24 | $this->expectException(InvalidArgumentException::class); 25 | $this->expectExceptionMessage(sprintf('Mapper needs to implement "%s"', DataMapperInterface::class)); 26 | new ChainMapper([ 27 | 'foo', 28 | 'bar' 29 | ]); 30 | } 31 | 32 | public function testMappersInChainWithEmptyMappersArray() 33 | { 34 | $this->expectException(InvalidArgumentException::class); 35 | $this->expectExceptionMessage(sprintf('Mapper needs to implement "%s"', DataMapperInterface::class)); 36 | new ChainMapper([ 37 | 'foo', 38 | 'bar' 39 | ]); 40 | } 41 | 42 | public function testGetDataFromTwoMappers() 43 | { 44 | $mapper = $this->createMock(DataMapperInterface::class); 45 | $mapper1 = $this->createMock(DataMapperInterface::class); 46 | 47 | $mapper->expects($this->once()) 48 | ->method('getData') 49 | ->will($this->throwException(new DataMappingException)); 50 | 51 | $mapper1->expects($this->once()) 52 | ->method('getData') 53 | ->will($this->returnValue('foo')); 54 | 55 | $chain = new ChainMapper([$mapper, $mapper1]); 56 | 57 | $this->assertSame( 58 | 'foo', 59 | $chain->getData('foo', 'bar') 60 | ); 61 | } 62 | 63 | public function testSetDataWithTwoMappers() 64 | { 65 | $mapper = $this->createMock(DataMapperInterface::class); 66 | $mapper1 = $this->createMock(DataMapperInterface::class); 67 | 68 | $mapper->expects($this->once()) 69 | ->method('setData') 70 | ->will($this->throwException(new DataMappingException)); 71 | 72 | $mapper1->expects($this->once()) 73 | ->method('setData') 74 | ->with('foo', 'bar', 'test') 75 | ->will($this->returnValue(true)); 76 | 77 | $chain = new ChainMapper([$mapper, $mapper1]); 78 | 79 | $chain->setData('foo', 'bar', 'test'); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /tests/DataMapper/ReflectionMapperTest.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | declare(strict_types=1); 11 | 12 | namespace FSi\Component\DataGrid\Tests\DataMapper; 13 | 14 | use FSi\Component\DataGrid\DataMapper\ReflectionMapper; 15 | use FSi\Component\DataGrid\Tests\Fixtures\EntityMapper; 16 | use FSi\Component\DataGrid\Exception\DataMappingException; 17 | use PHPUnit\Framework\TestCase; 18 | 19 | class ReflectionMapperTest extends TestCase 20 | { 21 | public function testGetter() 22 | { 23 | $mapper = new ReflectionMapper(); 24 | $entity = new EntityMapper(); 25 | $entity->setName('fooname'); 26 | 27 | $this->assertSame('fooname',$mapper->getData('name', $entity)); 28 | } 29 | 30 | public function testProtectedGetter() 31 | { 32 | $mapper = new ReflectionMapper(); 33 | $entity = new EntityMapper(); 34 | $entity->setSurname('foosurname'); 35 | 36 | $this->expectException(DataMappingException::class); 37 | $this->expectExceptionMessage(sprintf('Method "getSurname()" is not public in class "%s"', EntityMapper::class)); 38 | $mapper->getData('surname', $entity); 39 | } 40 | 41 | public function testHaser() 42 | { 43 | $mapper = new ReflectionMapper(); 44 | $entity = new EntityMapper(); 45 | $entity->setCollection('collection'); 46 | 47 | $this->assertTrue($mapper->getData('collection', $entity)); 48 | } 49 | 50 | public function testProtectedHaser() 51 | { 52 | $mapper = new ReflectionMapper(); 53 | $entity = new EntityMapper(); 54 | $entity->setPrivateCollection('collection'); 55 | 56 | $this->expectException(DataMappingException::class); 57 | $this->expectExceptionMessage(sprintf('Method "hasPrivateCollection()" is not public in class "%s"', EntityMapper::class)); 58 | $mapper->getData('private_collection', $entity); 59 | } 60 | 61 | public function testIser() 62 | { 63 | $mapper = new ReflectionMapper(); 64 | $entity = new EntityMapper(); 65 | $entity->setReady(true); 66 | 67 | $this->assertTrue($mapper->getData('ready', $entity)); 68 | } 69 | 70 | public function testProtectedIser() 71 | { 72 | $mapper = new ReflectionMapper(); 73 | $entity = new EntityMapper(); 74 | $entity->setProtectedReady(true); 75 | 76 | $this->expectException(DataMappingException::class); 77 | $this->expectExceptionMessage(sprintf('Method "isProtectedReady()" is not public in class "%s"', EntityMapper::class)); 78 | $mapper->getData('protected_ready', $entity); 79 | } 80 | 81 | public function testProperty() 82 | { 83 | $mapper = new ReflectionMapper(); 84 | $entity = new EntityMapper(); 85 | $entity->setId('bar'); 86 | 87 | $this->assertSame('bar',$mapper->getData('id', $entity)); 88 | } 89 | 90 | public function testPrivateProperty() 91 | { 92 | $mapper = new ReflectionMapper(); 93 | $entity = new EntityMapper(); 94 | $entity->setPrivateId('bar'); 95 | 96 | $this->expectException(DataMappingException::class); 97 | $this->expectExceptionMessage(sprintf('Property "private_id" is not public in class "%s"', EntityMapper::class)); 98 | $mapper->getData('private_id', $entity); 99 | } 100 | 101 | public function testSetter() 102 | { 103 | $mapper = new ReflectionMapper(); 104 | $entity = new EntityMapper(); 105 | 106 | $mapper->setData('name', $entity, 'fooname'); 107 | $this->assertSame('fooname',$entity->getName()); 108 | } 109 | 110 | public function testProtectedSetter() 111 | { 112 | $mapper = new ReflectionMapper(); 113 | $entity = new EntityMapper(); 114 | 115 | $this->expectException(DataMappingException::class); 116 | $this->expectExceptionMessage(sprintf('Method "setProtectedName()" is not public in class "%s"', EntityMapper::class)); 117 | $mapper->setData('protected_name', $entity, 'fooname'); 118 | } 119 | 120 | public function testAdder() 121 | { 122 | $mapper = new ReflectionMapper(); 123 | $entity = new EntityMapper(); 124 | 125 | $mapper->setData('tag', $entity, 'bar'); 126 | $this->assertSame(['bar'],$entity->getTags()); 127 | } 128 | 129 | public function testProtectedAdder() 130 | { 131 | $mapper = new ReflectionMapper(); 132 | $entity = new EntityMapper(); 133 | 134 | $this->expectException(DataMappingException::class); 135 | $this->expectExceptionMessage(sprintf('Method "addProtectedTag()" is not public in class "%s"', EntityMapper::class)); 136 | $mapper->setData('protected_tag', $entity, 'bar'); 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /tests/Extension/Core/ColumnType/ActionTest.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | declare(strict_types=1); 11 | 12 | namespace FSi\Component\DataGrid\Tests\Extension\Core; 13 | 14 | use FSi\Component\DataGrid\Extension\Core\ColumnType\Action; 15 | use FSi\Component\DataGrid\Extension\Core\ColumnTypeExtension\DefaultColumnOptionsExtension; 16 | use InvalidArgumentException; 17 | use PHPUnit\Framework\TestCase; 18 | use Symfony\Component\OptionsResolver\Exception\InvalidOptionsException; 19 | 20 | class ActionTest extends TestCase 21 | { 22 | /** 23 | * @var Action 24 | */ 25 | private $column; 26 | 27 | protected function setUp() 28 | { 29 | $column = new Action(); 30 | $column->setName('action'); 31 | $column->initOptions(); 32 | 33 | $extension = new DefaultColumnOptionsExtension(); 34 | $extension->initOptions($column); 35 | 36 | $this->column = $column; 37 | } 38 | 39 | public function testFilterValueEmptyActionsOptionType() 40 | { 41 | $this->expectException(InvalidOptionsException::class); 42 | $this->expectExceptionMessage('The option "actions" with value "boo" is expected to be of type "array", but is of type "string".'); 43 | $this->column->setOption('actions', 'boo'); 44 | $this->column->filterValue([]); 45 | } 46 | 47 | public function testFilterValueInvalidActionInActionsOption() 48 | { 49 | $this->expectException(InvalidArgumentException::class); 50 | $this->column->setOption('actions', ['edit' => 'asasdas']); 51 | $this->column->filterValue([]); 52 | } 53 | 54 | public function testFilterValueRequiredActionInActionsOption() 55 | { 56 | $this->column->setOption('actions', [ 57 | 'edit' => [ 58 | 'uri_scheme' => '/test/%s', 59 | ] 60 | ]); 61 | 62 | $this->assertSame( 63 | [ 64 | 'edit' => [ 65 | 'url' => '/test/bar', 66 | 'field_mapping_values' => [ 67 | 'foo' => 'bar' 68 | ] 69 | ] 70 | ], 71 | $this->column->filterValue([ 72 | 'foo' => 'bar' 73 | ]) 74 | ); 75 | } 76 | 77 | public function testFilterValueAvailableActionInActionsOption() 78 | { 79 | $this->column->setOption('actions', [ 80 | 'edit' => [ 81 | 'uri_scheme' => '/test/%s', 82 | 'domain' => 'fsi.pl', 83 | 'protocol' => 'https://', 84 | 'redirect_uri' => 'http://onet.pl/' 85 | ] 86 | ]); 87 | 88 | $this->assertSame( 89 | [ 90 | 'edit' => [ 91 | 'url' => 'https://fsi.pl/test/bar?redirect_uri=' . urlencode('http://onet.pl/'), 92 | 'field_mapping_values' => [ 93 | 'foo' => 'bar' 94 | ] 95 | ] 96 | ], 97 | $this->column->filterValue([ 98 | 'foo' => 'bar' 99 | ]) 100 | ); 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /tests/Extension/Core/ColumnType/BooleanTest.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | declare(strict_types=1); 11 | 12 | namespace FSi\Component\DataGrid\Tests\Extension\Core\ColumnType; 13 | 14 | use FSi\Component\DataGrid\Extension\Core\ColumnType\Boolean; 15 | use PHPUnit\Framework\TestCase; 16 | 17 | class BooleanTest extends TestCase 18 | { 19 | /** 20 | * @var Boolean 21 | */ 22 | private $column; 23 | 24 | protected function setUp() 25 | { 26 | $column = new Boolean(); 27 | $column->setName('available'); 28 | $column->initOptions(); 29 | 30 | $this->column = $column; 31 | } 32 | 33 | public function testBasicFilterValue() 34 | { 35 | $this->column->setOptions([ 36 | 'true_value' => 'true', 37 | 'false_value'=> 'false' 38 | ]); 39 | 40 | $this->assertSame($this->column->filterValue(true), 'true'); 41 | $this->assertNotSame($this->column->filterValue(true), 'false'); 42 | } 43 | 44 | public function testFilterValueWithTrueValuesInArray() 45 | { 46 | $this->column->setOption('true_value', 'true'); 47 | 48 | $this->assertSame( 49 | $this->column->filterValue([ 50 | true, 51 | true 52 | ]), 53 | 'true' 54 | ); 55 | } 56 | 57 | public function testFilterValueWithMixedValuesInArray() 58 | { 59 | $this->column->setOptions([ 60 | 'true_value' => 'true', 61 | 'false_value'=> 'false' 62 | ]); 63 | 64 | $this->assertSame( 65 | $this->column->filterValue([ 66 | true, 67 | 1, 68 | new \DateTime() 69 | ]), 70 | 'true' 71 | ); 72 | } 73 | 74 | 75 | public function testFilterValueWithFalseValuesInArray() 76 | { 77 | $this->column->setOptions([ 78 | 'true_value' => 'true', 79 | 'false_value'=> 'false' 80 | ]); 81 | 82 | $this->assertSame( 83 | $this->column->filterValue([ 84 | false, 85 | false 86 | ]), 87 | 'false' 88 | ); 89 | } 90 | 91 | public function testFilterValueWithMixedValuesAndFalseInArray() 92 | { 93 | $this->column->setOptions([ 94 | 'true_value' => 'true', 95 | 'false_value'=> 'false' 96 | ]); 97 | 98 | $this->assertSame( 99 | $this->column->filterValue([ 100 | true, 101 | 1, 102 | new \DateTime(), 103 | false 104 | ]), 105 | 'false' 106 | ); 107 | } 108 | 109 | public function testFilterValueWithMixedValuesAndNullInArray() 110 | { 111 | $this->column->setOptions([ 112 | 'true_value' => 'true', 113 | 'false_value'=> 'false' 114 | ]); 115 | 116 | $this->assertSame( 117 | $this->column->filterValue([ 118 | true, 119 | 1, 120 | new \DateTime(), 121 | null 122 | ]), 123 | 'true' 124 | ); 125 | } 126 | 127 | public function testFilterValueWithAllNullsInArray() 128 | { 129 | $this->column->setOptions([ 130 | 'true_value' => 'true', 131 | 'false_value'=> 'false' 132 | ]); 133 | 134 | $this->assertSame( 135 | $this->column->filterValue([ 136 | null, 137 | null 138 | ]), 139 | '' 140 | ); 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /tests/Extension/Core/ColumnType/CollectionTest.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | declare(strict_types=1); 11 | 12 | namespace FSi\Component\DataGrid\Tests\Extension\Core\ColumnType; 13 | 14 | use FSi\Component\DataGrid\Extension\Core\ColumnType\Collection; 15 | use PHPUnit\Framework\TestCase; 16 | 17 | class CollectionTest extends TestCase 18 | { 19 | public function test_filter_value() 20 | { 21 | $column = new Collection(); 22 | $column->initOptions(); 23 | $column->setOption('collection_glue', ' '); 24 | $value = [ 25 | ['foo', 'bar'], 26 | 'test' 27 | ]; 28 | 29 | $this->assertSame( 30 | ['foo bar', 'test'], 31 | $column->filterValue($value) 32 | ); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /tests/Extension/Core/ColumnType/MoneyTest.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | declare(strict_types=1); 11 | 12 | namespace FSi\Component\DataGrid\Tests\Extension\Core\ColumnType; 13 | 14 | use FSi\Component\DataGrid\Extension\Core\ColumnType\Money; 15 | use FSi\Component\DataGrid\Extension\Core\ColumnTypeExtension\DefaultColumnOptionsExtension; 16 | use PHPUnit\Framework\TestCase; 17 | 18 | class MoneyTest extends TestCase 19 | { 20 | /** 21 | * @var Money 22 | */ 23 | private $column; 24 | 25 | protected function setUp() 26 | { 27 | $column = new Money(); 28 | $column->setName('money'); 29 | $column->initOptions(); 30 | 31 | $extension = new DefaultColumnOptionsExtension(); 32 | $extension->initOptions($column); 33 | 34 | $this->column = $column; 35 | } 36 | 37 | public function testCurrencyOption() 38 | { 39 | $value = [ 40 | 'value' => 10, 41 | ]; 42 | 43 | $this->column->setOption('currency', 'PLN'); 44 | 45 | $this->assertSame( 46 | $this->column->filterValue($value), 47 | [ 48 | 'value' => '10.00 PLN', 49 | ] 50 | ); 51 | } 52 | 53 | public function testCurrencySeparatorOption() 54 | { 55 | $value = [ 56 | 'value' => 10, 57 | ]; 58 | 59 | $this->column->setOption('currency', 'PLN'); 60 | $this->column->setOption('value_currency_separator', '$ '); 61 | 62 | $this->assertSame( 63 | $this->column->filterValue($value), 64 | [ 65 | 'value' => '10.00$ PLN', 66 | ] 67 | ); 68 | } 69 | 70 | public function testCurrencyDecPointOption() 71 | { 72 | $value = [ 73 | 'value' => 10, 74 | ]; 75 | 76 | $this->column->setOption('currency', 'PLN'); 77 | $this->column->setOption('dec_point', '-'); 78 | 79 | $this->assertSame( 80 | $this->column->filterValue($value), 81 | [ 82 | 'value' => '10-00 PLN', 83 | ] 84 | ); 85 | } 86 | 87 | public function testCurrencyDecimalsOption() 88 | { 89 | $value = [ 90 | 'value' => 10, 91 | ]; 92 | 93 | $this->column->setOption('currency', 'PLN'); 94 | $this->column->setOption('decimals', 0); 95 | 96 | $this->assertSame( 97 | $this->column->filterValue($value), 98 | [ 99 | 'value' => '10 PLN', 100 | ] 101 | ); 102 | 103 | $this->column->setOption('currency', 'PLN'); 104 | $this->column->setOption('decimals', 5); 105 | 106 | $this->assertSame( 107 | $this->column->filterValue($value), 108 | [ 109 | 'value' => '10.00000 PLN', 110 | ] 111 | ); 112 | } 113 | 114 | public function testCurrencyPrecisionOption() 115 | { 116 | $value = [ 117 | 'value' => 10.326 118 | ]; 119 | 120 | $this->column->setOption('currency', 'PLN'); 121 | $this->column->setOption('precision', 2); 122 | 123 | $this->assertSame( 124 | $this->column->filterValue($value), 125 | [ 126 | 'value' => '10.33 PLN', 127 | ] 128 | ); 129 | 130 | $value = [ 131 | 'value' => 10.324, 132 | ]; 133 | $this->assertSame( 134 | $this->column->filterValue($value), 135 | [ 136 | 'value' => '10.32 PLN', 137 | ] 138 | ); 139 | } 140 | 141 | public function testCurrencyThousandsSepOption() 142 | { 143 | $value = [ 144 | 'value' => 10000, 145 | ]; 146 | 147 | $this->column->setOption('currency', 'PLN'); 148 | $this->column->setOption('thousands_sep', '.'); 149 | 150 | $this->assertSame( 151 | $this->column->filterValue($value), 152 | [ 153 | 'value' => '10.000.00 PLN', 154 | ] 155 | ); 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /tests/Extension/Core/ColumnType/NumberTest.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | declare(strict_types=1); 11 | 12 | namespace FSi\Component\DataGrid\Tests\Extension\Core\ColumnType; 13 | 14 | use FSi\Component\DataGrid\Extension\Core\ColumnType\Number; 15 | use FSi\Component\DataGrid\Extension\Core\ColumnTypeExtension\DefaultColumnOptionsExtension; 16 | use PHPUnit\Framework\TestCase; 17 | 18 | class NumberTest extends TestCase 19 | { 20 | /** 21 | * @var Number 22 | */ 23 | private $column; 24 | 25 | protected function setUp() 26 | { 27 | $column = new Number(); 28 | $column->setName('number'); 29 | $column->initOptions(); 30 | 31 | $extension = new DefaultColumnOptionsExtension(); 32 | $extension->initOptions($column); 33 | 34 | $this->column = $column; 35 | } 36 | 37 | public function testPrecision() 38 | { 39 | $value = [ 40 | 'number' => 10.123, 41 | ]; 42 | 43 | $this->column->setOption('precision', 2); 44 | $this->column->setOption('round_mode', Number::ROUND_HALF_UP); 45 | 46 | $this->assertSame( 47 | $this->column->filterValue($value), 48 | [ 49 | 'number' => 10.12, 50 | ] 51 | ); 52 | } 53 | 54 | public function testRoundMode() 55 | { 56 | $this->column->setOption('round_mode', Number::ROUND_HALF_UP); 57 | $this->assertSame( 58 | $this->column->filterValue([ 59 | 'number' => 10.123, 60 | ]), 61 | [ 62 | 'number' => 10.12, 63 | ] 64 | ); 65 | 66 | $this->assertSame( 67 | $this->column->filterValue([ 68 | 'number' => 10.126, 69 | ]), 70 | [ 71 | 'number' => 10.13, 72 | ] 73 | ); 74 | } 75 | 76 | public function testNumberFormat() 77 | { 78 | $this->assertEquals( 79 | [ 80 | 'number' => 12345678.1, 81 | ], 82 | $this->column->filterValue([ 83 | 'number' => 12345678.1, 84 | ]) 85 | ); 86 | 87 | $this->column->setOption('format', true); 88 | 89 | $this->assertEquals( 90 | [ 91 | 'number' => '12,345,678.10', 92 | ], 93 | $this->column->filterValue([ 94 | 'number' => 12345678.1, 95 | ]) 96 | ); 97 | 98 | $this->column->setOption('format_decimals', 0); 99 | 100 | $this->assertEquals( 101 | [ 102 | 'number' => '12,345,678', 103 | ], 104 | $this->column->filterValue([ 105 | 'number' => 12345678.1, 106 | ]) 107 | ); 108 | 109 | $this->column->setOption('format_decimals', 2); 110 | 111 | $this->assertEquals( 112 | [ 113 | 'number' => '12,345,678.10', 114 | ], 115 | $this->column->filterValue([ 116 | 'number' => 12345678.1, 117 | ]) 118 | ); 119 | 120 | $this->column->setOption('format_dec_point', ','); 121 | $this->column->setOption('format_thousands_sep', ' '); 122 | 123 | $this->assertEquals( 124 | [ 125 | 'number' => '12 345 678,10', 126 | ], 127 | $this->column->filterValue([ 128 | 'number' => 12345678.1, 129 | ]) 130 | ); 131 | 132 | $this->assertEquals( 133 | [ 134 | 'number' => '1 000,00', 135 | ], 136 | $this->column->filterValue([ 137 | 'number' => 1000, 138 | ]) 139 | ); 140 | 141 | $this->column->setOption('format_decimals', 0); 142 | 143 | $this->assertEquals( 144 | [ 145 | 'number' => '1 000', 146 | ], 147 | $this->column->filterValue([ 148 | 'number' => 1000, 149 | ]) 150 | ); 151 | 152 | $this->column->setOption('format', false); 153 | $this->assertEquals( 154 | [ 155 | 'number' => '1000', 156 | ], 157 | $this->column->filterValue([ 158 | 'number' => 1000, 159 | ]) 160 | ); 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /tests/Extension/Core/ColumnType/TextTest.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | declare(strict_types=1); 11 | 12 | namespace FSi\Component\DataGrid\Tests\Extension\Core\ColumnType; 13 | 14 | use FSi\Component\DataGrid\Extension\Core\ColumnType\Text; 15 | use PHPUnit\Framework\TestCase; 16 | 17 | class TextTest extends TestCase 18 | { 19 | public function testTrimOption() 20 | { 21 | $column = new Text(); 22 | $column->initOptions(); 23 | $column->setOption('trim', true); 24 | 25 | $value = [ 26 | ' VALUE ', 27 | ]; 28 | 29 | $this->assertSame( 30 | ['VALUE'], 31 | $column->filterValue($value) 32 | ); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /tests/Extension/Core/ColumnTypeExtension/DefaultColumnOptionsExtensionTest.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | declare(strict_types=1); 11 | 12 | namespace FSi\Component\DataGrid\Tests\Extension\Core\ColumntypeExtension; 13 | 14 | use FSi\Component\DataGrid\Extension\Core\ColumnTypeExtension\DefaultColumnOptionsExtension; 15 | use FSi\Component\DataGrid\Column\HeaderViewInterface; 16 | use FSi\Component\DataGrid\Column\ColumnTypeInterface; 17 | use PHPUnit\Framework\TestCase; 18 | 19 | class DefaultColumnOptionsExtensionTest extends TestCase 20 | { 21 | public function testBuildHeaderView() 22 | { 23 | $extension = new DefaultColumnOptionsExtension(); 24 | 25 | $column = $this->createMock(ColumnTypeInterface::class); 26 | $view = $this->createMock(HeaderViewInterface::class); 27 | 28 | $column->expects($this->at(0)) 29 | ->method('getOption') 30 | ->with('label') 31 | ->will($this->returnValue('foo')); 32 | 33 | $column->expects($this->at(1)) 34 | ->method('getOption') 35 | ->with('display_order') 36 | ->will($this->returnValue(100)); 37 | 38 | $view->expects($this->at(0)) 39 | ->method('setLabel') 40 | ->with('foo'); 41 | 42 | $view->expects($this->at(1)) 43 | ->method('setAttribute') 44 | ->with('display_order', 100); 45 | 46 | $extension->buildHeaderView($column, $view); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /tests/Extension/Core/CoreExtensionTest.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | declare(strict_types=1); 11 | 12 | namespace FSi\Component\DataGrid\Tests\Extension\Core; 13 | 14 | use FSi\Component\DataGrid\Extension\Core\CoreExtension; 15 | use FSi\Component\DataGrid\Extension\Core\EventSubscriber\ColumnOrder; 16 | use FSi\Component\DataGrid\DataGridEventInterface; 17 | use FSi\Component\DataGrid\DataGridViewInterface; 18 | use FSi\Component\DataGrid\Column\HeaderViewInterface; 19 | use PHPUnit\Framework\TestCase; 20 | 21 | class CoreExtensionTest extends TestCase 22 | { 23 | public function testLoadedTypes() 24 | { 25 | $extension = new CoreExtension(); 26 | $this->assertTrue($extension->hasColumnType('text')); 27 | $this->assertTrue($extension->hasColumnType('number')); 28 | $this->assertTrue($extension->hasColumnType('datetime')); 29 | $this->assertTrue($extension->hasColumnType('action')); 30 | $this->assertTrue($extension->hasColumnType('money')); 31 | $this->assertTrue($extension->hasColumnType('action')); 32 | 33 | $this->assertFalse($extension->hasColumnType('foo')); 34 | } 35 | 36 | public function testLoadedExtensions() 37 | { 38 | $extension = new CoreExtension(); 39 | $this->assertTrue($extension->hasColumnTypeExtensions('text')); 40 | $this->assertTrue($extension->hasColumnTypeExtensions('text')); 41 | $this->assertTrue($extension->hasColumnTypeExtensions('number')); 42 | $this->assertTrue($extension->hasColumnTypeExtensions('datetime')); 43 | $this->assertTrue($extension->hasColumnTypeExtensions('action')); 44 | $this->assertTrue($extension->hasColumnTypeExtensions('money')); 45 | $this->assertTrue($extension->hasColumnTypeExtensions('gedmo_tree')); 46 | $this->assertTrue($extension->hasColumnTypeExtensions('entity')); 47 | } 48 | 49 | public function testColumnOrder() 50 | { 51 | $subscriber = new ColumnOrder(); 52 | 53 | $cases = [ 54 | [ 55 | 'columns' => [ 56 | 'negative2' => -2, 57 | 'neutral1' => null, 58 | 'negative1' => -1, 59 | 'neutral2' => null, 60 | 'positive1' => 1, 61 | 'neutral3' => null, 62 | 'positive2' => 2, 63 | ], 64 | 'sorted' => [ 65 | 'negative2', 66 | 'negative1', 67 | 'neutral1', 68 | 'neutral2', 69 | 'neutral3', 70 | 'positive1', 71 | 'positive2', 72 | ] 73 | ], 74 | [ 75 | 'columns' => [ 76 | 'neutral1' => null, 77 | 'neutral2' => null, 78 | 'neutral3' => null, 79 | 'neutral4' => null, 80 | ], 81 | 'sorted' => [ 82 | 'neutral1', 83 | 'neutral2', 84 | 'neutral3', 85 | 'neutral4', 86 | ] 87 | ] 88 | ]; 89 | 90 | foreach ($cases as $case) { 91 | $columns = []; 92 | 93 | foreach ($case['columns'] as $name => $order) { 94 | $columnHeader = $this->createMock(HeaderViewInterface::class); 95 | 96 | $columnHeader 97 | ->expects($this->atLeastOnce()) 98 | ->method('getName') 99 | ->will($this->returnValue($name)); 100 | 101 | $columnHeader 102 | ->expects($this->atLeastOnce()) 103 | ->method('hasAttribute') 104 | ->will($this->returnCallback(function ($attribute) use ($order) { 105 | if (($attribute == 'display_order') && isset($order)) { 106 | return true; 107 | } else { 108 | return false; 109 | } 110 | })); 111 | 112 | $columnHeader 113 | ->expects($this->any()) 114 | ->method('getAttribute') 115 | ->will($this->returnCallback(function ($attribute) use ($order) { 116 | if (($attribute == 'display_order') && isset($order)) { 117 | return $order; 118 | } else { 119 | return null; 120 | } 121 | })); 122 | 123 | $columns[] = $columnHeader; 124 | } 125 | 126 | $view = $this->createMock(DataGridViewInterface::class); 127 | 128 | $self = $this; 129 | 130 | $view 131 | ->expects($this->once()) 132 | ->method('getColumns') 133 | ->will($this->returnValue($columns)); 134 | 135 | $view 136 | ->expects($this->once()) 137 | ->method('setColumns') 138 | ->will($this->returnCallback(function (array $columns) use ($self, $case) { 139 | $sorted = []; 140 | foreach ($columns as $column) { 141 | $sorted[] = $column->getName(); 142 | } 143 | $self->assertSame($case['sorted'], $sorted); 144 | })); 145 | 146 | $event = $this->createMock(DataGridEventInterface::class); 147 | $event 148 | ->expects($this->once()) 149 | ->method('getData') 150 | ->will($this->returnValue($view)); 151 | 152 | $subscriber->postBuildView($event); 153 | } 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /tests/Extension/Doctrine/ColumnType/EntityTypeTest.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | declare(strict_types=1); 11 | 12 | namespace FSi\Component\DataGrid\Tests\Extension\Doctrine\ColumnType; 13 | 14 | use FSi\Component\DataGrid\Tests\Fixtures\Entity as Fixture; 15 | use FSi\Component\DataGrid\Extension\Doctrine\ColumnType\Entity; 16 | use FSi\Component\DataGrid\Extension\Core\ColumnTypeExtension\DefaultColumnOptionsExtension; 17 | use FSi\Component\DataGrid\DataMapper\DataMapperInterface; 18 | use FSi\Component\DataGrid\DataGridInterface; 19 | use PHPUnit\Framework\TestCase; 20 | 21 | class EntityTypeTest extends TestCase 22 | { 23 | public function testGetValue() 24 | { 25 | $column = new Entity(); 26 | $column->setName('foo'); 27 | $column->initOptions(); 28 | 29 | $extension = new DefaultColumnOptionsExtension(); 30 | $extension->initOptions($column); 31 | 32 | // Call resolve at OptionsResolver. 33 | $column->setOptions([]); 34 | 35 | $object = new Fixture('object'); 36 | 37 | $dataGrid = $this->createMock(DataGridInterface::class); 38 | $dataMapper = $this->createMock(DataMapperInterface::class); 39 | 40 | $dataMapper->expects($this->once()) 41 | ->method('getData') 42 | ->will($this->returnValue(['foo' => 'bar'])); 43 | 44 | $dataGrid->expects($this->any()) 45 | ->method('getDataMapper') 46 | ->will($this->returnValue($dataMapper)); 47 | 48 | $column->setDataGrid($dataGrid); 49 | 50 | $column->getValue($object); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /tests/Extension/Doctrine/DoctrineExtensionTest.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | declare(strict_types=1); 11 | 12 | namespace FSi\Component\DataGrid\Tests\Extension\Doctrine; 13 | 14 | use FSi\Component\DataGrid\Extension\Doctrine\DoctrineExtension; 15 | use PHPUnit\Framework\TestCase; 16 | 17 | class DoctrineExtensionTest extends TestCase 18 | { 19 | public function testLoadedTypes() 20 | { 21 | $extension = new DoctrineExtension(); 22 | 23 | $this->assertTrue($extension->hasColumnType('entity')); 24 | $this->assertFalse($extension->hasColumnType('foo')); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /tests/Extension/Gedmo/ColumnType/Tree/TreeTypeTest.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | declare(strict_types=1); 11 | 12 | namespace FSi\Component\DataGrid\Tests\Extension\Gedmo\ColumnType; 13 | 14 | use FSi\Component\DataGrid\Tests\Fixtures\EntityTree; 15 | use FSi\Component\DataGrid\Tests\Fixtures\EntityManagerMock; 16 | use FSi\Component\DataGrid\Tests\Fixtures\EventManagerMock; 17 | use FSi\Component\DataGrid\Extension\Gedmo\ColumnType\Tree; 18 | use FSi\Component\DataGrid\Extension\Core\ColumnTypeExtension\DefaultColumnOptionsExtension; 19 | use FSi\Component\DataGrid\DataMapper\DataMapperInterface; 20 | use FSi\Component\DataGrid\DataGridInterface; 21 | use PHPUnit\Framework\TestCase; 22 | use Doctrine\Common\Persistence\ManagerRegistry; 23 | use Gedmo\Tree\TreeListener; 24 | use Doctrine\Common\Persistence\ObjectManager; 25 | use Doctrine\Common\Persistence\Mapping\ClassMetadataFactory; 26 | use Doctrine\ORM\Mapping\ClassMetadataInfo; 27 | use Gedmo\Tree\Strategy; 28 | 29 | class TreeTypeTest extends TestCase 30 | { 31 | public function testWrongValue() 32 | { 33 | $registry = $this->createMock(ManagerRegistry::class); 34 | $column = new Tree($registry); 35 | $column->setName('tree'); 36 | $column->initOptions(); 37 | 38 | $extension = new DefaultColumnOptionsExtension(); 39 | $extension->initOptions($column); 40 | 41 | $object = 'This is string, not object'; 42 | 43 | $this->expectException(\InvalidArgumentException::class); 44 | $this->expectExceptionMessage('Column "gedmo_tree" must read value from object.'); 45 | $column->getValue($object); 46 | } 47 | 48 | public function testGetValue() 49 | { 50 | $dataGrid = $this->createMock(DataGridInterface::class); 51 | $registry = $this->getManagerRegistry(); 52 | $dataMapper = $this->createMock(DataMapperInterface::class); 53 | 54 | $dataMapper->expects($this->any()) 55 | ->method('getData') 56 | ->will($this->returnValue(new EntityTree('foo'))); 57 | 58 | $column = new Tree($registry); 59 | $column->setName('tree'); 60 | $column->initOptions(); 61 | 62 | $extension = new DefaultColumnOptionsExtension(); 63 | $extension->initOptions($column); 64 | 65 | $column->setDataMapper($dataMapper); 66 | $column->setOption('field_mapping', ['foo']); 67 | $column->setDataGrid($dataGrid); 68 | $object = new EntityTree("foo"); 69 | 70 | $column->getValue($object); 71 | 72 | $view = $column->createCellView($object, '0'); 73 | $column->buildCellView($view); 74 | 75 | $this->assertSame( 76 | [ 77 | "row" => "0", 78 | "id" => "foo", 79 | "root" => "root", 80 | "left" => "left", 81 | "right" => "right", 82 | "level" => "level", 83 | "children" => 2, 84 | "parent" => "bar", 85 | ], 86 | $view->getAttributes() 87 | ); 88 | } 89 | 90 | protected function getManagerRegistry() 91 | { 92 | $managerRegistry = $this->createMock(ManagerRegistry::class); 93 | $managerRegistry->expects($this->any()) 94 | ->method('getManagerForClass') 95 | ->will($this->returnCallback(function() { 96 | $manager = $this->createMock(ObjectManager::class); 97 | $manager->expects($this->any()) 98 | ->method('getMetadataFactory') 99 | ->will($this->returnCallback(function() { 100 | $metadataFactory = $this->createMock(ClassMetadataFactory::class); 101 | 102 | $metadataFactory->expects($this->any()) 103 | ->method('getMetadataFor') 104 | ->will($this->returnCallback(function($class) { 105 | switch ($class) { 106 | case EntityTree::class: 107 | $metadata = $this->createMock(ClassMetadataInfo::class); 108 | $metadata->expects($this->any()) 109 | ->method('getIdentifierFieldNames') 110 | ->will($this->returnValue([ 111 | 'id' 112 | ])); 113 | break; 114 | } 115 | 116 | return $metadata; 117 | })); 118 | 119 | return $metadataFactory; 120 | })); 121 | 122 | $manager->expects($this->any()) 123 | ->method('getClassMetadata') 124 | ->will($this->returnCallback(function($class) { 125 | switch ($class) { 126 | case EntityTree::class: 127 | $metadata = $this->createMock(ClassMetadataInfo::class); 128 | $metadata->expects($this->any()) 129 | ->method('getIdentifierFieldNames') 130 | ->will($this->returnValue([ 131 | 'id' 132 | ])); 133 | $metadata->isMappedSuperclass = false; 134 | $metadata->rootEntityName = $class; 135 | break; 136 | } 137 | 138 | return $metadata; 139 | })); 140 | 141 | return $manager; 142 | })); 143 | 144 | $treeListener = $this->createMock(TreeListener::class); 145 | $strategy = $this->createMock(Strategy::class); 146 | 147 | $treeListener->expects($this->once()) 148 | ->method('getStrategy') 149 | ->will($this->returnValue($strategy)); 150 | 151 | $treeListener->expects($this->any()) 152 | ->method('getConfiguration') 153 | ->will($this->returnValue( 154 | [ 155 | 'left' => 'left', 156 | 'right' => 'right', 157 | 'root' => 'root', 158 | 'level' => 'level', 159 | 'parent' => 'parent' 160 | ] 161 | )); 162 | 163 | $strategy->expects($this->any()) 164 | ->method('getName') 165 | ->will($this->returnValue('nested')); 166 | 167 | $evm = new EventManagerMock([$treeListener]); 168 | $em = new EntityManagerMock(); 169 | $em->_setEventManager($evm); 170 | 171 | $managerRegistry->expects($this->any()) 172 | ->method('getManager') 173 | ->will($this->returnValue($em)); 174 | 175 | return $managerRegistry; 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /tests/Extension/Gedmo/GedmoDoctrineExtensionTest.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | declare(strict_types=1); 11 | 12 | namespace FSi\Component\DataGrid\Tests\Extension\Doctrine; 13 | 14 | use FSi\Component\DataGrid\Extension\Gedmo\GedmoDoctrineExtension; 15 | use Doctrine\Common\Persistence\ManagerRegistry; 16 | use PHPUnit\Framework\TestCase; 17 | 18 | class GedmoDoctrineExtensionTest extends TestCase 19 | { 20 | public function testLoadedTypes() 21 | { 22 | $registry = $this->createMock(ManagerRegistry::class); 23 | $extension = new GedmoDoctrineExtension($registry); 24 | 25 | $this->assertTrue($extension->hasColumnType('gedmo_tree')); 26 | $this->assertFalse($extension->hasColumnType('foo')); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /tests/Fixtures/ColumnType/FooType.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | declare(strict_types=1); 11 | 12 | namespace FSi\Component\DataGrid\Tests\Fixtures\ColumnType; 13 | 14 | use FSi\Component\DataGrid\Column\ColumnAbstractType; 15 | 16 | class FooType extends ColumnAbstractType 17 | { 18 | public function getId(): string 19 | { 20 | return 'foo'; 21 | } 22 | 23 | public function filterValue($value) 24 | { 25 | return $value; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /tests/Fixtures/Entity.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | declare(strict_types=1); 11 | 12 | namespace FSi\Component\DataGrid\Tests\Fixtures; 13 | 14 | class Entity 15 | { 16 | private $name; 17 | 18 | private $author; 19 | 20 | private $category; 21 | 22 | public function __construct($name) 23 | { 24 | $this->name = $name; 25 | } 26 | 27 | public function setName($name) 28 | { 29 | $this->name = $name; 30 | return $this; 31 | } 32 | 33 | public function getName() 34 | { 35 | return $this->name; 36 | } 37 | 38 | public function setAuthor($author) 39 | { 40 | $this->author = $author; 41 | return $this; 42 | } 43 | 44 | public function getAuthor() 45 | { 46 | return $this->author; 47 | } 48 | 49 | /** 50 | * @return EntityCategory 51 | */ 52 | public function getCategory() 53 | { 54 | return $this->category; 55 | } 56 | 57 | public function setCategory($category) 58 | { 59 | $this->category = $category; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /tests/Fixtures/EntityCategory.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | declare(strict_types=1); 11 | 12 | namespace FSi\Component\DataGrid\Tests\Fixtures; 13 | 14 | class EntityCategory 15 | { 16 | public $id; 17 | public $name; 18 | 19 | public function __construct($id, $name) 20 | { 21 | $this->name = $name; 22 | $this->id = $id; 23 | } 24 | 25 | public function getName() 26 | { 27 | return $this->name; 28 | } 29 | 30 | public function setName($name) 31 | { 32 | $this->name = $name; 33 | } 34 | 35 | public function __toString() 36 | { 37 | return $this->name; 38 | } 39 | 40 | public function getId() 41 | { 42 | return $this->id; 43 | } 44 | 45 | public function setId($id) 46 | { 47 | $this->id = $id; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /tests/Fixtures/EntityManagerMock.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | declare(strict_types=1); 11 | 12 | namespace FSi\Component\DataGrid\Tests\Fixtures; 13 | 14 | use Doctrine\ORM\EntityManager; 15 | use FSi\Component\DataGrid\Tests\Fixtures\EntityRepositoryMock; 16 | 17 | class EntityManagerMock extends EntityManager 18 | { 19 | protected $eventManager; 20 | 21 | protected $metadataFactory; 22 | 23 | public function __construct() 24 | { 25 | } 26 | 27 | public function _setEventManager($eventManager) 28 | { 29 | $this->eventManager = $eventManager; 30 | } 31 | 32 | public function _setMetadataFactory($metadataFactory) 33 | { 34 | $this->metadataFactory = $metadataFactory; 35 | } 36 | 37 | public function getMetadataFactory() 38 | { 39 | return $this->metadataFactory; 40 | } 41 | 42 | public function getEventManager() 43 | { 44 | return $this->eventManager; 45 | } 46 | 47 | public function getClassMetadata($className) 48 | { 49 | return null; 50 | } 51 | 52 | public function getRepository($entityName) 53 | { 54 | return new EntityRepositoryMock(); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /tests/Fixtures/EntityMapper.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | declare(strict_types=1); 11 | 12 | namespace FSi\Component\DataGrid\Tests\Fixtures; 13 | 14 | class EntityMapper 15 | { 16 | public $id; 17 | 18 | private $private_id; 19 | 20 | private $name; 21 | 22 | private $surname; 23 | 24 | private $collection; 25 | 26 | private $private_collection; 27 | 28 | private $ready; 29 | 30 | private $protected_ready; 31 | 32 | private $tags = []; 33 | 34 | public function setId($id) 35 | { 36 | $this->id = $id; 37 | } 38 | 39 | public function setName($name) 40 | { 41 | $this->name = $name; 42 | return $this; 43 | } 44 | 45 | protected function setProtectedName($name) 46 | { 47 | $this->name = $name; 48 | return $this; 49 | } 50 | 51 | public function setPrivateId($id) 52 | { 53 | $this->private_id = $id; 54 | } 55 | 56 | public function getName() 57 | { 58 | return $this->name; 59 | } 60 | 61 | protected function getSurname() 62 | { 63 | return $this->surname; 64 | } 65 | 66 | public function setSurname($surname) 67 | { 68 | $this->surname = $surname; 69 | } 70 | 71 | public function hasCollection() 72 | { 73 | return isset($this->collection); 74 | } 75 | 76 | public function setCollection($collection) 77 | { 78 | $this->collection = $collection; 79 | } 80 | 81 | public function setPrivateCollection($collection) 82 | { 83 | $this->private_collection = $collection; 84 | } 85 | 86 | private function hasPrivateCollection() 87 | { 88 | return isset($this->privatecollection); 89 | } 90 | 91 | public function setReady($ready) 92 | { 93 | $this->ready = (boolean)$ready; 94 | } 95 | 96 | public function isReady() 97 | { 98 | return $this->ready; 99 | } 100 | 101 | public function setProtectedReady($ready) 102 | { 103 | $this->protected_ready = (boolean)$ready; 104 | } 105 | 106 | protected function isProtectedReady() 107 | { 108 | return $this->protected_ready; 109 | } 110 | 111 | public function addTag($tag) 112 | { 113 | $this->tags[] = $tag; 114 | } 115 | 116 | public function getTags() 117 | { 118 | return $this->tags; 119 | } 120 | 121 | protected function addProtectedTag($tag) 122 | { 123 | $this->tags[] = $tag; 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /tests/Fixtures/EntityRepositoryMock.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | declare(strict_types=1); 11 | 12 | namespace FSi\Component\DataGrid\Tests\Fixtures; 13 | 14 | use Gedmo\Tree\RepositoryInterface; 15 | 16 | class EntityRepositoryMock implements RepositoryInterface 17 | { 18 | public function getRootNodes($sortByField = null, $direction = 'asc') 19 | { 20 | } 21 | 22 | public function getNodesHierarchy($node = null, $direct = false, array $options = [], $includeNode = false) 23 | { 24 | } 25 | 26 | public function getChildren($node = null, $direct = false, $sortByField = null, $direction = 'ASC', $includeNode = false) 27 | { 28 | } 29 | 30 | public function childCount($node = null, $direct = false) 31 | { 32 | return 2; 33 | } 34 | 35 | public function childrenHierarchy($node = null, $direct = false, array $options = [], $includeNode = false) 36 | { 37 | } 38 | 39 | public function buildTree(array $nodes, array $options = []) 40 | { 41 | } 42 | 43 | public function buildTreeArray(array $nodes) 44 | { 45 | } 46 | 47 | public function setChildrenIndex($childrenIndex) 48 | { 49 | } 50 | 51 | public function getChildrenIndex() 52 | { 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /tests/Fixtures/EntityTree.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | declare(strict_types=1); 11 | 12 | namespace FSi\Component\DataGrid\Tests\Fixtures; 13 | 14 | class EntityTree 15 | { 16 | public $id; 17 | public $left = 'left'; 18 | public $right = 'right'; 19 | public $root = 'root'; 20 | public $level = 'level'; 21 | public $parent; 22 | 23 | public function __construct($id = null) 24 | { 25 | $this->id = $id; 26 | } 27 | 28 | public function getParent() 29 | { 30 | if (!isset($this->parent)) { 31 | $this->parent = new EntityTree("bar"); 32 | } 33 | 34 | return $this->parent; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /tests/Fixtures/EventManagerMock.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | declare(strict_types=1); 11 | 12 | namespace FSi\Component\DataGrid\Tests\Fixtures; 13 | 14 | class EventManagerMock 15 | { 16 | protected $listeners; 17 | 18 | public function __construct($listeners) 19 | { 20 | $this->listeners = $listeners; 21 | } 22 | 23 | public function getListeners() 24 | { 25 | return [$this->listeners]; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /tests/Fixtures/FooExtension.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | declare(strict_types=1); 11 | 12 | namespace FSi\Component\DataGrid\Tests\Fixtures; 13 | 14 | use FSi\Component\DataGrid\DataGridAbstractExtension; 15 | use FSi\Component\DataGrid\Tests\Fixtures\ColumnType; 16 | 17 | class FooExtension extends DataGridAbstractExtension 18 | { 19 | protected function loadColumnTypes(): array 20 | { 21 | return [ 22 | new ColumnType\FooType(), 23 | ]; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /tests/Fixtures/GedmoTreeListenerMock.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | declare(strict_types=1); 11 | 12 | namespace FSi\Component\DataGrid\Tests\Fixtures; 13 | 14 | class GedmoTreeListenerMock 15 | { 16 | } 17 | --------------------------------------------------------------------------------