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

[Turbo] Add Helper/TurboStream::append() et al. methods #2196

Open
wants to merge 1 commit into
base: 2.x
Choose a base branch
from

Conversation

nicolas-grekas
Copy link
Member

@nicolas-grekas nicolas-grekas commented Sep 23, 2024

Q A
Bug fix? no
New feature? yes
Issues -
License MIT

For thoughts, this might help, WDYT?

return new TurboResponse(TurboStream::append($target, $html));

(Note that I'm also wondering about having a twig component to render streams from the twig side, stay tuned)

@carsonbot carsonbot added Feature New Feature Turbo Status: Needs Review Needs to be reviewed labels Sep 23, 2024
@nicolas-grekas nicolas-grekas force-pushed the turbo-helper branch 2 times, most recently from 1fdcd34 to 1e0a0cc Compare September 23, 2024 10:43
Copy link
Collaborator

@Kocal Kocal left a comment

Choose a reason for hiding this comment

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

I'm not a Turbo user, but I think it can be useful yes.

Just, can you add some documentation for this new feature please? And also fix failing checks (IMO we can ignore the error found by PHPStan).

Thanks! :)

@nicolas-grekas
Copy link
Member Author

Updated

@rvmourik
Copy link

Great addition imho, but what about multiple streams in a single response? Or is that the Twig solution you are talking about @nicolas-grekas?

return new TurboResponse(TurboStream::append($target, $html));

Regarding DX it is not optimal to have this I guess:

return new TurboResponse(TurboStream::append($target, $html) . TurboStream::replace($target2, $html2));

Is this something to consider?

@smnandre
Copy link
Collaborator

Thanks a lot @nicolas-grekas! More comments tomorrow :)

@nicolas-grekas
Copy link
Member Author

@rvmourik concatenation FTW indeed. I don't see a better option. In the end, any alternative would just be a fancy way to do concatenation.

@rvmourik
Copy link

@rvmourik concatenation FTW indeed. I don't see a better option. In the end, any alternative would just be a fancy way to do concatenation.

@nicolas-grekas thanks for the quick reply.

I know the TurboResponse is extending the base Response class but if we take a look at the JsonResponse there is also an option to pass an array or a mixed value, when it is mixed in the end is converts it to an ArrayObject. Could the TurboResponse do something similar, always convert the passed stream(s) to be a collection of streams and processing them before passing it to the base Response?

Maybe I am overthinking it? 😇


use Symfony\Component\HttpFoundation\Response;

class TurboResponse extends Response
Copy link
Collaborator

Choose a reason for hiding this comment

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

TurboStreamResponse ?

Comment on lines +18 to +19
public function __construct(?string $content = '', int $status = 200, array $headers = [])
{
Copy link
Collaborator

Choose a reason for hiding this comment

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

Both HttpFoundation StreamedResponse and StreamedJsonResponse have specialized constructors.

What if we made TurboStream a stringable object and have this kind of signature

/**
 * @param string|Stringable|iterable<string|Stringable> $stream
 */ 
public function __construct(string|iterable $streams = '', int $status = 200, array $headers = [])
{

(Genuine suggestion, not sure what would be the best DX here)


class TurboResponse extends Response
{
public function __construct(?string $content = '', int $status = 200, array $headers = [])
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
public function __construct(?string $content = '', int $status = 200, array $headers = [])
/**
* @param array<string, string|string[]> $headers
*/
public function __construct(?string $content = '', int $status = 200, array $headers = [])

To please the CI :)

@javiereguiluz
Copy link
Member

javiereguiluz commented Sep 24, 2024

If we decide to add some helpers to make using Turbo more user-friendly, what if we all DX and provide a fluent interface to not deal with any internal details?

return TurboResponse()
    ->append($targetId, $html)
    ->prepend($targetId, $html)
    ->replace($targetId, $html, $method = 'morph')
    ->update($targetId, $html, $method = 'morph')
    ->remove($targetId, $html)
    ->before($targetId, $html)
    ->after($targetId, $html)
    // useful if the action (append, prepend, etc.) is a PHP variable:
    ->stream($action, $targetId, $html)
;

@rvmourik your example now would look like this:

// before
return new TurboResponse(TurboStream::append($target, $html) . TurboStream::replace($target2, $html2));

// after
return new TurboResponse()
    ->append($target, $html)
    ->append($target2, $html2)
;

There's a final consideration, which is not covered either in the current proposal of this PR. In addition to target (the targetID), you can use targets (a CSS selector) to apply the stream to multiple targets (See https://turbo.hotwired.dev/handbook/streams#actions-with-multiple-targets).

Adding an optional targets param would look ugly in my opinion:

return TurboResponse()
    ->append(string|null $targetId, string|null $targets, string $html)
    ->prepend(string|null $targetId, string|null $targets, string $html)
    // ...

We could add a *All() series of methods:

return TurboResponse()
    ->append(string $targetId, string $html)
    ->appendAll(string $targets, string $html)

    ->prepend(string $targetId, string $html)
    ->prependAll(string $targets, string $html)
    // ...

What do you think?

@rvmourik
Copy link

If we decide to add some helpers to make using Turbo more user-friendly, what if we all DX and provide a fluent interface to not deal with any internal details?

return TurboResponse()
    ->append($targetId, $html)
    ->prepend($targetId, $html)
    ->replace($targetId, $html, $method = 'morph')
    ->update($targetId, $html, $method = 'morph')
    ->remove($targetId, $html)
    ->before($targetId, $html)
    ->after($targetId, $html)
    // useful if the action (append, prepend, etc.) is a PHP variable:
    ->stream($action, $targetId, $html)
;

@rvmourik your example now would look like this:

// before
return new TurboResponse(TurboStream::append($target, $html) . TurboStream::replace($target2, $html2));

// after
return new TurboResponse()
    ->append($target, $html)
    ->append($target2, $html2)
;

There's a final consideration, which is not covered either in the current proposal of this PR. In addition to target (the targetID), you can use targets (a CSS selector) to apply the stream to multiple targets (See https://turbo.hotwired.dev/handbook/streams#actions-with-multiple-targets).

Adding an optional targets param would look ugly in my opinion:

return TurboResponse()
    ->append(string|null $targetId, string|null $targets, $html)
    ->prepend(string|null $targetId, string|null $targets, $html)
    // ...

We could add a *All() series of methods:

return TurboResponse()
    ->append(string $targetId, $html)
    ->appendAll(string $targets, $html)

    ->prepend(string $targetId, string $html)
    ->prependAll(string $targets, $html)
    // ...

What do you think?

I think this is a good suggestion, maybe in a future PR the same can be done with adding a helper for a regular turbo frame. I find my self often doing one of the following:

a) creating a twig file only with the frame and HTML in it
b) manually building a response like in the helper in this PR en load the twig file between the tag in the response.

Thanks all.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Feature New Feature Status: Needs Review Needs to be reviewed Turbo
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants