Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

RomainMazB
Copy link
Contributor

1️⃣ Is this something that is wanted/needed? Did you create an issue / discussion about it first?
Yes, it's a wanted feature, multiple PR has previously opened and closed due to API breaking change.

2️⃣ Does it contain multiple, unrelated changes? Please separate the PRs out.
No, but it's an extension of a previous mandatory PR #2663 . I've spitted them as even if this one is not merged, the previous one is still a nice feature to have.

3️⃣ Does it include tests, if possible? (Not a deal-breaker, just a nice-to-have)
Yes, unit and browser

4️⃣ Please include a thorough description of the improvement and reasons why it's useful.
This PR gives us the ability to manage multiple pagination in a single component, using different properties to store the different page state and an array to store the name of each paginator:

class ComponentWithMultiplePaginationStub extends Component
{
    use WithPagination;

    public $postsPage = 1;

    protected $paginatorNames = ['page', 'postsPage'];

    public function render()
    {
        return view('show-name'[
            'posts' => Post::paginate(3, ['*'], 'postsPage'),
            'messages' => Message::paginate(3), // Will use the default $page attribute
        ]);
    }
}

The initial API is not broken, we now can just pass an additional parameter to select the paginator we want to increment/decrement or change:

// Old API still work, it still modify the $page attribute
<button wire:click="previousPage"></button>
<button wire:click="nextPage"></button>
<button wire:click="goToPage(3)"></button>

// New API for multiple paginator component:
<button wire:click="previousPage('postsPage')"></button>
<button wire:click="nextPage('postsPage')"></button>
<button wire:click="goToPage(3, 'postsPage')"></button>

Ping @mokhosh as I felt he was in demand of this feature.

5️⃣ Thanks for contributing! 🙌
Hopefully it will help some of us :)

@mokhosh
Copy link
Contributor

mokhosh commented Mar 25, 2021

Thanks. Yes I've resorted to multiple child components and a custom pagination trait for now.

I wish you didn't have to declare the variable and have it in an array too. It would be nice to just have the array.

That's what I tried to achieve haphazardly in this PR #1997

@RomainMazB
Copy link
Contributor Author

RomainMazB commented Mar 25, 2021

Hi, yes I've done this way instead of storing the pagination into an associative array, like you've done in your PR because :

  • this would have make the $page property useless
  • even if we could redefine a "page" index into the array, it would have been more verbose to increment it:
goToPage(3, 'page')
nextPage('page')

Now that the tests are passing, if Caleb feels good about this PR, we could talk about some syntax sugar to improve the DX.

Using magic method we could even imagine, without an array to have this kind of syntax:

// Old API still work, modify the $page property
<button wire:click="previousPage"></button>
<button wire:click="nextPage"></button>
<button wire:click="goToPage(3)"></button>

// New API modifying custom page property
<button wire:click="previousPostsPage"></button>
<button wire:click="nextPostsPage"></button>
<button wire:click="goToPostsPage(3)"></button>

These methods would proxy the modification to the wanted properties, without the need to define them into an array:

class ComponentWithMultiplePaginationStub extends Component
{
    use WithPagination;

    public $postsPage = 1;

    public function render()
    {
        return view('show-name'[
            'posts' => Post::paginate(3, ['*'], 'postsPage'),
            'messages' => Message::paginate(3), // Will use the default $page attribute
        ]);
    }

The only down side to this it the fact that it creates a breaking change for those who've created custom pagination system or just goTo[...]Page methods like goToLeftPage

Maybe this sugar syntax could be added into 3.0

@mokhosh
Copy link
Contributor

mokhosh commented Mar 25, 2021

even if we could redefine a "page" index into the array, it would have been more verbose to increment it

Couldn't that be done with default values for the page parameter?

@RomainMazB
Copy link
Contributor Author

RomainMazB commented Mar 25, 2021

You mean default value to ['page'] for the $paginatorNames property?

Yes it could be done that way but this would create a breaking change IMO for those who rely on the $page property in custom code as the $page property will never get incremented anymore:

class ComponentWithPagination extends Component
{
    use WithPagination;

    // protected $paginatorNames = ['page' => 1]; // Default from WithPagination trait, just here for clarity

    public function render()
    {
        return view('show-name', [ 'posts' => Post::paginate(3) ]);
    }
}
<span>
You are now on the page {{$page}}
</span>

@mokhosh
Copy link
Contributor

mokhosh commented Mar 25, 2021

We could have the default parameter values, plus a default $paginatorNames = ['page' => 1].

@RomainMazB
Copy link
Contributor Author

RomainMazB commented Mar 25, 2021

In such case, how to know which property should be changed?

Another way to do would have to explicitly define the component to handle multiple pagination, or to not define the $paginatorNames in the trait and only define it in the instantiated components which need multiple pagination.

Doing so we could proxy the method to the $page property if the $paginatorNames is not defined and to the $paginatorNames['page'] if it is.

Doing so, the developers explicitly know they can't use the {{ $page }} variable into view.

@mokhosh
Copy link
Contributor

mokhosh commented Mar 25, 2021

I'm not sure if I understood the question, but how about we change whatever they specify, and change page as fallback if they don't specify a page name?

@RomainMazB
Copy link
Contributor Author

The question was about the previous comment:

We could have the default parameter values, plus a default $paginatorNames = ['page' => 1]

How do we know which "page storage" to increment if the WithPagination trait is defined like so:

trait WithPagination {
    public $page = 1;

    public $pageNames = ['page' => 1];
}

but how about we change whatever they specify, and change page as fallback if they don't specify a page name?

I think this is what I just exposed before, we could have something like this:

trait WithPagination {
    public $page = 1;

    public function setPage($page, $pageName = 'page') {
        if ($pageName !== 'page' && property_exists($this, 'paginatorNames')) {
              $this->paginatorNames[$pageName] = $page;
        } else {
           $this->page = $page;
        }
    }
}

Doing so, the developer can still use the {{ $page }} into his view and can define paginatorNames to handle additional pagination.

@mokhosh
Copy link
Contributor

mokhosh commented Mar 25, 2021

No you have this:

trait WithPagination {
    public $pageNames = ['page' => 1];
}

and only change the array, and set public properties based on the array, and default to the name page in methods:

public function goToPage($page, $pageName = 'page')

@RomainMazB
Copy link
Contributor Author

RomainMazB commented Mar 25, 2021

The problem here is the fact that it breaks the initial Laravel and livewire implementation and introduce a breaking change.

I understood that you've built many components to workaround your previous PR, so you've created two components that rely on the native $page property.

Imagine somewhere in your component view, you displayed the number of the page:

<span>You are on the {{ $page }} page</span>

This code won't work anymore as this property won't exist anymore, the refactor code would be:

<span>You are on the {{ $pageNames['page'] }} page</span>

This is why we must keep the $page property whatever it cost, even if it's not the most elegant way to do, this is mandatory for this PR to get merged.

@mokhosh
Copy link
Contributor

mokhosh commented Mar 25, 2021

I haven't been able to communicate what i have in mind. It's not breaking at all. You can set public properties in php dynamically without declaring them in the class.

We just need to find the right hook to initialize all the page number public properties based on the array before anyone tries to access them.

The array is only the blueprint for dynamically setting the public properties.

@RomainMazB
Copy link
Contributor Author

We just need to find the right hook to initialize all the page number public properties based on the array before anyone tries to access them.
The array is only the blueprint for dynamically setting the public properties.

Ok, I think I got you.

This should work but I dislike to work with dynamic properties (and so does my IDE 😄), but I'm totally open to the conversation:

As you correctly said, we should search for the perfect hook to make sure the property is not accessed before its declaration, and actually we can't make sure of that: our best chance is probably the initializeWithPagination, but imagine someone use - for any reason - the $page variable into the component's constructor, it would suddenly fail because the initializeWithPagination method wouldn't have been called.

AFAIK, we cannot make sure someone didn't use the variable before any hook we could register from the trait.

@mokhosh
Copy link
Contributor

mokhosh commented Mar 26, 2021

I understand this is a trade off.

Elegance + accounting for 99% of the usage. Or ugly duplicate code + 100% of the theoretically possible cases. (excuse the emphatic language)
About the IDE thing, we're already working with Laravel ecosystem so having 100% IDE integration is irrelevant.

@RomainMazB
Copy link
Contributor Author

No problem, we are here to debate and that's exactly why I pinged you :)

For now I'm working on something else, so I can't dive into this for few days. But I'll modify the PR if needed.

Maybe some feedback here @calebporzio to know what would be acceptable?

@calebporzio
Copy link
Collaborator

Thanks for tackling this. Going to close this for now. More info here: #2727

@RomainMazB RomainMazB deleted the impl-multi-pagination branch April 26, 2021 19:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants