Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

[RFC] CSV import for options form field #444

Merged
merged 42 commits into from
Jan 27, 2017

Conversation

qzminski
Copy link
Member

Added the CSV import feature for the options field in the form generator. Also introduced the CSV Import utility which is now used also by ListWizard and TableWizard widgets.

@leofeyer leofeyer added this to the 4.2.0 milestone Feb 12, 2016
@Toflar
Copy link
Member

Toflar commented Feb 15, 2016

Thank you for the PR Kamil! I would like to make some general comments on what I don't like about the architecture of this PR. I think, if you added the unit tests - that are mandatory to make it into the core-bundle - already, you would have noticed some of the problems :-)

  1. General question: I think there are many CSV libraries out there and I wonder why we don't make use of one of them?

  2. There are a few violations of separation of concerns. Especially, the architecture of CsvImportUtil feels very strange to me:

    a) Why would it need the framework? Contao\Message should be adapted so it can handle Symfony's flash messages, which would immediately drop the need of that one.
    b) Why would it need the framework part 2: You don't need System::setCookie(). Firstly, we set cookies on the request now, not on System anymore and then why is it the task of the CsvImportUtil to set cookies?
    c) Why does the CsvImportUtil throw RedirectResponseException's? Redirecting is certainly not a task of a "csv import util" but belongs to a controller :-)

A good indicator of wrong dependencies are the constructor arguments. Why should a csv import utility care about a service named tokenStorage?

Imho, you have a good start here but what you need to do is to split up the tasks of your CsvImportUtil even more:

  1. The real CSV handling stuff, I doubt you need to reinvent the wheel here and you can maybe use a third party library from packagist.
  2. The view component which is responsible to generate your back end views.

@qzminski
Copy link
Member Author

Just as a word for introduction, the class is currently designed to handle only the backend CSV import that is commonly used for different widgets in Contao.

  1. Why would you like to use the CSV library if reading a CSV file is a matter of three lines of code using fgetcsv function?

2a. Indeed Contao\Message should be adapted to Symfony's flash messages but at the time being it is not. Once it is adapted, I agree it should be used instead.

2b. That was just copy-pasted from the old method. Can go the controller.

2c. Yeah, that should probably go to the controller.

The tokenStorage is passed to the class so it can determine the current user file uploader. It can belong to controller as well which will tell which FileUpload instance should the class use.

I'll introduce those changes soon. Controller will grow a little bigger than it currently is but that shouldn't be a problem huh? @aschempp ?

@aschempp
Copy link
Member

I haven't completely reviewed the code yet, but I remember we discussed the FileUpload instance should be passed to the utility class, which would probably make $tokenStorage unnecessary.


$csvImport = new CsvImportUtil(
$this->connection,
new Message(),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should use $this->framework->createInstance('Contao\Message');

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Of course not.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All the methods are static??

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah well, so you mean we should use getAdapter :D

@Toflar
Copy link
Member

Toflar commented Feb 15, 2016

2a. Indeed Contao\Message should be adapted to Symfony's flash messages but at the time being it is not. Once it is adapted, I agree it should be used instead.

Here you go: #445 😄

@qzminski
Copy link
Member Author

I have introduced some improvements. However, I am not sure how to move RedirectResponseException to the controller. Should then controller listen on util class if it throws a regular \Exception and if so trigger RedirectResponseException? Any suggestions?

@aschempp
Copy link
Member

I have introduced some improvements. However, I am not sure how to move RedirectResponseException to the controller. Should then controller listen on util class if it throws a regular \Exception and if so trigger RedirectResponseException? Any suggestions?

I'm not entirely sure. But maybe it should throw an "generic" exception including the error message that is catched by the controller. The controller would then add the flash message, and it would do the reload. For the other redirect, the run() method could return true/false (or just nothing, really) and the controller could then also do a redirect. It always needs to redirect if run() succeeds (which feels kinda strange…)

@qzminski
Copy link
Member Author

I have done some improvements, please review them.

}

