Paginate results with Vue.Js / Inertia.js and Laravel - laravel

I'm trying to paginate data from Laravel in Vue.Js. I'm using Inertia.js as well.
In my Laravel Controller I have:
$data['participants'] = User::with('groups')->select('id', 'name')->paginate(2);
return inertia('Dashboard/Participants', $data);
This outputs my two users in a row well in Vue. I am also expecting the links object to use for pagination. I don't see this in my Vue props.
If I inspect my Vue props I have the following object:
participants:Object
current_page:1
data:Array[2]
first_page_url:"http://localhost:3000/dashboard/participants?page=1"
from:1
last_page:51
last_page_url:"http://localhost:3000/dashboard/participants?page=51"
next_page_url:"http://localhost:3000/dashboard/participants?page=2"
path:"http://localhost:3000/dashboard/participants"
per_page:2
prev_page_url:null
to:2
total:101
If I:
dd($data['participants']->links());
in the controller I can see:
Illuminate\View\View {#316 ▼
#factory: Illuminate\View\Factory {#310 ▶}
#engine: Facade\Ignition\Views\Engines\CompilerEngine {#328 ▶}
#view: "pagination::bootstrap-4"
#data: array:2 [▶]
#path: "/Users/ejntaylor/Documents/Laravel/motional/vendor/laravel/framework/src/Illuminate/Pagination/resources/views/bootstrap-4.blade.php"
}
I have been looking at PingCRM for inspiration but without luck - I've referenced in the link. Help appreciated.

It would seem the default Laravel pagination does not work with Inertia.JS so you must head to your AppServiceProvider.php file and add the following to get pagination to work.
This is taken from PingCRM
protected function registerLengthAwarePaginator()
{
$this->app->bind(LengthAwarePaginator::class, function ($app, $values) {
return new class(...array_values($values)) extends LengthAwarePaginator {
public function only(...$attributes)
{
return $this->transform(function ($item) use ($attributes) {
return $item->only($attributes);
});
}
public function transform($callback)
{
$this->items->transform($callback);
return $this;
}
public function toArray()
{
return [
'data' => $this->items->toArray(),
'links' => $this->links(),
];
}
public function links($view = null, $data = [])
{
$this->appends(Request::all());
$window = UrlWindow::make($this);
$elements = array_filter([
$window['first'],
is_array($window['slider']) ? '...' : null,
$window['slider'],
is_array($window['last']) ? '...' : null,
$window['last'],
]);
return Collection::make($elements)->flatMap(function ($item) {
if (is_array($item)) {
return Collection::make($item)->map(function ($url, $page) {
return [
'url' => $url,
'label' => $page,
'active' => $this->currentPage() === $page,
];
});
} else {
return [
[
'url' => null,
'label' => '...',
'active' => false,
],
];
}
})->prepend([
'url' => $this->previousPageUrl(),
'label' => 'Previous',
'active' => false,
])->push([
'url' => $this->nextPageUrl(),
'label' => 'Next',
'active' => false,
]);
}
};
});
}

I had a similar problem and noticed the basic links() wouldn't work with Inertia and Vue so i made a simple Vue component where I use the Paginator Object I get from the Inertia render method and it works very well for me without the ServiceProvider workaround.
I can just use
public function index()
{
return Inertia::render('MyUserList',[
'paginator' => User::paginate(10)
]);
}
as I normally would.
Then i bind the paginator Object to my Vue Component so I can leverage it to create a basic Paginator component. This way I can use and reuse the <Paginator :paginator="paginator" /> anywhere I want.
<template>
<Paginator :paginator="paginator" />
</template>
<script>
import Paginator from "#/Components/Paginator";
export default {
props: {
paginator: Object
},
}
</script>
I also created a package so you can pull it in via composer if you want.
See https://github.com/svnwa/InertiaVuePaginator for more Info and the "Components" folder for the paginator component i created.
I hope it helps others in the future :)

Related

My laravel-livewire is returning 404 not found

I am designing web application in laravel which I used livewire in a module, but when ever I perform any action in my livewire blade component example wire:click="action" its returns 404 NOT FOUND
My config/modules-livewire.php
'custom_modules' => [
'Chat' => [
'path' => base_path('libraries/Chat'),
'module_namespace' => 'Libraries\\Chat',
// 'namespace' => 'Http\\Livewire',
// 'view' => 'Resources/views/livewire',
// 'name_lower' => 'chat',
],
],
<?php
My livewire component codes
namespace Modules\Student\Http\Livewire\Pages;
use Livewire\Component;
class RoutineLivewire extends Component
{
public $test = null;
public function me($v){
$this->test = $v;
}
public function render()
{
return view('student::livewire.pages.routine-livewire');
}
}
My blade code
<div>
<button wire:click="me('ibrahim')">click me</button>
{{$test}}
HELLO ROUTINE.
</div>

Laravel Livewire Dynamic Form Data Not Working

Quite new to Laravel Livewire. I'm trying to create a dynamic form for application but I couldn't quite understand how to attach additional data when storing.
The user selects the instructor(faculty_id), schoolyear(sy) and semester(sem). And new schedule and option to add more rows()
This is from my controller store() method
public function store()
{
$order = Emp_sched::create([
'faculty_id'=>$this->faculty_id,
'sem'=>$this->sem,
'sy'=>$this->sy,
]);
foreach ($this->createScheds as $sched) {
$order=(['corsdes' => $sched['corsdes']],
['c_time' => $sched['c_time']], ['day' => $sched['day']], ['room' => $sched['room']]);
}
return 'Schedules Saved!';
}
You must call Model::create inside loop
public function store()
{
foreach ($this->createScheds as $sched) {
$createArray = array_merge([
'faculty_id' => $this->faculty_id,
'sem' => $this->sem,
'sy' => $this->sy,
],[
'corsdes' => $sched['corsdes'],
'c_time' => $sched['c_time'],
'day' => $sched['day'],
'room' => $sched['room'],
]);
Emp_sched::create($createArray);
}
return 'Schedules Saved!';
}

How to show Order count of logged in user across the views?

I want to show the order count on top of the navbar across views. I know about Shared Views but the problem I am having that Auth::check() is always false here even I am logged in. How can I make the following happen in boot method of AppServiceProvider? Is there some other way to do it or I have to rely on Session vars?
if(Auth::check()) {
$orders = Order::where( [ 'user_id' => Auth::user()->id, 'status' => 0 ] )->get();
dd($orders);
}
In your App\Providers\AppServiceProvider, in the boot method:
public function boot()
{
view()->composer('*', function ($view) {
if(Auth::check()) {
$order_count = Order::where( [ 'user_id' => Auth::user()->id, 'status' => 0 ] )->count();
$view->with('order_count', $order_count);
}
});
}

Laravel: How to get model instance after create that has a global scope

I struggle to get the model instance with global scopes.
Normally, if you write:
$offer = Offer::createNew();
with
public static function createNew()
{
return static::create([
'user_id' => auth()->id(),
'reviewed' => false
]);
}
You would get the model instance. But now I've added a global scope and cannot get it to work.
The model is "nearly" empty as you expect because in my case I want only to get Offers that are reviewed. However, if I add:
public static function createNew()
{
return static:::withoutGlobalScopes()->create([
'user_id' => auth()->id(),
'reviewed' => false
]);
}
I get a result from a limited model that only contains these attributes:
#attributes: array:5 [
"user_id" => 1
"reviewed" => false
"updated_at" => "2018-09-24 11:48:27"
"created_at" => "2018-09-24 11:48:27"
"id" => 2
]
But my model has obviously more attributes than that. If I add get(), I'm only getting
Illuminate\Database\Eloquent\Collection {#1635
#items: []
}
So how would you get the model with create when having a global scope?
Edit
My first workaround looks like this:
public static function createNew()
{
$model = static::create([
'user_id' => auth()->id(),
'reviewed' => false
]);
return static::withoutGlobalScopes()->find($model->id);
}
Edit 2
My Globalscope looks like this:
class FoodScope implements Scope
{
public function apply(Builder $builder, Model $model)
{
$builder->where('reviewed', true)
->where('paused', false);
}
}
This behavior is not caused by the global scope.
Use ->fresh() when you want to get the other attributes:
public static function createNew()
{
return static::create([
'user_id' => auth()->id(),
'reviewed' => false
])->fresh();
}

Laravel Validation with vue js

i want to post ajax request using vue-resource this.$http.post request. it worked perfectly fine if i passed all validation rules but i want to get some validations if it fails. so far i keep getting 500 error if i don't fill out some input fields. it's hard for me to debug the error because it didn't appeared on the network tab.
here's what i've done so far
//my modal component
<script>
export default {
props: ['show'],
data() {
return {
input: {
id: '',
name: '',
address: '',
email: ''
},
errorInputs: {}
}
},
methods: {
createStudent() {
this.$http.post('/students', this.$data.input)
.then((response) => {
alert('added new row!)
}, (response) => {
console.log(response.data);
});
}
}
}
</script>
// my controller
public function store(Request $request) {
$validator = $this->validate($request,[
'id' => 'required',
'name' => 'required|unique:students',
'email' => 'required|unique:students|email',
'address' => 'required',
]);
if($validator->passes()){
Student::create($request->all());
return response()->json([], 201);
}
$errors = json_decode($validator->errors());
return response()->json([
'success' => false,
'message' => $errors
],422);
}
any helps and references would be appreciated. i am using laravel 5.3 and vue js 2
$this->validate() returns 422 error response alongside your validation errors, so you should get those errors in then() second callback (like you do now). Your vue component body should be like this:
{
data() {
// ...
},
createStudent() {
this.$http
.post('/students', this.input)
.then(this.handleSuccess, this.handleError)
},
handleSuccess(res) {
alert('student created')
},
handleError(res) {
if (res.status === 422) {
this.errorInputs = res.body
} else {
alert('Unkown error!')
}
}
}
Remember to add v-model="input.fieldName" properties to your inputs.
Remember to include your session token along with your post, unless of course you are disabling csrf tokens for that route.
Since Laravel 5.1 you can disable this in your verifytoken middleware
<?php namespace App\Http\Middleware;
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as ...
class VerifyCsrfToken extends ... {
protected $except = [
'payment/*',
];
}

Resources