-
-
Notifications
You must be signed in to change notification settings - Fork 356
Enabling files upload #834
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
Conversation
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.
Hey @Lustmored!
Nice to see this PR :). I'm following your logic. Changing the POST situation to send normal "form" data where one key is JSON (to make room for the files)... seems to make sense to me.
So, I'm a 👍 for this. However, while I think the "small steps" approach is the way to go, I'm not sure it makes sense to merge this in without having some fully-functional version of it (i.e. these are building blocks, but not useable in any way yet). Perhaps we do need a bigger PR, but building it piece-by-piece is a nice method (this WAS relatively easy to review).
@@ -26,9 +27,15 @@ export default class { | |||
Accept: 'application/vnd.live-component+html', | |||
}; | |||
|
|||
const totalFiles = Object.entries(files).reduce( |
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.
Could just be?
const totalFiles = Object.keys(files).length
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 think it's not - that would count FileList
objects passed, but they can be empty (like inputs with no files selected). In that case we should not count them.
Actually maybe what would be more fitting is to first filter files
to remove empty FileList
s and then just use what you propose.
@@ -31,17 +31,19 @@ public function testCanBatchActions(): void | |||
|
|||
$this->browser() | |||
->throwExceptions() | |||
->get('/_components/with_actions', ['json' => ['props' => $dehydrated->getProps()]]) | |||
->get('/_components/with_actions', ['query' => ['props' => json_encode($dehydrated->getProps())]]) |
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.
This is an unrelated fix, right? I mean, it was "wrong" before and was just working by some chance, right?
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.
Right - it was simulating GET
request, but with data sent in body request as a JSON. I think it shouldn't be possible, but it was for some reason. Now that POST
is handled differently it needed a proper fix :)
On the other hand - in GET
we use props
as a key to send all data, while for the POST
method I used data
. I only notices this inconsistency at the very end. But on the other hand it looks weird to me to have props
key that contains an array with props
method in it. Do you think it's worth to change data
key to props
to match query parameter name?
Loos like they're related to batch controller? I don't see anything obvious, unfortunately. It must be related to the new data format, but you've updated things like I would expect. |
To clarify, on the back-end, the uploaded files will be available via |
Sure thing, I will push it a bit until we can actually upload files this way (so that they are available via
With this PR - not yet, but this is the initial goal right now, and more sophisticated backend stuff will follow 👍 |
I think if we can access |
That would probably be enough. We’ll need some frontend implementation, and some thought should be given to when these are sent. Like, it probably doesn’t make sense to upload a file during a normal re-render / model send, as you’d upload the file… but your component wouldn’t be saving yet… so there is some complexity with that. Sending on an action makes more sense. But my point is, we need to think through a minimally viable, real-world workflow :). But I think that would be a great first place to land. |
RequestBuilder
level
I'm back with more goodies :) I have taken inspiration from previous PR (#299). It was so easy to implement that I was surprised - current project structure is really pleasant to work with 👍 How it works? Whenever any file input changes, underlying files are being stored in live component (in A new action modifier is introduced - I have added a very simple (and ugly, but that's what happens when I touch Twig files) demo to ux.symfony.com just to showcase this. It would probably need some love if we want to show it publicly. Or you can check it out and experiment with it to see how things work and remove it. The bad news here is that I have no idea how to approach functional testing this (in JS, there is nothing to test in PHP as this is regular file upload with nothing added on top of it). As far as I gone digging I believe current test suite doesn't support adding files to mocked requests. Regarding failing tests in PHP - I have switched them from asserting status and context to expecting exceptions. For some reason underlying library in some cases unconditionally handles exceptions even when they are expected. And it fails only in some versions probably because the logic looks for exception page by class names that could've changed between versions (?). Seems weird to me, but the change makes them work and pass. I have also added a simple docs section. English is not my native language, so it surely needs a bit of love 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.
Looking good!
@kbond Thanks a lot for your help. I have adapted the docs to your suggestions and removed the |
@Lustmored Sorry for the delay - and thank you for the demo - SUPER useful. And yea, we'll be able to think of a nice example for a final demo and make it pretty - nothing to worry about now. You're right that it works really smoothly! I have two questions: A) How are/should the files be "cleared? For example, in your demo, imagine that we DO process the file uploads in the ![]() And so also, every time you trigger the B) I added a normal string Thanks! |
Regarding your questions: A) The file should not be sent again and again - changing the field triggers queuing it for upload and when it is being sent - it's removed from |
that’s very nice!
I think so, yes. Let’s think about the use-cases. I MAY have a situation where, after I process the upload, I don’t even render the file field anymore. That use-case, I think, already works wonderfully. The other case is that I DO keep the file field… in case you want to upload again. My thinking is that if we don’t clear that field for them, then they’ll be stuck with this filled-in field with no way to clear it. So yes, unless I’m not thinking of some angle or use-case, let’s proceed with clearing when they are sent :) thank you! |
I agree with clearing the field. |
7be946d
to
8cc63ec
Compare
So with a bit of rewiring the inputs are now cleared and I made sure the same files aren't sent over again (they were actually as I missed clearing one queue) 👍 |
@Lustmored I just rebased and made some tweaks to the demo. But I think this is ready to go as a V1 - it works great! We DO need some documentation - do you want to try that? Also, here are the rough spots / questions I'm thinking about. These aren't blockers, but these are not as smooth as I'd like: A) How would it look to use this in a Symfony form? Perhaps we should create a 2nd demo that submits to a B) I added validation, but it looks terrible (not your fault). Previously, you were storing the Cheers! |
@weaverryan Regarding the documentation - I think I'm not the best person for the job, but given some guide points about what to document and how to approach it, I can surely give it a try 👍 Regarding your points - both are excellent and valid points. And also a reason I wanted to start small, as both the topics can be approached separately (maybe even others will hop in to help) once we have any kind of working upload support. But I have some thought, so here they are: A) Regarding forms - I believe one change that could be useful would be to automatically submit all form files on B) For that problem I believe @kbond filesystem approach could be the best way to go. It solves many (if not all) problems regarding hydration/temporary storage/validation and so on. Before working on this PR I was working with him on bringing functions useful for live components into the library, so that there is a chance it will be very pleasant combo out of the box. That's what I intent to experiment with after merging this PR and see where it goes. To be honest I have no vision of scope required to achieve easy work with files without that library. |
That's a really interesting idea. Thinking out loud, we would need to:
Fair enough. Validation is ugly, but not impossible. So let's move forward. I'm going to merge this and write some docs. My hope is that we (I'll definitely need your lead on this) can continue to push this forward as this v1 is functional, but not what either of us want in the end! Thanks! |
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 all for merging this and building on it!
Thank you @Lustmored! |
Hello and thanks for the work! I tried to upload file with version 2.9.1 (it should support files) but an error appears in the console saying |
The version number in the docs is wrong, it should be 2.11 instead. I guess it was forgotten as the PR was likely written when 2.9 was not released yet. |
Thanks, so there is way for this change to work on PHP 8.0? |
No, as from v2.10 all UX packages require PHP 8.1. |
Would it be really possible to backport just the files upload to the 2.9x branch? Thanks again. |
It would not. It was rather big effort with breaking changes involved to achieve the goal, so backporting is unfortunately not an option here. |
Ok, thanks! Are there any workarounds or different strategies for uploading files along with a form in a live component? |
I don't think there is something simple. You would have to manage file upload by another stimulus controller within a live component and update some hidden field with it's value so that component handles just raw form data. With 2.9 data transfer method live components simply aren't capable of even receiving files alongside other data. |
Maybe I could create a hidden field with a base64 version of the file taken from a dummy input (so it can be uploaded). Then I would need to manually validate the form and the file, convert and save it. |
…support (jmsche) This PR was merged into the 2.x branch. Discussion ---------- [LiveComponent][Docs] Fix version added for file uploads support | Q | A | ------------- | --- | Bug fix? | no | New feature? | no | Tickets | N/A | License | MIT File uploads support for Live Components was introduced with v2.11, however the docs mention v2.9 instead. See #834 (comment) Commits ------- 16b83ec [LiveComponent][Docs] Fix version added for file uploads support
…support (jmsche) This PR was merged into the 2.x branch. Discussion ---------- [LiveComponent][Docs] Fix version added for file uploads support | Q | A | ------------- | --- | Bug fix? | no | New feature? | no | Tickets | N/A | License | MIT File uploads support for Live Components was introduced with v2.11, however the docs mention v2.9 instead. See symfony/ux#834 (comment) Commits ------- 16b83ec0 [LiveComponent][Docs] Fix version added for file uploads support
Getting back on the file uploading front I've decided to go slowly this time, as in the end it should allow us to get basic support faster and then build up on it, without determining all the little details of both backend and frontend implementations upfront.
This PR aims at enabling frontend tools to send files back to the server. It does not add file upload functions to the live controller or handle the files on the backend. Instead it focuses only on changes that are essential to get any kind of file upload in the future.
What exactly is done here:
RequestBuilder
gets an additional parameter (files
) that takes a map ofFileList
objects that should be uploaded with the request (and the keys by which they should be available). This additional parameter is added as an empty map everywhere right now.data
key.RequestBuilder
tests are adapted to the changes and tests for appending files to the request are added.LiveComponentSubscriber
is adapted to read data from JSON-encodeddata
parameter instead of request content (in case of a POST request), without any further functional changes.Two tests are failing for me locally (and in 2 seemingly random CI configurations), while logic tells me they shouldn't and I'm unable to find the cause right now. Help would be appreciated.
I would love to get that first stepping stone finalized and merged before further work. It is a simple change, but has strong implications, therefore getting those out of the way will make further work much more comfortable. Also - splitting the work will hopefully make it easier for you to review.