// Set the backend offset cookie
$this->framework->getAdapter('Contao\System')->setCookie('BE_PAGE_OFFSET', 0, 0);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we really have to reset the Cookie.
If not, we only need to inject the max filesize below and are completely independent from the framework in this class, which I consider a goal for every new class in Contao.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess that's just how it currently behaves…

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't we reset the cookie in other way, i.e. not through Contao framework? Create the RedirectResponse with appropriate cookie which would then be used as constructor argument in ResponseException?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pretty sure you can (and should!) add the cookie to the response :-)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@aschempp
Copy link
Member

I think we should fall back to FileUpload if no token or user is available. Also, it should be possible to set the uploader script.

@qzminski
Copy link
Member Author

@aschempp uploader fallback set in the CsvImportUtil itself or in the backend controller? Frankly I would put it in the utility class so it behaves just similar to the getTemplate() method (returns fallback if no template was set).

@aschempp
Copy link
Member

Sorry, I totally agree. But the controller should fall back to the default if the user can't be found (instead of not creating an instance of the utility).

@aschempp
Copy link
Member

aschempp commented Mar 3, 2016

I think this can be updated to RFC or RTM, what do you think?

@qzminski qzminski changed the title [POC] CSV import for options form field [RTM] CSV import for options form field Mar 3, 2016
@qzminski
Copy link
Member Author

qzminski commented Mar 3, 2016

@contao/developers - request for feedback

@leofeyer leofeyer changed the title [RTM] CSV import for options form field [RFC] CSV import for options form field Mar 3, 2016
@leofeyer
Copy link
Member

leofeyer commented Mar 3, 2016

RTM = ready to merge
RFC = request for comments

I have changed the title accordingly :)

@aschempp
Copy link
Member

bump

@leofeyer leofeyer modified the milestone: 4.2.0 Apr 15, 2016
*
* @author Kamil Kuzminski <https://github.com/qzminski>
*/
class BackendCsvImportController
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Controllers in Symfony use Action as method suffix. I think they should all use that too here.

@Toflar
Copy link
Member

Toflar commented Apr 28, 2016

As I stated on February 15th already, Unit Tests are missing. I know that not everything of it will be testable. Especially the parts where we are working in legacy code. However, the controllers as well as the import util is new code and if it's not unit testable, something must be wrong. We need tests here :)

@qzminski
Copy link
Member Author

@Toflar can you please review if the last commits cover our Skype discussion conclusions?

contao.controller.abstract_csv_import:
class: Contao\CoreBundle\Controller\AbstractCsvImportController
abstract: true
calls:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As these are mandatory, you must use constructor injection rather than setter injections. Setter injections are for optional dependencies.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have changed that but left the setters to allow service overriding by child classes.

