New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Refactored the Grid component #9470
Refactored the Grid component #9470
Conversation
src/Core/Grid/GridFactory.php
Outdated
/** | ||
* @param GridDefinitionFactoryInterface $definitionFactory | ||
* @param GridDataProviderInterface $dataProvider | ||
* @param GridDataFactoryInterface $dataFactory |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PHPDoc is missing here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I may join to help you with update if needed. 👍
/** | ||
* @param ColumnCollectionInterface $columns | ||
*/ | ||
public function setColumns($columns) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
are you sure we need these setters?
- they are not part of contract
- you can do columns and actions modification using
$definition->getColumns()->add(..add column)
or$definition->getColumns()->remove(...column to remove)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't need them to be part of the contract as we retrieve a Definition instance and not a DefinitionInterface.
We need at least setName
, isn't it? And people will expect setColumns() for sure
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
but shouldnt Definition
be final as it implements DefinitionInterface
?
Regarding setName()
, i guess you should be able to change it, but with other setter, i dont know.. :/
Definition already has ColumnCollection set (in constructor when created), there is no need to be able to set it again, because if you want to change something in it, you can do using $definition->getColumns()
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok so the module and the docs should describe that: I keep the setter for the name.
Also, there is no link between implementing an interface and being final. The way I see it:
-
implementing the interface means you have access to methods available into the interface, you'll see that in the end the DefinitionFactory create a concrete implementation of Definition class :) This is why it's ok to have a setter into our concrete implementation that is not part of contract because we never assume we have this setter on something that expect the interface.
-
it is true that I'd use to make my final classes implement an interface to let developers use their own implementations: we're doing an extensible CMS and I'm removing overrides system
-
for me, making my class final means "this class have and only have the behavior we expect for it, if you want to alter it, rely on the contract and not on our own"
this is really defensive programming, I try to stabilize a 10 years old software and I need to ensure that what we provide can't be broken :)
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
final public function create() | ||
final public function getDefinition() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hmm, isnt create()
or createNew()
a better name? It is a factory, it's responsibility is to create new instances every time you ask for it, getDefinition()
is weird imo. 🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no, create or createNew isn't giving me any information on what I retrieve.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
$productsDefinition = $productsGridDefinitionFactory->createNew()
it gives information when variable naming is correct :) as getDefinition()
does not give information whether we get new or same instance every time (my opinion).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it's a factory: what do you expect to retrieve from a factory? 👼
} | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
public function createUsingSearchCriteria(SearchCriteriaInterface $searchCriteria) | ||
public function getGrid(SearchCriteriaInterface $searchCriteria) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same here with naming, are you getting or creating new grid. What's more, we should not repeat Grid
in method names is guess?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it's consistent naming:
- getDefinition() : DefinitionInterface
- getGrid() : GridInterface
- ...
this way people can't be wrong on what they expect to get using these methods, even if there have no access to docs.
src/Core/Grid/GridFactory.php
Outdated
$definition = $this->definitionFactory->getDefinition(); | ||
|
||
$this->hookDispatcher->dispatchForParameters('action'.$definition->getId().'GridDefinitionModifier', [ | ||
'definition' => &$definition, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you dont need to pass $definition
as a reference since it is object, all modifications performed inside hooks will be applied either way.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not really sure of it, this needs to be tested carefully (and so on, some objects should be available for edition and other ones only for read)
src/Core/Grid/GridFactory.php
Outdated
|
||
$filterForm = $this->filterFormFactory->create($definition); | ||
$filterForm->setData($searchCriteria->getFilters()); | ||
$this->hookDispatcher->dispatchForParameters('action'.$definition->getId().'GridFilterFormModifier', [ | ||
'filterForm' => &$filterForm, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same with reference here.
src/Core/Grid/GridFactory.php
Outdated
|
||
$data = $this->dataProvider->getData($searchCriteria); | ||
$filterForm->setData($searchCriteria->getFilters()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe setData
before hook is dispatched?
@@ -97,7 +97,7 @@ public function indexAction(Request $request, EmailLogsFilter $filters) | |||
public function searchAction(Request $request) | |||
{ | |||
$definitionFactory = $this->get('prestashop.core.grid.definition.factory.email_logs'); | |||
$emailLogsDefinition = $definitionFactory->create(); | |||
$emailLogsDefinition = $definitionFactory->getDefinition(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
since hook GridDefinitionModifier
were move from DefinitionFactory
to GridFactory
we have an issue here.
Imagine some module updated grid definition with new filter/column, the changes wont be reflected here, but when presenting grid, they would be as hooks are dispatched in Presenter
only.
That's why I wanted to keep hooks in factories to make sure that each time you create new instance, hook is dispatched and you have final instance.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not really, it's because we're doing object construction in controllers when we shouldn't do it: we should rely on the grid directly instead of rebuilding the form from grid definition :/
in all cases, depending on hook dispatcher everywhere is a terrible idea: it's like having access to the context
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it's because we're doing object construction in controllers when we shouldn't do
agree, but if we were to create whole grid, we would need to do unnecessary SQL queries even though db data is not used here, only filter form is important. :/
At the end:
“There are no solutions. There are only trade-offs.”
― Thomas Sowell
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As I can't find (right now) a better way to do this, I change but I'm not satisfied :(
@@ -1,3 +1,5 @@ | |||
parameters: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
at some point this file could be split into several files, like grid/data_factory.yml
, grid/definition_factory.yml
& etc. Similar as we did with forms in bundle, as there will be quite a few service definitions regarding grid, wdyt? :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree, but looking at services I'm pretty sure we can experiment auto configuration instead: don't you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you mean autowire?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
both!
|
||
use Exception; | ||
|
||
class InvalidDataException extends Exception implements ExceptionInterface |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i guess we could have base GridException extends Exception implements ExceptionInterface
and instead class InvalidDataException extends GridException
? other exceptions would need to be updated too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed with @sarjon 👍
'grid' => $grid, | ||
]); | ||
|
||
return $presentedGrid; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
according to our schema, it should produce immutable view, but array is mutable. What if we had GridView
instance? Similar as Forms do, except much simpler.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
later :)
@mickaelandrieu regarding component update, i have few ideas :)
|
And this will make the component dependent of the core => meh
Don't get caught into this trap! Every use case can evolve and have his own lifecycle, from a domain point of view, Grid actions, Bulk actions and Row actions are different things |
then lets get rid of it? 😃 it is not really that useful (as it seemed before) and not even sure methods provided by this abstract collection are used at all. |
we need the collections to be countable and iterable and I'm pretty sure we will add some helpers like filter(), map(), something you can retrieve in Laravel Collections for instance :) But I want the community to ask for it before introduce the features |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@sarjon Is it ok for you?
@sarjon we'll address the AbstractException question if we need to add more exceptions, the scope of this pull request was to be compliant with docs about Hooks and naming. But now it's merged, you can rebase your pull request about data |
ping @eternoendless this don't needs QA, in case of error, I'll fix it. |
This change is