jmacdiarmid's avatar

Unable to get Livewire v2 pagination working.

I have a companies component and been trying to get this to work for what seems like at least a week.

[rant]The Laravel docs are good for some things however it's missing a lot including more FULL examples, not vague partial examples. I've run into this many times and I'm left to seek out other sources for information. [/rant]

Anyway, it looks like I need to use a ResourceCollection . The only thing is that it's not working either so here's what my code looks like. Could someone help me fix this please?

Companies Livewire Component : Companies.php

namespace App\Http\Livewire\Admin;

use App\Http\Resources\CompanyResource;
use App\Models\Company;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Pagination\LengthAwarePaginator;
use Livewire\Component;
use Livewire\WithPagination;
use Log;


class Companies extends Component
{
    use WithPagination;

    /**
     * Company List Properties
     */
    public int $currentPage = 1;
    public string $success = '';
    public int $perPage = 10;
    public bool $showModal = false;
    public bool $createOrdEditModal = false;


    public $companies = null;
    public ?Company $company = null;
    public int $companyId = 0;
    public string $name     = '';
    public string $street_address   = '';
    public string $bldg_ste_rm      = '';
    public string $city             = '';
    public string $state            = '';
    public string $postal_code      = '';
    public string $contact_name     = '';
    public string $contact_phone    = '';
    public string $contact_email    = '';
    public string $webmaster_phone  = '';
    public string $webmaster_email  = '';
    public string $landing_page_url = '';

    public array $pages = [
        1 => [
            'heading' => 'Company Information',
            'subheading' => 'Enter Company Information To Get Started.',
        ],
        2 => [
            'heading' => 'Company Contact Information',
            'subheading' => 'Create Company Contact Information',
        ],
    ];

    private array $validationRules = [
        1 => [
            'company_name'      => ['required'],
            'street_address'    => ['required'],
            'bldg_ste_rm'       => ['required'],
            'city'              => ['required'],
            'state'             => ['required'],
            'postal_code'       => ['required'],
            'landing_page_url'  => ['required'],
        ],
        2 => [
            'contact_name'      => ['required'],
            'contact_phone'     => ['required'],
            'contact_email'     => ['required'],
            'webmaster_phone'   => ['required'],
            'webmaster_email'   => ['required'],
        ],
    ];

    public function render()
    {
        $companies = CompanyResource::collection(Company::paginate($this->perPage));
        $this->companies = response()->json($companies->resource);
        return view('livewire.admin.companies');
    }

    public function updated(): void
    {

    }

    public function refreshList(): void
    {
    }

    public function create(): void
    {
        $this->resetInputFields();
        $this->createOrdEditModal = true;
        $this->openModal();
    }

    public function openModal(): void
    {
        $this->showModal = true;
        $this->resetErrorBag();
        $this->resetValidation();
    }

    public function closeModal(): void
    {
        $this->showModal = false;
        $this->createOrdEditModal = false;
    }

    private function resetInputFields(): void
    {
        $this->companyId = 0;
        $this->name             = '';
        $this->street_address   = '';
        $this->bldg_ste_rm      = '';
        $this->city             = '';
        $this->state            = '';
        $this->postal_code      = '';
        $this->contact_name     = '';
        $this->contact_phone    = '';
        $this->contact_email    = '';
        $this->webmaster_phone  = '';
        $this->webmaster_email  = '';
        $this->landing_page_url = '';
    }

    public function goToNextPage(): void
    {
        $this->validate($this->validationRules[$this->currentPage]);
        $this->currentPage++;
    }

    public function goToPreviousPage(): void
    {
        $this->currentPage--;
    }

    public function resetSuccess(): void
    {
        $this->reset('success');
    }

    public function submit(): void
    {
        $rules = collect($this->validationRules)->collapse()->toArray();

        $this->validate($rules);

        $company = Company::updateOrCreate([
            'name'              => $this->name,
            'street_address'    => $this->street_address,
            'bldg_ste_rm'       => $this->bldg_ste_rm,
            'city'              => $this->city,
            'state'             => $this->state,
            'postal_code'       => $this->postal_code,
            'landing_page_url'  => $this->landing_page_url,
            'contact_name'      => $this->contact_name,
            'contact_phone'     => $this->contact_phone,
            'contact_email'     => $this->contact_email,
            'webmaster_phone'   => $this->webmaster_phone,
            'webmaster_email'   => $this->webmaster_email,
        ]);

        $this->reset();
        $this->resetValidation();

        $this->success = 'Company created successfully!';
    }

    public function edit(Company $company): void
    {
        $this->company = $company;
        $this->createOrdEditModal = true;
        $this->openModal();
    }

    public function view(Company $company): void
    {
        $this->company = $company;
        $this->openModal();
    }

    public function delete(Company $company): void
    {
        //$this->company_id = $id;
        //$company = Company::find($id);
        $company->delete();
//        $this->refreshList();
        session()->flash('message', 'Company deleted successfully.');
    }
}

App/Http/Resources/CompanyResource.php

namespace App\Http\Resources;

use Illuminate\Http\Resources\Json\ResourceCollection;

class CompanyResource extends ResourceCollection
{

    public function toArray($request): array
    {
//        return parent::toArray($request);

        return [
            'id'                    => $this->id,
            'name'                  => $this->name,
            'street_address'        => $this->streetAddress,
            'bldg_ste_rm'           => $this->bldg_ste_rm,
            'city'                  => $this->city,
            'state'                 => $this->state,
            'postal_code'           => $this->postcode,
            'landing_page_url'      => $this->url,
            'contact_name'          => $this->contact_name,
            'contact_phone'         => $this->contact_phone,
            'contact_email'         => $this->contact_email,
            'webmaster_phone'       => $this->webmaster_phone,
            'webmaster_email'       => $this->webmaster_email
        ];
    }
}
0 likes
9 replies
tykus's avatar

Anyway, it looks like I need to use a ResourceCollection

Why do you believe that; you are working with Blade, not with an API?

jmacdiarmid's avatar

Why?? Because the Company list should be visually rendered using the companies.blade.php template. Why would I use an api to render a company list? Eventually it will be using a datatable.

tykus's avatar

This is just wrong; you are setting the companies property on the Component to a Response???

$this->companies = response()->json($companies->resource);
return view('livewire.admin.companies');

Eventually it will be using a datatable.

A Livewire Datatable? Does not require JSON.

jmacdiarmid's avatar

Thanks. I appreciate your insight. Truth be told, I'm still in the process of learning Laravel and was trying to figure it out on my own. So now I'm looking for a bit of assistance that will give me further insight and nudge me in the right direction. Allow me to reiterate. My end goal is to create a "crud" component that will list all companies, as well as provide action buttons in the last column for view, edit and delete on each record. Another requirement is to have the list paginated. I can get it to work without pagination.

jmacdiarmid's avatar
jmacdiarmid
OP
Best Answer
Level 2

Finally! I'm posting this here for anyone else that runs into this massive headache. I followed the solution on this webpage: https://www.infyom.com/blog/how-to-build-pagination-with-laravel-livewire

The way this developer describes the solution is this:

use Livewire\WithPagination;
use Livewire\Component;
use App\User;

class UserListing extends Component 
{
	use WithPagination;

	public function render() 
    {
        return view('livewire.users.index', [
              'users'  => User::paginate(10);
        ]);
    }
}

In the blade template:

@forelse ($users as $user)

	  {{ $user->name }}

@empty

      {{ __('No Data Found')  }}

@endforelse

{{ $users->links() }}

Granted, this is simple, but that's all I'm looking for at the moment.

Hopefully this will help someone.

tykus's avatar

Yeah... no Eloquent Resource necessary like I said.

This is not really a Livewire-specific thing either... it is basic Laravel

jmacdiarmid's avatar

Still, I'm confused. Since the following code returns an instance of Illuminate\Pagination\LengthAwarePaginator

 return view('livewire.users.index', [ 'users'  => User::paginate(10) ]);

What's the difference between that and the following:

use Illuminate\Pagination\LengthAwarePaginator;
use Livewire\WithPagination;

public LengthAwarePaginator $users;

class UserList extends Component 
{
		use WithPagination;
      
       public function render()
       {
              $this->users = Users::paginate(20);  <-- returns the same LengthAwarePaginator but does not work. 
			  return view('livewire.users.index');	
       }

}

When trying the code above, I get the following error:

Livewire\Exceptions\PublicPropertyTypeNotAllowedException
Livewire component's [admin.userlist.index] public property [users] must be of type: [numeric, string, array, null, or boolean]. Only protected or private properties can be set as other types because JavaScript doesn't need to access them.

If I remove or comment out the public LengthAwarePaginator $users, everything works.

tykus's avatar

Livewire is limited by the types of public properties that components can have. https://laravel-livewire.com/docs/2.x/properties#important-notes A LengthAwarePaginator instance is not one of the allowed types

You need to pass the data as view data - just as you would a view returned from a controller

public function render()
{
	return view('livewire.users.index', ['users' => Users::paginate(20)]);
}

Please or to participate in this conversation.