*
* @return Response
*/
protected function importFromTemplate(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CS violation

private function getSeparators($allowLinebreak = false)
{
$separators = [
self::SEPARATOR_COMMA => [
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CS violation

throw new \RuntimeException(sprintf($GLOBALS['TL_LANG']['ERR']['filetype'], $extension));
}

// Drop TL_ROOT when we got rid of FileUpload which uses it
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment can be removed. It is missing the TODO: prefix and I guess we are all aware of the TL_ROOT deprecation.

@leofeyer
Copy link
Member

I actually don't understand why we need this change. Now we have rewritten two of many legacy widgets – why not all of them? We have had the exact same discussion in #355 and this was the summary:

As discussed in Mumble on April 28th, this change is a mixture between legacy code and new code, which we cannot really befriend with.

What makes this PR different?

@leofeyer leofeyer added this to the 4.4.0 milestone Jan 25, 2017
@leofeyer
Copy link
Member

@qzminski
Copy link
Member Author

Just did.

@leofeyer
Copy link
Member

Thanks. 😉

@leofeyer
Copy link
Member

I have replaced

<?php if ($this->messages): ?>
    <div class="tl_message">
        <?php foreach ($this->messages as $type => $messages): ?>
            <?php foreach ($messages as $message): ?>
                <p class="tl_<?= $type ?>"><?= $message ?></p>
            <?php endforeach; ?>
        <?php endforeach; ?>
    </div>
<?php endif; ?>

with

<?= Message::generate() ?>

which does exactly the same, doesn't it? Or am I missing something?

{
$this->framework->initialize();

$request = $this->requestStack->getCurrentRequest();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

$request can be null and we are not handling the case. What would be the right thing to do in this case?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

throw an exception? We can't handle that case, it cannot happen in the Contao backend.

@qzminski
Copy link
Member Author

I think the only difference with Message::generate() is that it gets the flash messages from the session top level service, whereas the initial code takes those messages from the session bound to the request object we use in controller. But I guess that session instance is always the same.

Also previously we fetched the messages by getting all() of them from flash bag whereas Message takes only those of Contao scope. But that should not be an issue here either.

Thus I'm fine with that change, just also drop line 214 in that case:

https://github.com/qzminski/contao-core-bundle/blob/1023d3c798d71bd037dc43e0eb0542f8ef7df03a/src/Controller/BackendCsvImportController.php#L214

@aschempp
Copy link
Member

Be aware that there is only one session. Subrequests will inherit the main session from the master request, so I guess @leofeyer is right with the changes.

@leofeyer leofeyer self-assigned this Jan 27, 2017
@leofeyer leofeyer merged commit 481012f into contao:develop Jan 27, 2017
@leofeyer
Copy link
Member

Big thanks @qzminski, @aschempp and @Toflar. This was a huge PR. 😄

agoat pushed a commit to agoat/contao-core-bundle that referenced this pull request Apr 10, 2017
* Make Symfony 3.2 the minimum requirement (see contao#630).

* Set the encryption key from the kernel secret by default (see contao#660).

* Stop using the deprecated QuestionHelper::setInputStream() method.

* Update Dropzone to version 4.

* Prefer the caret operator over the tilde operator in the composer.json file.

* Add the contao.root_dir parameter (see contao#662).

* Update the change log.

* Stop using the contao-components/all meta package.

* Update the README.md file.

* Deprecate the contao:version command (see contao#668).

* Update the installation path.

* Auto-select the active page in the quick navigation/link module (see contao/core#8587).

* Look up the form class and allow to choose the type (see contao/core#8527).

* Add PHP Toolbox support (see contao#565).

* Remove the arrow brackets in the book navigation template (see contao/core#8607).

* Add a bottom padding to the buttons layer.

* Add the contao.web_dir parameter (see contao/installation-bundle#40).

* Fix the tests.

* Match security firewall based on request scope (see contao#677).

* Fix an issue found by Scrutinizer.

* Use the contao.web_dir parameter in the Combiner (see contao#679).

* Fix the tests.

* Add stripRootDir() method to System class (see contao#683).

* Add the contao.image.target_dir parameter (see contao#684).

* The ContaoCoreExtension::overwriteImageTargetDir() is not deprecated.

* Support custom backend routes (see contao#512).

* Use the scope matcher instead of checking the request attribute (see contao#688).

* Replace every occurrence of $contaoFramework with $framework.

* Fix an issue found by Scrutinizer.

* Fix deprecations in unit tests (see contao#687).

* Added a DBAL field type for UUIDs (see contao#415).

* Support importing form field options from a CSV field (see contao#444).

* Fix the coding style and the unit tests.

* Add the Doctrine field type in the config.yml file.

doctrine:
    dbal:
        types:
            binary_string:
                class: "Contao\\CoreBundle\\Doctrine\\DBAL\\Types\\BinaryStringType"
                commented: true

* Add a basic unit test for the BackendCsvImportController class.

* Update the change log.

* Fix rebuilding the search index (see contao#689).

* Also handle „no origin“ and „empty origin“ in the CORS provider.

* Remove an unused use statement.

* Remove the security.yml file and update the README.md file.

* Improve the e-mail extraction in the text element (thanks to Martin Auswöger).

* Rename the Test namespace to Tests.

* Update the composer.json file.

* Update the .php_cs file.

* Raise the minimum PHP version to 5.6 (see contao#701).

* Support using objects in callback arrays (see contao#699).

* Use try-finally blocks to close all output buffers when downloading a file (see contao#714).

* Fix the coding style.

* Only prefix an all numeric alias when standardizing (see contao#707).

* Adjust the test namespaces.

* Allow to manually pass a value to any widget (see contao#674).

* Add a change log entry and fix the tests.

* Disable the picker buttons if the main window does not show a picker.

* Use the file manager instead of the file picker.

* Use the site structure instead of the page picker.

* Always show the selected nodes.

* Add the menu builder.
leofeyer added a commit that referenced this pull request Apr 25, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants